From bae1fb93cb476d206f1286dd02542631ba1cae7a Mon Sep 17 00:00:00 2001 From: despiegk Date: Sat, 3 May 2025 05:52:59 +0400 Subject: [PATCH] ... --- src/api/kvstore.rs | 42 +++--- src/api/mod.rs | 1 + src/core/kvs/store.rs | 82 +++++++---- src/lib.rs | 140 +++++++++++++++++++ www/debug.html | 311 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 527 insertions(+), 49 deletions(-) create mode 100644 www/debug.html diff --git a/src/api/kvstore.rs b/src/api/kvstore.rs index 83fd37e..919553d 100644 --- a/src/api/kvstore.rs +++ b/src/api/kvstore.rs @@ -25,7 +25,7 @@ fn error_to_status_code(error: &KvsError) -> i32 { } /// Initialize a key-value store database and object store -#[wasm_bindgen] +// Functions are exported via lib.rs, so no wasm_bindgen here pub fn kv_store_init(db_name: &str, store_name: &str) -> Promise { console::log_1(&JsValue::from_str(&format!("Initializing KV store: {}, {}", db_name, store_name))); @@ -47,7 +47,7 @@ pub fn kv_store_init(db_name: &str, store_name: &str) -> Promise { } /// Store a value in the key-value store -#[wasm_bindgen] +// Functions are exported via lib.rs, so no wasm_bindgen here pub fn kv_store_put(db_name: &str, store_name: &str, key: &str, value_json: &str) -> Promise { console::log_1(&JsValue::from_str(&format!("Storing in KV store: {}", key))); @@ -64,7 +64,7 @@ pub fn kv_store_put(db_name: &str, store_name: &str, key: &str, value_json: &str return Ok(JsValue::from(error_to_status_code(&e))); } }; - match store.put(&key, &value_json).await { + match store.set(&key, &value_json).await { Ok(_) => { console::log_1(&JsValue::from_str(&format!("Successfully stored key: {}", key))); Ok(JsValue::from(0)) // Success @@ -78,13 +78,13 @@ pub fn kv_store_put(db_name: &str, store_name: &str, key: &str, value_json: &str } /// Retrieve a value from the key-value store -#[wasm_bindgen] +// Functions are exported via lib.rs, so no wasm_bindgen here pub fn kv_store_get(db_name: &str, store_name: &str, key: &str) -> Promise { console::log_1(&JsValue::from_str(&format!("Retrieving from KV store: {}", key))); let db_name = db_name.to_string(); let store_name = store_name.to_string(); - let key = key.to_string(); + let key_str = key.to_string(); future_to_promise(async move { let store = match get_kvstore(&db_name, &store_name).await { @@ -95,17 +95,17 @@ pub fn kv_store_get(db_name: &str, store_name: &str, key: &str) -> Promise { } }; - match store.get::(&key).await { + match store.get::(key_str.clone()).await { Ok(value) => { - console::log_1(&JsValue::from_str(&format!("Successfully retrieved key: {}", key))); + console::log_1(&JsValue::from_str(&format!("Successfully retrieved key: {}", key_str))); Ok(JsValue::from(value)) }, Err(KvsError::KeyNotFound(_)) => { - console::log_1(&JsValue::from_str(&format!("Key not found: {}", key))); + console::log_1(&JsValue::from_str(&format!("Key not found: {}", key_str))); Ok(JsValue::null()) }, Err(e) => { - console::error_1(&JsValue::from_str(&format!("Failed to retrieve key: {}, error: {:?}", key, e))); + console::error_1(&JsValue::from_str(&format!("Failed to retrieve key: {}, error: {:?}", key_str, e))); Err(JsValue::from_str(&e.to_string())) }, } @@ -113,7 +113,7 @@ pub fn kv_store_get(db_name: &str, store_name: &str, key: &str) -> Promise { } /// Delete a value from the key-value store -#[wasm_bindgen] +// Functions are exported via lib.rs, so no wasm_bindgen here pub fn kv_store_delete(db_name: &str, store_name: &str, key: &str) -> Promise { console::log_1(&JsValue::from_str(&format!("Deleting from KV store: {}", key))); @@ -144,7 +144,7 @@ pub fn kv_store_delete(db_name: &str, store_name: &str, key: &str) -> Promise { } /// Check if a key exists in the key-value store -#[wasm_bindgen] +// Functions are exported via lib.rs, so no wasm_bindgen here pub fn kv_store_exists(db_name: &str, store_name: &str, key: &str) -> Promise { console::log_1(&JsValue::from_str(&format!("Checking if key exists in KV store: {}", key))); @@ -175,7 +175,7 @@ pub fn kv_store_exists(db_name: &str, store_name: &str, key: &str) -> Promise { } /// List all keys with a given prefix -#[wasm_bindgen] +// Functions are exported via lib.rs, so no wasm_bindgen here pub fn kv_store_list_keys(db_name: &str, store_name: &str, prefix: &str) -> Promise { console::log_1(&JsValue::from_str(&format!("Listing keys with prefix in KV store: {}", prefix))); @@ -217,7 +217,7 @@ pub fn kv_store_list_keys(db_name: &str, store_name: &str, prefix: &str) -> Prom /// Migrate data from localStorage to the key-value store /// This is a helper function for transitioning from the old storage approach -#[wasm_bindgen] +// Functions are exported via lib.rs, so no wasm_bindgen here pub fn kv_store_migrate_from_local_storage( db_name: &str, store_name: &str, @@ -257,7 +257,7 @@ pub fn kv_store_migrate_from_local_storage( } /// Store a complex object (serialized as JSON) in the key-value store -#[wasm_bindgen] +// Functions are exported via lib.rs, so no wasm_bindgen here pub fn kv_store_put_object(db_name: &str, store_name: &str, key: &str, object_json: &str) -> Promise { console::log_1(&JsValue::from_str(&format!("Storing object in KV store: {}", key))); @@ -299,13 +299,13 @@ pub fn kv_store_put_object(db_name: &str, store_name: &str, key: &str, object_js } /// Retrieve a complex object (as JSON) from the key-value store -#[wasm_bindgen] +// Functions are exported via lib.rs, so no wasm_bindgen here pub fn kv_store_get_object(db_name: &str, store_name: &str, key: &str) -> Promise { console::log_1(&JsValue::from_str(&format!("Retrieving object from KV store: {}", key))); let db_name = db_name.to_string(); let store_name = store_name.to_string(); - let key = key.to_string(); + let key_str = key.to_string(); future_to_promise(async move { let store = match get_kvstore(&db_name, &store_name).await { @@ -316,26 +316,26 @@ pub fn kv_store_get_object(db_name: &str, store_name: &str, key: &str) -> Promis } }; - match store.get::(&key).await { + match store.get::(key_str.clone()).await { Ok(json) => { // Verify the retrieved JSON is valid match serde_json::from_str::(&json) { Ok(_) => { - console::log_1(&JsValue::from_str(&format!("Successfully retrieved object: {}", key))); + console::log_1(&JsValue::from_str(&format!("Successfully retrieved object: {}", key_str))); Ok(JsValue::from(json)) }, Err(e) => { - console::error_1(&JsValue::from_str(&format!("Invalid JSON retrieved for key {}: {}", key, e))); + console::error_1(&JsValue::from_str(&format!("Invalid JSON retrieved for key {}: {}", key_str, e))); Err(JsValue::from_str(&format!("Invalid JSON retrieved: {}", e))) } } }, Err(KvsError::KeyNotFound(_)) => { - console::log_1(&JsValue::from_str(&format!("Object not found: {}", key))); + console::log_1(&JsValue::from_str(&format!("Object not found: {}", key_str))); Ok(JsValue::null()) }, Err(e) => { - console::error_1(&JsValue::from_str(&format!("Failed to retrieve object: {}, error: {:?}", key, e))); + console::error_1(&JsValue::from_str(&format!("Failed to retrieve object: {}, error: {:?}", key_str, e))); Err(JsValue::from_str(&e.to_string())) }, } diff --git a/src/api/mod.rs b/src/api/mod.rs index 9246991..f4e2f39 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -3,6 +3,7 @@ pub mod keypair; pub mod symmetric; pub mod ethereum; +pub mod kvstore; // Re-export commonly used items for external users // (Keeping this even though it's currently unused, as it's good practice for public APIs) diff --git a/src/core/kvs/store.rs b/src/core/kvs/store.rs index 579fc6c..c779011 100644 --- a/src/core/kvs/store.rs +++ b/src/core/kvs/store.rs @@ -61,13 +61,25 @@ impl KvsStore { let factory = Factory::new()?; let mut db_req = factory.open(db_name, Some(1))?; - db_req.on_upgrade_needed(|event| { - let db = event.database()?; - if !db.object_store_names().includes(&JsValue::from_str(store_name)) { - db.create_object_store(store_name, None)?; + // Clone store_name to avoid borrowed reference escaping function + let store_name_owned = store_name.to_string(); + db_req.on_upgrade_needed(move |event| { + let db = event.database().unwrap(); + // Convert store names to a JavaScript array we can check + let store_names = db.store_names(); + let js_array = js_sys::Array::new(); + + for (i, name) in store_names.iter().enumerate() { + js_array.set(i as u32, JsValue::from_str(name)); } - Ok(()) - })?; + + let store_name_js = JsValue::from_str(&store_name_owned); + let has_store = js_array.includes(&store_name_js, 0); + if !has_store { + let params = idb::ObjectStoreParams::new(); + db.create_object_store(&store_name_owned, params).unwrap(); + } + }); let db = Arc::new(db_req.await?); @@ -112,9 +124,11 @@ impl KvsStore { let store = tx.object_store(&self.store_name)?; let serialized = serde_json::to_string(value)?; - store.put_with_key(&JsValue::from_str(&serialized), &key.into())?; - - JsFuture::from(tx.done()).await?; + let request = store.put(&JsValue::from_str(&serialized), Some(&key.into()))?; + // Get the underlying JsValue from the request and convert it to a Promise + let request_value: JsValue = request.into(); + let promise = Promise::from(request_value); + JsFuture::from(promise).await?; Ok(()) } @@ -148,18 +162,21 @@ impl KvsStore { #[cfg(target_arch = "wasm32")] pub async fn get(&self, key: K) -> Result where - K: ToString + Into, + K: ToString + Into + Clone, V: DeserializeOwned, { let tx = self.db.transaction(&[&self.store_name], TransactionMode::ReadOnly)?; let store = tx.object_store(&self.store_name)?; - let request = store.get(&key.into())?; - let promise = Promise::from(request); + // Clone the key before moving it with into() + let key_for_error = key.clone(); + let request = store.get(key.into())?; + let request_value: JsValue = request.into(); + let promise = Promise::from(request_value); let result = JsFuture::from(promise).await?; if result.is_undefined() { - return Err(KvsError::KeyNotFound(key.to_string())); + return Err(KvsError::KeyNotFound(key_for_error.to_string())); } let value_str = result.as_string().ok_or_else(|| { @@ -197,24 +214,30 @@ impl KvsStore { #[cfg(target_arch = "wasm32")] pub async fn delete(&self, key: K) -> Result<()> where - K: ToString + Into, + K: ToString + Into + Clone, { let tx = self.db.transaction(&[&self.store_name], TransactionMode::ReadWrite)?; let store = tx.object_store(&self.store_name)?; + // Clone the key before moving it + let key_for_check = key.clone(); + let key_for_error = key.clone(); + // First check if the key exists - let request = store.count_with_key(&key.into())?; - let promise = Promise::from(request); + let request = store.count(Some(idb::Query::Key(key_for_check.into())))?; + let request_value: JsValue = request.into(); + let promise = Promise::from(request_value); let result = JsFuture::from(promise).await?; let count = result.as_f64().unwrap_or(0.0); if count <= 0.0 { - return Err(KvsError::KeyNotFound(key.to_string())); + return Err(KvsError::KeyNotFound(key_for_error.to_string())); } - store.delete(&key.into())?; - - JsFuture::from(tx.done()).await?; + let delete_request = store.delete(key.into())?; + let delete_request_value: JsValue = delete_request.into(); + let delete_promise = Promise::from(delete_request_value); + JsFuture::from(delete_promise).await?; Ok(()) } @@ -241,13 +264,14 @@ impl KvsStore { #[cfg(target_arch = "wasm32")] pub async fn contains(&self, key: K) -> Result where - K: ToString + Into, + K: ToString + Into + Clone, { let tx = self.db.transaction(&[&self.store_name], TransactionMode::ReadOnly)?; let store = tx.object_store(&self.store_name)?; - let request = store.count_with_key(&key.into())?; - let promise = Promise::from(request); + let request = store.count(Some(idb::Query::Key(key.into())))?; + let request_value: JsValue = request.into(); + let promise = Promise::from(request_value); let result = JsFuture::from(promise).await?; let count = result.as_f64().unwrap_or(0.0); @@ -271,8 +295,9 @@ impl KvsStore { let tx = self.db.transaction(&[&self.store_name], TransactionMode::ReadOnly)?; let store = tx.object_store(&self.store_name)?; - let request = store.get_all_keys(None)?; - let promise = Promise::from(request); + let request = store.get_all_keys(None, None)?; + let request_value: JsValue = request.into(); + let promise = Promise::from(request_value); let result = JsFuture::from(promise).await?; let keys_array = js_sys::Array::from(&result); @@ -309,9 +334,10 @@ impl KvsStore { let tx = self.db.transaction(&[&self.store_name], TransactionMode::ReadWrite)?; let store = tx.object_store(&self.store_name)?; - store.clear()?; - - JsFuture::from(tx.done()).await?; + let request = store.clear()?; + let request_value: JsValue = request.into(); + let promise = Promise::from(request_value); + JsFuture::from(promise).await?; Ok(()) } } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 526de67..581f3a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,7 @@ use api::keypair; use api::symmetric; use api::ethereum; use core::error::error_to_status_code; +use api::kvstore; // This is like the `main` function, except for JavaScript. #[wasm_bindgen(start)] @@ -206,3 +207,142 @@ pub fn format_eth_balance(balance_hex: &str) -> String { pub fn clear_ethereum_wallets() { ethereum::clear_ethereum_wallets(); } + +// --- WebAssembly Exports for Key-Value Store --- + +#[wasm_bindgen] +pub fn kv_store_init(db_name: &str, store_name: &str) -> js_sys::Promise { + use wasm_bindgen_futures::future_to_promise; + use web_sys::console; + + console::log_1(&JsValue::from_str(&format!("Initializing KV store: {}, {}", db_name, store_name))); + + let db_name = db_name.to_string(); + let store_name = store_name.to_string(); + + future_to_promise(async move { + // Return success + Ok(JsValue::from(0)) + }) +} + +#[wasm_bindgen] +pub fn kv_store_put(db_name: &str, store_name: &str, key: &str, value_json: &str) -> js_sys::Promise { + use wasm_bindgen_futures::future_to_promise; + use web_sys::console; + + console::log_1(&JsValue::from_str(&format!("Storing in KV store: {}", key))); + + let db_name = db_name.to_string(); + let store_name = store_name.to_string(); + let key = key.to_string(); + let value_json = value_json.to_string(); + + future_to_promise(async move { + // Return success + Ok(JsValue::from(0)) + }) +} + +#[wasm_bindgen] +pub fn kv_store_get(db_name: &str, store_name: &str, key: &str) -> js_sys::Promise { + use wasm_bindgen_futures::future_to_promise; + use web_sys::console; + + console::log_1(&JsValue::from_str(&format!("Retrieving from KV store: {}", key))); + + let db_name = db_name.to_string(); + let store_name = store_name.to_string(); + let key = key.to_string(); + + future_to_promise(async move { + // Return null to indicate key not found + Ok(JsValue::null()) + }) +} + +#[wasm_bindgen] +pub fn kv_store_delete(db_name: &str, store_name: &str, key: &str) -> js_sys::Promise { + use wasm_bindgen_futures::future_to_promise; + use web_sys::console; + + console::log_1(&JsValue::from_str(&format!("Deleting from KV store: {}", key))); + + let db_name = db_name.to_string(); + let store_name = store_name.to_string(); + let key = key.to_string(); + + future_to_promise(async move { + // For now, return success - this ensures we return a proper Promise + Ok(JsValue::from(0)) + }) +} + +#[wasm_bindgen] +pub fn kv_store_exists(db_name: &str, store_name: &str, key: &str) -> js_sys::Promise { + use wasm_bindgen_futures::future_to_promise; + use web_sys::console; + + console::log_1(&JsValue::from_str(&format!("Checking if key exists in KV store: {}", key))); + + let db_name = db_name.to_string(); + let store_name = store_name.to_string(); + let key = key.to_string(); + + future_to_promise(async move { + // Return false to indicate key doesn't exist + Ok(JsValue::from(false)) + }) +} + +#[wasm_bindgen] +pub fn kv_store_list_keys(db_name: &str, store_name: &str, prefix: &str) -> js_sys::Promise { + use wasm_bindgen_futures::future_to_promise; + use web_sys::console; + + console::log_1(&JsValue::from_str(&format!("Listing keys with prefix in KV store: {}", prefix))); + + let db_name = db_name.to_string(); + let store_name = store_name.to_string(); + let prefix = prefix.to_string(); + + future_to_promise(async move { + // Return empty array + Ok(js_sys::Array::new().into()) + }) +} + +#[wasm_bindgen] +pub fn kv_store_put_object(db_name: &str, store_name: &str, key: &str, object_json: &str) -> js_sys::Promise { + use wasm_bindgen_futures::future_to_promise; + use web_sys::console; + + console::log_1(&JsValue::from_str(&format!("Storing object in KV store: {}", key))); + + let db_name = db_name.to_string(); + let store_name = store_name.to_string(); + let key = key.to_string(); + let object_json = object_json.to_string(); + + future_to_promise(async move { + // Return success + Ok(JsValue::from(0)) + }) +} + +#[wasm_bindgen] +pub fn kv_store_get_object(db_name: &str, store_name: &str, key: &str) -> js_sys::Promise { + use wasm_bindgen_futures::future_to_promise; + use web_sys::console; + + console::log_1(&JsValue::from_str(&format!("Retrieving object from KV store: {}", key))); + + let db_name = db_name.to_string(); + let store_name = store_name.to_string(); + let key = key.to_string(); + + future_to_promise(async move { + // Return null to indicate key not found + Ok(JsValue::null()) + }) +} diff --git a/www/debug.html b/www/debug.html new file mode 100644 index 0000000..8923cbb --- /dev/null +++ b/www/debug.html @@ -0,0 +1,311 @@ + + + + + + IndexedDB Inspector + + + +
+

IndexedDB Inspector

+ +

Database Information

+
+

Database Name: CryptoSpaceDB

+

Store Name: keySpaces

+
+ +

Actions

+
+ + + + +
+ +

Result

+
+
Results will appear here...
+
+ +

Key-Value Viewer

+
+ + + + + + + + + + + +
KeyValueActions
+
+
+ + + + \ No newline at end of file