unc_sdk/store/lazy_option/
mod.rs1mod impls;
2
3use borsh::{BorshDeserialize, BorshSerialize};
4use once_cell::unsync::OnceCell;
5use unc_sdk_macros::unc;
6
7use crate::env;
8use crate::store::lazy::{load_and_deserialize, serialize_and_store};
9use crate::utils::{CacheEntry, EntryState};
10use crate::IntoStorageKey;
11
12#[unc(inside_uncsdk)]
36pub struct LazyOption<T>
37where
38 T: BorshSerialize,
39{
40 prefix: Box<[u8]>,
42
43 #[borsh(skip, bound(deserialize = ""))] cache: OnceCell<CacheEntry<T>>,
46}
47
48impl<T> LazyOption<T>
49where
50 T: BorshSerialize,
51{
52 pub fn new<S>(prefix: S, value: Option<T>) -> Self
57 where
58 S: IntoStorageKey,
59 {
60 let cache = match value {
61 Some(value) => CacheEntry::new_modified(Some(value)),
62 None => CacheEntry::new_cached(None),
63 };
64
65 Self { prefix: prefix.into_storage_key().into_boxed_slice(), cache: OnceCell::from(cache) }
66 }
67
68 pub fn set(&mut self, value: Option<T>) {
70 if let Some(v) = self.cache.get_mut() {
71 *v.value_mut() = value;
72 } else {
73 self.cache
74 .set(CacheEntry::new_modified(value))
75 .unwrap_or_else(|_| env::abort());
77 }
78 }
79
80 pub fn flush(&mut self) {
84 if let Some(v) = self.cache.get_mut() {
85 if !v.is_modified() {
86 return;
87 }
88
89 match v.value().as_ref() {
90 Some(value) => serialize_and_store(&self.prefix, value),
91 None => {
92 env::storage_remove(&self.prefix);
93 }
94 }
95
96 v.replace_state(EntryState::Cached);
99 }
100 }
101}
102
103impl<T> LazyOption<T>
104where
105 T: BorshSerialize + BorshDeserialize,
106{
107 pub fn get(&self) -> &Option<T> {
111 let entry = self.cache.get_or_init(|| load_and_deserialize(&self.prefix));
112 entry.value()
113 }
114
115 pub fn get_mut(&mut self) -> &mut Option<T> {
119 self.cache.get_or_init(|| load_and_deserialize(&self.prefix));
120 let entry = self.cache.get_mut().unwrap_or_else(|| env::abort());
121 entry.value_mut()
122 }
123}
124
125#[cfg(not(target_arch = "wasm32"))]
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
131 pub fn test_lazy_option() {
132 let mut a = LazyOption::new(b"a", None);
133 assert!(a.is_none());
134 assert!(!env::storage_has_key(b"a"));
135
136 a.set(Some(42u32));
138 assert!(a.is_some());
139 assert_eq!(a.get(), &Some(42));
140
141 a.flush();
143 assert!(env::storage_has_key(b"a"));
144 assert_eq!(u32::try_from_slice(&env::storage_read(b"a").unwrap()).unwrap(), 42);
145
146 *a = Some(49u32);
148 assert!(a.is_some());
149 assert_eq!(a.get(), &Some(49));
150
151 let old = a.replace(69u32);
153 assert!(a.is_some());
154 assert_eq!(old, Some(49));
155
156 let taken = a.take();
158 assert!(a.is_none());
159 assert_eq!(taken, Some(69));
160
161 drop(a);
163 assert!(!env::storage_has_key(b"a"));
164 }
165
166 #[test]
167 pub fn test_debug() {
168 let mut lazy_option = LazyOption::new(b"m", None);
169 if cfg!(feature = "expensive-debug") {
170 assert_eq!(format!("{:?}", lazy_option), "None");
171 } else {
172 assert_eq!(
173 format!("{:?}", lazy_option),
174 "LazyOption { storage_key: [109], cache: Some(CacheEntry { value: None, state: Cached }) }"
175 );
176 }
177
178 *lazy_option = Some(1u8);
179 if cfg!(feature = "expensive-debug") {
180 assert_eq!(format!("{:?}", lazy_option), "Some(1)");
181 } else {
182 assert_eq!(
183 format!("{:?}", lazy_option),
184 "LazyOption { storage_key: [109], cache: Some(CacheEntry { value: Some(1), state: Modified }) }"
185 );
186 }
187
188 let serialized = borsh::to_vec(&lazy_option).unwrap();
190 drop(lazy_option);
191 let lazy_option = LazyOption::<u8>::try_from_slice(&serialized).unwrap();
192 if cfg!(feature = "expensive-debug") {
193 assert_eq!(format!("{:?}", lazy_option), "Some(1)");
194 } else {
195 assert_eq!(
196 format!("{:?}", lazy_option),
197 "LazyOption { storage_key: [109], cache: None }"
198 );
199 }
200 }
201}