1mod mapping;
22mod vec;
23
24#[doc(inline)]
25pub use self::mapping::Mapping;
26pub use self::vec::StorageVec;
27
28use crate::traits::{
29 AutoKey,
30 StorableHint,
31 StorageKey,
32};
33use core::marker::PhantomData;
34use ink_primitives::Key;
35use ink_storage_traits::Storable;
36use scale::{
37 Error,
38 Input,
39 Output,
40};
41
42#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))]
99pub struct Lazy<V, KeyType: StorageKey = AutoKey> {
100 _marker: PhantomData<fn() -> (V, KeyType)>,
101}
102
103impl<V, KeyType> Default for Lazy<V, KeyType>
105where
106 KeyType: StorageKey,
107{
108 fn default() -> Self {
109 Self::new()
110 }
111}
112
113impl<V, KeyType> Lazy<V, KeyType>
114where
115 KeyType: StorageKey,
116{
117 pub const fn new() -> Self {
119 Self {
120 _marker: PhantomData,
121 }
122 }
123}
124
125impl<V, KeyType> core::fmt::Debug for Lazy<V, KeyType>
126where
127 KeyType: StorageKey,
128{
129 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
130 f.debug_struct("Lazy").field("key", &KeyType::KEY).finish()
131 }
132}
133
134impl<V, KeyType> Lazy<V, KeyType>
135where
136 V: Storable,
137 KeyType: StorageKey,
138{
139 pub fn get(&self) -> Option<V> {
145 match ink_env::get_contract_storage::<Key, V>(&KeyType::KEY) {
146 Ok(Some(value)) => Some(value),
147 _ => None,
148 }
149 }
150
151 #[cfg(feature = "unstable-hostfn")]
161 pub fn try_get(&self) -> Option<ink_env::Result<V>> {
162 let key_size = <Key as Storable>::encoded_size(&KeyType::KEY);
163
164 if key_size >= ink_env::BUFFER_SIZE {
165 return Some(Err(ink_env::Error::BufferTooSmall));
166 }
167
168 let value_size: usize = ink_env::contains_contract_storage(&KeyType::KEY)?
169 .try_into()
170 .expect("targets of less than 32bit pointer size are not supported; qed");
171
172 if key_size.saturating_add(value_size) > ink_env::BUFFER_SIZE {
173 return Some(Err(ink_env::Error::BufferTooSmall));
174 }
175
176 self.get().map(Ok)
177 }
178
179 pub fn set(&mut self, value: &V) {
185 ink_env::set_contract_storage::<Key, V>(&KeyType::KEY, value);
186 }
187
188 pub fn try_set(&mut self, value: &V) -> ink_env::Result<()> {
193 let key_size = <Key as Storable>::encoded_size(&KeyType::KEY);
194 let value_size = <V as Storable>::encoded_size(value);
195
196 if key_size.saturating_add(value_size) > ink_env::BUFFER_SIZE {
197 return Err(ink_env::Error::BufferTooSmall);
198 };
199
200 self.set(value);
201
202 Ok(())
203 }
204}
205
206impl<V, KeyType> Lazy<V, KeyType>
207where
208 V: Storable + Default,
209 KeyType: StorageKey,
210{
211 pub fn get_or_default(&self) -> V {
215 match ink_env::get_contract_storage::<Key, V>(&KeyType::KEY) {
216 Ok(Some(value)) => value,
217 _ => Default::default(),
218 }
219 }
220}
221
222impl<V, KeyType> Storable for Lazy<V, KeyType>
223where
224 KeyType: StorageKey,
225{
226 #[inline(always)]
227 fn encode<T: Output + ?Sized>(&self, _dest: &mut T) {}
228
229 #[inline(always)]
230 fn decode<I: Input>(_input: &mut I) -> Result<Self, Error> {
231 Ok(Default::default())
232 }
233
234 #[inline(always)]
235 fn encoded_size(&self) -> usize {
236 0
237 }
238}
239
240impl<V, Key, InnerKey> StorableHint<Key> for Lazy<V, InnerKey>
241where
242 Key: StorageKey,
243 InnerKey: StorageKey,
244 V: StorableHint<Key>,
245{
246 type Type = Lazy<V::Type, Key>;
247 type PreferredKey = InnerKey;
248}
249
250impl<V, KeyType> StorageKey for Lazy<V, KeyType>
251where
252 KeyType: StorageKey,
253{
254 const KEY: Key = KeyType::KEY;
255}
256
257#[cfg(feature = "std")]
258const _: () = {
259 use crate::traits::StorageLayout;
260 use ink_metadata::layout::{
261 Layout,
262 LayoutKey,
263 RootLayout,
264 };
265
266 impl<V, KeyType> StorageLayout for Lazy<V, KeyType>
267 where
268 V: StorageLayout + scale_info::TypeInfo + 'static,
269 KeyType: StorageKey + scale_info::TypeInfo + 'static,
270 {
271 fn layout(_: &Key) -> Layout {
272 Layout::Root(RootLayout::new(
273 LayoutKey::from(&KeyType::KEY),
274 <V as StorageLayout>::layout(&KeyType::KEY),
275 scale_info::meta_type::<Self>(),
276 ))
277 }
278 }
279};
280
281#[cfg(test)]
282mod tests {
283 use super::*;
284 use crate::traits::ManualKey;
285
286 #[test]
287 fn set_and_get_work() {
288 ink_env::test::run_test::<ink_env::DefaultEnvironment, _>(|_| {
289 let mut storage: Lazy<u8> = Lazy::new();
290 storage.set(&2);
291 assert_eq!(storage.get(), Some(2));
292
293 Ok(())
294 })
295 .unwrap()
296 }
297
298 #[test]
299 fn set_and_get_work_for_two_lazy_with_same_manual_key() {
300 ink_env::test::run_test::<ink_env::DefaultEnvironment, _>(|_| {
301 let mut storage: Lazy<u8, ManualKey<123>> = Lazy::new();
302 storage.set(&2);
303
304 let storage2: Lazy<u8, ManualKey<123>> = Lazy::new();
305 assert_eq!(storage2.get(), Some(2));
306
307 Ok(())
308 })
309 .unwrap()
310 }
311
312 #[test]
313 fn gets_or_default_if_no_key_set() {
314 ink_env::test::run_test::<ink_env::DefaultEnvironment, _>(|_| {
315 let storage: Lazy<u8> = Lazy::new();
316 assert_eq!(storage.get_or_default(), 0);
317
318 Ok(())
319 })
320 .unwrap()
321 }
322
323 #[test]
324 fn gets_returns_none_if_no_value_was_set() {
325 ink_env::test::run_test::<ink_env::DefaultEnvironment, _>(|_| {
326 let storage: Lazy<u8> = Lazy::new();
327 assert_eq!(storage.get(), None);
328
329 Ok(())
330 })
331 .unwrap()
332 }
333
334 #[test]
335 fn fallible_storage_works_for_fitting_data() {
336 ink_env::test::run_test::<ink_env::DefaultEnvironment, _>(|_| {
337 const KEY_SIZE: usize = 4;
339 const VALUE_SIZE: usize = ink_env::BUFFER_SIZE - KEY_SIZE;
340
341 let mut storage: Lazy<[u8; VALUE_SIZE]> = Lazy::new();
342
343 let value = [0u8; VALUE_SIZE];
344 assert_eq!(storage.try_set(&value), Ok(()));
345 assert_eq!(storage.try_get(), Some(Ok(value)));
346
347 Ok(())
348 })
349 .unwrap()
350 }
351
352 #[test]
353 fn fallible_storage_fails_gracefully_for_overgrown_data() {
354 ink_env::test::run_test::<ink_env::DefaultEnvironment, _>(|_| {
355 const KEY_SIZE: usize = 4;
357 const VALUE_SIZE: usize = ink_env::BUFFER_SIZE - KEY_SIZE + 1;
358
359 let mut storage: Lazy<[u8; VALUE_SIZE]> = Lazy::new();
360
361 let value = [0u8; VALUE_SIZE];
362 assert_eq!(storage.try_get(), None);
363 assert_eq!(storage.try_set(&value), Err(ink_env::Error::BufferTooSmall));
364
365 ink_env::set_contract_storage(&storage.key(), &value);
368 assert_eq!(storage.try_get(), Some(Err(ink_env::Error::BufferTooSmall)));
369
370 Ok(())
371 })
372 .unwrap()
373 }
374}