1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
use std::marker::PhantomData;
use thiserror::Error;
use crate::codec::{BorshCodec, EncodeKeyLike, StateCodec, StateKeyCodec, StateValueCodec};
use crate::storage::StorageKey;
use crate::{Prefix, StateReaderAndWriter, Storage, WorkingSet};
/// A container that maps keys to values.
///
/// # Type parameters
/// [`StateMap`] is generic over:
/// - a key type `K`;
/// - a value type `V`;
/// - a [`StateValueCodec`] `Codec`.
#[derive(
Debug,
Clone,
PartialEq,
borsh::BorshDeserialize,
borsh::BorshSerialize,
serde::Serialize,
serde::Deserialize,
)]
pub struct StateMap<K, V, Codec = BorshCodec> {
_phantom: (PhantomData<K>, PhantomData<V>),
codec: Codec,
prefix: Prefix,
}
/// Error type for the [`StateMap::get`] method.
#[derive(Debug, Error)]
pub enum StateMapError {
#[error("Value not found for prefix: {0} and: storage key {1}")]
MissingValue(Prefix, StorageKey),
}
impl<K, V> StateMap<K, V> {
/// Creates a new [`StateMap`] with the given prefix and the default
/// [`StateValueCodec`] (i.e. [`BorshCodec`]).
pub fn new(prefix: Prefix) -> Self {
Self::with_codec(prefix, BorshCodec)
}
}
impl<K, V, Codec> StateMap<K, V, Codec> {
/// Creates a new [`StateMap`] with the given prefix and [`StateValueCodec`].
pub fn with_codec(prefix: Prefix, codec: Codec) -> Self {
Self {
_phantom: (PhantomData, PhantomData),
codec,
prefix,
}
}
pub fn codec(&self) -> &Codec {
&self.codec
}
/// Returns the prefix used when this [`StateMap`] was created.
pub fn prefix(&self) -> &Prefix {
&self.prefix
}
}
impl<K, V, Codec> StateMap<K, V, Codec>
where
Codec: StateCodec,
Codec::KeyCodec: StateKeyCodec<K>,
Codec::ValueCodec: StateValueCodec<V>,
{
/// Inserts a key-value pair into the map.
///
/// Much like [`StateMap::get`], the key may be any borrowed form of the
/// map’s key type.
pub fn set<Q, S: Storage>(&self, key: &Q, value: &V, working_set: &mut WorkingSet<S>)
where
Codec::KeyCodec: EncodeKeyLike<Q, K>,
Q: ?Sized,
{
working_set.set_value(self.prefix(), key, value, &self.codec)
}
/// Returns the value corresponding to the key, or [`None`] if the map
/// doesn't contain the key.
///
/// # Examples
///
/// The key may be any item that implements [`EncodeKeyLike`] the map's key type
/// using your chosen codec.
///
/// ```
/// use sov_state::{StateMap, Storage, WorkingSet};
///
/// fn foo<S>(map: StateMap<Vec<u8>, u64>, key: &[u8], ws: &mut WorkingSet<S>) -> Option<u64>
/// where
/// S: Storage,
/// {
/// // We perform the `get` with a slice, and not the `Vec`. it is so because `Vec` borrows
/// // `[T]`.
/// map.get(key, ws)
/// }
/// ```
///
/// If the map's key type does not implement [`EncodeKeyLike`] for your desired
/// target type, you'll have to convert the key to something else. An
/// example of this would be "slicing" an array to use in [`Vec`]-keyed
/// maps:
///
/// ```
/// use sov_state::{StateMap, Storage, WorkingSet};
///
/// fn foo<S>(map: StateMap<Vec<u8>, u64>, key: [u8; 32], ws: &mut WorkingSet<S>) -> Option<u64>
/// where
/// S: Storage,
/// {
/// map.get(&key[..], ws)
/// }
/// ```
pub fn get<Q, S: Storage>(&self, key: &Q, working_set: &mut WorkingSet<S>) -> Option<V>
where
Codec: StateCodec,
Codec::KeyCodec: EncodeKeyLike<Q, K>,
Codec::ValueCodec: StateValueCodec<V>,
Q: ?Sized,
{
working_set.get_value(self.prefix(), key, &self.codec)
}
/// Returns the value corresponding to the key or [`StateMapError`] if key is absent in
/// the map.
pub fn get_or_err<Q, S: Storage>(
&self,
key: &Q,
working_set: &mut WorkingSet<S>,
) -> Result<V, StateMapError>
where
Codec: StateCodec,
Codec::KeyCodec: EncodeKeyLike<Q, K>,
Codec::ValueCodec: StateValueCodec<V>,
Q: ?Sized,
{
self.get(key, working_set).ok_or_else(|| {
StateMapError::MissingValue(
self.prefix().clone(),
StorageKey::new(self.prefix(), key, self.codec.key_codec()),
)
})
}
/// Removes a key from the map, returning the corresponding value (or
/// [`None`] if the key is absent).
pub fn remove<Q, S: Storage>(&self, key: &Q, working_set: &mut WorkingSet<S>) -> Option<V>
where
Codec: StateCodec,
Codec::KeyCodec: EncodeKeyLike<Q, K>,
Codec::ValueCodec: StateValueCodec<V>,
Q: ?Sized,
{
working_set.remove_value(self.prefix(), key, &self.codec)
}
/// Removes a key from the map, returning the corresponding value (or
/// [`StateMapError`] if the key is absent).
///
/// Use [`StateMap::remove`] if you want an [`Option`] instead of a [`Result`].
pub fn remove_or_err<Q, S: Storage>(
&self,
key: &Q,
working_set: &mut WorkingSet<S>,
) -> Result<V, StateMapError>
where
Codec: StateCodec,
Codec::KeyCodec: EncodeKeyLike<Q, K>,
Codec::ValueCodec: StateValueCodec<V>,
Q: ?Sized,
{
self.remove(key, working_set).ok_or_else(|| {
StateMapError::MissingValue(
self.prefix().clone(),
StorageKey::new(self.prefix(), key, self.codec.key_codec()),
)
})
}
/// Deletes a key-value pair from the map.
///
/// This is equivalent to [`StateMap::remove`], but doesn't deserialize and
/// return the value beforing deletion.
pub fn delete<Q, S: Storage>(&self, key: &Q, working_set: &mut WorkingSet<S>)
where
Codec: StateCodec,
Codec::KeyCodec: EncodeKeyLike<Q, K>,
Q: ?Sized,
{
working_set.delete_value(self.prefix(), key, &self.codec);
}
}
#[cfg(feature = "arbitrary")]
impl<'a, K, V, Codec> StateMap<K, V, Codec>
where
K: arbitrary::Arbitrary<'a>,
V: arbitrary::Arbitrary<'a>,
Codec: StateCodec + Default,
Codec::KeyCodec: StateKeyCodec<K>,
Codec::ValueCodec: StateValueCodec<V>,
{
pub fn arbitrary_workset<S>(
u: &mut arbitrary::Unstructured<'a>,
working_set: &mut WorkingSet<S>,
) -> arbitrary::Result<Self>
where
S: Storage,
{
use arbitrary::Arbitrary;
let prefix = Prefix::arbitrary(u)?;
let len = u.arbitrary_len::<(K, V)>()?;
let codec = Codec::default();
let map = StateMap::with_codec(prefix, codec);
(0..len).try_fold(map, |map, _| {
let key = K::arbitrary(u)?;
let value = V::arbitrary(u)?;
map.set(&key, &value, working_set);
Ok(map)
})
}
}