Skip to main content

zrx_storage/
storage.rs

1// Copyright (c) 2025-2026 Zensical and contributors
2
3// SPDX-License-Identifier: MIT
4// All contributions are certified under the DCO
5
6// Permission is hereby granted, free of charge, to any person obtaining a copy
7// of this software and associated documentation files (the "Software"), to
8// deal in the Software without restriction, including without limitation the
9// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10// sell copies of the Software, and to permit persons to whom the Software is
11// furnished to do so, subject to the following conditions:
12
13// The above copyright notice and this permission notice shall be included in
14// all copies or substantial portions of the Software.
15
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22// IN THE SOFTWARE.
23
24// ----------------------------------------------------------------------------
25
26//! Storage.
27
28use ahash::HashMap;
29use std::ops::{Deref, DerefMut};
30
31use zrx_store::{Collection, Key, Value};
32
33pub mod accessor;
34pub mod borrow;
35pub mod convert;
36mod error;
37pub mod set;
38
39pub use error::{Error, Result};
40
41// ----------------------------------------------------------------------------
42// Structs
43// ----------------------------------------------------------------------------
44
45/// Storage.
46///
47/// Fundamentally, storages are solely thin wrappers around implementors of the
48/// [`Collection`] trait, which allows to dereference or downcast to a concrete
49/// type, if known, both, immutably and mutably. [`Collection`] is a convenience
50/// trait, is dyn-compatible, and combines several of the [`Store`][] traits.
51///
52/// While [`zrx-store`][] implements generic key-value stores, [`zrx-storage`][]
53/// focuses on providing abstractions and utilities for orchestrating immutable
54/// and mutable access across multiple key-value stores with varying semantics,
55/// encapsulating boilerplate, and making it easy to write reusable operators.
56///
57/// [`Store`]: zrx_store::Store
58/// [`zrx-store`]: zrx_store
59/// [`zrx-storage`]: crate
60///
61/// # Examples
62///
63/// ```
64/// use zrx_storage::Storage;
65///
66/// // Create storage and initial state
67/// let mut storage = Storage::default();
68/// storage.insert("a", 4);
69/// storage.insert("b", 2);
70/// storage.insert("c", 3);
71/// storage.insert("d", 1);
72///
73/// // Create iterator over the storage
74/// for (key, value) in &*storage {
75///     println!("{key}: {value}");
76/// }
77/// ```
78#[derive(Debug)]
79pub struct Storage<K, V> {
80    /// Inner collection.
81    inner: Box<dyn Collection<K, V>>,
82}
83
84// ----------------------------------------------------------------------------
85// Implementations
86// ----------------------------------------------------------------------------
87
88impl<K, V> Storage<K, V> {
89    /// Creates a new storage from the given collection.
90    ///
91    /// # Examples
92    ///
93    /// ```
94    /// use std::collections::HashMap;
95    /// use zrx_storage::Storage;
96    ///
97    /// // Create storage
98    /// let mut storage = Storage::new(HashMap::new());
99    ///
100    /// // Insert value
101    /// storage.insert("key", 42);
102    /// ```
103    pub fn new<T>(collection: T) -> Self
104    where
105        T: Collection<K, V>,
106    {
107        Self { inner: Box::new(collection) }
108    }
109
110    /// Attempts to downcast to a mutable reference of `T`.
111    ///
112    /// This method is a wrapper around the method with the same name that is
113    /// implemented for [`Collection`] trait objects, and is primarily provided
114    /// for convenience, so failed downcasts return a [`Result`] instead of an
115    /// [`Option`], turning them into errors.
116    ///
117    /// # Errors
118    ///
119    /// Returns [`Error::Downcast`] if conversion fails.
120    ///
121    /// # Examples
122    ///
123    /// ```
124    /// # use std::error::Error;
125    /// # fn main() -> Result<(), Box<dyn Error>> {
126    /// use std::collections::HashMap;
127    /// use zrx_storage::Storage;
128    ///
129    /// // Create storage and initial state
130    /// let mut storage = Storage::new(HashMap::new());
131    /// storage.insert("key", 42);
132    ///
133    /// // Downcast storage to inner collection
134    /// let store = storage.downcast_ref::<HashMap<_, _>>()?;
135    /// # Ok(())
136    /// # }
137    /// ```
138    #[inline]
139    pub fn downcast_ref<T>(&self) -> Result<&T>
140    where
141        T: Collection<K, V>,
142    {
143        self.inner.downcast_ref().ok_or(Error::Downcast)
144    }
145
146    /// Attempts to downcast to a mutable reference of `T`.
147    ///
148    /// This method is a wrapper around the method with the same name that is
149    /// implemented for [`Collection`] trait objects, and is primarily provided
150    /// for convenience, so failed downcasts return a [`Result`] instead of an
151    /// [`Option`], turning them into errors.
152    ///
153    /// # Errors
154    ///
155    /// Returns [`Error::Downcast`] if conversion fails.
156    ///
157    /// # Examples
158    ///
159    /// ```
160    /// # use std::error::Error;
161    /// # fn main() -> Result<(), Box<dyn Error>> {
162    /// use std::collections::HashMap;
163    /// use zrx_storage::Storage;
164    ///
165    /// // Create storage and initial state
166    /// let mut storage = Storage::new(HashMap::new());
167    /// storage.insert("key", 42);
168    ///
169    /// // Downcast storage to inner collection
170    /// let store = storage.downcast_mut::<HashMap<_, _>>()?;
171    /// # Ok(())
172    /// # }
173    /// ```
174    #[inline]
175    pub fn downcast_mut<T>(&mut self) -> Result<&mut T>
176    where
177        T: Collection<K, V>,
178    {
179        self.inner.downcast_mut().ok_or(Error::Downcast)
180    }
181}
182
183// ----------------------------------------------------------------------------
184// Trait implementations
185// ----------------------------------------------------------------------------
186
187impl<T, K, V> From<T> for Storage<K, V>
188where
189    T: IntoIterator<Item = (K, V)>,
190    K: Key,
191    V: Value,
192{
193    /// Creates a storage from an iterator.
194    ///
195    /// This implementation allows to create storages directly from iterators
196    /// of key-value pairs, which is particularly useful for testing, as well
197    /// as documentation examples. Note that the inner collection will always
198    /// be a [`HashMap`] when using this method.
199    ///
200    /// # Examples
201    ///
202    /// ```
203    /// use zrx_storage::Storage;
204    ///
205    /// // Create storage from iterator
206    /// let mut storage = Storage::from([("key", 42)]);
207    /// ```
208    #[inline]
209    fn from(iter: T) -> Self {
210        Self::from_iter(iter)
211    }
212}
213
214// ----------------------------------------------------------------------------
215
216impl<K, V> FromIterator<(K, V)> for Storage<K, V>
217where
218    K: Key,
219    V: Value,
220{
221    /// Creates a storage from an iterator.
222    ///
223    /// When creating storages directly from an iterator, which is primarily
224    /// intended for testing, the inner collection is a [`HashMap`].
225    ///
226    /// # Examples
227    ///
228    /// ```
229    /// use zrx_storage::Storage;
230    ///
231    /// // Create storage from iterator
232    /// let mut storage = Storage::from_iter([("key", 42)]);
233    /// ```
234    #[inline]
235    fn from_iter<T>(iter: T) -> Self
236    where
237        T: IntoIterator<Item = (K, V)>,
238    {
239        Self {
240            inner: Box::new(HashMap::from_iter(iter)),
241        }
242    }
243}
244
245// ----------------------------------------------------------------------------
246
247impl<K, V> Deref for Storage<K, V> {
248    type Target = dyn Collection<K, V>;
249
250    /// Dereferences to the inner collection.
251    #[inline]
252    fn deref(&self) -> &Self::Target {
253        &*self.inner
254    }
255}
256
257impl<K, V> DerefMut for Storage<K, V> {
258    /// Dereferences to the inner collection mutably.
259    #[inline]
260    fn deref_mut(&mut self) -> &mut Self::Target {
261        &mut *self.inner
262    }
263}
264
265// ----------------------------------------------------------------------------
266
267impl<K, V> Default for Storage<K, V>
268where
269    K: Key,
270    V: Value,
271{
272    /// Creates a storage with [`HashMap::default`][] as a collection.
273    ///
274    /// Note that this method does not allow to customize the [`BuildHasher`][],
275    /// but uses [`ahash`] by default, which is the fastest known hasher.
276    ///
277    /// [`BuildHasher`]: std::hash::BuildHasher
278    /// [`HashMap::default`]: Default::default
279    ///
280    /// # Examples
281    ///
282    /// ```
283    /// use zrx_storage::Storage;
284    ///
285    /// // Create storage
286    /// let mut storage = Storage::default();
287    ///
288    /// // Insert value
289    /// storage.insert("key", 42);
290    /// ```
291    #[inline]
292    fn default() -> Self {
293        Self::new(HashMap::default())
294    }
295}