1pub use crate::prelude::*;
4use cosmwasm_std::{from_json, Binary, Empty, QuerierWrapper};
5use cw_storage_plus::{KeyDeserialize, Prefixer, PrimaryKey};
6
7pub type MonotonicMultilevelMap<'a, K, T> = Map<(K, u64), T>;
13
14pub fn push_to_monotonic_multilevel_map<'a, K, T>(
16 store: &mut dyn Storage,
17 m: MonotonicMultilevelMap<'a, K, T>,
18 k: K,
19 t: &T,
20) -> Result<u64>
21where
22 K: PrimaryKey<'a> + Prefixer<'a> + KeyDeserialize,
23 T: serde::Serialize + serde::de::DeserializeOwned,
24{
25 let next_id = m
26 .prefix(k.clone())
27 .keys(store, None, None, cosmwasm_std::Order::Descending)
28 .next()
29 .transpose()?
30 .map_or(0, |x| x + 1);
31 m.save(store, (k, next_id), t)?;
32 Ok(next_id)
33}
34
35pub fn collect_monotonic_multilevel_map<'a, K, T>(
37 store: &dyn Storage,
38 m: MonotonicMultilevelMap<'a, K, T>,
39 k: K,
40 after_id: Option<u64>,
41 limit: Option<u32>,
42 order: Order,
43) -> Result<Vec<(u64, T)>>
44where
45 K: PrimaryKey<'a> + Prefixer<'a> + KeyDeserialize,
46 T: serde::Serialize + serde::de::DeserializeOwned,
47{
48 let iter = m
49 .prefix(k)
50 .range(store, after_id.map(Bound::exclusive), None, order)
51 .map(|res| res.map_err(|err| err.into()));
52
53 match limit {
54 Some(limit) => iter.take(limit.try_into()?).collect(),
55 None => iter.collect(),
56 }
57}
58
59pub type MonotonicMap<'a, T> = Map<u64, T>;
64
65pub fn push_to_monotonic_map<T>(store: &mut dyn Storage, m: MonotonicMap<T>, t: &T) -> Result<u64>
67where
68 T: serde::Serialize + serde::de::DeserializeOwned,
69{
70 let next_id = m
71 .keys(store, None, None, cosmwasm_std::Order::Descending)
72 .next()
73 .transpose()?
74 .map_or(0, |x| x + 1);
75 m.save(store, next_id, t)?;
76 Ok(next_id)
77}
78
79pub fn collect_monotonic_map<T>(
81 store: &dyn Storage,
82 m: MonotonicMap<T>,
83 after_id: Option<u64>,
84 limit: Option<u32>,
85 order: Order,
86) -> Result<Vec<(u64, T)>>
87where
88 T: serde::Serialize + serde::de::DeserializeOwned,
89{
90 let iter = m
91 .range(store, after_id.map(Bound::exclusive), None, order)
92 .map(|res| res.map_err(|err| err.into()));
93
94 match limit {
95 Some(limit) => iter.take(limit.try_into()?).collect(),
96 None => iter.collect(),
97 }
98}
99
100pub fn load_external_item<T: serde::de::DeserializeOwned>(
102 querier: &QuerierWrapper<Empty>,
103 contract_addr: impl Into<String>,
104 key: impl Into<Binary>,
105) -> anyhow::Result<T> {
106 let key: Binary = key.into();
110 let debug_key_name = if cfg!(debug_assertions) {
111 from_json::<String>(&key).ok()
112 } else {
113 None
114 };
115
116 external_helper(querier, contract_addr, key, || {
117 anyhow!(PerpError::new(
118 ErrorId::Any,
119 ErrorDomain::Default,
120 format!(
121 "unable to load external item {}",
122 debug_key_name.unwrap_or_default()
123 )
124 ))
125 })
126}
127
128pub fn load_external_map<'a, T: serde::de::DeserializeOwned>(
130 querier: &QuerierWrapper<Empty>,
131 contract_addr: impl Into<String>,
132 namespace: &str,
133 key: &impl PrimaryKey<'a>,
134) -> anyhow::Result<T> {
135 external_helper(querier, contract_addr, map_key(namespace, key), || {
136 anyhow!(PerpError::new(
137 ErrorId::Any,
138 ErrorDomain::Default,
139 format!("unable to load external map {}", namespace)
140 ))
141 })
142}
143
144pub fn external_map_has<'a>(
146 querier: &QuerierWrapper<Empty>,
147 contract_addr: impl Into<String>,
148 namespace: &str,
149 key: &impl PrimaryKey<'a>,
150) -> anyhow::Result<bool> {
151 querier
152 .query_wasm_raw(contract_addr, map_key(namespace, key))
153 .map(|x| x.is_some())
154 .map_err(|e| e.into())
155}
156
157fn external_helper<T: serde::de::DeserializeOwned>(
158 querier: &QuerierWrapper<Empty>,
159 contract_addr: impl Into<String>,
160 key: impl Into<Binary>,
161 mk_error_message: impl FnOnce() -> anyhow::Error,
162) -> anyhow::Result<T> {
163 let res = querier
164 .query_wasm_raw(contract_addr, key)?
165 .ok_or_else(mk_error_message)?;
166 serde_json_wasm::from_slice(&res).map_err(|err| err.into())
167}
168pub fn map_key<'a, K: PrimaryKey<'a>>(namespace: &str, key: &K) -> Vec<u8> {
170 let mut size = namespace.len();
174 let key = key.key();
175 assert!(!key.is_empty());
176
177 for x in &key {
178 size += x.as_ref().len() + 2;
179 }
180
181 let mut out = Vec::<u8>::with_capacity(size);
182
183 for prefix in std::iter::once(namespace.as_bytes())
184 .chain(key.iter().take(key.len() - 1).map(|key| key.as_ref()))
185 {
186 out.extend_from_slice(&encode_length(prefix));
187 out.extend_from_slice(prefix);
188 }
189
190 if let Some(last) = key.last() {
191 out.extend_from_slice(last.as_ref());
192 }
193
194 out
195}
196
197fn encode_length(bytes: &[u8]) -> [u8; 2] {
198 if let Ok(len) = u16::try_from(bytes.len()) {
199 len.to_be_bytes()
200 } else {
201 panic!("only supports namespaces up to length 0xFFFF")
202 }
203}
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208 use cosmwasm_std::testing::mock_dependencies;
209 use cw_storage_plus::Map;
210
211 #[test]
212 fn simple_keys_work() {
213 let mut deps = mock_dependencies();
214 let m = Map::<&str, String>::new("foobarbazbin");
215 m.save(&mut deps.storage, "somekey", &"somevalue".to_owned())
216 .unwrap();
217 let key = map_key("foobarbazbin", &"somekey");
218 assert_eq!(deps.as_ref().storage.get(&key).unwrap(), b"\"somevalue\"");
219 }
220
221 #[test]
222 fn complex_keys_work() {
223 const NAMESPACE: &str = "👋";
224 let mut deps = mock_dependencies();
225 let m = Map::<ComplexKey, String>::new(NAMESPACE);
226 let key = (
227 ("level1".to_owned(), "level™️💪".to_owned()),
228 "level-".to_owned(),
229 );
230 let storage_key = map_key(NAMESPACE, &key);
231 m.save(&mut deps.storage, key, &"somevalue".to_owned())
232 .unwrap();
233 assert_eq!(
234 deps.as_ref().storage.get(&storage_key).unwrap(),
235 b"\"somevalue\""
236 );
237 }
238
239 type ComplexKey = ((String, String), String);
240}