deno.land / x / deno@v1.28.2 / ext / webstorage / lib.rs
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
// NOTE to all: use **cached** prepared statements when interfacing with SQLite.
use deno_core::error::AnyError;use deno_core::include_js_files;use deno_core::op;use deno_core::Extension;use deno_core::OpState;use rusqlite::params;use rusqlite::Connection;use rusqlite::OptionalExtension;use std::fmt;use std::path::PathBuf;
pub use rusqlite;
#[derive(Clone)]struct OriginStorageDir(PathBuf);
const MAX_STORAGE_BYTES: u32 = 10 * 1024 * 1024;
pub fn init(origin_storage_dir: Option<PathBuf>) -> Extension { Extension::builder() .js(include_js_files!( prefix "deno:ext/webstorage", "01_webstorage.js", )) .ops(vec![ op_webstorage_length::decl(), op_webstorage_key::decl(), op_webstorage_set::decl(), op_webstorage_get::decl(), op_webstorage_remove::decl(), op_webstorage_clear::decl(), op_webstorage_iterate_keys::decl(), ]) .state(move |state| { if let Some(origin_storage_dir) = &origin_storage_dir { state.put(OriginStorageDir(origin_storage_dir.clone())); } Ok(()) }) .build()}
pub fn get_declaration() -> PathBuf { PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("lib.deno_webstorage.d.ts")}
struct LocalStorage(Connection);struct SessionStorage(Connection);
fn get_webstorage( state: &mut OpState, persistent: bool,) -> Result<&Connection, AnyError> { let conn = if persistent { if state.try_borrow::<LocalStorage>().is_none() { let path = state.try_borrow::<OriginStorageDir>().ok_or_else(|| { DomExceptionNotSupportedError::new( "LocalStorage is not supported in this context.", ) })?; std::fs::create_dir_all(&path.0)?; let conn = Connection::open(path.0.join("local_storage"))?; // Enable write-ahead-logging and tweak some other stuff. let initial_pragmas = " -- enable write-ahead-logging mode PRAGMA journal_mode=WAL; PRAGMA synchronous=NORMAL; PRAGMA temp_store=memory; PRAGMA page_size=4096; PRAGMA mmap_size=6000000; PRAGMA optimize; ";
conn.execute_batch(initial_pragmas)?; conn.set_prepared_statement_cache_capacity(128); { let mut stmt = conn.prepare_cached( "CREATE TABLE IF NOT EXISTS data (key VARCHAR UNIQUE, value VARCHAR)", )?; stmt.execute(params![])?; } state.put(LocalStorage(conn)); }
&state.borrow::<LocalStorage>().0 } else { if state.try_borrow::<SessionStorage>().is_none() { let conn = Connection::open_in_memory()?; { let mut stmt = conn.prepare_cached( "CREATE TABLE data (key VARCHAR UNIQUE, value VARCHAR)", )?; stmt.execute(params![])?; } state.put(SessionStorage(conn)); }
&state.borrow::<SessionStorage>().0 };
Ok(conn)}
#[op]pub fn op_webstorage_length( state: &mut OpState, persistent: bool,) -> Result<u32, AnyError> { let conn = get_webstorage(state, persistent)?;
let mut stmt = conn.prepare_cached("SELECT COUNT(*) FROM data")?; let length: u32 = stmt.query_row(params![], |row| row.get(0))?;
Ok(length)}
#[op]pub fn op_webstorage_key( state: &mut OpState, index: u32, persistent: bool,) -> Result<Option<String>, AnyError> { let conn = get_webstorage(state, persistent)?;
let mut stmt = conn.prepare_cached("SELECT key FROM data LIMIT 1 OFFSET ?")?;
let key: Option<String> = stmt .query_row(params![index], |row| row.get(0)) .optional()?;
Ok(key)}
#[op]pub fn op_webstorage_set( state: &mut OpState, key: String, value: String, persistent: bool,) -> Result<(), AnyError> { let conn = get_webstorage(state, persistent)?;
let mut stmt = conn .prepare_cached("SELECT SUM(pgsize) FROM dbstat WHERE name = 'data'")?; let size: u32 = stmt.query_row(params![], |row| row.get(0))?;
if size >= MAX_STORAGE_BYTES { return Err( deno_web::DomExceptionQuotaExceededError::new( "Exceeded maximum storage size", ) .into(), ); }
let mut stmt = conn .prepare_cached("INSERT OR REPLACE INTO data (key, value) VALUES (?, ?)")?; stmt.execute(params![key, value])?;
Ok(())}
#[op]pub fn op_webstorage_get( state: &mut OpState, key_name: String, persistent: bool,) -> Result<Option<String>, AnyError> { let conn = get_webstorage(state, persistent)?;
let mut stmt = conn.prepare_cached("SELECT value FROM data WHERE key = ?")?; let val = stmt .query_row(params![key_name], |row| row.get(0)) .optional()?;
Ok(val)}
#[op]pub fn op_webstorage_remove( state: &mut OpState, key_name: String, persistent: bool,) -> Result<(), AnyError> { let conn = get_webstorage(state, persistent)?;
let mut stmt = conn.prepare_cached("DELETE FROM data WHERE key = ?")?; stmt.execute(params![key_name])?;
Ok(())}
#[op]pub fn op_webstorage_clear( state: &mut OpState, persistent: bool,) -> Result<(), AnyError> { let conn = get_webstorage(state, persistent)?;
let mut stmt = conn.prepare_cached("DELETE FROM data")?; stmt.execute(params![])?;
Ok(())}
#[op]pub fn op_webstorage_iterate_keys( state: &mut OpState, persistent: bool,) -> Result<Vec<String>, AnyError> { let conn = get_webstorage(state, persistent)?;
let mut stmt = conn.prepare_cached("SELECT key FROM data")?; let keys = stmt .query_map(params![], |row| row.get::<_, String>(0))? .map(|r| r.unwrap()) .collect();
Ok(keys)}
#[derive(Debug)]pub struct DomExceptionNotSupportedError { pub msg: String,}
impl DomExceptionNotSupportedError { pub fn new(msg: &str) -> Self { DomExceptionNotSupportedError { msg: msg.to_string(), } }}
impl fmt::Display for DomExceptionNotSupportedError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.pad(&self.msg) }}
impl std::error::Error for DomExceptionNotSupportedError {}
pub fn get_not_supported_error_class_name( e: &AnyError,) -> Option<&'static str> { e.downcast_ref::<DomExceptionNotSupportedError>() .map(|_| "DOMExceptionNotSupportedError")}
Version Info