From a687c2ae1b18cbda4fbf56b5251ac1b4152bf3df Mon Sep 17 00:00:00 2001 From: Sandi Fatic Date: Mon, 4 Nov 2024 15:08:25 +0100 Subject: [PATCH] feat: introduce basic vector and set collections to storage (#914) Co-authored-by: Miraculous Owonubi --- apps/kv-store/src/lib.rs | 9 +- crates/storage-macros/tests/atomic_unit.rs | 20 +- crates/storage-macros/tests/collection.rs | 16 +- .../tests/compile_fail/collection.rs | 2 +- crates/storage/src/collections.rs | 14 + crates/storage/src/collections/error.rs | 17 + .../storage/src/collections/unordered_map.rs | 421 ++++++++++++++++++ .../storage/src/collections/unordered_set.rs | 270 +++++++++++ crates/storage/src/collections/vector.rs | 321 +++++++++++++ crates/storage/src/entities.rs | 6 +- crates/storage/src/lib.rs | 2 +- crates/storage/src/tests/entities.rs | 46 +- crates/storage/src/tests/interface.rs | 68 +-- crates/storage/src/types.rs | 204 --------- 14 files changed, 1136 insertions(+), 280 deletions(-) create mode 100644 crates/storage/src/collections.rs create mode 100644 crates/storage/src/collections/error.rs create mode 100644 crates/storage/src/collections/unordered_map.rs create mode 100644 crates/storage/src/collections/unordered_set.rs create mode 100644 crates/storage/src/collections/vector.rs delete mode 100644 crates/storage/src/types.rs diff --git a/apps/kv-store/src/lib.rs b/apps/kv-store/src/lib.rs index 1aa546e43..ba5d4d5fc 100644 --- a/apps/kv-store/src/lib.rs +++ b/apps/kv-store/src/lib.rs @@ -4,9 +4,8 @@ use std::collections::BTreeMap; use calimero_sdk::types::Error; use calimero_sdk::{app, env}; -use calimero_storage::address::Path; +use calimero_storage::collections::UnorderedMap; use calimero_storage::entities::Element; -use calimero_storage::types::Map; use calimero_storage::AtomicUnit; #[app::state(emits = for<'a> Event<'a>)] @@ -14,7 +13,7 @@ use calimero_storage::AtomicUnit; #[root] #[type_id(1)] pub struct KvStore { - items: Map, + items: UnorderedMap, #[storage] storage: Element, } @@ -32,7 +31,7 @@ impl KvStore { #[app::init] pub fn init() -> KvStore { KvStore { - items: Map::new(&Path::new("::items").unwrap()).unwrap(), + items: UnorderedMap::new().unwrap(), storage: Element::root(), } } @@ -87,7 +86,7 @@ impl KvStore { self.get(key)?.ok_or_else(|| Error::msg("Key not found.")) } - pub fn remove(&mut self, key: &str) -> Result, Error> { + pub fn remove(&mut self, key: &str) -> Result { env::log(&format!("Removing key: {:?}", key)); app::emit!(Event::Removed { key }); diff --git a/crates/storage-macros/tests/atomic_unit.rs b/crates/storage-macros/tests/atomic_unit.rs index 7cac8b516..08b0508e8 100644 --- a/crates/storage-macros/tests/atomic_unit.rs +++ b/crates/storage-macros/tests/atomic_unit.rs @@ -48,7 +48,7 @@ impl Private { Self { public: String::new(), private: String::new(), - storage: Element::new(path), + storage: Element::new(path, None), } } } @@ -68,7 +68,7 @@ impl Simple { Self { name: String::new(), value: 0, - storage: Element::new(path), + storage: Element::new(path, None), } } } @@ -89,7 +89,7 @@ impl Skipped { Self { included: String::new(), skipped: String::new(), - storage: Element::new(path), + storage: Element::new(path, None), } } } @@ -251,8 +251,8 @@ mod hashing { let mut hasher = Sha256::new(); hasher.update(unit.id().as_bytes()); - hasher.update(&to_vec(&unit.public).unwrap()); - hasher.update(&to_vec(&unit.element().metadata()).unwrap()); + hasher.update(to_vec(&unit.public).unwrap()); + hasher.update(to_vec(&unit.element().metadata()).unwrap()); let expected_hash: [u8; 32] = hasher.finalize().into(); assert_eq!(unit.calculate_merkle_hash().unwrap(), expected_hash); @@ -274,9 +274,9 @@ mod hashing { let mut hasher = Sha256::new(); hasher.update(unit.id().as_bytes()); - hasher.update(&to_vec(&unit.name).unwrap()); - hasher.update(&to_vec(&unit.value).unwrap()); - hasher.update(&to_vec(&unit.element().metadata()).unwrap()); + hasher.update(to_vec(&unit.name).unwrap()); + hasher.update(to_vec(&unit.value).unwrap()); + hasher.update(to_vec(&unit.element().metadata()).unwrap()); let expected_hash: [u8; 32] = hasher.finalize().into(); assert_eq!(unit.calculate_merkle_hash().unwrap(), expected_hash); @@ -294,8 +294,8 @@ mod hashing { let mut hasher = Sha256::new(); hasher.update(unit.id().as_bytes()); - hasher.update(&to_vec(&unit.included).unwrap()); - hasher.update(&to_vec(&unit.element().metadata()).unwrap()); + hasher.update(to_vec(&unit.included).unwrap()); + hasher.update(to_vec(&unit.element().metadata()).unwrap()); let expected_hash: [u8; 32] = hasher.finalize().into(); assert_eq!(unit.calculate_merkle_hash().unwrap(), expected_hash); diff --git a/crates/storage-macros/tests/collection.rs b/crates/storage-macros/tests/collection.rs index 217741bef..7145ef22a 100644 --- a/crates/storage-macros/tests/collection.rs +++ b/crates/storage-macros/tests/collection.rs @@ -43,7 +43,7 @@ impl Child { fn new(path: &Path) -> Self { Self { content: String::new(), - storage: Element::new(path), + storage: Element::new(path, None), } } } @@ -53,7 +53,7 @@ impl Child { struct Group; impl Group { - fn new() -> Self { + const fn new() -> Self { Self {} } } @@ -74,7 +74,7 @@ impl Parent { Self { title: String::new(), children: Group::new(), - storage: Element::new(path), + storage: Element::new(path, None), } } } @@ -94,7 +94,7 @@ impl Simple { Self { name: String::new(), value: 0, - storage: Element::new(path), + storage: Element::new(path, None), } } } @@ -135,8 +135,8 @@ mod hashing { let mut hasher = Sha256::new(); hasher.update(child.id().as_bytes()); - hasher.update(&to_vec(&child.content).unwrap()); - hasher.update(&to_vec(&child.element().metadata()).unwrap()); + hasher.update(to_vec(&child.content).unwrap()); + hasher.update(to_vec(&child.element().metadata()).unwrap()); let expected_hash: [u8; 32] = hasher.finalize().into(); assert_eq!(child.calculate_merkle_hash().unwrap(), expected_hash); @@ -149,8 +149,8 @@ mod hashing { let mut hasher = Sha256::new(); hasher.update(parent.id().as_bytes()); - hasher.update(&to_vec(&parent.title).unwrap()); - hasher.update(&to_vec(&parent.element().metadata()).unwrap()); + hasher.update(to_vec(&parent.title).unwrap()); + hasher.update(to_vec(&parent.element().metadata()).unwrap()); let expected_hash: [u8; 32] = hasher.finalize().into(); assert_eq!(parent.calculate_merkle_hash().unwrap(), expected_hash); diff --git a/crates/storage-macros/tests/compile_fail/collection.rs b/crates/storage-macros/tests/compile_fail/collection.rs index 565a0f8eb..2c1ecc34a 100644 --- a/crates/storage-macros/tests/compile_fail/collection.rs +++ b/crates/storage-macros/tests/compile_fail/collection.rs @@ -27,7 +27,7 @@ fn main() { fn child_type_specification() { let parent: Parent = Parent { group: Group {}, - storage: Element::new(&Path::new("::root::node").unwrap()), + storage: Element::new(&Path::new("::root::node").unwrap(), None), }; let _: Vec = Interface::children_of(parent.id(), &parent.group).unwrap(); diff --git a/crates/storage/src/collections.rs b/crates/storage/src/collections.rs new file mode 100644 index 000000000..1122bb681 --- /dev/null +++ b/crates/storage/src/collections.rs @@ -0,0 +1,14 @@ +//! High-level data structures for storage. + +/// This module provides functionality for hashmap data structures. +pub mod unordered_map; +pub use unordered_map::UnorderedMap; +/// This module provides functionality for hashset data structures. +pub mod unordered_set; +pub use unordered_set::UnorderedSet; +/// This module provides functionality for vector data structures. +pub mod vector; +pub use vector::Vector; +/// This module provides functionality for handling errors. +pub mod error; +pub use error::StoreError; diff --git a/crates/storage/src/collections/error.rs b/crates/storage/src/collections/error.rs new file mode 100644 index 000000000..bd91c67ff --- /dev/null +++ b/crates/storage/src/collections/error.rs @@ -0,0 +1,17 @@ +use thiserror::Error; + +// fixme! macro expects `calimero_storage` to be in deps +use crate::address::PathError; +use crate::interface::StorageError; + +/// General error type for storage operations while interacting with complex collections. +#[derive(Debug, Error)] +#[non_exhaustive] +pub enum StoreError { + /// Error while interacting with storage. + #[error(transparent)] + StorageError(#[from] StorageError), + /// Error while interacting with a path. + #[error(transparent)] + PathError(#[from] PathError), +} diff --git a/crates/storage/src/collections/unordered_map.rs b/crates/storage/src/collections/unordered_map.rs new file mode 100644 index 000000000..7eaea3726 --- /dev/null +++ b/crates/storage/src/collections/unordered_map.rs @@ -0,0 +1,421 @@ +use core::borrow::Borrow; +use core::marker::PhantomData; + +use borsh::{BorshDeserialize, BorshSerialize}; +use sha2::{Digest, Sha256}; + +// fixme! macro expects `calimero_storage` to be in deps +use crate as calimero_storage; +use crate::address::{Id, Path}; +use crate::collections::error::StoreError; +use crate::entities::{Data, Element}; +use crate::interface::Interface; +use crate::{AtomicUnit, Collection}; + +/// A map collection that stores key-value pairs. +#[derive(AtomicUnit, Clone, Debug, Eq, PartialEq, PartialOrd)] +#[type_id(255)] +#[root] +pub struct UnorderedMap { + /// The id used for the map's entries. + id: Id, + /// The entries in the map. + entries: Entries, + /// The storage element for the map. + #[storage] + storage: Element, +} + +/// A collection of entries in a map. +#[derive(Collection, Clone, Debug, Eq, PartialEq, PartialOrd)] +#[children(Entry)] +struct Entries { + /// Helper to associate the generic types with the collection. + _priv: PhantomData<(K, V)>, +} + +/// An entry in a map. +#[derive(AtomicUnit, Clone, Debug, Eq, PartialEq, PartialOrd)] +#[type_id(254)] +pub struct Entry { + /// The key for the entry. + key: K, + /// The value for the entry. + value: V, + /// The storage element for the entry. + #[storage] + storage: Element, +} + +impl + UnorderedMap +{ + /// Create a new map collection. + /// + /// # Errors + /// + /// If an error occurs when interacting with the storage system, or a child + /// [`Element`](crate::entities::Element) cannot be found, an error will be + /// returned. + /// + pub fn new() -> Result { + let id = Id::random(); + let mut this = Self { + id: id, + entries: Entries::default(), + storage: Element::new(&Path::new(format!("::unused::map::{id}::path"))?, Some(id)), + }; + + let _ = Interface::save(&mut this)?; + + Ok(this) + } + + /// Compute the ID for a key. + fn compute_id(&self, key: &[u8]) -> Id { + let mut hasher = Sha256::new(); + hasher.update(self.id.as_bytes()); + hasher.update(key); + Id::new(hasher.finalize().into()) + } + + /// Get the raw entry for a key in the map. + /// + /// # Errors + /// + /// If an error occurs when interacting with the storage system, or a child + /// [`Element`](crate::entities::Element) cannot be found, an error will be + /// returned. + /// + fn get_raw(&self, key: &Q) -> Result>, StoreError> + where + K: Borrow, + Q: PartialEq + AsRef<[u8]> + ?Sized, + { + Ok(Interface::find_by_id::>( + self.compute_id(key.as_ref()), + )?) + } + + /// Insert a key-value pair into the map. + /// + /// # Errors + /// + /// If an error occurs when interacting with the storage system, or a child + /// [`Element`](crate::entities::Element) cannot be found, an error will be + /// returned. + /// + pub fn insert(&mut self, key: K, value: V) -> Result, StoreError> + where + K: AsRef<[u8]> + PartialEq, + { + if let Some(mut entry) = self.get_raw(&key)? { + let ret_value = entry.value; + entry.value = value; + // has to be called to update the entry + entry.element_mut().update(); + let _ = Interface::save(&mut entry)?; + return Ok(Some(ret_value)); + } + + let path = self.path(); + let storage = Element::new(&path, Some(self.compute_id(key.as_ref()))); + let _ = Interface::add_child_to( + self.storage.id(), + &mut self.entries, + &mut Entry { + key, + value, + storage, + }, + )?; + + Ok(None) + } + + /// Get an iterator over the entries in the map. + /// + /// # Errors + /// + /// If an error occurs when interacting with the storage system, or a child + /// [`Element`](crate::entities::Element) cannot be found, an error will be + /// returned. + /// + pub fn entries(&self) -> Result, StoreError> { + let entries = Interface::children_of(self.id(), &self.entries)?; + + Ok(entries.into_iter().map(|entry| (entry.key, entry.value))) + } + + /// Get the number of entries in the map. + /// + /// # Errors + /// + /// If an error occurs when interacting with the storage system, or a child + /// [`Element`](crate::entities::Element) cannot be found, an error will be + /// returned. + /// + pub fn len(&self) -> Result { + Ok(Interface::child_info_for(self.id(), &self.entries)?.len()) + } + + /// Get the value for a key in the map. + /// + /// # Errors + /// + /// If an error occurs when interacting with the storage system, or a child + /// [`Element`](crate::entities::Element) cannot be found, an error will be + /// returned. + /// + pub fn get(&self, key: &Q) -> Result, StoreError> + where + K: Borrow, + Q: PartialEq + AsRef<[u8]> + ?Sized, + { + let entry = Interface::find_by_id::>(self.compute_id(key.as_ref()))?; + let value = entry.map(|e| e.value); + + Ok(value) + } + + /// Check if the map contains a key. + /// + /// # Errors + /// + /// If an error occurs when interacting with the storage system, or a child + /// [`Element`](crate::entities::Element) cannot be found, an error will be + /// returned. + /// + pub fn contains(&self, key: &Q) -> Result + where + K: Borrow + PartialEq, + Q: PartialEq + AsRef<[u8]> + ?Sized, + { + Ok(self.get_raw(key)?.is_some()) + } + + /// Remove a key from the map, returning the value at the key if it previously existed. + /// + /// # Errors + /// + /// If an error occurs when interacting with the storage system, or a child + /// [`Element`](crate::entities::Element) cannot be found, an error will be + /// returned. + /// + pub fn remove(&mut self, key: &Q) -> Result + where + K: Borrow, + Q: PartialEq + AsRef<[u8]> + ?Sized, + { + let entry = Element::new(&self.path(), Some(self.compute_id(key.as_ref()))); + + Ok(Interface::remove_child_from( + self.id(), + &mut self.entries, + entry.id(), + )?) + } + + /// Clear the map, removing all entries. + /// + /// # Errors + /// + /// If an error occurs when interacting with the storage system, or a child + /// [`Element`](crate::entities::Element) cannot be found, an error will be + /// returned. + /// + pub fn clear(&mut self) -> Result<(), StoreError> { + let entries = Interface::children_of(self.id(), &self.entries)?; + + for entry in entries { + let _ = Interface::remove_child_from(self.id(), &mut self.entries, entry.id())?; + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use crate::collections::unordered_map::UnorderedMap; + + #[test] + fn test_unordered_map_basic_operations() { + let mut map = UnorderedMap::::new().expect("failed to create map"); + + assert!(map + .insert("key".to_string(), "value".to_string()) + .expect("insert failed") + .is_none()); + + assert_eq!( + map.get("key").expect("get failed"), + Some("value".to_string()) + ); + assert_ne!( + map.get("key").expect("get failed"), + Some("value2".to_string()) + ); + + assert_eq!( + map.insert("key".to_string(), "value2".to_string()) + .expect("insert failed"), + Some("value".to_string()) + ); + assert!(map + .insert("key2".to_string(), "value".to_string()) + .expect("insert failed") + .is_none()); + + assert_eq!( + map.get("key").expect("get failed"), + Some("value2".to_string()) + ); + assert_eq!( + map.get("key2").expect("get failed"), + Some("value".to_string()) + ); + + assert_eq!(map.remove("key").expect("error while removing key"), true); + assert_eq!(map.remove("key").expect("error while removing key"), false); + + assert_eq!(map.get("key").expect("get failed"), None); + } + + #[test] + fn test_unordered_map_insert_and_get() { + let mut map = UnorderedMap::::new().expect("failed to create map"); + + assert!(map + .insert("key1".to_string(), "value1".to_string()) + .expect("insert failed") + .is_none()); + assert!(map + .insert("key2".to_string(), "value2".to_string()) + .expect("insert failed") + .is_none()); + + assert_eq!( + map.get("key1").expect("get failed"), + Some("value1".to_string()) + ); + assert_eq!( + map.get("key2").expect("get failed"), + Some("value2".to_string()) + ); + } + + #[test] + fn test_unordered_map_update_value() { + let mut map = UnorderedMap::::new().expect("failed to create map"); + + assert!(map + .insert("key".to_string(), "value".to_string()) + .expect("insert failed") + .is_none()); + assert!(!map + .insert("key".to_string(), "new_value".to_string()) + .expect("insert failed") + .is_none()); + + assert_eq!( + map.get("key").expect("get failed"), + Some("new_value".to_string()) + ); + } + + #[test] + fn test_remove() { + let mut map = UnorderedMap::::new().expect("failed to create map"); + + assert!(map + .insert("key".to_string(), "value".to_string()) + .expect("insert failed") + .is_none()); + + assert_eq!(map.remove("key").expect("remove failed"), true); + assert_eq!(map.get("key").expect("get failed"), None); + } + + #[test] + fn test_clear() { + let mut map = UnorderedMap::::new().expect("failed to create map"); + + assert!(map + .insert("key1".to_string(), "value1".to_string()) + .expect("insert failed") + .is_none()); + assert!(map + .insert("key2".to_string(), "value2".to_string()) + .expect("insert failed") + .is_none()); + + map.clear().expect("clear failed"); + + assert_eq!(map.get("key1").expect("get failed"), None); + assert_eq!(map.get("key2").expect("get failed"), None); + } + + #[test] + fn test_unordered_map_len() { + let mut map = UnorderedMap::::new().expect("failed to create map"); + + assert_eq!(map.len().expect("len failed"), 0); + + assert!(map + .insert("key1".to_string(), "value1".to_string()) + .expect("insert failed") + .is_none()); + assert!(map + .insert("key2".to_string(), "value2".to_string()) + .expect("insert failed") + .is_none()); + assert!(!map + .insert("key2".to_string(), "value3".to_string()) + .expect("insert failed") + .is_none()); + + assert_eq!(map.len().expect("len failed"), 2); + + assert_eq!(map.remove("key1").expect("remove failed"), true); + + assert_eq!(map.len().expect("len failed"), 1); + } + + #[test] + fn test_unordered_map_contains() { + let mut map = UnorderedMap::::new().expect("failed to create map"); + + assert!(map + .insert("key".to_string(), "value".to_string()) + .expect("insert failed") + .is_none()); + + assert_eq!(map.contains("key").expect("contains failed"), true); + assert_eq!(map.contains("nonexistent").expect("contains failed"), false); + } + + #[test] + fn test_unordered_map_entries() { + let mut map = UnorderedMap::::new().expect("failed to create map"); + + assert!(map + .insert("key1".to_string(), "value1".to_string()) + .expect("insert failed") + .is_none()); + assert!(map + .insert("key2".to_string(), "value2".to_string()) + .expect("insert failed") + .is_none()); + assert!(!map + .insert("key2".to_string(), "value3".to_string()) + .expect("insert failed") + .is_none()); + + let entries: Vec<(String, String)> = map.entries().expect("entries failed").collect(); + + assert_eq!(entries.len(), 2); + assert!(entries.contains(&("key1".to_string(), "value1".to_string()))); + assert!(entries.contains(&("key2".to_string(), "value3".to_string()))); + } +} diff --git a/crates/storage/src/collections/unordered_set.rs b/crates/storage/src/collections/unordered_set.rs new file mode 100644 index 000000000..378ef5bb7 --- /dev/null +++ b/crates/storage/src/collections/unordered_set.rs @@ -0,0 +1,270 @@ +use core::borrow::Borrow; +use core::marker::PhantomData; + +use borsh::{BorshDeserialize, BorshSerialize}; +use sha2::{Digest, Sha256}; + +// fixme! macro expects `calimero_storage` to be in deps +use crate as calimero_storage; +use crate::address::{Id, Path}; +use crate::collections::error::StoreError; +use crate::entities::{Data, Element}; +use crate::interface::Interface; +use crate::{AtomicUnit, Collection}; + +/// A set collection that stores unqiue values once. +#[derive(AtomicUnit, Clone, Debug, Eq, PartialEq, PartialOrd)] +#[type_id(253)] +#[root] +pub struct UnorderedSet { + /// The prefix used for the set's entries. + id: Id, + /// The entries in the set. + entries: Entries, + /// The storage element for the set. + #[storage] + storage: Element, +} + +/// A collection of entries in a set. +#[derive(Collection, Clone, Debug, Eq, PartialEq, PartialOrd)] +#[children(Entry)] +struct Entries { + /// Helper to associate the generic types with the collection. + _priv: PhantomData, +} + +/// An entry in a set. +#[derive(AtomicUnit, Clone, Debug, Eq, PartialEq, PartialOrd)] +#[type_id(252)] +pub struct Entry { + /// The value for the entry. + value: V, + /// The storage element for the entry. + #[storage] + storage: Element, +} + +impl UnorderedSet { + /// Create a new set collection. + /// + /// # Errors + /// + /// If an error occurs when interacting with the storage system, or a child + /// [`Element`](crate::entities::Element) cannot be found, an error will be + /// returned. + /// + pub fn new() -> Result { + let id = Id::random(); + let mut this = Self { + id: id, + entries: Entries::default(), + storage: Element::new(&Path::new(format!("::unused::set::{id}::path"))?, Some(id)), + }; + + let _ = Interface::save(&mut this)?; + + Ok(this) + } + + /// Compute the ID for a value in the set. + fn compute_id(&self, value: &[u8]) -> Id { + let mut hasher = Sha256::new(); + hasher.update(self.id.as_bytes()); + hasher.update(value); + Id::new(hasher.finalize().into()) + } + + /// Insert a value pair into the set collection if the element does not already exist. + /// + /// # Errors + /// + /// If an error occurs when interacting with the storage system, or a child + /// [`Element`](crate::entities::Element) cannot be found, an error will be + /// returned. + /// + pub fn insert(&mut self, value: V) -> Result + where + V: AsRef<[u8]> + PartialEq, + { + let path = self.path(); + + if self.contains(&value)? { + return Ok(false); + } + + let storage = Element::new(&path, Some(self.compute_id(value.as_ref()))); + let _ = Interface::add_child_to( + self.storage.id(), + &mut self.entries, + &mut Entry { value, storage }, + )?; + + Ok(true) + } + + /// Get an iterator over the entries in the set. + /// + /// # Errors + /// + /// If an error occurs when interacting with the storage system, or a child + /// [`Element`](crate::entities::Element) cannot be found, an error will be + /// returned. + /// + pub fn entries(&self) -> Result, StoreError> { + let entries = Interface::children_of(self.id(), &self.entries)?; + + Ok(entries.into_iter().map(|entry| entry.value)) + } + + /// Get the number of entries in the set. + /// + /// # Errors + /// + /// If an error occurs when interacting with the storage system, or a child + /// [`Element`](crate::entities::Element) cannot be found, an error will be + /// returned. + /// + pub fn len(&self) -> Result { + Ok(Interface::child_info_for(self.id(), &self.entries)?.len()) + } + + /// Get the value for a key in the set. + /// + /// # Errors + /// + /// If an error occurs when interacting with the storage system, or a child + /// [`Element`](crate::entities::Element) cannot be found, an error will be + /// returned. + /// + pub fn contains(&self, value: &Q) -> Result + where + V: Borrow, + Q: PartialEq + ?Sized + AsRef<[u8]>, + { + let entry = Interface::find_by_id::>(self.compute_id(value.as_ref()))?; + Ok(entry.is_some()) + } + + /// Remove a key from the set, returning the value at the key if it previously existed. + /// + /// # Errors + /// + /// If an error occurs when interacting with the storage system, or a child + /// [`Element`](crate::entities::Element) cannot be found, an error will be + /// returned. + /// + pub fn remove(&mut self, value: &Q) -> Result + where + V: Borrow, + Q: PartialEq + AsRef<[u8]> + ?Sized, + { + let entry = Element::new(&self.path(), Some(self.compute_id(value.as_ref()))); + + Ok(Interface::remove_child_from( + self.id(), + &mut self.entries, + entry.id(), + )?) + } + + /// Clear the set, removing all entries. + /// + /// # Errors + /// + /// If an error occurs when interacting with the storage system, or a child + /// [`Element`](crate::entities::Element) cannot be found, an error will be + /// returned. + /// + pub fn clear(&mut self) -> Result<(), StoreError> { + let entries = Interface::children_of(self.id(), &self.entries)?; + + for entry in entries { + let _ = Interface::remove_child_from(self.id(), &mut self.entries, entry.id())?; + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use crate::collections::UnorderedSet; + + #[test] + fn test_unordered_set_operations() { + let mut set = UnorderedSet::::new().expect("failed to create set"); + + assert!(set.insert("value1".to_string()).expect("insert failed")); + + assert_eq!( + set.contains(&"value1".to_string()) + .expect("contains failed"), + true + ); + + assert!(!set.insert("value1".to_string()).expect("insert failed")); + assert!(set.insert("value2".to_string()).expect("insert failed")); + + assert_eq!(set.contains("value3").expect("get failed"), false); + assert_eq!(set.contains("value2").expect("get failed"), true); + + assert_eq!( + set.remove("value1").expect("error while removing key"), + true + ); + assert_eq!( + set.remove("value3").expect("error while removing key"), + false + ); + } + + #[test] + fn test_unordered_set_len() { + let mut set = UnorderedSet::::new().expect("failed to create set"); + + assert!(set.insert("value1".to_string()).expect("insert failed")); + assert!(set.insert("value2".to_string()).expect("insert failed")); + assert!(!set.insert("value2".to_string()).expect("insert failed")); + + assert_eq!(set.len().expect("len failed"), 2); + + assert!(set.remove("value1").expect("remove failed")); + + assert_eq!(set.len().expect("len failed"), 1); + } + + #[test] + fn test_unordered_set_clear() { + let mut set = UnorderedSet::::new().expect("failed to create set"); + + assert!(set.insert("value1".to_string()).expect("insert failed")); + assert!(set.insert("value2".to_string()).expect("insert failed")); + + assert_eq!(set.len().expect("len failed"), 2); + + set.clear().expect("clear failed"); + + assert_eq!(set.len().expect("len failed"), 0); + assert_eq!(set.contains("value1").expect("contains failed"), false); + assert_eq!(set.contains("value2").expect("contains failed"), false); + } + + #[test] + fn test_unordered_set_entries() { + let mut set = UnorderedSet::::new().expect("failed to create set"); + + assert!(set.insert("value1".to_string()).expect("insert failed")); + assert!(set.insert("value2".to_string()).expect("insert failed")); + + let entries: Vec = set.entries().expect("entries failed").collect(); + + assert_eq!(entries.len(), 2); + assert!(entries.contains(&"value1".to_string())); + assert!(entries.contains(&"value2".to_string())); + + assert!(set.remove("value1").expect("remove failed")); + let entries: Vec = set.entries().expect("entries failed").collect(); + assert_eq!(entries.len(), 1); + } +} diff --git a/crates/storage/src/collections/vector.rs b/crates/storage/src/collections/vector.rs new file mode 100644 index 000000000..4454d337b --- /dev/null +++ b/crates/storage/src/collections/vector.rs @@ -0,0 +1,321 @@ +use core::borrow::Borrow; +use core::marker::PhantomData; + +use borsh::{BorshDeserialize, BorshSerialize}; + +// fixme! macro expects `calimero_storage` to be in deps +use crate::address::{Id, Path}; +use crate::collections::error::StoreError; +use crate::entities::{Data, Element}; +use crate::interface::{Interface, StorageError}; +use crate::{self as calimero_storage, AtomicUnit, Collection}; + +/// A vector collection that stores key-value pairs. +#[derive(AtomicUnit, Clone, Debug, Eq, PartialEq, PartialOrd)] +#[type_id(251)] +#[root] +pub struct Vector { + /// The entries in the vector. + entries: Entries, + /// The storage element for the vector. + #[storage] + storage: Element, +} + +/// A collection of entries in a vector. +#[derive(Collection, Clone, Debug, Eq, PartialEq, PartialOrd)] +#[children(Entry)] +struct Entries { + /// Helper to associate the generic types with the collection. + _priv: PhantomData, +} + +/// An entry in a vector. +#[derive(AtomicUnit, Clone, Debug, Eq, PartialEq, PartialOrd)] +#[type_id(250)] +pub struct Entry { + /// The value for the entry. + value: V, + /// The storage element for the entry. + #[storage] + storage: Element, +} + +impl Vector { + /// Create a new vector collection. + /// + /// # Errors + /// + /// If an error occurs when interacting with the storage system, or a child + /// [`Element`](crate::entities::Element) cannot be found, an error will be + /// returned. + /// + pub fn new() -> Result { + let id = Id::random(); + let mut this = Self { + entries: Entries::default(), + storage: Element::new( + &Path::new(format!("::unused::vector::{id}::path"))?, + Some(id), + ), + }; + + let _ = Interface::save(&mut this)?; + + Ok(this) + } + + /// Add a value to the end of the vector. + /// + /// # Errors + /// + /// If an error occurs when interacting with the storage system, or a child + /// [`Element`](crate::entities::Element) cannot be found, an error will be + /// returned. + /// + pub fn push(&mut self, value: V) -> Result<(), StoreError> { + let mut entry = Entry { + value, + storage: Element::new(&self.path(), None), + }; + + if Interface::add_child_to(self.id(), &mut self.entries, &mut entry)? { + return Ok(()); + } + + Err(StoreError::StorageError(StorageError::ActionNotAllowed( + "Failed to add child".to_owned(), + ))) + } + + /// Remove and return the last value from the vector. + /// + /// # Errors + /// + /// If an error occurs when interacting with the storage system, or a child + /// [`Element`](crate::entities::Element) cannot be found, an error will be + /// returned. + /// + pub fn pop(&mut self) -> Result, StoreError> { + let id = match Interface::child_info_for(self.id(), &self.entries)?.pop() { + Some(info) => info.id(), + None => return Ok(None), + }; + let entry = Interface::find_by_id::>(id)?; + let _ = Interface::remove_child_from(self.id(), &mut self.entries, id)?; + Ok(entry.map(|e| e.value)) + } + + /// Get the raw storage entry at a specific index in the vector. + /// + /// # Errors + /// + /// If an error occurs when interacting with the storage system, or a child + /// [`Element`](crate::entities::Element) cannot be found, an error will be + /// returned. + /// + fn get_raw(&self, index: usize) -> Result>, StoreError> { + let id = match Interface::child_info_for(self.id(), &self.entries)?.get(index) { + Some(info) => info.id(), + None => return Ok(None), + }; + + let entry = match Interface::find_by_id::>(id) { + Ok(entry) => entry, + Err(_) => return Ok(None), + }; + + Ok(entry) + } + + /// Get the value at a specific index in the vector. + /// + /// # Errors + /// + /// If an error occurs when interacting with the storage system, or a child + /// [`Element`](crate::entities::Element) cannot be found, an error will be + /// returned. + /// + pub fn get(&self, index: usize) -> Result, StoreError> { + Ok(self.get_raw(index)?.map(|entry| entry.value)) + } + + /// Update the value at a specific index in the vector. + /// + /// # Errors + /// + /// If an error occurs when interacting with the storage system, or a child + /// [`Element`](crate::entities::Element) cannot be found, an error will be + /// returned. + /// + pub fn update(&mut self, index: usize, value: V) -> Result<(), StoreError> { + let mut entry = self.get_raw(index)?.ok_or(StoreError::StorageError( + StorageError::ActionNotAllowed("error".to_owned()), + ))?; + + // has to be called to update the entry + entry.value = value; + entry.element_mut().update(); + + let _ = Interface::save::>(&mut entry)?; + + Ok(()) + } + + /// Get an iterator over the entries in the vector. + /// + /// # Errors + /// + /// If an error occurs when interacting with the storage system, or a child + /// [`Element`](crate::entities::Element) cannot be found, an error will be + /// returned. + /// + pub fn entries(&self) -> Result, StoreError> { + let entries = Interface::children_of(self.id(), &self.entries)?; + + Ok(entries.into_iter().map(|entry| (entry.value))) + } + + /// Get the number of entries in the vector. + /// + /// # Errors + /// + /// If an error occurs when interacting with the storage system, or a child + /// [`Element`](crate::entities::Element) cannot be found, an error will be + /// returned. + /// + #[expect(clippy::len_without_is_empty, reason = "TODO: will be implemented")] + pub fn len(&self) -> Result { + Ok(Interface::child_info_for(self.id(), &self.entries)?.len()) + } + + /// Get the value for a key in the vector. + /// + /// # Errors + /// + /// If an error occurs when interacting with the storage system, or a child + /// [`Element`](crate::entities::Element) cannot be found, an error will be + /// returned. + /// + pub fn contains(&self, value: &Q) -> Result + where + V: Borrow, + Q: PartialEq + ?Sized, + { + for entry in self.entries()? { + if value.borrow() == &entry { + return Ok(true); + } + } + + Ok(false) + } + + /// Clear the vector, removing all entries. + /// + /// # Errors + /// + /// If an error occurs when interacting with the storage system, or a child + /// [`Element`](crate::entities::Element) cannot be found, an error will be + /// returned. + /// + pub fn clear(&mut self) -> Result<(), StoreError> { + let entries = Interface::children_of(self.id(), &self.entries)?; + + for entry in entries { + let _ = Interface::remove_child_from(self.id(), &mut self.entries, entry.id())?; + } + + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use crate::collections::error::StoreError; + use crate::collections::vector::Vector; + + #[test] + fn test_vector_new() { + let vector: Result, StoreError> = Vector::new(); + assert!(vector.is_ok()); + } + + #[test] + fn test_vector_push() { + let mut vector: Vector = Vector::new().unwrap(); + let value = "test_data".to_string(); + let result = vector.push(value.clone()); + assert!(result.is_ok()); + assert_eq!(vector.len().unwrap(), 1); + } + + #[test] + fn test_vector_get() { + let mut vector: Vector = Vector::new().unwrap(); + let value = "test_data".to_string(); + let _ = vector.push(value.clone()).unwrap(); + let retrieved_value = vector.get(0).unwrap(); + assert_eq!(retrieved_value, Some(value)); + } + + #[test] + fn test_vector_update() { + let mut vector: Vector = Vector::new().unwrap(); + let value1 = "test_data1".to_string(); + let value2 = "test_data2".to_string(); + let _ = vector.push(value1.clone()).unwrap(); + let _ = vector.update(0, value2.clone()).unwrap(); + let retrieved_value = vector.get(0).unwrap(); + assert_eq!(retrieved_value, Some(value2)); + } + + #[test] + fn test_vector_get_non_existent() { + let vector: Vector = Vector::new().unwrap(); + match vector.get(0) { + Ok(retrieved_value) => assert_eq!(retrieved_value, None), + Err(e) => panic!("Error occurred: {:?}", e), + } + } + + #[test] + fn test_vector_pop() { + let mut vector: Vector = Vector::new().unwrap(); + let value = "test_data".to_string(); + let _ = vector.push(value.clone()).unwrap(); + let popped_value = vector.pop().unwrap(); + assert_eq!(popped_value, Some(value)); + assert_eq!(vector.len().unwrap(), 0); + } + + #[test] + fn test_vector_entries() { + let mut vector: Vector = Vector::new().unwrap(); + let value1 = "test_data1".to_string(); + let value2 = "test_data2".to_string(); + let _ = vector.push(value1.clone()).unwrap(); + let _ = vector.push(value2.clone()).unwrap(); + let entries: Vec = vector.entries().unwrap().collect(); + assert_eq!(entries, vec![value1, value2]); + } + + #[test] + fn test_vector_contains() { + let mut vector: Vector = Vector::new().unwrap(); + let value = "test_data".to_string(); + let _ = vector.push(value.clone()).unwrap(); + assert!(vector.contains(&value).unwrap()); + let non_existent_value = "non_existent".to_string(); + assert!(!vector.contains(&non_existent_value).unwrap()); + } + + #[test] + fn test_vector_clear() { + let mut vector: Vector = Vector::new().unwrap(); + let value = "test_data".to_string(); + let _ = vector.push(value.clone()).unwrap(); + vector.clear().unwrap(); + assert_eq!(vector.len().unwrap(), 0); + } +} diff --git a/crates/storage/src/entities.rs b/crates/storage/src/entities.rs index 7a3fb7717..5de099efa 100644 --- a/crates/storage/src/entities.rs +++ b/crates/storage/src/entities.rs @@ -644,6 +644,7 @@ impl Element { /// # Parameters /// /// * `path` - The path to the [`Element`] in the hierarchy of the storage. + /// * `id` - The id of the [`Element`] in the hierarchy of the storage. /// /// # Panics /// @@ -651,10 +652,11 @@ impl Element { /// before the Unix epoch, which should never ever happen! /// #[must_use] - pub fn new(path: &Path) -> Self { + pub fn new(path: &Path, id: Option) -> Self { let timestamp = time_now(); + let element_id = id.unwrap_or_else(Id::random); Self { - id: Id::random(), + id: element_id, is_dirty: true, metadata: Metadata { created_at: timestamp, diff --git a/crates/storage/src/lib.rs b/crates/storage/src/lib.rs index 1a61a882b..206f248a6 100644 --- a/crates/storage/src/lib.rs +++ b/crates/storage/src/lib.rs @@ -62,13 +62,13 @@ )] pub mod address; +pub mod collections; pub mod entities; pub mod env; pub mod index; pub mod integration; pub mod interface; pub mod store; -pub mod types; pub use calimero_storage_macros::{AtomicUnit, Collection}; diff --git a/crates/storage/src/tests/entities.rs b/crates/storage/src/tests/entities.rs index b6987305c..6db091247 100644 --- a/crates/storage/src/tests/entities.rs +++ b/crates/storage/src/tests/entities.rs @@ -28,7 +28,7 @@ mod data__public_methods { #[test] fn calculate_merkle_hash() { - let element = Element::new(&Path::new("::root::node::leaf").unwrap()); + let element = Element::new(&Path::new("::root::node::leaf").unwrap(), None); let person = Person { name: "Alice".to_owned(), age: 30, @@ -47,9 +47,9 @@ mod data__public_methods { #[test] fn calculate_merkle_hash_for_child__valid() { - let parent = Element::new(&Path::new("::root::node").unwrap()); + let parent = Element::new(&Path::new("::root::node").unwrap(), None); let mut page = Page::new_from_element("Node", parent); - let child1 = Element::new(&Path::new("::root::node::leaf").unwrap()); + let child1 = Element::new(&Path::new("::root::node::leaf").unwrap(), None); let mut para1 = Paragraph::new_from_element("Leaf1", child1); assert!(Interface::save(&mut page).unwrap()); @@ -61,7 +61,7 @@ mod data__public_methods { let expected_hash1 = para1.calculate_merkle_hash().unwrap(); assert_eq!(para1_hash, expected_hash1); - let child2 = Element::new(&Path::new("::root::node::leaf").unwrap()); + let child2 = Element::new(&Path::new("::root::node::leaf").unwrap(), None); let para2 = Paragraph::new_from_element("Leaf2", child2); let para2_slice = to_vec(¶2).unwrap(); let para2_hash = page @@ -72,9 +72,9 @@ mod data__public_methods { #[test] fn calculate_merkle_hash_for_child__invalid() { - let parent = Element::new(&Path::new("::root::node").unwrap()); + let parent = Element::new(&Path::new("::root::node").unwrap(), None); let mut page = Page::new_from_element("Node", parent); - let child1 = Element::new(&Path::new("::root::node::leaf").unwrap()); + let child1 = Element::new(&Path::new("::root::node::leaf").unwrap(), None); let mut para1 = Paragraph::new_from_element("Leaf1", child1); assert!(Interface::save(&mut page).unwrap()); @@ -86,9 +86,9 @@ mod data__public_methods { #[test] fn calculate_merkle_hash_for_child__unknown_collection() { - let parent = Element::new(&Path::new("::root::node").unwrap()); + let parent = Element::new(&Path::new("::root::node").unwrap(), None); let mut page = Page::new_from_element("Node", parent); - let child = Element::new(&Path::new("::root::node::leaf").unwrap()); + let child = Element::new(&Path::new("::root::node::leaf").unwrap(), None); let mut para = Paragraph::new_from_element("Leaf", child); assert!(Interface::save(&mut page).unwrap()); @@ -103,7 +103,7 @@ mod data__public_methods { #[test] fn collections() { - let parent = Element::new(&Path::new("::root::node").unwrap()); + let parent = Element::new(&Path::new("::root::node").unwrap(), None); let page = Page::new_from_element("Node", parent); assert_eq!( page.collections(), @@ -112,7 +112,7 @@ mod data__public_methods { } ); - let child = Element::new(&Path::new("::root::node::leaf").unwrap()); + let child = Element::new(&Path::new("::root::node::leaf").unwrap(), None); let para = Paragraph::new_from_element("Leaf", child); assert_eq!(para.collections(), BTreeMap::new()); } @@ -120,7 +120,7 @@ mod data__public_methods { #[test] fn element() { let path = Path::new("::root::node::leaf").unwrap(); - let element = Element::new(&path); + let element = Element::new(&path, None); let person = Person { name: "Alice".to_owned(), age: 30, @@ -132,7 +132,7 @@ mod data__public_methods { #[test] fn element_mut() { let path = Path::new("::root::node::leaf").unwrap(); - let element = Element::new(&path); + let element = Element::new(&path, None); let mut person = Person { name: "Bob".to_owned(), age: 40, @@ -148,7 +148,7 @@ mod data__public_methods { #[test] fn id() { let path = Path::new("::root::node::leaf").unwrap(); - let element = Element::new(&path); + let element = Element::new(&path, None); let id = element.id; let person = Person { name: "Eve".to_owned(), @@ -161,7 +161,7 @@ mod data__public_methods { #[test] fn path() { let path = Path::new("::root::node::leaf").unwrap(); - let element = Element::new(&path); + let element = Element::new(&path, None); let person = Person { name: "Steve".to_owned(), age: 50, @@ -239,7 +239,7 @@ mod element__constructor { .unwrap() .as_nanos() as u64; let path = Path::new("::root::node::leaf").unwrap(); - let element = Element::new(&path); + let element = Element::new(&path, None); let timestamp2 = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() @@ -263,7 +263,7 @@ mod element__public_methods { .duration_since(UNIX_EPOCH) .unwrap() .as_nanos() as u64; - let element = Element::new(&Path::new("::root::node::leaf").unwrap()); + let element = Element::new(&Path::new("::root::node::leaf").unwrap(), None); let timestamp2 = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() @@ -275,13 +275,13 @@ mod element__public_methods { #[test] fn id() { let path = Path::new("::root::node::leaf").unwrap(); - let element = Element::new(&path); + let element = Element::new(&path, None); assert_eq!(element.id(), element.id); } #[test] fn is_dirty() { - let element = Element::new(&Path::new("::root::node::leaf").unwrap()); + let element = Element::new(&Path::new("::root::node::leaf").unwrap(), None); assert!(element.is_dirty()); let mut person = Person { @@ -298,7 +298,7 @@ mod element__public_methods { #[test] fn merkle_hash() { - let element = Element::new(&Path::new("::root::node::leaf").unwrap()); + let element = Element::new(&Path::new("::root::node::leaf").unwrap(), None); let mut person = Person { name: "Steve".to_owned(), age: 50, @@ -321,13 +321,13 @@ mod element__public_methods { #[test] fn path() { let path = Path::new("::root::node::leaf").unwrap(); - let element = Element::new(&path); + let element = Element::new(&path, None); assert_eq!(element.path(), element.path); } #[test] fn update() { - let element = Element::new(&Path::new("::root::node::leaf").unwrap()); + let element = Element::new(&Path::new("::root::node::leaf").unwrap(), None); let updated_at = element.metadata.updated_at; let mut person = Person { name: "Bob".to_owned(), @@ -348,7 +348,7 @@ mod element__public_methods { .duration_since(UNIX_EPOCH) .unwrap() .as_nanos() as u64; - let element = Element::new(&Path::new("::root::node::leaf").unwrap()); + let element = Element::new(&Path::new("::root::node::leaf").unwrap(), None); let mut person = Person { name: "Eve".to_owned(), age: 20, @@ -378,7 +378,7 @@ mod element__traits { #[test] fn display() { let path = Path::new("::root::node::leaf").unwrap(); - let element = Element::new(&path); + let element = Element::new(&path, None); assert_eq!( format!("{element}"), format!("Element {}: ::root::node::leaf", element.id()) diff --git a/crates/storage/src/tests/interface.rs b/crates/storage/src/tests/interface.rs index 45e24c7a6..941547027 100644 --- a/crates/storage/src/tests/interface.rs +++ b/crates/storage/src/tests/interface.rs @@ -14,7 +14,7 @@ mod interface__public_methods { #[test] fn children_of() { - let element = Element::new(&Path::new("::root::node").unwrap()); + let element = Element::new(&Path::new("::root::node").unwrap(), None); let mut page = Page::new_from_element("Node", element); assert!(Interface::save(&mut page).unwrap()); assert_eq!( @@ -22,9 +22,9 @@ mod interface__public_methods { vec![] ); - let child1 = Element::new(&Path::new("::root::node::leaf1").unwrap()); - let child2 = Element::new(&Path::new("::root::node::leaf2").unwrap()); - let child3 = Element::new(&Path::new("::root::node::leaf3").unwrap()); + let child1 = Element::new(&Path::new("::root::node::leaf1").unwrap(), None); + let child2 = Element::new(&Path::new("::root::node::leaf2").unwrap(), None); + let child3 = Element::new(&Path::new("::root::node::leaf3").unwrap(), None); let mut para1 = Paragraph::new_from_element("Leaf1", child1); let mut para2 = Paragraph::new_from_element("Leaf2", child2); let mut para3 = Paragraph::new_from_element("Leaf3", child3); @@ -40,7 +40,7 @@ mod interface__public_methods { #[test] fn find_by_id__existent() { - let element = Element::new(&Path::new("::root::node").unwrap()); + let element = Element::new(&Path::new("::root::node").unwrap(), None); let mut page = Page::new_from_element("Leaf", element); let id = page.id(); assert!(Interface::save(&mut page).unwrap()); @@ -73,7 +73,7 @@ mod interface__public_methods { #[test] fn save__basic() { - let element = Element::new(&Path::new("::root::node").unwrap()); + let element = Element::new(&Path::new("::root::node").unwrap(), None); let mut page = Page::new_from_element("Node", element); assert_ok!(Interface::save(&mut page)); @@ -81,8 +81,8 @@ mod interface__public_methods { #[test] fn save__multiple() { - let element1 = Element::new(&Path::new("::root::node1").unwrap()); - let element2 = Element::new(&Path::new("::root::node2").unwrap()); + let element1 = Element::new(&Path::new("::root::node1").unwrap(), None); + let element2 = Element::new(&Path::new("::root::node2").unwrap(), None); let mut page1 = Page::new_from_element("Node1", element1); let mut page2 = Page::new_from_element("Node2", element2); @@ -94,7 +94,7 @@ mod interface__public_methods { #[test] fn save__not_dirty() { - let element = Element::new(&Path::new("::root::node").unwrap()); + let element = Element::new(&Path::new("::root::node").unwrap(), None); let mut page = Page::new_from_element("Node", element); assert!(Interface::save(&mut page).unwrap()); @@ -104,7 +104,7 @@ mod interface__public_methods { #[test] fn save__too_old() { - let element1 = Element::new(&Path::new("::root::node").unwrap()); + let element1 = Element::new(&Path::new("::root::node").unwrap(), None); let mut page1 = Page::new_from_element("Node", element1); let mut page2 = page1.clone(); @@ -118,7 +118,7 @@ mod interface__public_methods { #[test] fn save__update_existing() { - let element = Element::new(&Path::new("::root::node").unwrap()); + let element = Element::new(&Path::new("::root::node").unwrap(), None); let mut page = Page::new_from_element("Node", element); let id = page.id(); assert!(Interface::save(&mut page).unwrap()); @@ -163,7 +163,10 @@ mod interface__apply_actions { #[test] fn apply_action__add() { - let page = Page::new_from_element("Test Page", Element::new(&Path::new("::test").unwrap())); + let page = Page::new_from_element( + "Test Page", + Element::new(&Path::new("::test").unwrap(), None), + ); let serialized = to_vec(&page).unwrap(); let action = Action::Add { id: page.id(), @@ -182,8 +185,10 @@ mod interface__apply_actions { #[test] fn apply_action__update() { - let mut page = - Page::new_from_element("Old Title", Element::new(&Path::new("::test").unwrap())); + let mut page = Page::new_from_element( + "Old Title", + Element::new(&Path::new("::test").unwrap(), None), + ); assert!(Interface::save(&mut page).unwrap()); page.title = "New Title".to_owned(); @@ -205,8 +210,10 @@ mod interface__apply_actions { #[test] fn apply_action__delete() { - let mut page = - Page::new_from_element("Test Page", Element::new(&Path::new("::test").unwrap())); + let mut page = Page::new_from_element( + "Test Page", + Element::new(&Path::new("::test").unwrap(), None), + ); assert!(Interface::save(&mut page).unwrap()); let action = Action::Delete { @@ -223,7 +230,10 @@ mod interface__apply_actions { #[test] fn apply_action__compare() { - let page = Page::new_from_element("Test Page", Element::new(&Path::new("::test").unwrap())); + let page = Page::new_from_element( + "Test Page", + Element::new(&Path::new("::test").unwrap(), None), + ); let action = Action::Compare { id: page.id() }; // Compare should fail @@ -232,7 +242,10 @@ mod interface__apply_actions { #[test] fn apply_action__wrong_type() { - let page = Page::new_from_element("Test Page", Element::new(&Path::new("::test").unwrap())); + let page = Page::new_from_element( + "Test Page", + Element::new(&Path::new("::test").unwrap(), None), + ); let serialized = to_vec(&page).unwrap(); let action = Action::Add { id: page.id(), @@ -247,7 +260,10 @@ mod interface__apply_actions { #[test] fn apply_action__non_existent_update() { - let page = Page::new_from_element("Test Page", Element::new(&Path::new("::test").unwrap())); + let page = Page::new_from_element( + "Test Page", + Element::new(&Path::new("::test").unwrap(), None), + ); let serialized = to_vec(&page).unwrap(); let action = Action::Update { id: page.id(), @@ -274,7 +290,7 @@ mod interface__comparison { #[test] fn compare_trees__identical() { - let element = Element::new(&Path::new("::root::node").unwrap()); + let element = Element::new(&Path::new("::root::node").unwrap(), None); let mut local = Page::new_from_element("Test Page", element); let mut foreign = local.clone(); @@ -295,7 +311,7 @@ mod interface__comparison { #[test] fn compare_trees__local_newer() { - let element = Element::new(&Path::new("::root::node").unwrap()); + let element = Element::new(&Path::new("::root::node").unwrap(), None); let mut local = Page::new_from_element("Test Page", element.clone()); let mut foreign = Page::new_from_element("Old Test Page", element); @@ -327,7 +343,7 @@ mod interface__comparison { #[test] fn compare_trees__foreign_newer() { - let element = Element::new(&Path::new("::root::node").unwrap()); + let element = Element::new(&Path::new("::root::node").unwrap(), None); let mut local = Page::new_from_element("Old Test Page", element.clone()); let mut foreign = Page::new_from_element("Test Page", element); @@ -359,10 +375,10 @@ mod interface__comparison { #[test] fn compare_trees__with_collections() { - let page_element = Element::new(&Path::new("::root::node").unwrap()); - let para1_element = Element::new(&Path::new("::root::node::leaf1").unwrap()); - let para2_element = Element::new(&Path::new("::root::node::leaf2").unwrap()); - let para3_element = Element::new(&Path::new("::root::node::leaf3").unwrap()); + let page_element = Element::new(&Path::new("::root::node").unwrap(), None); + let para1_element = Element::new(&Path::new("::root::node::leaf1").unwrap(), None); + let para2_element = Element::new(&Path::new("::root::node::leaf2").unwrap(), None); + let para3_element = Element::new(&Path::new("::root::node::leaf3").unwrap(), None); let mut local_page = Page::new_from_element("Local Page", page_element.clone()); let mut local_para1 = diff --git a/crates/storage/src/types.rs b/crates/storage/src/types.rs deleted file mode 100644 index 751d6c2d3..000000000 --- a/crates/storage/src/types.rs +++ /dev/null @@ -1,204 +0,0 @@ -//! High-level data structures for storage. - -use core::borrow::Borrow; -use core::marker::PhantomData; - -use borsh::{BorshDeserialize, BorshSerialize}; -use thiserror::Error; - -// fixme! macro expects `calimero_storage` to be in deps -use crate as calimero_storage; -use crate::address::{Path, PathError}; -use crate::entities::{Data, Element}; -use crate::interface::{Interface, StorageError}; -use crate::{AtomicUnit, Collection}; - -/// General error type for storage operations while interacting with complex collections. -#[derive(Debug, Error)] -#[non_exhaustive] -pub enum StoreError { - /// Error while interacting with storage. - #[error(transparent)] - StorageError(#[from] StorageError), - /// Error while interacting with a path. - #[error(transparent)] - PathError(#[from] PathError), -} - -/// A map collection that stores key-value pairs. -#[derive(AtomicUnit, Clone, Debug, Eq, PartialEq, PartialOrd)] -#[type_id(255)] -#[root] -pub struct Map { - /// The entries in the map. - entries: Entries, - /// The storage element for the map. - #[storage] - storage: Element, -} - -/// A collection of entries in a map. -#[derive(Collection, Clone, Debug, Eq, PartialEq, PartialOrd)] -#[children(Entry)] -struct Entries { - /// Helper to associate the generic types with the collection. - _priv: PhantomData<(K, V)>, -} - -/// An entry in a map. -#[derive(AtomicUnit, Clone, Debug, Eq, PartialEq, PartialOrd)] -#[type_id(254)] -pub struct Entry { - /// The key for the entry. - key: K, - /// The value for the entry. - value: V, - /// The storage element for the entry. - #[storage] - storage: Element, -} - -impl Map { - /// Create a new map collection. - /// - /// # Errors - /// - /// If an error occurs when interacting with the storage system, or a child - /// [`Element`](crate::entities::Element) cannot be found, an error will be - /// returned. - /// - pub fn new(path: &Path) -> Result { - let mut this = Self { - entries: Entries::default(), - storage: Element::new(path), - }; - - _ = Interface::save(&mut this)?; - - Ok(this) - } - - /// Insert a key-value pair into the map. - /// - /// # Errors - /// - /// If an error occurs when interacting with the storage system, or a child - /// [`Element`](crate::entities::Element) cannot be found, an error will be - /// returned. - /// - pub fn insert(&mut self, key: K, value: V) -> Result<(), StoreError> { - let path = self.path(); - // fixme! Reusing the Map's path for now. We "could" concatenate, but it's - // fixme! non-trivial and currently non-functional, so it's been left out - - let storage = Element::new(&path); - // fixme! This uses a random id for the map's entries, which will impair - // fixme! perf on the lookup, as we'd have to fetch and look through all - // fixme! entries to find the one that matches the key we're looking for - // fixme! ideally, the Id should be defined as hash(concat(map_id, key)) - // fixme! which will save on map-wide lookups, getting the item directly - - _ = Interface::add_child_to( - self.storage.id(), - &mut self.entries, - &mut Entry { - key, - value, - storage, - }, - )?; - - Ok(()) - } - - /// Get an iterator over the entries in the map. - /// - /// # Errors - /// - /// If an error occurs when interacting with the storage system, or a child - /// [`Element`](crate::entities::Element) cannot be found, an error will be - /// returned. - /// - pub fn entries(&self) -> Result, StoreError> { - let entries = Interface::children_of(self.id(), &self.entries)?; - - Ok(entries.into_iter().map(|entry| (entry.key, entry.value))) - } - - /// Get the number of entries in the map. - /// - /// # Errors - /// - /// If an error occurs when interacting with the storage system, or a child - /// [`Element`](crate::entities::Element) cannot be found, an error will be - /// returned. - /// - #[expect(clippy::len_without_is_empty, reason = "TODO: will be implemented")] - pub fn len(&self) -> Result { - Ok(Interface::child_info_for(self.id(), &self.entries)?.len()) - } - - /// Get the value for a key in the map. - /// - /// # Errors - /// - /// If an error occurs when interacting with the storage system, or a child - /// [`Element`](crate::entities::Element) cannot be found, an error will be - /// returned. - /// - pub fn get(&self, key: &Q) -> Result, StoreError> - where - K: Borrow, - Q: PartialEq + ?Sized, - { - for (key_, value) in self.entries()? { - if key_.borrow() == key { - return Ok(Some(value)); - } - } - - Ok(None) - } - - /// Remove a key from the map, returning the value at the key if it previously existed. - /// - /// # Errors - /// - /// If an error occurs when interacting with the storage system, or a child - /// [`Element`](crate::entities::Element) cannot be found, an error will be - /// returned. - /// - pub fn remove(&mut self, key: &Q) -> Result, StoreError> - where - K: Borrow, - Q: PartialEq + ?Sized, - { - let entries = Interface::children_of(self.id(), &self.entries)?; - - let entry_opt = entries.into_iter().find(|entry| entry.key.borrow() == key); - - if let Some(ref entry) = entry_opt { - _ = Interface::remove_child_from(self.id(), &mut self.entries, entry.id())?; - } - - Ok(entry_opt.map(|entry| entry.value)) - } - - /// Clear the map, removing all entries. - /// - /// # Errors - /// - /// If an error occurs when interacting with the storage system, or a child - /// [`Element`](crate::entities::Element) cannot be found, an error will be - /// returned. - /// - pub fn clear(&mut self) -> Result<(), StoreError> { - let entries = Interface::children_of(self.id(), &self.entries)?; - - for entry in entries { - _ = Interface::remove_child_from(self.id(), &mut self.entries, entry.id())?; - } - - Ok(()) - } -}