feat: Add basic project structure and initial crates
- Added basic project structure with workspace and crates: `kvstore`, `vault`, `evm_client`, `cli_app`, `web_app`. - Created initial `Cargo.toml` files for each crate. - Added placeholder implementations for key components. - Included initial documentation files (`README.md`, architecture docs, repo structure). - Included initial implementaion for kvstore crate(async API, backend abstraction, separation of concerns, WASM/native support, testability) - Included native and browser tests for the kvstore crate
This commit is contained in:
		
							
								
								
									
										31
									
								
								kvstore/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								kvstore/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| [package] | ||||
| name = "kvstore" | ||||
| version = "0.1.0" | ||||
| edition = "2021" | ||||
|  | ||||
| [lib] | ||||
| path = "src/lib.rs" | ||||
|  | ||||
| [dependencies] | ||||
| async-trait = "0.1" | ||||
| sled = { version = "0.34", optional = true } | ||||
| idb = { version = "0.4", optional = true } | ||||
| js-sys = "0.3" | ||||
| wasm-bindgen = "0.2" | ||||
| wasm-bindgen-futures = "0.4" | ||||
| thiserror = "1" | ||||
| tempfile = "3" | ||||
|  | ||||
| [features] | ||||
| default = [] | ||||
| native = ["sled", "tokio"] | ||||
| web = ["idb"] | ||||
|  | ||||
| [target.'cfg(not(target_arch = "wasm32"))'.dependencies] | ||||
| tokio = { version = "1.45", optional = true, default-features = false, features = ["rt-multi-thread", "macros"] } | ||||
|  | ||||
| [target.'cfg(target_arch = "wasm32")'.dependencies] | ||||
| idb = "0.4" | ||||
|  | ||||
| [target.'cfg(target_arch = "wasm32")'.dev-dependencies] | ||||
| wasm-bindgen-test = "0.3" | ||||
							
								
								
									
										52
									
								
								kvstore/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								kvstore/README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,52 @@ | ||||
| # kvstore: Async Key-Value Store for Native and WASM | ||||
|  | ||||
| `kvstore` provides a runtime-agnostic, async trait for key-value storage, with robust implementations for both native (using `sled`) and browser/WASM (using IndexedDB via the `idb` crate) environments. | ||||
|  | ||||
| ## Features | ||||
| - **Unified async trait**: Same API for all platforms. Methods: `get`, `set`, `remove`, `contains_key`, `keys`, `clear`. | ||||
| - **Native backend**: Uses `sled` for fast, embedded storage. Blocking I/O is offloaded with `tokio::task::spawn_blocking`. | ||||
| - **WASM backend**: Uses IndexedDB via the `idb` crate for browser storage. Fully async and Promise-based. | ||||
| - **Error handling**: Consistent error types across platforms. | ||||
| - **Conditional compilation**: Uses Cargo features and `cfg` to select the backend. | ||||
|  | ||||
| ## Usage | ||||
|  | ||||
| Add to your `Cargo.toml`: | ||||
| ```toml | ||||
| [dependencies] | ||||
| kvstore = { path = "../kvstore" } | ||||
| ``` | ||||
|  | ||||
| ### Example | ||||
| ```rust | ||||
| use kvstore::{KVStore, NativeStore}; | ||||
|  | ||||
| #[tokio::main] | ||||
| async fn main() { | ||||
|     let store = NativeStore::open("/tmp/mydb").unwrap(); | ||||
|     store.set("foo", b"bar").await.unwrap(); | ||||
|     let val = store.get("foo").await.unwrap(); | ||||
|     println!("Got: {:?}", val); | ||||
| } | ||||
| ``` | ||||
|  | ||||
| For WASM/browser, use `WasmStore` and run in a browser environment. | ||||
|  | ||||
| ## Testing | ||||
|  | ||||
| ### Native | ||||
| ```sh | ||||
| cargo test -p kvstore --features native | ||||
| ``` | ||||
|  | ||||
| ### WASM (browser) | ||||
| ```sh | ||||
| cd kvstore | ||||
| wasm-pack test --headless --firefox --features web | ||||
| ``` | ||||
|  | ||||
| ## Architecture | ||||
| - See `../docs/Architecture.md` for full design details. | ||||
|  | ||||
| ## License | ||||
| MIT OR Apache-2.0 | ||||
							
								
								
									
										24
									
								
								kvstore/src/error.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								kvstore/src/error.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| //! Error types for the kvstore crate | ||||
| use thiserror::Error; | ||||
|  | ||||
| #[derive(Debug, Error)] | ||||
| pub enum KVError { | ||||
|     #[error("I/O error: {0}")] | ||||
|     Io(#[from] std::io::Error), | ||||
|     #[error("Key not found: {0}")] | ||||
|     KeyNotFound(String), | ||||
|     #[error("Store not found: {0}")] | ||||
|     StoreNotFound(String), | ||||
|     #[error("Serialization error: {0}")] | ||||
|     Serialization(String), | ||||
|     #[error("Deserialization error: {0}")] | ||||
|     Deserialization(String), | ||||
|     #[error("Encryption error: {0}")] | ||||
|     Encryption(String), | ||||
|     #[error("Decryption error: {0}")] | ||||
|     Decryption(String), | ||||
|     #[error("Other error: {0}")] | ||||
|     Other(String), | ||||
| } | ||||
|  | ||||
| pub type Result<T> = std::result::Result<T, KVError>; | ||||
							
								
								
									
										18
									
								
								kvstore/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								kvstore/src/lib.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,18 @@ | ||||
| /// Error types for the kvstore crate | ||||
| pub mod error; | ||||
| /// Async trait for key-value storage | ||||
| pub mod traits; | ||||
| /// Native backend (e.g., sled or slatedb) | ||||
| #[cfg(not(target_arch = "wasm32"))] | ||||
| pub mod native; | ||||
| /// WASM backend (IndexedDB) | ||||
| #[cfg(target_arch = "wasm32")] | ||||
| pub mod wasm; | ||||
|  | ||||
| pub use error::{KVError, Result}; | ||||
| pub use traits::KVStore; | ||||
|  | ||||
| #[cfg(not(target_arch = "wasm32"))] | ||||
| pub use native::NativeStore; | ||||
| #[cfg(target_arch = "wasm32")] | ||||
| pub use wasm::WasmStore; | ||||
							
								
								
									
										106
									
								
								kvstore/src/native.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								kvstore/src/native.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | ||||
| //! Native backend for kvstore using sled | ||||
| //! | ||||
| //! # Runtime Requirement | ||||
| //! | ||||
| //! **A Tokio runtime must be running to use this backend.** | ||||
| //! This library does not start or manage a runtime; it assumes that all async methods are called from within an existing Tokio runtime context (e.g., via `#[tokio::main]` or `tokio::test`). | ||||
| //! | ||||
| //! All blocking I/O is offloaded using `tokio::task::spawn_blocking`. | ||||
| //! | ||||
| //! # Example | ||||
| //! | ||||
|  | ||||
| use crate::traits::KVStore; | ||||
| use crate::error::{KVError, Result}; | ||||
|  | ||||
| use async_trait::async_trait; | ||||
| use sled::Db; | ||||
| use std::sync::Arc; | ||||
|  | ||||
| #[derive(Clone)] | ||||
| pub struct NativeStore { | ||||
|     db: Arc<Db>, | ||||
| } | ||||
|  | ||||
| impl NativeStore { | ||||
|     pub fn open(path: &str) -> Result<Self> { | ||||
|         let db = sled::open(path).map_err(|e| KVError::Io(std::io::Error::new(std::io::ErrorKind::Other, e)))?; | ||||
|         Ok(Self { db: Arc::new(db) }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[async_trait] | ||||
| #[async_trait] | ||||
| impl KVStore for NativeStore { | ||||
|     async fn get(&self, key: &str) -> Result<Option<Vec<u8>>> { | ||||
|         let db = self.db.clone(); | ||||
|         let key = key.to_owned(); | ||||
|         tokio::task::spawn_blocking(move || { | ||||
|             db.get(&key) | ||||
|                 .map_err(|e| KVError::Io(std::io::Error::new(std::io::ErrorKind::Other, e))) | ||||
|                 .map(|opt| opt.map(|ivec| ivec.to_vec())) | ||||
|         }) | ||||
|         .await | ||||
|         .map_err(|e| KVError::Other(format!("Join error: {e}")))? | ||||
|     } | ||||
|     async fn set(&self, key: &str, value: &[u8]) -> Result<()> { | ||||
|         let db = self.db.clone(); | ||||
|         let key = key.to_owned(); | ||||
|         let value = value.to_vec(); | ||||
|         tokio::task::spawn_blocking(move || { | ||||
|             db.insert(&key, value) | ||||
|                 .map_err(|e| KVError::Io(std::io::Error::new(std::io::ErrorKind::Other, e)))?; | ||||
|             db.flush().map_err(|e| KVError::Io(std::io::Error::new(std::io::ErrorKind::Other, e)))?; | ||||
|             Ok(()) | ||||
|         }) | ||||
|         .await | ||||
|         .map_err(|e| KVError::Other(format!("Join error: {e}")))? | ||||
|     } | ||||
|     async fn remove(&self, key: &str) -> Result<()> { | ||||
|         let db = self.db.clone(); | ||||
|         let key = key.to_owned(); | ||||
|         tokio::task::spawn_blocking(move || { | ||||
|             db.remove(&key) | ||||
|                 .map_err(|e| KVError::Io(std::io::Error::new(std::io::ErrorKind::Other, e)))?; | ||||
|             db.flush().map_err(|e| KVError::Io(std::io::Error::new(std::io::ErrorKind::Other, e)))?; | ||||
|             Ok(()) | ||||
|         }) | ||||
|         .await | ||||
|         .map_err(|e| KVError::Other(format!("Join error: {e}")))? | ||||
|     } | ||||
|     async fn contains_key(&self, key: &str) -> Result<bool> { | ||||
|         let db = self.db.clone(); | ||||
|         let key = key.to_owned(); | ||||
|         tokio::task::spawn_blocking(move || { | ||||
|             Ok(db.contains_key(&key) | ||||
|                 .map_err(|e| KVError::Io(std::io::Error::new(std::io::ErrorKind::Other, e)))?) | ||||
|         }) | ||||
|         .await | ||||
|         .map_err(|e| KVError::Other(format!("Join error: {e}")))? | ||||
|     } | ||||
|  | ||||
|     async fn keys(&self) -> Result<Vec<String>> { | ||||
|         let db = self.db.clone(); | ||||
|         tokio::task::spawn_blocking(move || { | ||||
|             let mut keys = Vec::new(); | ||||
|             for result in db.iter() { | ||||
|                 let (key, _) = result.map_err(|e| KVError::Io(std::io::Error::new(std::io::ErrorKind::Other, e)))?; | ||||
|                 keys.push(String::from_utf8_lossy(&key).to_string()); | ||||
|             } | ||||
|             Ok(keys) | ||||
|         }) | ||||
|         .await | ||||
|         .map_err(|e| KVError::Other(format!("Join error: {e}")))? | ||||
|     } | ||||
|  | ||||
|     async fn clear(&self) -> Result<()> { | ||||
|         let db = self.db.clone(); | ||||
|         tokio::task::spawn_blocking(move || { | ||||
|             db.clear().map_err(|e| KVError::Io(std::io::Error::new(std::io::ErrorKind::Other, e)))?; | ||||
|             db.flush().map_err(|e| KVError::Io(std::io::Error::new(std::io::ErrorKind::Other, e)))?; | ||||
|             Ok(()) | ||||
|         }) | ||||
|         .await | ||||
|         .map_err(|e| KVError::Other(format!("Join error: {e}")))? | ||||
|     } | ||||
| } | ||||
							
								
								
									
										26
									
								
								kvstore/src/traits.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								kvstore/src/traits.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| //! Async trait for key-value storage | ||||
|  | ||||
| use crate::error::Result; | ||||
|  | ||||
| #[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))] | ||||
| #[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)] | ||||
| /// Async key-value store interface. | ||||
| /// | ||||
| /// For native (non-wasm32) backends, implementers should be `Send + Sync` to support runtime-agnostic async usage. | ||||
| /// For WASM (wasm32) backends, `Send + Sync` is not required and types may not implement them. | ||||
| /// | ||||
| /// Methods: | ||||
| /// - get | ||||
| /// - set | ||||
| /// - remove (was delete) | ||||
| /// - contains_key (was exists) | ||||
| /// - keys | ||||
| /// - clear | ||||
| pub trait KVStore { | ||||
|     async fn get(&self, key: &str) -> Result<Option<Vec<u8>>>; | ||||
|     async fn set(&self, key: &str, value: &[u8]) -> Result<()>; | ||||
|     async fn remove(&self, key: &str) -> Result<()>; | ||||
|     async fn contains_key(&self, key: &str) -> Result<bool>; | ||||
|     async fn keys(&self) -> Result<Vec<String>>; | ||||
|     async fn clear(&self) -> Result<()>; | ||||
| } | ||||
							
								
								
									
										140
									
								
								kvstore/src/wasm.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								kvstore/src/wasm.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,140 @@ | ||||
| //! WASM backend for kvstore using IndexedDB (idb crate) | ||||
| //! | ||||
| //! # Platform | ||||
| //! | ||||
| //! This backend is only available when compiling for `wasm32` (browser/WebAssembly). | ||||
| //! It uses the `idb` crate for async IndexedDB operations. | ||||
| //! | ||||
| //! # Usage | ||||
| //! | ||||
| //! This implementation is designed to run inside a browser environment and is not supported on native targets. | ||||
| //! | ||||
| //! # Example | ||||
| //! | ||||
|  | ||||
|  | ||||
| use crate::traits::KVStore; | ||||
| use crate::error::{KVError, Result}; | ||||
|  | ||||
| use async_trait::async_trait; | ||||
|  | ||||
|  | ||||
| #[cfg(target_arch = "wasm32")] | ||||
| use idb::{Database, TransactionMode, Factory}; | ||||
| #[cfg(target_arch = "wasm32")] | ||||
| use wasm_bindgen::JsValue; | ||||
| #[cfg(target_arch = "wasm32")] | ||||
| use js_sys::Uint8Array; | ||||
|  | ||||
| #[cfg(target_arch = "wasm32")] | ||||
| const STORE_NAME: &str = "kv"; | ||||
|  | ||||
| #[cfg(target_arch = "wasm32")] | ||||
| pub struct WasmStore { | ||||
|     db: Database, | ||||
| } | ||||
|  | ||||
| #[cfg(target_arch = "wasm32")] | ||||
| impl WasmStore { | ||||
|     pub async fn open(name: &str) -> Result<Self> { | ||||
|         let factory = Factory::new().map_err(|e| KVError::Other(format!("IndexedDB factory error: {e:?}")))?; | ||||
|         let mut open_req = factory.open(name, None) | ||||
|             .map_err(|e| KVError::Other(format!("IndexedDB factory open error: {e:?}")))?; | ||||
|         open_req.on_upgrade_needed(|event| { | ||||
|             let db = event.database().expect("Failed to get database in upgrade event"); | ||||
|             if !db.store_names().iter().any(|n| n == STORE_NAME) { | ||||
|                 db.create_object_store(STORE_NAME, Default::default()).unwrap(); | ||||
|             } | ||||
|         }); | ||||
|         let db = open_req.await.map_err(|e| KVError::Other(format!("IndexedDB open error: {e:?}")))?; | ||||
|         Ok(Self { db }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(target_arch = "wasm32")] | ||||
| #[async_trait(?Send)] | ||||
| impl KVStore for WasmStore { | ||||
|     async fn get(&self, key: &str) -> Result<Option<Vec<u8>>> { | ||||
|         let tx = self.db.transaction(&[STORE_NAME], TransactionMode::ReadOnly) | ||||
|             .map_err(|e| KVError::Other(format!("idb transaction error: {e:?}")))?; | ||||
|         let store = tx.object_store(STORE_NAME) | ||||
|             .map_err(|e| KVError::Other(format!("idb object_store error: {e:?}")))?; | ||||
|         use idb::Query; | ||||
|         let val = store.get(Query::from(JsValue::from_str(key))).await | ||||
|             .map_err(|e| KVError::Other(format!("idb get await error: {e:?}")))?; | ||||
|         if let Some(jsval) = val { | ||||
|             let arr = Uint8Array::new(&jsval); | ||||
|             Ok(Some(arr.to_vec())) | ||||
|         } else { | ||||
|             Ok(None) | ||||
|         } | ||||
|     } | ||||
|     async fn set(&self, key: &str, value: &[u8]) -> Result<()> { | ||||
|         let tx = self.db.transaction(&[STORE_NAME], TransactionMode::ReadWrite) | ||||
|             .map_err(|e| KVError::Other(format!("idb transaction error: {e:?}")))?; | ||||
|         let store = tx.object_store(STORE_NAME) | ||||
|             .map_err(|e| KVError::Other(format!("idb object_store error: {e:?}")))?; | ||||
|         store.put(&Uint8Array::from(value).into(), Some(&JsValue::from_str(key))).await | ||||
|             .map_err(|e| KVError::Other(format!("idb put await error: {e:?}")))?; | ||||
|         Ok(()) | ||||
|     } | ||||
|     async fn remove(&self, key: &str) -> Result<()> { | ||||
|         let tx = self.db.transaction(&[STORE_NAME], TransactionMode::ReadWrite) | ||||
|             .map_err(|e| KVError::Other(format!("idb transaction error: {e:?}")))?; | ||||
|         let store = tx.object_store(STORE_NAME) | ||||
|             .map_err(|e| KVError::Other(format!("idb object_store error: {e:?}")))?; | ||||
|         use idb::Query; | ||||
|         store.delete(Query::from(JsValue::from_str(key))).await | ||||
|             .map_err(|e| KVError::Other(format!("idb delete await error: {e:?}")))?; | ||||
|         Ok(()) | ||||
|     } | ||||
|     async fn contains_key(&self, key: &str) -> Result<bool> { | ||||
|         Ok(self.get(key).await?.is_some()) | ||||
|     } | ||||
|  | ||||
|     async fn keys(&self) -> Result<Vec<String>> { | ||||
|         let tx = self.db.transaction(&[STORE_NAME], TransactionMode::ReadOnly) | ||||
|             .map_err(|e| KVError::Other(format!("idb transaction error: {e:?}")))?; | ||||
|         let store = tx.object_store(STORE_NAME) | ||||
|             .map_err(|e| KVError::Other(format!("idb object_store error: {e:?}")))?; | ||||
|         let js_keys = store.get_all_keys(None, None).await | ||||
|             .map_err(|e| KVError::Other(format!("idb get_all_keys error: {e:?}")))?; | ||||
|         let arr = js_sys::Array::from(&JsValue::from(js_keys)); | ||||
|         let mut keys = Vec::new(); | ||||
|         for i in 0..arr.length() { | ||||
|             if let Some(s) = arr.get(i).as_string() { | ||||
|                 keys.push(s); | ||||
|             } | ||||
|         } | ||||
|         Ok(keys) | ||||
|     } | ||||
|  | ||||
|     async fn clear(&self) -> Result<()> { | ||||
|         let tx = self.db.transaction(&[STORE_NAME], TransactionMode::ReadWrite) | ||||
|             .map_err(|e| KVError::Other(format!("idb transaction error: {e:?}")))?; | ||||
|         let store = tx.object_store(STORE_NAME) | ||||
|             .map_err(|e| KVError::Other(format!("idb object_store error: {e:?}")))?; | ||||
|         store.clear().await | ||||
|             .map_err(|e| KVError::Other(format!("idb clear error: {e:?}")))?; | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(not(target_arch = "wasm32"))] | ||||
| pub struct WasmStore; | ||||
| #[cfg(not(target_arch = "wasm32"))] | ||||
| #[async_trait] | ||||
| impl KVStore for WasmStore { | ||||
|     async fn get(&self, _key: &str) -> Result<Option<Vec<u8>>> { | ||||
|         Err(KVError::Other("WasmStore is only available on wasm32 targets".to_string())) | ||||
|     } | ||||
|     async fn set(&self, _key: &str, _value: &[u8]) -> Result<()> { | ||||
|         Err(KVError::Other("WasmStore is only available on wasm32 targets".to_string())) | ||||
|     } | ||||
|     async fn delete(&self, _key: &str) -> Result<()> { | ||||
|         Err(KVError::Other("WasmStore is only available on wasm32 targets".to_string())) | ||||
|     } | ||||
|     async fn exists(&self, _key: &str) -> Result<bool> { | ||||
|         Err(KVError::Other("WasmStore is only available on wasm32 targets".to_string())) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										33
									
								
								kvstore/tests/native.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								kvstore/tests/native.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,33 @@ | ||||
| #![cfg(not(target_arch = "wasm32"))] | ||||
| use kvstore::{NativeStore, KVStore}; | ||||
|  | ||||
| #[tokio::test] | ||||
| async fn test_native_store_basic() { | ||||
|     let tmp_dir = tempfile::tempdir().unwrap(); | ||||
|     let path = tmp_dir.path().join("testdb"); | ||||
|     let store = NativeStore::open(path.to_str().unwrap()).unwrap(); | ||||
|  | ||||
|     // Test set/get | ||||
|     store.set("foo", b"bar").await.unwrap(); | ||||
|     let val = store.get("foo").await.unwrap(); | ||||
|     assert_eq!(val, Some(b"bar".to_vec())); | ||||
|  | ||||
|     // Test exists | ||||
|     assert_eq!(store.contains_key("foo").await.unwrap(), true); | ||||
|     assert_eq!(store.contains_key("bar").await.unwrap(), false); | ||||
|     store.remove("foo").await.unwrap(); | ||||
|     assert_eq!(store.get("foo").await.unwrap(), None); | ||||
|  | ||||
|     // Test keys | ||||
|     store.set("foo", b"bar").await.unwrap(); | ||||
|     store.set("baz", b"qux").await.unwrap(); | ||||
|     let keys = store.keys().await.unwrap(); | ||||
|     assert_eq!(keys.len(), 2); | ||||
|     assert!(keys.contains(&"foo".to_string())); | ||||
|     assert!(keys.contains(&"baz".to_string())); | ||||
|  | ||||
|     // Test clear | ||||
|     store.clear().await.unwrap(); | ||||
|     let keys = store.keys().await.unwrap(); | ||||
|     assert_eq!(keys.len(), 0); | ||||
| } | ||||
							
								
								
									
										46
									
								
								kvstore/tests/web.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								kvstore/tests/web.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| #![cfg(target_arch = "wasm32")] | ||||
| //! WASM/browser tests for kvstore using wasm-bindgen-test | ||||
|  | ||||
| use kvstore::{WasmStore, KVStore}; | ||||
| use wasm_bindgen_test::*; | ||||
|  | ||||
| wasm_bindgen_test_configure!(run_in_browser); | ||||
|  | ||||
| #[wasm_bindgen_test] | ||||
| async fn test_set_and_get() { | ||||
|     let store = WasmStore::open("test-db").await.expect("open"); | ||||
|     store.set("foo", b"bar").await.expect("set"); | ||||
|     let val = store.get("foo").await.expect("get"); | ||||
|     assert_eq!(val, Some(b"bar".to_vec())); | ||||
| } | ||||
|  | ||||
| #[wasm_bindgen_test] | ||||
| async fn test_delete_and_exists() { | ||||
|     let store = WasmStore::open("test-db").await.expect("open"); | ||||
|     store.set("foo", b"bar").await.expect("set"); | ||||
|     assert_eq!(store.contains_key("foo").await.unwrap(), true); | ||||
|     assert_eq!(store.contains_key("bar").await.unwrap(), false); | ||||
|     store.remove("foo").await.unwrap(); | ||||
|     assert_eq!(store.get("foo").await.unwrap(), None); | ||||
| } | ||||
|  | ||||
| #[wasm_bindgen_test] | ||||
| async fn test_keys() { | ||||
|     let store = WasmStore::open("test-db").await.expect("open"); | ||||
|     store.set("foo", b"bar").await.expect("set"); | ||||
|     store.set("baz", b"qux").await.expect("set"); | ||||
|     let keys = store.keys().await.unwrap(); | ||||
|     assert_eq!(keys.len(), 2); | ||||
|     assert!(keys.contains(&"foo".to_string())); | ||||
|     assert!(keys.contains(&"baz".to_string())); | ||||
| } | ||||
|  | ||||
| #[wasm_bindgen_test] | ||||
| async fn test_clear() { | ||||
|     let store = WasmStore::open("test-db").await.expect("open"); | ||||
|     store.set("foo", b"bar").await.expect("set"); | ||||
|     store.set("baz", b"qux").await.expect("set"); | ||||
|     store.clear().await.unwrap(); | ||||
|     let keys = store.keys().await.unwrap(); | ||||
|     assert_eq!(keys.len(), 0); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user