From 9c3112a4a8f11ae6635554dffe64c0b63deaf47d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roger=20Ku=CC=88ng?= Date: Wed, 4 Sep 2024 15:39:44 +0200 Subject: [PATCH 1/4] implement remove for redb_store --- src/lib.rs | 42 +++++++++++++++++++++++++- src/redb_store.rs | 76 ++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 103 insertions(+), 15 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 7b20898..8c75ad1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,12 +14,15 @@ use serde::{de::DeserializeOwned, Serialize}; trait StoreImpl { type GetError; type SetError; + type RemoveError; fn set_string(&mut self, key: &str, value: &str) -> Result<(), Self::SetError> { self.set(key, &value.to_string()) } fn get(&self, key: &str) -> Result; fn set(&mut self, key: &str, value: &T) -> Result<(), Self::SetError>; + fn remove(&mut self, key: &str) -> Result<(), Self::RemoveError>; + fn remove_and_get(&mut self, key: &str) -> Result, Self::RemoveError>; fn clear(&mut self) -> Result<(), Self::SetError>; } @@ -42,7 +45,7 @@ mod rocksdb_store; use rocksdb_store::{self as backend}; // todo: Look into unifying these types? -pub use backend::{GetError, SetError}; +pub use backend::{GetError, SetError, RemoveError}; enum Location<'a> { PlatformDefault(&'a PlatformDefault), @@ -125,6 +128,15 @@ impl PkvStore { pub fn get(&self, key: impl AsRef) -> Result { self.inner.get(key.as_ref()) } + /// Remove the value from the store + /// returns the removed value if one existed + pub fn remove_and_get(&mut self, key: impl AsRef) -> Result, RemoveError> { + self.inner.remove_and_get(key.as_ref()) + } + + pub fn remove(&mut self, key: impl AsRef) -> Result<(), RemoveError> { + self.inner.remove(key.as_ref()) + } /// Clear all key values data /// returns Err(SetError) if clear error @@ -237,4 +249,32 @@ mod tests { store.set("user", &user).unwrap(); assert_eq!(store.get::("user").unwrap(), user); } + + #[test] + fn remove() { + setup(); + let mut store = PkvStore::new("BevyPkv", "test_set"); + let user = User { + name: "alice".to_string(), + age: 32, + }; + store.set("user", &user).unwrap(); + store.remove("user").unwrap(); + assert_eq!(store.get::("user").ok(), None); + } + + #[test] + fn remove_and_get() { + setup(); + let mut store = PkvStore::new("BevyPkv", "test_set"); + let user = User { + name: "alice".to_string(), + age: 32, + }; + store.set("user", &user).unwrap(); + let removed_user = store.remove_and_get::("user").unwrap().unwrap(); + assert_eq!(user, removed_user); + assert_eq!(store.get::("user").ok(), None); + } + } diff --git a/src/redb_store.rs b/src/redb_store.rs index df0900a..33af035 100644 --- a/src/redb_store.rs +++ b/src/redb_store.rs @@ -33,6 +33,26 @@ pub enum GetError { MessagePack(#[from] rmp_serde::decode::Error), } +/// Errors that can occur during `PkvStore::` +#[derive(thiserror::Error, Debug)] +pub enum RemoveError { + /// An internal commit error from the `redb` crate + #[error("ReDbCommitError error")] + ReDbCommitError(#[from] redb::CommitError), + /// An internal storage error from the `redb` crate + #[error("ReDbStorageError error")] + ReDbStorageError(#[from] redb::StorageError), + /// An internal transaction error from the `redb` crate + #[error("ReDbTransactionError error")] + ReDbTransactionError(#[from] redb::TransactionError), + /// An internal table error from the `redb` crate + #[error("ReDbTableError error")] + ReDbTableError(#[from] redb::TableError), + /// Error when serializing the value + #[error("MessagePack serialization error")] + MessagePack(#[from] rmp_serde::encode::Error), +} + /// Errors that can occur during `PkvStore::set` #[derive(thiserror::Error, Debug)] pub enum SetError { @@ -74,20 +94,7 @@ const TABLE: TableDefinition<&str, &[u8]> = TableDefinition::new("redb"); impl StoreImpl for ReDbStore { type GetError = GetError; type SetError = SetError; - - /// Serialize and store the value - fn set(&mut self, key: &str, value: &T) -> Result<(), Self::SetError> { - let mut serializer = rmp_serde::Serializer::new(Vec::new()).with_struct_map(); - value.serialize(&mut serializer)?; - let write_txn = self.db.begin_write()?; - { - let mut table = write_txn.open_table(TABLE).unwrap(); - table.insert(key, serializer.into_inner().as_slice())?; - } - write_txn.commit()?; - - Ok(()) - } + type RemoveError = RemoveError; /// More or less the same as set::, but can take a &str fn set_string(&mut self, key: &str, value: &str) -> Result<(), Self::SetError> { @@ -113,6 +120,47 @@ impl StoreImpl for ReDbStore { Ok(value) } + /// Serialize and store the value + fn set(&mut self, key: &str, value: &T) -> Result<(), Self::SetError> { + let mut serializer = rmp_serde::Serializer::new(Vec::new()).with_struct_map(); + value.serialize(&mut serializer)?; + let write_txn = self.db.begin_write()?; + { + let mut table = write_txn.open_table(TABLE).unwrap(); + table.insert(key, serializer.into_inner().as_slice())?; + } + write_txn.commit()?; + + Ok(()) + } + + fn remove_and_get(&mut self, key: &str) -> Result, Self::RemoveError> { + let value: Option; + let write_txn = self.db.begin_write()?; + { + let mut table = write_txn.open_table(TABLE).unwrap(); + value = match table.remove(key)? { + Some(kv) => {rmp_serde::from_slice(kv.value()).ok()} + None => None + }; + } + write_txn.commit()?; + + Ok(value) + } + + fn remove(&mut self, key: &str) -> Result<(), Self::RemoveError> { + let write_txn = self.db.begin_write()?; + { + let mut table = write_txn.open_table(TABLE).unwrap(); + table.remove(key)?; + + } + write_txn.commit()?; + + Ok(()) + } + /// Clear all keys and their values fn clear(&mut self) -> Result<(), Self::SetError> { let write_txn = self.db.begin_write()?; From 7ae44986d322be49ef06df04b5759976cc257a17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roger=20Ku=CC=88ng?= Date: Wed, 4 Sep 2024 17:42:37 +0200 Subject: [PATCH 2/4] implement removal of all data stores --- src/lib.rs | 21 +++++++++++++-------- src/redb_store.rs | 12 +++++++----- src/rocksdb_store.rs | 31 ++++++++++++++++++++++++++++++- src/sled_store.rs | 30 ++++++++++++++++++++++++++++++ 4 files changed, 80 insertions(+), 14 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 8c75ad1..ac4bd90 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,7 +22,10 @@ trait StoreImpl { fn get(&self, key: &str) -> Result; fn set(&mut self, key: &str, value: &T) -> Result<(), Self::SetError>; fn remove(&mut self, key: &str) -> Result<(), Self::RemoveError>; - fn remove_and_get(&mut self, key: &str) -> Result, Self::RemoveError>; + fn remove_and_get( + &mut self, + key: &str, + ) -> Result, Self::RemoveError>; fn clear(&mut self) -> Result<(), Self::SetError>; } @@ -45,7 +48,7 @@ mod rocksdb_store; use rocksdb_store::{self as backend}; // todo: Look into unifying these types? -pub use backend::{GetError, SetError, RemoveError}; +pub use backend::{GetError, RemoveError, SetError}; enum Location<'a> { PlatformDefault(&'a PlatformDefault), @@ -128,12 +131,15 @@ impl PkvStore { pub fn get(&self, key: impl AsRef) -> Result { self.inner.get(key.as_ref()) } - /// Remove the value from the store + /// Remove the value from the store for the given key /// returns the removed value if one existed - pub fn remove_and_get(&mut self, key: impl AsRef) -> Result, RemoveError> { + pub fn remove_and_get( + &mut self, + key: impl AsRef, + ) -> Result, RemoveError> { self.inner.remove_and_get(key.as_ref()) } - + /// Remove the value from the store for the given key pub fn remove(&mut self, key: impl AsRef) -> Result<(), RemoveError> { self.inner.remove(key.as_ref()) } @@ -253,7 +259,7 @@ mod tests { #[test] fn remove() { setup(); - let mut store = PkvStore::new("BevyPkv", "test_set"); + let mut store = PkvStore::new("BevyPkv", "test_remove"); let user = User { name: "alice".to_string(), age: 32, @@ -266,7 +272,7 @@ mod tests { #[test] fn remove_and_get() { setup(); - let mut store = PkvStore::new("BevyPkv", "test_set"); + let mut store = PkvStore::new("BevyPkv", "test_remove_and_get"); let user = User { name: "alice".to_string(), age: 32, @@ -276,5 +282,4 @@ mod tests { assert_eq!(user, removed_user); assert_eq!(store.get::("user").ok(), None); } - } diff --git a/src/redb_store.rs b/src/redb_store.rs index 33af035..c8bd63e 100644 --- a/src/redb_store.rs +++ b/src/redb_store.rs @@ -134,14 +134,17 @@ impl StoreImpl for ReDbStore { Ok(()) } - fn remove_and_get(&mut self, key: &str) -> Result, Self::RemoveError> { + fn remove_and_get( + &mut self, + key: &str, + ) -> Result, Self::RemoveError> { let value: Option; let write_txn = self.db.begin_write()?; { let mut table = write_txn.open_table(TABLE).unwrap(); - value = match table.remove(key)? { - Some(kv) => {rmp_serde::from_slice(kv.value()).ok()} - None => None + value = match table.remove(key)? { + Some(kv) => rmp_serde::from_slice(kv.value()).ok(), + None => None, }; } write_txn.commit()?; @@ -154,7 +157,6 @@ impl StoreImpl for ReDbStore { { let mut table = write_txn.open_table(TABLE).unwrap(); table.remove(key)?; - } write_txn.commit()?; diff --git a/src/rocksdb_store.rs b/src/rocksdb_store.rs index d25fe22..8370684 100644 --- a/src/rocksdb_store.rs +++ b/src/rocksdb_store.rs @@ -21,7 +21,6 @@ pub enum GetError { #[error("No value found for the given key")] NotFound, } - /// Errors that can occur during `PkvStore::set` #[derive(thiserror::Error, Debug)] pub enum SetError { @@ -33,6 +32,20 @@ pub enum SetError { MessagePack(#[from] rmp_serde::encode::Error), } +/// Errors that can occur during `PkvStore::remove` +#[derive(thiserror::Error, Debug)] +pub enum RemoveError { + /// An internal error from the rocksdb crate + #[error("Rocksdb error")] + Rocksdb(#[from] rocksdb::Error), + /// Error when deserializing the value + #[error("MessagePack deserialization error")] + MessagePack(#[from] rmp_serde::decode::Error), + /// The value for the given key was not found + #[error("No value found for the given key")] + NotFound, +} + impl RocksDBStore { pub(crate) fn new(location: Location) -> Self { let mut options = rocksdb::Options::default(); @@ -49,6 +62,7 @@ impl RocksDBStore { impl StoreImpl for RocksDBStore { type GetError = GetError; type SetError = SetError; + type RemoveError = RemoveError; /// Serialize and store the value fn set(&mut self, key: &str, value: &T) -> Result<(), Self::SetError> { @@ -87,4 +101,19 @@ impl StoreImpl for RocksDBStore { Ok(()) } + + fn remove(&mut self, key: &str) -> Result<(), Self::RemoveError> { + self.db.delete(key)?; + Ok(()) + } + + fn remove_and_get( + &mut self, + key: &str, + ) -> Result, Self::RemoveError> { + let bytes = self.db.get(key)?.ok_or(Self::RemoveError::NotFound)?; + let value = rmp_serde::from_slice(&bytes)?; + self.db.delete(key)?; + Ok(value) + } } diff --git a/src/sled_store.rs b/src/sled_store.rs index a38e36a..345890d 100644 --- a/src/sled_store.rs +++ b/src/sled_store.rs @@ -33,6 +33,21 @@ pub enum SetError { MessagePack(#[from] rmp_serde::encode::Error), } +/// Errors that can occur during `PkvStore::remove` +#[derive(thiserror::Error, Debug)] +pub enum RemoveError { + /// An internal error from the sled crate + #[error("Sled error")] + Sled(#[from] sled::Error), + /// Error when deserializing the value + #[error("MessagePack deserialization error")] + MessagePack(#[from] rmp_serde::decode::Error), + /// The value for the given key was not found + #[error("No value found for the given key")] + NotFound, +} + + impl SledStore { pub(crate) fn new(location: Location) -> Self { let db_path = location.get_path().join("bevy_pkv.sled"); @@ -44,6 +59,7 @@ impl SledStore { impl StoreImpl for SledStore { type GetError = GetError; type SetError = SetError; + type RemoveError = RemoveError; /// Serialize and store the value fn set(&mut self, key: &str, value: &T) -> Result<(), Self::SetError> { @@ -77,4 +93,18 @@ impl StoreImpl for SledStore { self.db.flush()?; Ok(()) } + + fn remove(&mut self, key: &str) -> Result<(), Self::RemoveError> { + self.db.remove(key)?; + Ok(()) + } + + fn remove_and_get( + &mut self, + key: &str, + ) -> Result, Self::RemoveError> { + let bytes = self.db.remove(key)?.ok_or(Self::RemoveError::NotFound)?; + let value = rmp_serde::from_slice(&bytes)?; + Ok(value) + } } From 47cfa2e070dae8c02ca2ba17b892c7daee93edb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roger=20Ku=CC=88ng?= Date: Wed, 4 Sep 2024 18:59:15 +0200 Subject: [PATCH 3/4] implement local storage --- src/local_storage_store.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/local_storage_store.rs b/src/local_storage_store.rs index c2b4ddc..e7efd0f 100644 --- a/src/local_storage_store.rs +++ b/src/local_storage_store.rs @@ -27,6 +27,19 @@ pub enum SetError { Clear(wasm_bindgen::JsValue), } + +#[derive(thiserror::Error, Debug)] +pub enum RemoveError { + #[error("No value found for the given key")] + NotFound, + #[error("error deserializing json")] + Json(#[from] serde_json::Error), + #[error("JavaScript error from getItem")] + GetItem(wasm_bindgen::JsValue), + #[error("JavaScript error from clear")] + Clear(wasm_bindgen::JsValue), +} + impl LocalStorageStore { fn storage(&self) -> web_sys::Storage { web_sys::window() @@ -59,6 +72,7 @@ impl LocalStorageStore { impl StoreImpl for LocalStorageStore { type GetError = GetError; type SetError = SetError; + type RemoveError = RemoveError; fn set_string(&mut self, key: &str, value: &str) -> Result<(), SetError> { let json = serde_json::to_string(value)?; @@ -99,4 +113,20 @@ impl StoreImpl for LocalStorageStore { } Ok(()) } + + fn remove(&mut self, key: &str) -> Result<(), Self::RemoveError> { + let storage = self.storage(); + let key = self.format_key(key); + storage.remove_item(&key).map_err(RemoveError::Clear)?; + Ok(()) + } + + fn remove_and_get( + &mut self, + key: &str, + ) -> Result, Self::RemoveError> { + let previous_value = self.get(key).map_err(|_| RemoveError::NotFound)?; + self.remove(key)?; + Ok(Some(previous_value)) + } } From 6594949b9701c0c398e3cb540f80013da418755c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roger=20Ku=CC=88ng?= Date: Wed, 4 Sep 2024 19:03:29 +0200 Subject: [PATCH 4/4] fmt alignment --- src/local_storage_store.rs | 5 ++--- src/rocksdb_store.rs | 22 +++++++++++----------- src/sled_store.rs | 5 ++--- 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/local_storage_store.rs b/src/local_storage_store.rs index e7efd0f..20a5508 100644 --- a/src/local_storage_store.rs +++ b/src/local_storage_store.rs @@ -27,7 +27,6 @@ pub enum SetError { Clear(wasm_bindgen::JsValue), } - #[derive(thiserror::Error, Debug)] pub enum RemoveError { #[error("No value found for the given key")] @@ -113,14 +112,14 @@ impl StoreImpl for LocalStorageStore { } Ok(()) } - + fn remove(&mut self, key: &str) -> Result<(), Self::RemoveError> { let storage = self.storage(); let key = self.format_key(key); storage.remove_item(&key).map_err(RemoveError::Clear)?; Ok(()) } - + fn remove_and_get( &mut self, key: &str, diff --git a/src/rocksdb_store.rs b/src/rocksdb_store.rs index 8370684..a09b23a 100644 --- a/src/rocksdb_store.rs +++ b/src/rocksdb_store.rs @@ -35,15 +35,15 @@ pub enum SetError { /// Errors that can occur during `PkvStore::remove` #[derive(thiserror::Error, Debug)] pub enum RemoveError { - /// An internal error from the rocksdb crate - #[error("Rocksdb error")] - Rocksdb(#[from] rocksdb::Error), - /// Error when deserializing the value - #[error("MessagePack deserialization error")] - MessagePack(#[from] rmp_serde::decode::Error), - /// The value for the given key was not found - #[error("No value found for the given key")] - NotFound, + /// An internal error from the rocksdb crate + #[error("Rocksdb error")] + Rocksdb(#[from] rocksdb::Error), + /// Error when deserializing the value + #[error("MessagePack deserialization error")] + MessagePack(#[from] rmp_serde::decode::Error), + /// The value for the given key was not found + #[error("No value found for the given key")] + NotFound, } impl RocksDBStore { @@ -101,12 +101,12 @@ impl StoreImpl for RocksDBStore { Ok(()) } - + fn remove(&mut self, key: &str) -> Result<(), Self::RemoveError> { self.db.delete(key)?; Ok(()) } - + fn remove_and_get( &mut self, key: &str, diff --git a/src/sled_store.rs b/src/sled_store.rs index 345890d..36f9be0 100644 --- a/src/sled_store.rs +++ b/src/sled_store.rs @@ -47,7 +47,6 @@ pub enum RemoveError { NotFound, } - impl SledStore { pub(crate) fn new(location: Location) -> Self { let db_path = location.get_path().join("bevy_pkv.sled"); @@ -93,12 +92,12 @@ impl StoreImpl for SledStore { self.db.flush()?; Ok(()) } - + fn remove(&mut self, key: &str) -> Result<(), Self::RemoveError> { self.db.remove(key)?; Ok(()) } - + fn remove_and_get( &mut self, key: &str,