unc_sdk/collections/
lazy_option.rs1use std::marker::PhantomData;
7
8use borsh::{to_vec, BorshDeserialize, BorshSerialize};
9
10use crate::env;
11use crate::IntoStorageKey;
12use unc_sdk_macros::unc;
13
14const ERR_VALUE_SERIALIZATION: &str = "Cannot serialize value with Borsh";
15const ERR_VALUE_DESERIALIZATION: &str = "Cannot deserialize value with Borsh";
16
17#[unc(inside_uncsdk)]
19pub struct LazyOption<T> {
20 storage_key: Vec<u8>,
21 #[borsh(skip)]
22 el: PhantomData<T>,
23}
24
25impl<T> LazyOption<T> {
26 pub fn is_some(&self) -> bool {
28 env::storage_has_key(&self.storage_key)
29 }
30
31 pub fn is_none(&self) -> bool {
33 !self.is_some()
34 }
35
36 fn get_raw(&self) -> Option<Vec<u8>> {
38 env::storage_read(&self.storage_key)
39 }
40
41 fn remove_raw(&mut self) -> bool {
44 env::storage_remove(&self.storage_key)
45 }
46
47 fn take_raw(&mut self) -> Option<Vec<u8>> {
49 if self.remove_raw() {
50 Some(env::storage_get_evicted().unwrap())
51 } else {
52 None
53 }
54 }
55
56 fn set_raw(&mut self, raw_value: &[u8]) -> bool {
57 env::storage_write(&self.storage_key, raw_value)
58 }
59
60 fn replace_raw(&mut self, raw_value: &[u8]) -> Option<Vec<u8>> {
61 if self.set_raw(raw_value) {
62 Some(env::storage_get_evicted().unwrap())
63 } else {
64 None
65 }
66 }
67}
68
69impl<T> LazyOption<T>
70where
71 T: BorshSerialize + BorshDeserialize,
72{
73 pub fn new<S>(storage_key: S, value: Option<&T>) -> Self
84 where
85 S: IntoStorageKey,
86 {
87 let mut this = Self { storage_key: storage_key.into_storage_key(), el: PhantomData };
88 if let Some(value) = value {
89 this.set(value);
90 }
91 this
92 }
93
94 fn serialize_value(value: &T) -> Vec<u8> {
95 match to_vec(value) {
96 Ok(x) => x,
97 Err(_) => env::panic_str(ERR_VALUE_SERIALIZATION),
98 }
99 }
100
101 fn deserialize_value(raw_value: &[u8]) -> T {
102 match T::try_from_slice(raw_value) {
103 Ok(x) => x,
104 Err(_) => env::panic_str(ERR_VALUE_DESERIALIZATION),
105 }
106 }
107
108 pub fn remove(&mut self) -> bool {
122 self.remove_raw()
123 }
124
125 pub fn take(&mut self) -> Option<T> {
138 self.take_raw().map(|v| Self::deserialize_value(&v))
139 }
140
141 pub fn get(&self) -> Option<T> {
155 self.get_raw().map(|v| Self::deserialize_value(&v))
156 }
157
158 pub fn set(&mut self, value: &T) -> bool {
171 self.set_raw(&Self::serialize_value(value))
172 }
173
174 pub fn replace(&mut self, value: &T) -> Option<T> {
186 self.replace_raw(&Self::serialize_value(value)).map(|v| Self::deserialize_value(&v))
187 }
188}
189
190impl<T> std::fmt::Debug for LazyOption<T>
191where
192 T: std::fmt::Debug + BorshSerialize + BorshDeserialize,
193{
194 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
195 if cfg!(feature = "expensive-debug") {
196 self.get().fmt(f)
197 } else {
198 f.debug_struct("LazyOption").field("storage_key", &self.storage_key).finish()
199 }
200 }
201}
202
203#[cfg(not(target_arch = "wasm32"))]
204#[cfg(test)]
205mod tests {
206 use super::*;
207
208 #[test]
209 pub fn test_all() {
210 let mut a = LazyOption::new(b"a", None);
211 assert!(a.is_none());
212 a.set(&42u32);
213 assert!(a.is_some());
214 assert_eq!(a.get(), Some(42));
215 assert!(a.is_some());
216 assert_eq!(a.replace(&95), Some(42));
217 assert!(a.is_some());
218 assert_eq!(a.take(), Some(95));
219 assert!(a.is_none());
220 assert_eq!(a.replace(&105), None);
221 assert!(a.is_some());
222 assert_eq!(a.get(), Some(105));
223 assert!(a.remove());
224 assert!(a.is_none());
225 assert_eq!(a.get(), None);
226 assert_eq!(a.take(), None);
227 assert!(a.is_none());
228 }
229
230 #[test]
231 pub fn test_multi() {
232 let mut a = LazyOption::new(b"a", None);
233 let mut b = LazyOption::new(b"b", None);
234 assert!(a.is_none());
235 assert!(b.is_none());
236 a.set(&42u32);
237 assert!(b.is_none());
238 assert!(a.is_some());
239 assert_eq!(a.get(), Some(42));
240 b.set(&32u32);
241 assert!(a.is_some());
242 assert!(b.is_some());
243 assert_eq!(a.get(), Some(42));
244 assert_eq!(b.get(), Some(32));
245 }
246
247 #[test]
248 pub fn test_init_value() {
249 let a = LazyOption::new(b"a", Some(&42u32));
250 assert!(a.is_some());
251 assert_eq!(a.get(), Some(42));
252 }
253
254 #[test]
255 pub fn test_debug() {
256 let mut lazy_option = LazyOption::new(b"m", None);
257 if cfg!(feature = "expensive-debug") {
258 assert_eq!(format!("{:?}", lazy_option), "None");
259 } else {
260 assert_eq!(format!("{:?}", lazy_option), "LazyOption { storage_key: [109] }");
261 }
262
263 lazy_option.set(&1u64);
264 if cfg!(feature = "expensive-debug") {
265 assert_eq!(format!("{:?}", lazy_option), "Some(1)");
266 } else {
267 assert_eq!(format!("{:?}", lazy_option), "LazyOption { storage_key: [109] }");
268 }
269 }
270}