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}