cw_storage_plus/
path.rs

1use cosmwasm_std::storage_keys::namespace_with_key;
2use serde::de::DeserializeOwned;
3use serde::Serialize;
4use std::marker::PhantomData;
5
6use crate::helpers::not_found_object_info;
7use cosmwasm_std::{from_json, to_json_vec, StdError, StdResult, Storage};
8use std::ops::Deref;
9
10#[derive(Debug, Clone)]
11pub struct Path<T>
12where
13    T: Serialize + DeserializeOwned,
14{
15    /// all namespaces prefixes and concatenated with the key
16    pub(crate) storage_key: Vec<u8>,
17    // see https://doc.rust-lang.org/std/marker/struct.PhantomData.html#unused-type-parameters for why this is needed
18    data: PhantomData<T>,
19}
20
21impl<T> Deref for Path<T>
22where
23    T: Serialize + DeserializeOwned,
24{
25    type Target = [u8];
26
27    fn deref(&self) -> &[u8] {
28        &self.storage_key
29    }
30}
31
32impl<T> Path<T>
33where
34    T: Serialize + DeserializeOwned,
35{
36    pub fn new(namespace: &[u8], keys: &[&[u8]]) -> Self {
37        let l = keys.len();
38
39        // Combine namespace and all but last keys.
40        // This is a single vector allocation with references as elements.
41        let calculated_len = 1 + keys.len() - 1;
42        let mut combined: Vec<&[u8]> = Vec::with_capacity(calculated_len);
43        combined.push(namespace);
44        combined.extend(keys[0..l - 1].iter());
45        debug_assert_eq!(calculated_len, combined.len()); // as long as we calculate correctly, we don't need to reallocate
46        let storage_key = namespace_with_key(&combined, keys[l - 1]);
47        Path {
48            storage_key,
49            data: PhantomData,
50        }
51    }
52
53    /// save will serialize the model and store, returns an error on serialization issues
54    pub fn save(&self, store: &mut dyn Storage, data: &T) -> StdResult<()> {
55        store.set(&self.storage_key, &to_json_vec(data)?);
56        Ok(())
57    }
58
59    pub fn remove(&self, store: &mut dyn Storage) {
60        store.remove(&self.storage_key);
61    }
62
63    /// load will return an error if no data is set at the given key, or on parse error
64    pub fn load(&self, store: &dyn Storage) -> StdResult<T> {
65        if let Some(value) = store.get(&self.storage_key) {
66            from_json(value)
67        } else {
68            let object_info = not_found_object_info::<T>(&self.storage_key);
69            Err(StdError::msg(format!("{object_info} not found")))
70        }
71    }
72
73    /// may_load will parse the data stored at the key if present, returns Ok(None) if no data there.
74    /// returns an error on issues parsing
75    pub fn may_load(&self, store: &dyn Storage) -> StdResult<Option<T>> {
76        let value = store.get(&self.storage_key);
77        value.map(|v| from_json(v)).transpose()
78    }
79
80    /// has returns true or false if any data is at this key, without parsing or interpreting the
81    /// contents. It will returns true for an length-0 byte array (Some(b"")), if you somehow manage to set that.
82    pub fn has(&self, store: &dyn Storage) -> bool {
83        store.get(&self.storage_key).is_some()
84    }
85
86    /// Loads the data, perform the specified action, and store the result
87    /// in the database. This is shorthand for some common sequences, which may be useful.
88    ///
89    /// If the data exists, `action(Some(value))` is called. Otherwise, `action(None)` is called.
90    pub fn update<A, E>(&self, store: &mut dyn Storage, action: A) -> Result<T, E>
91    where
92        A: FnOnce(Option<T>) -> Result<T, E>,
93        E: From<StdError>,
94    {
95        let input = self.may_load(store)?;
96        let output = action(input)?;
97        self.save(store, &output)?;
98        Ok(output)
99    }
100}