soroban_tools/storage/
impl.rs

1/*
2    Copyright (c) 2023-2024 Frederic Kyung-jin Rezeau (오경진 吳景振)
3
4    This file is part of soroban-kit.
5
6    Licensed under the MIT License, this software is provided "AS IS",
7    no liability assumed. For details, see the LICENSE file in the
8    root directory.
9
10    Author: Fred Kyung-jin Rezeau <fred@litemint.com>
11*/
12
13#[cfg(not(feature = "mock-storage"))]
14use soroban_sdk::storage::{Instance, Persistent, Temporary};
15
16#[cfg(feature = "mock-storage")]
17pub use crate::mock_storage::{
18    with_instance_storage, with_persistent_storage, with_temporary_storage,
19};
20
21use soroban_sdk::{Env, IntoVal, TryFromVal, Val};
22
23use core::marker::PhantomData;
24
25/// Execute the provided closure with instance storage.
26#[inline]
27#[cfg(not(feature = "mock-storage"))]
28pub fn with_instance_storage<F, T>(env: &Env, f: F) -> T
29where
30    F: FnOnce(&Instance) -> T,
31{
32    f(&env.storage().instance())
33}
34
35/// Execute the provided closure with persistent storage.
36#[inline]
37#[cfg(not(feature = "mock-storage"))]
38pub fn with_persistent_storage<F, T>(env: &Env, f: F) -> T
39where
40    F: FnOnce(&Persistent) -> T,
41{
42    f(&env.storage().persistent())
43}
44
45/// Execute the provided closure with temporary storage.
46#[inline]
47#[cfg(not(feature = "mock-storage"))]
48pub fn with_temporary_storage<F, T>(env: &Env, f: F) -> T
49where
50    F: FnOnce(&Temporary) -> T,
51{
52    f(&env.storage().temporary())
53}
54
55/// Generic proxy for storage operations.
56pub struct StorageProxy<'a, K, T>
57where
58    K: 'a + IntoVal<Env, Val> + TryFromVal<Env, Val>,
59{
60    key: &'a K,
61    _data: PhantomData<*const T>,
62}
63
64impl<'a, K, T> StorageProxy<'a, K, T>
65where
66    K: IntoVal<Env, Val> + TryFromVal<Env, Val>,
67{
68    fn new(key: &'a K) -> Self {
69        StorageProxy {
70            key,
71            _data: PhantomData,
72        }
73    }
74
75    pub fn get_key(&self) -> &'a K {
76        self.key
77    }
78}
79
80/// Trait for storage operations.
81pub trait StorageOps<T> {
82    fn get(&self, env: &Env) -> Option<T>;
83    fn set(&self, env: &Env, data: &T);
84    fn remove(&self, env: &Env);
85    fn has(&self, env: &Env) -> bool;
86    fn extend_ttl(&self, env: &Env, threshold: u32, extend_to: u32);
87}
88
89pub fn get<'a, K, T>(env: &Env, key: &'a K) -> Option<T>
90where
91    StorageProxy<'a, K, T>: StorageOps<T>,
92    K: IntoVal<Env, Val> + TryFromVal<Env, Val> + ?Sized,
93{
94    StorageProxy::<'a, K, T>::new(key).get(env)
95}
96
97pub fn get_or_else<'a, K, T, F, R>(env: &Env, key: &'a K, handler: F) -> R
98where
99    StorageProxy<'a, K, T>: StorageOps<T>,
100    K: IntoVal<Env, Val> + TryFromVal<Env, Val> + ?Sized,
101    F: FnOnce(Option<T>) -> R,
102{
103    handler(StorageProxy::<'a, K, T>::new(key).get(env))
104}
105
106pub fn set<'a, K, T>(env: &Env, key: &'a K, data: &T)
107where
108    StorageProxy<'a, K, T>: StorageOps<T>,
109    K: IntoVal<Env, Val> + TryFromVal<Env, Val> + ?Sized,
110{
111    StorageProxy::<'a, K, T>::new(key).set(env, data);
112}
113
114pub fn has<'a, K, T>(env: &Env, key: &'a K) -> bool
115where
116    StorageProxy<'a, K, T>: StorageOps<T>,
117    K: IntoVal<Env, Val> + TryFromVal<Env, Val> + ?Sized,
118{
119    StorageProxy::<'a, K, T>::new(key).has(env)
120}
121
122pub fn remove<'a, K, T>(env: &Env, key: &'a K)
123where
124    StorageProxy<'a, K, T>: StorageOps<T>,
125    K: IntoVal<Env, Val> + TryFromVal<Env, Val> + ?Sized,
126{
127    StorageProxy::<'a, K, T>::new(key).remove(env);
128}
129
130pub fn extend_ttl<'a, K, T>(
131    env: &Env,
132    key: &'a K,
133    threshold: u32,
134    extend_to: u32,
135) where
136    StorageProxy<'a, K, T>: StorageOps<T>,
137    K: IntoVal<Env, Val> + TryFromVal<Env, Val> + ?Sized,
138{
139    StorageProxy::<'a, K, T>::new(key).extend_ttl(env, threshold, extend_to);
140}
141
142#[macro_export]
143macro_rules! impl_key_constraint {
144    ($key_type:ty, $key_trait:ident) => {
145        pub trait $key_trait {}
146        impl $key_trait for $key_type {}
147    };
148}
149
150#[macro_export]
151macro_rules! impl_storage {
152    (Instance, $data_type:ty $(, $key_trait:ident)?) => {
153        impl<'a, K> $crate::storage::StorageOps<$data_type>
154            for $crate::storage::StorageProxy<'a, K, $data_type>
155        where
156            K: $( $key_trait + )? soroban_sdk::IntoVal<soroban_sdk::Env, soroban_sdk::Val>
157                + soroban_sdk::TryFromVal<soroban_sdk::Env, soroban_sdk::Val>,
158        {
159            fn get(&self, env: &soroban_sdk::Env) -> Option<$data_type> {
160                $crate::storage::with_instance_storage(env, |storage| storage.get(self.get_key()))
161            }
162
163            fn set(&self, env: &soroban_sdk::Env, data: &$data_type) {
164                $crate::storage::with_instance_storage(env, |storage| {
165                    storage.set(self.get_key(), data)
166                });
167            }
168
169            fn remove(&self, env: &soroban_sdk::Env) {
170                $crate::storage::with_instance_storage(env, |storage| {
171                    storage.remove(self.get_key())
172                });
173            }
174
175            fn has(&self, env: &soroban_sdk::Env) -> bool {
176                $crate::storage::with_instance_storage(env, |storage| storage.has(self.get_key()))
177            }
178
179            fn extend_ttl(&self, env: &Env, threshold: u32, extend_to: u32) {
180                $crate::storage::with_instance_storage(env, |storage| {
181                    storage.extend_ttl(
182                        threshold,
183                        extend_to,
184                    )
185                });
186            }
187        }
188    };
189    (Persistent, $data_type:ty $(, $key_type:ident)?) => {
190        impl<'a, K> $crate::storage::StorageOps<$data_type>
191            for $crate::storage::StorageProxy<'a, K, $data_type>
192        where
193            K: $( $key_type + )? soroban_sdk::IntoVal<soroban_sdk::Env, soroban_sdk::Val>
194                + soroban_sdk::TryFromVal<soroban_sdk::Env, soroban_sdk::Val>,
195        {
196            fn get(&self, env: &soroban_sdk::Env) -> Option<$data_type> {
197                $crate::storage::with_persistent_storage(env, |storage| storage.get(self.get_key()))
198            }
199
200            fn set(&self, env: &soroban_sdk::Env, data: &$data_type) {
201                $crate::storage::with_persistent_storage(env, |storage| {
202                    storage.set(self.get_key(), data)
203                });
204            }
205
206            fn remove(&self, env: &soroban_sdk::Env) {
207                $crate::storage::with_persistent_storage(env, |storage| {
208                    storage.remove(self.get_key())
209                });
210            }
211
212            fn has(&self, env: &soroban_sdk::Env) -> bool {
213                $crate::storage::with_persistent_storage(env, |storage| storage.has(self.get_key()))
214            }
215
216            fn extend_ttl(&self, env: &Env, threshold: u32, extend_to: u32) {
217                $crate::storage::with_persistent_storage(env, |storage| {
218                    storage.extend_ttl(
219                        self.get_key(),
220                        threshold,
221                        extend_to,
222                    )
223                });
224            }
225        }
226    };
227    (Temporary, $data_type:ty $(, $key_type:ident)?) => {
228        impl<'a, K> $crate::storage::StorageOps<$data_type>
229            for $crate::storage::StorageProxy<'a, K, $data_type>
230        where
231            K: $( $key_type + )? soroban_sdk::IntoVal<soroban_sdk::Env, soroban_sdk::Val>
232                + soroban_sdk::TryFromVal<soroban_sdk::Env, soroban_sdk::Val>,
233        {
234            fn get(&self, env: &soroban_sdk::Env) -> Option<$data_type> {
235                $crate::storage::with_temporary_storage(env, |storage| storage.get(self.get_key()))
236            }
237
238            fn set(&self, env: &soroban_sdk::Env, data: &$data_type) {
239                $crate::storage::with_temporary_storage(env, |storage| {
240                    storage.set(self.get_key(), data)
241                });
242            }
243
244            fn remove(&self, env: &soroban_sdk::Env) {
245                $crate::storage::with_temporary_storage(env, |storage| {
246                    storage.remove(self.get_key())
247                });
248            }
249
250            fn has(&self, env: &soroban_sdk::Env) -> bool {
251                $crate::storage::with_temporary_storage(env, |storage| storage.has(self.get_key()))
252            }
253
254            fn extend_ttl(&self, env: &Env, threshold: u32, extend_to: u32) {
255                $crate::storage::with_temporary_storage(env, |storage| {
256                    storage.extend_ttl(
257                        self.get_key(),
258                        threshold,
259                        extend_to,
260                    )
261                });
262            }
263        }
264    };
265}