diff --git a/crates/cw-storey/src/backend.rs b/crates/cw-storey/src/backend.rs index 1aa3c76..112eb06 100644 --- a/crates/cw-storey/src/backend.rs +++ b/crates/cw-storey/src/backend.rs @@ -1,5 +1,6 @@ use storey::storage::{StorageBackend, StorageBackendMut}; +/// A wrapper around a type implementing [`cosmwasm_std::Storage`] that integrates it with [`storey`]. pub struct CwStorage(pub S); impl StorageBackend for CwStorage diff --git a/crates/cw-storey/src/containers.rs b/crates/cw-storey/src/containers.rs index 59963e2..7bf97f4 100644 --- a/crates/cw-storey/src/containers.rs +++ b/crates/cw-storey/src/containers.rs @@ -1,4 +1,17 @@ +//! Storage containers for use with [*CosmWasm*] smart contracts. +//! +//! [*CosmWasm*]: https://github.com/CosmWasm/cosmwasm + +/// The [`storey::containers::Item`] type with the default encoding for [*CosmWasm*] smart +/// contracts. +/// +/// [*CosmWasm*]: https://github.com/CosmWasm/cosmwasm pub type Item = storey::containers::Item; + +/// The [`storey::containers::Column`] type with the default encoding for [*CosmWasm*] smart +/// contracts. +/// +/// [*CosmWasm*]: https://github.com/CosmWasm/cosmwasm pub type Column = storey::containers::Column; pub use storey::containers::Map; diff --git a/crates/cw-storey/src/encoding.rs b/crates/cw-storey/src/encoding.rs index 88d8c2a..24a5c05 100644 --- a/crates/cw-storey/src/encoding.rs +++ b/crates/cw-storey/src/encoding.rs @@ -1,5 +1,14 @@ use storey::encoding::{Cover, DecodableWithImpl, EncodableWithImpl, Encoding}; +/// A simple encoding that uses [*MessagePack*] to encode and decode data. +/// +/// This type implements the [`Encoding`] trait (see [`storey::encoding`]), which means it can +/// be used with some of [`storey`]'s containers to encode and decode values. +/// +/// You're unlikely to need to use this type directly for basic library usage. You might +/// need it if you're trying to use third-party containers this crate does not provide. +/// +/// [*MessagePack*]: https://msgpack.org/ pub struct CwEncoding; impl Encoding for CwEncoding { diff --git a/crates/cw-storey/src/lib.rs b/crates/cw-storey/src/lib.rs index b58563f..3dd7878 100644 --- a/crates/cw-storey/src/lib.rs +++ b/crates/cw-storey/src/lib.rs @@ -1,5 +1,18 @@ +//! An integration of [`storey`] with [*CosmWasm*]. +//! +//! This crate provides +//! - a [*CosmWasm*] storage backend for use with [`storey`] collections, +//! - a [*MessagePack*] encoding integration to be used for serializing and deserializing +//! values, and +//! - a set of container re-exports that remove the need to manually specify the +//! encoding, instead relying on the default [*MessagePack*] encoding. +//! +//! [*CosmWasm*]: https://github.com/CosmWasm/cosmwasm +//! [*MessagePack*]: https://msgpack.org/ + mod backend; pub mod containers; mod encoding; pub use backend::CwStorage; +pub use encoding::CwEncoding; diff --git a/crates/storey-encoding/src/lib.rs b/crates/storey-encoding/src/lib.rs index 915bee2..15317f5 100644 --- a/crates/storey-encoding/src/lib.rs +++ b/crates/storey-encoding/src/lib.rs @@ -1,5 +1,8 @@ pub trait Encoding { + /// The error type returned when encoding fails. type EncodeError; + + /// The error type returned when decoding fails. type DecodeError; } diff --git a/crates/storey-storage/src/backend.rs b/crates/storey-storage/src/backend.rs index f112eeb..e15b957 100644 --- a/crates/storey-storage/src/backend.rs +++ b/crates/storey-storage/src/backend.rs @@ -1,15 +1,30 @@ use super::storage::{Storage, StorageMut}; +/// A trait for immutably accessing a storage backend. +/// +/// A collection of basic read operations that can be performed on a storage backend. +/// +/// You should only have to interact with this trait if you are implementing a custom storage backend. pub trait StorageBackend { + /// Get the value associated with the given key. fn get(&self, key: &[u8]) -> Option>; + /// Check if the given key exists in the storage backend. fn has(&self, key: &[u8]) -> bool { self.get(key).is_some() } } +/// A trait for mutably accessing a storage backend. +/// +/// A collection of basic write operations that can be performed on a storage backend. +/// +/// You should only have to interact with this trait if you are implementing a custom storage backend. pub trait StorageBackendMut { + /// Set the value associated with the given key. fn set(&mut self, key: &[u8], value: &[u8]); + + /// Remove the value associated with the given key. fn remove(&mut self, key: &[u8]); } diff --git a/crates/storey-storage/src/storage.rs b/crates/storey-storage/src/storage.rs index 88ca72a..28c8f2f 100644 --- a/crates/storey-storage/src/storage.rs +++ b/crates/storey-storage/src/storage.rs @@ -1,37 +1,87 @@ +/// A read interface for binary key-value storage. pub trait Storage { + /// Get the value of the key. fn get(&self, key: &[u8]) -> Option>; + /// Check if the key exists. fn has(&self, key: &[u8]) -> bool { self.get(key).is_some() } + /// Get the value of the key in the metadata namespace. fn get_meta(&self, _key: &[u8]) -> Option>; + + /// Check if the key exists in the metadata namespace. fn has_meta(&self, key: &[u8]) -> bool { self.get_meta(key).is_some() } } +/// A write interface for binary key-value storage. pub trait StorageMut { + /// Set the value of the key. fn set(&mut self, key: &[u8], value: &[u8]); + + /// Remove the key. fn remove(&mut self, key: &[u8]); + /// Set the value of the key in the metadata namespace. fn set_meta(&mut self, _key: &[u8], _value: &[u8]); + + /// Remove the key in the metadata namespace. fn remove_meta(&mut self, _key: &[u8]); } +/// Iteration interface for binary key-value storage. +/// +/// The iterator should iterate over key-value pairs in lexicographical order of keys. pub trait IterableStorage { + /// The type of the iterator returned by [`keys`](Self::keys). type KeysIterator<'a>: Iterator> where Self: 'a; + + /// The type of the iterator returned by [`values`](Self::values). type ValuesIterator<'a>: Iterator> where Self: 'a; + + /// The type of the iterator returned by [`pairs`](Self::pairs). type PairsIterator<'a>: Iterator, Vec)> where Self: 'a; + /// Get an iterator over keys. + /// + /// The iterator should iterate over keys in lexicographical order. + /// + /// If `start` is `None`, the iterator should start from the first key. + /// If `end` is `None`, the iterator should iterate until the last key. + /// If both `start` and `end` are `None`, the iterator should iterate over all keys. + /// + /// The range is inclusive for `start` and exclusive for `end`. fn keys<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::KeysIterator<'a>; + + /// Get an iterator over values. + /// + /// The iterator should iterate over values corresponding to keys in lexicographical order. + /// + /// If `start` is `None`, the iterator should start from the first key. + /// If `end` is `None`, the iterator should iterate until the last key. + /// If both `start` and `end` are `None`, the iterator should iterate over all keys. + /// + /// The range is inclusive for `start` and exclusive for `end`. fn values<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::ValuesIterator<'a>; + + /// Get an iterator over key-value pairs. + /// + /// The iterator should iterate over key-value pairs in lexicographical order. + /// + /// If `start` is `None`, the iterator should start from the first key. + /// If `end` is `None`, the iterator should iterate until the last key. + /// If both `start` and `end` are `None`, the iterator should iterate over all keys. + /// + /// The range is inclusive for `start` and exclusive for `end`. fn pairs<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::PairsIterator<'a>; } @@ -40,14 +90,41 @@ impl IterableStorage for &T { type ValuesIterator<'a> = T::ValuesIterator<'a> where Self: 'a; type PairsIterator<'a> = T::PairsIterator<'a> where Self: 'a; + /// Get an iterator over keys. + /// + /// The iterator should iterate over keys in lexicographical order. + /// + /// If `start` is `None`, the iterator should start from the first key. + /// If `end` is `None`, the iterator should iterate until the last key. + /// If both `start` and `end` are `None`, the iterator should iterate over all keys. + /// + /// The range is inclusive for `start` and exclusive for `end`. fn keys<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::KeysIterator<'a> { (**self).keys(start, end) } + /// Get an iterator over values. + /// + /// The iterator should iterate over values corresponding to keys in lexicographical order. + /// + /// If `start` is `None`, the iterator should start from the first key. + /// If `end` is `None`, the iterator should iterate until the last key. + /// If both `start` and `end` are `None`, the iterator should iterate over all keys. + /// + /// The range is inclusive for `start` and exclusive for `end`. fn values<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::ValuesIterator<'a> { (**self).values(start, end) } + /// Get an iterator over key-value pairs. + /// + /// The iterator should iterate over key-value pairs in lexicographical order. + /// + /// If `start` is `None`, the iterator should start from the first key. + /// If `end` is `None`, the iterator should iterate until the last key. + /// If both `start` and `end` are `None`, the iterator should iterate over all keys. + /// + /// The range is inclusive for `start` and exclusive for `end`. fn pairs<'a>(&'a self, start: Option<&[u8]>, end: Option<&[u8]>) -> Self::PairsIterator<'a> { (**self).pairs(start, end) } @@ -71,13 +148,21 @@ impl IterableStorage for &mut T { } } +/// Iteration interface for binary key-value storage in reverse order. +/// +/// The iterator should iterate over key-value pairs in reverse lexicographical order of keys. pub trait RevIterableStorage { + /// The type of the iterator returned by [`rev_keys`](Self::rev_keys). type RevKeysIterator<'a>: Iterator> where Self: 'a; + + /// The type of the iterator returned by [`rev_values`](Self::rev_values). type RevValuesIterator<'a>: Iterator> where Self: 'a; + + /// The type of the iterator returned by [`rev_pairs`](Self::rev_pairs). type RevPairsIterator<'a>: Iterator, Vec)> where Self: 'a; diff --git a/crates/storey/src/containers/column.rs b/crates/storey/src/containers/column.rs index 39d2ec6..0ebac3f 100644 --- a/crates/storey/src/containers/column.rs +++ b/crates/storey/src/containers/column.rs @@ -12,6 +12,28 @@ use super::{IterableAccessor, KeyDecodeError, Storable}; const META_NEXT_IX: &[u8] = &[0]; const META_LEN: &[u8] = &[1]; +/// A collection of rows indexed by `u32` keys. This is somewhat similar to a traditional +/// database table with an auto-incrementing primary key. +/// +/// The key is encoded as a big-endian `u32` integer. +/// +/// # Example +/// ``` +/// # use mocks::encoding::TestEncoding; +/// # use mocks::backend::TestStorage; +/// use storey::containers::Column; +/// +/// let mut storage = TestStorage::new(); +/// let column = Column::::new(&[0]); +/// let mut access = column.access(&mut storage); +/// +/// access.push(&1337).unwrap(); +/// access.push(&42).unwrap(); +/// +/// assert_eq!(access.get(0).unwrap(), Some(1337)); +/// assert_eq!(access.get(1).unwrap(), Some(42)); +/// assert_eq!(access.get(2).unwrap(), None); +/// ``` pub struct Column { prefix: &'static [u8], phantom: PhantomData<(T, E)>, @@ -22,6 +44,12 @@ where E: Encoding, T: EncodableWith + DecodableWith, { + /// Create a new column associated with the given storage prefix. + /// + /// It is the responsibility of the user to ensure the prefix is unique and does not conflict + /// with other keys in the storage. + /// + /// The key provided here is used as a prefix for all keys the column itself might generate. pub const fn new(prefix: &'static [u8]) -> Self { Self { prefix, @@ -29,6 +57,24 @@ where } } + /// Acquire an accessor for this column. + /// + /// # Example + /// ``` + /// # use mocks::encoding::TestEncoding; + /// # use mocks::backend::TestStorage; + /// use storey::containers::Column; + /// + /// // immutable accessor + /// let storage = TestStorage::new(); + /// let column = Column::::new(&[0]); + /// let access = column.access(&storage); + /// + /// // mutable accessor + /// let mut storage = TestStorage::new(); + /// let column = Column::::new(&[0]); + /// let mut access = column.access(&mut storage); + /// ``` pub fn access(&self, storage: S) -> ColumnAccess> { Self::access_impl(StorageBranch::new(storage, self.prefix.to_vec())) } @@ -62,6 +108,9 @@ where } } +/// An accessor for a `Column`. +/// +/// This type provides methods for interacting with the column in storage. pub struct ColumnAccess { storage: S, phantom: PhantomData<(E, T)>, @@ -87,6 +136,22 @@ where T: EncodableWith + DecodableWith, S: Storage, { + /// Get the value associated with the given key. + /// + /// # Example + /// ``` + /// # use mocks::encoding::TestEncoding; + /// # use mocks::backend::TestStorage; + /// use storey::containers::Column; + /// + /// let mut storage = TestStorage::new(); + /// let column = Column::::new(&[0]); + /// let mut access = column.access(&mut storage); + /// + /// access.push(&1337).unwrap(); + /// assert_eq!(access.get(0).unwrap(), Some(1337)); + /// assert_eq!(access.get(1).unwrap(), None); + /// ``` pub fn get(&self, key: u32) -> Result, E::DecodeError> { self.storage .get(&encode_ix(key)) @@ -94,6 +159,25 @@ where .transpose() } + /// Get the length of the column. This is the number of elements actually stored, + /// taking the possibility of removed elements into account. + /// + /// # Example + /// ``` + /// # use mocks::encoding::TestEncoding; + /// # use mocks::backend::TestStorage; + /// use storey::containers::Column; + /// + /// let mut storage = TestStorage::new(); + /// let column = Column::::new(&[0]); + /// let mut access = column.access(&mut storage); + /// + /// assert_eq!(access.len().unwrap(), 0); + /// + /// access.push(&1337).unwrap(); + /// + /// assert_eq!(access.len().unwrap(), 1); + /// ``` pub fn len(&self) -> Result { // TODO: bounds check + error handlinge @@ -109,6 +193,24 @@ where .unwrap_or(Ok(0)) } + /// Check if the column is empty. + /// + /// # Example + /// ``` + /// # use mocks::encoding::TestEncoding; + /// # use mocks::backend::TestStorage; + /// use storey::containers::Column; + /// + /// let mut storage = TestStorage::new(); + /// let column = Column::::new(&[0]); + /// let mut access = column.access(&mut storage); + /// + /// assert_eq!(access.is_empty().unwrap(), true); + /// + /// access.push(&1337).unwrap(); + /// + /// assert_eq!(access.is_empty().unwrap(), false); + /// ``` pub fn is_empty(&self) -> Result { self.len().map(|len| len == 0) } @@ -134,6 +236,21 @@ where T: EncodableWith + DecodableWith, S: StorageMut + Storage, { + /// Append a new value to the end of the column. + /// + /// # Example + /// ``` + /// # use mocks::encoding::TestEncoding; + /// # use mocks::backend::TestStorage; + /// use storey::containers::Column; + /// + /// let mut storage = TestStorage::new(); + /// let column = Column::::new(&[0]); + /// let mut access = column.access(&mut storage); + /// + /// access.push(&1337).unwrap(); + /// access.push(&42).unwrap(); + /// ``` pub fn push(&mut self, value: &T) -> Result<(), E::EncodeError> { let bytes = value.encode()?; @@ -156,6 +273,24 @@ where Ok(()) } + /// Update the value associated with the given key. + /// + /// # Example + /// ``` + /// # use mocks::encoding::TestEncoding; + /// # use mocks::backend::TestStorage; + /// use storey::containers::Column; + /// + /// let mut storage = TestStorage::new(); + /// let column = Column::::new(&[0]); + /// let mut access = column.access(&mut storage); + /// + /// access.push(&1337).unwrap(); + /// assert_eq!(access.get(0).unwrap(), Some(1337)); + /// + /// access.update(0, &9001).unwrap(); + /// assert_eq!(access.get(0).unwrap(), Some(9001)); + /// ``` pub fn update(&mut self, key: u32, value: &T) -> Result<(), UpdateError> { self.storage .get(&encode_ix(key)) @@ -168,6 +303,26 @@ where Ok(()) } + /// Remove the value associated with the given key. + /// + /// This operation leaves behind an empty slot in the column. The key is not reused. + /// + /// # Example + /// ``` + /// # use mocks::encoding::TestEncoding; + /// # use mocks::backend::TestStorage; + /// use storey::containers::Column; + /// + /// let mut storage = TestStorage::new(); + /// let column = Column::::new(&[0]); + /// let mut access = column.access(&mut storage); + /// + /// access.push(&1337).unwrap(); + /// assert_eq!(access.get(0).unwrap(), Some(1337)); + /// + /// access.remove(0).unwrap(); + /// assert_eq!(access.get(0).unwrap(), None); + /// ``` pub fn remove(&mut self, key: u32) -> Result<(), RemoveError> { self.storage.remove(&encode_ix(key)); diff --git a/crates/storey/src/containers/item.rs b/crates/storey/src/containers/item.rs index 515b5da..3d338f3 100644 --- a/crates/storey/src/containers/item.rs +++ b/crates/storey/src/containers/item.rs @@ -6,6 +6,23 @@ use crate::storage::{Storage, StorageMut}; use super::{KeyDecodeError, Storable}; +/// A single item in the storage. +/// +/// This simple container doesn't manage a namespace of keys, but simply stores a single +/// value under a single key. +/// +/// # Example +/// ``` +/// # use mocks::encoding::TestEncoding; +/// # use mocks::backend::TestStorage; +/// use storey::containers::Item; +/// +/// let mut storage = TestStorage::new(); +/// let item = Item::::new(&[0]); +/// +/// item.access(&mut storage).set(&42).unwrap(); +/// assert_eq!(item.access(&storage).get().unwrap(), Some(42)); +/// ``` pub struct Item { prefix: &'static [u8], phantom: PhantomData<(T, E)>, @@ -16,6 +33,9 @@ where E: Encoding, T: EncodableWith + DecodableWith, { + /// Create a new item with the given key. + /// + /// It is the responsibility of the caller to ensure that the key is unique. pub const fn new(prefix: &'static [u8]) -> Self { Self { prefix, @@ -23,6 +43,23 @@ where } } + /// Acquire an accessor to the item. + /// + /// # Example + /// ``` + /// # use mocks::encoding::TestEncoding; + /// # use mocks::backend::TestStorage; + /// use storey::containers::Item; + /// + /// // immutable accessor + /// let storage = TestStorage::new(); + /// let item = Item::::new(&[0]); + /// let access = item.access(&storage); + /// + /// // mutable accessor + /// let mut storage = TestStorage::new(); + /// let item = Item::::new(&[0]); + /// let mut access = item.access(&mut storage); pub fn access(&self, storage: S) -> ItemAccess> { Self::access_impl(StorageBranch::new(storage, self.prefix.to_vec())) } @@ -58,6 +95,9 @@ where } } +/// An accessor for an `Item`. +/// +/// This type provides methods to get and set the value of the item. pub struct ItemAccess { storage: S, phantom: PhantomData<(E, T)>, @@ -69,6 +109,34 @@ where T: EncodableWith + DecodableWith, S: Storage, { + /// Get the value of the item. + /// + /// Returns `None` if the item doesn't exist (has not been set yet). + /// + /// # Examples + /// ``` + /// # use mocks::encoding::TestEncoding; + /// # use mocks::backend::TestStorage; + /// use storey::containers::Item; + /// + /// let storage = TestStorage::new(); + /// let item = Item::::new(&[0]); + /// let access = item.access(&storage); + /// + /// assert_eq!(access.get().unwrap(), None); + /// ``` + /// + /// ``` + /// # use mocks::encoding::TestEncoding; + /// # use mocks::backend::TestStorage; + /// use storey::containers::Item; + /// + /// let mut storage = TestStorage::new(); + /// let item = Item::::new(&[0]); + /// + /// item.access(&mut storage).set(&42).unwrap(); + /// assert_eq!(item.access(&storage).get().unwrap(), Some(42)); + /// ``` pub fn get(&self) -> Result, E::DecodeError> { self.storage .get(&[]) @@ -83,6 +151,20 @@ where T: EncodableWith + DecodableWith, S: StorageMut, { + /// Set the value of the item. + /// + /// # Example + /// ``` + /// # use mocks::encoding::TestEncoding; + /// # use mocks::backend::TestStorage; + /// use storey::containers::Item; + /// + /// let mut storage = TestStorage::new(); + /// let item = Item::::new(&[0]); + /// + /// item.access(&mut storage).set(&42).unwrap(); + /// assert_eq!(item.access(&storage).get().unwrap(), Some(42)); + /// ``` pub fn set(&mut self, value: &T) -> Result<(), E::EncodeError> { let bytes = value.encode()?; self.storage.set(&[], &bytes); diff --git a/crates/storey/src/containers/map.rs b/crates/storey/src/containers/map.rs index 00b192b..0d0ca71 100644 --- a/crates/storey/src/containers/map.rs +++ b/crates/storey/src/containers/map.rs @@ -6,6 +6,42 @@ use crate::storage::StorageBranch; use super::IterableAccessor; use super::{KeyDecodeError, Storable}; +/// A map that stores values of type `V` under keys of type `K`. +/// +/// The subkeys managed by the map are length-prefixed and appended to the map's prefix. +/// +/// A map does not directly manage the storage of its values. Instead, it doles out access to +/// a collection of other containers. +/// +/// # Examples +/// +/// ``` +/// # use mocks::encoding::TestEncoding; +/// # use mocks::backend::TestStorage; +/// use storey::containers::{Item, Map}; +/// +/// let mut storage = TestStorage::new(); +/// let map = Map::>::new(&[0]); +/// let mut access = map.access(&mut storage); +/// +/// access.entry_mut("foo").set(&1337).unwrap(); +/// assert_eq!(access.entry("foo").get().unwrap(), Some(1337)); +/// assert_eq!(access.entry("bar").get().unwrap(), None); +/// ``` +/// +/// ``` +/// # use mocks::encoding::TestEncoding; +/// # use mocks::backend::TestStorage; +/// use storey::containers::{Item, Map}; +/// +/// let mut storage = TestStorage::new(); +/// let map = Map::>>::new(&[0]); +/// let mut access = map.access(&mut storage); +/// +/// access.entry_mut("foo").entry_mut("bar").set(&1337).unwrap(); +/// assert_eq!(access.entry("foo").entry("bar").get().unwrap(), Some(1337)); +/// assert_eq!(access.entry("foo").entry("baz").get().unwrap(), None); +/// ``` pub struct Map { prefix: &'static [u8], phantom: PhantomData<(*const K, V)>, @@ -16,6 +52,12 @@ where K: OwnedKey, V: Storable, { + /// Creates a new map with the given prefix. + /// + /// It is the responsibility of the caller to ensure that the prefix is unique and does not conflict + /// with other keys in the storage. + /// + /// The key provided here is used as a prefix for all keys managed by the map. pub const fn new(prefix: &'static [u8]) -> Self { Self { prefix, @@ -23,6 +65,24 @@ where } } + /// Acquires an accessor for the map. + /// + /// # Example + /// ``` + /// # use mocks::encoding::TestEncoding; + /// # use mocks::backend::TestStorage; + /// use storey::containers::{Item, Map}; + /// + /// // immutable access + /// let storage = TestStorage::new(); + /// let map = Map::>::new(&[0]); + /// let access = map.access(&storage); + /// + /// // mutable access + /// let mut storage = TestStorage::new(); + /// let map = Map::>::new(&[0]); + /// let mut access = map.access(&mut storage); + /// ``` pub fn access(&self, storage: S) -> MapAccess> { Self::access_impl(StorageBranch::new(storage, self.prefix.to_vec())) } @@ -63,6 +123,9 @@ where } } +/// An accessor for a map. +/// +/// The accessor provides methods for interacting with the map in storage. pub struct MapAccess { storage: S, phantom: PhantomData<(*const K, V)>, @@ -73,6 +136,33 @@ where K: Key, V: Storable, { + /// Returns an immutable accessor for the inner container of this map. + /// + /// # Examples + /// + /// ``` + /// # use mocks::encoding::TestEncoding; + /// # use mocks::backend::TestStorage; + /// use storey::containers::{Item, Map}; + /// + /// let storage = TestStorage::new(); + /// let map = Map::>::new(&[0]); + /// let access = map.access(&storage); + /// + /// assert_eq!(access.entry("foo").get().unwrap(), None); + /// ``` + /// + /// ``` + /// # use mocks::encoding::TestEncoding; + /// # use mocks::backend::TestStorage; + /// use storey::containers::{Item, Map}; + /// + /// let storage = TestStorage::new(); + /// let map = Map::>>::new(&[0]); + /// let access = map.access(&storage); + /// + /// assert_eq!(access.entry("foo").entry("bar").get().unwrap(), None); + /// ``` pub fn entry(&self, key: &Q) -> V::AccessorT> where K: Borrow, @@ -88,6 +178,35 @@ where V::access_impl(StorageBranch::new(&self.storage, key)) } + /// Returns a mutable accessor for the inner container of this map. + /// + /// # Examples + /// + /// ``` + /// # use mocks::encoding::TestEncoding; + /// # use mocks::backend::TestStorage; + /// use storey::containers::{Item, Map}; + /// + /// let mut storage = TestStorage::new(); + /// let map = Map::>::new(&[0]); + /// let mut access = map.access(&mut storage); + /// + /// access.entry_mut("foo").set(&1337).unwrap(); + /// assert_eq!(access.entry("foo").get().unwrap(), Some(1337)); + /// ``` + /// + /// ``` + /// # use mocks::encoding::TestEncoding; + /// # use mocks::backend::TestStorage; + /// use storey::containers::{Item, Map}; + /// + /// let mut storage = TestStorage::new(); + /// let map = Map::>>::new(&[0]); + /// let mut access = map.access(&mut storage); + /// + /// access.entry_mut("foo").entry_mut("bar").set(&1337).unwrap(); + /// assert_eq!(access.entry("foo").entry("bar").get().unwrap(), Some(1337)); + /// ``` pub fn entry_mut(&mut self, key: &Q) -> V::AccessorT> where K: Borrow, diff --git a/crates/storey/src/containers/mod.rs b/crates/storey/src/containers/mod.rs index 0e0c6c1..91a1f68 100644 --- a/crates/storey/src/containers/mod.rs +++ b/crates/storey/src/containers/mod.rs @@ -1,4 +1,7 @@ -pub mod column; +//! This module contains both the traits for implementing collections/containers, as well as a +//! few fundamental collections/containers themselves. + +mod column; mod item; mod map; @@ -10,34 +13,75 @@ pub use map::{Map, MapAccess}; use crate::storage::IterableStorage; +/// The fundamental trait every collection/container should implement. pub trait Storable { + /// The accessor type for this collection/container. An accessor is a type that provides + /// methods for reading and writing to the collection/container and encapsulates the + /// specific [`Storage`] type used (the `S` type parameter here). + /// + /// [`Storage`]: crate::storage::Storage type AccessorT; + + /// The Key type for this collection/container. This is the type that will be used in + /// key iteration. + /// + /// For composable collections this is the "full" key, e.g. for [`Map`] + /// this is a tuple of the key and the sub-key. + /// + /// Containers that store one item and don't manage subkeys should use the `()` type here. type Key; + + /// The Value type for this collection/container. This is the type that will be used for + /// value iteration. type Value; + + /// The error type for decoding values. type ValueDecodeError; + /// Create an accessor for this collection/container, given a [`Storage`] implementation. + /// + /// [`Storage`]: crate::storage::Storage fn access_impl(storage: S) -> Self::AccessorT; + /// Decode a key from a byte slice. + /// + /// This method is used in key iteration to provide a typed key rather than raw bytes + /// to the user. fn decode_key(key: &[u8]) -> Result; + /// Decode a value from a byte slice. + /// + /// This method is used in value iteration to provide a typed value rather than raw bytes + /// to the user. fn decode_value(value: &[u8]) -> Result; } +/// A key decoding error. #[derive(Debug, PartialEq)] pub struct KeyDecodeError; +/// A key-value pair decoding error. #[derive(Debug, PartialEq)] pub enum KVDecodeError { Key, Value(V), } +/// A trait for collection accessors (see [`Storable::AccessorT`]) that provide iteration over +/// their contents. pub trait IterableAccessor { + /// The [`Storable`] type this accessor is associated with. type StorableT: Storable; + + /// The [`Storage`] type this accessor is associated with. + /// + /// [`Storage`]: crate::storage::Storage type StorageT: IterableStorage; + /// Get a reference to the storage this accessor is associated with. fn storage(&self) -> &Self::StorageT; + /// Iterate over key-value pairs in this collection. fn pairs<'s>( &'s self, start: Option<&[u8]>, @@ -49,6 +93,7 @@ pub trait IterableAccessor { } } + /// Iterate over keys in this collection. fn keys<'s>( &'s self, start: Option<&[u8]>, @@ -60,6 +105,7 @@ pub trait IterableAccessor { } } + /// Iterate over values in this collection. fn values<'s>( &'s self, start: Option<&[u8]>, @@ -72,6 +118,7 @@ pub trait IterableAccessor { } } +/// The iterator over key-value pairs in a collection. pub struct StorableIter<'i, S, B> where S: Storable, @@ -99,6 +146,7 @@ where } } +/// The iterator over keys in a collection. pub struct StorableKeys<'i, S, B> where S: Storable, @@ -120,6 +168,7 @@ where } } +/// The iterator over values in a collection. pub struct StorableValues<'i, S, B> where S: Storable, diff --git a/crates/storey/src/lib.rs b/crates/storey/src/lib.rs index de80f6a..a73698f 100644 --- a/crates/storey/src/lib.rs +++ b/crates/storey/src/lib.rs @@ -1,3 +1,19 @@ +//! `storey` is an abstraction layer for blockchain storage backends. +//! +//! Typically, blockchain storage backends are binary key-value stores. `storey` abstracts over +//! these stores, providing +//! - a typed (rather than binary) interface, +//! - composable collections, and +//! - traits simplifying the implementation of new collections/containers. +//! +//! The encoding of keys is the responsibility of this framework and its collections. +//! The encoding of values is abstracted away by the traits in the [`encoding`] module. +//! Specific value encodings are implemented outside of this crate. It's not hard +//! to plug in any encoding you like. +//! +//! Similarly, the storage backend is pluggable. The [`storage`] module provides traits +//! for that. + pub mod containers; pub mod encoding; pub mod storage; diff --git a/crates/storey/src/storage/branch.rs b/crates/storey/src/storage/branch.rs index 768d3a9..1599882 100644 --- a/crates/storey/src/storage/branch.rs +++ b/crates/storey/src/storage/branch.rs @@ -1,11 +1,32 @@ use crate::storage::{IterableStorage, RevIterableStorage, Storage, StorageMut}; +/// A type representing a storage namespace created by applying a prefix to all keys. +/// +/// This type implements the [`Storage`] and [`StorageMut`] traits, making the fact a prefix +/// is applied transparent to the user. +/// +/// You don't need to be aware of this type unless implementing a custom container. +/// +/// # Example +/// ``` +/// # use mocks::backend::TestStorage; +/// use storey::storage::{Storage as _, StorageMut as _, StorageBranch}; +/// +/// let mut storage = TestStorage::new(); +/// let mut branch = StorageBranch::new(&mut storage, b"foo".to_vec()); +/// +/// branch.set(b"bar", b"baz"); +/// +/// assert_eq!(branch.get(b"bar"), Some(b"baz".to_vec())); +/// assert_eq!(storage.get(b"foobar"), Some(b"baz".to_vec())); +/// ``` pub struct StorageBranch { backend: S, prefix: Vec, } impl StorageBranch { + /// Creates a new `StorageBranch` instance given a prefix. pub fn new(backend: S, prefix: Vec) -> Self { Self { backend, prefix } } @@ -165,6 +186,7 @@ fn sub_bounds( } } +/// An iterator over the keys of a `StorageBranch`. pub struct BranchKeysIter { inner: I, prefix_len: usize, @@ -181,6 +203,7 @@ where } } +/// An iterator over the key-value pairs of a `StorageBranch`. pub struct BranchKVIter { inner: I, prefix_len: usize, diff --git a/crates/storey/src/storage/mod.rs b/crates/storey/src/storage/mod.rs index c3e268d..3ec9f04 100644 --- a/crates/storey/src/storage/mod.rs +++ b/crates/storey/src/storage/mod.rs @@ -1,3 +1,18 @@ +//! A collection of traits/types for accessing storage and managing storage namespaces. +//! +//! [`StorageBackend`] and [`StorageBackendMut`] are for accessing the fundamental binary +//! key-value storage. You only need to interact with them if you're integrating `storey` with +//! a new storage backend. +//! +//! [`Storage`] and [`StorageMut`] provide a common interface for any binary storage type, +//! including a storage backend or a storage branch (namespace). Similarly, [`RevIterableStorage`] +//! and [`IterableStorage`] represent binary storage types that provide iteration. These traits +//! are something you might be interested in if you're implementing a new container. +//! +//! [`StorageBranch`] is a storage namespace. It can be used to divide a backend's key namespace +//! into smaller namespaces. This is a fundamental building block for the hierarchy of storage +//! containers. You only need to be aware of it if you're implementing a new container. + mod branch; pub use branch::StorageBranch;