multiversx_sc/storage/mappers/single_value_mapper.rs
1use core::{borrow::Borrow, marker::PhantomData};
2
3pub use super::{
4 StorageMapper, StorageMapperFromAddress,
5 source::{CurrentStorage, StorageAddress},
6};
7use crate::{
8 abi::{TypeAbi, TypeAbiFrom, TypeDescriptionContainer, TypeName},
9 api::StorageMapperApi,
10 codec::{
11 DecodeErrorHandler, EncodeErrorHandler, TopDecode, TopDecodeInput, TopEncode,
12 TopEncodeMulti, TopEncodeMultiOutput, TopEncodeOutput, multi_types::PlaceholderOutput,
13 },
14 storage::{StorageKey, storage_clear, storage_set},
15 types::{ManagedAddress, ManagedType},
16};
17
18/// A storage mapper for managing a single serializable value with atomic read/write operations.
19///
20/// # Storage Layout
21///
22/// The `SingleValueMapper` stores a single value directly at its storage key:
23///
24/// - `base_key` → encoded value of type `T`
25///
26/// This is the simplest storage mapper - just one key storing one value.
27///
28/// # Main Operations
29///
30/// - **Write**: `set(value)` - Stores a value (accepts owned or borrowed). O(1).
31/// - **Read**: `get()` - Retrieves the stored value. O(1).
32/// - **Conditional write**: `set_if_empty(value)` - Stores only if empty. O(1).
33/// - **Update**: `update(|v| {...})` - Read-modify-write in one operation. O(1).
34/// - **Replace**: `replace(new_value)` - Swaps value and returns old one. O(1).
35/// - **Take**: `take()` - Retrieves value and clears storage. O(1).
36/// - **Clear**: `clear()` - Removes value from storage. O(1).
37/// - **Check**: `is_empty()` - Returns true if no value stored. O(1).
38///
39/// # Value Semantics
40///
41/// - Empty storage: Returns the default/zero value when calling `get()`
42/// - Setting a value: Overwrites any existing value
43/// - Clearing: Removes the value entirely from storage
44/// - Zero values: A value set to its zero/default may be indistinguishable from empty storage
45///
46/// # Trade-offs
47///
48/// - **Pros**: Simplest storage pattern; minimal overhead; direct key-value mapping; very efficient.
49/// - **Cons**: Only one value per mapper; no built-in indexing or collections.
50///
51/// # Use Cases
52///
53/// - Configuration values (flags, thresholds, addresses)
54/// - Global counters or accumulators
55/// - Contract state variables (owner, paused status, etc.)
56/// - Cached computed values
57/// - Simple on-chain variables that don't need collection semantics
58///
59/// # Example
60///
61/// ```rust
62/// # use multiversx_sc::storage::mappers::{StorageMapper, SingleValueMapper};
63/// # use multiversx_sc::types::ManagedAddress;
64/// # fn example<SA: multiversx_sc::api::StorageMapperApi>(owner: ManagedAddress<SA>) {
65/// # let mapper = SingleValueMapper::<SA, ManagedAddress<SA>>::new(
66/// # multiversx_sc::storage::StorageKey::new(&b"owner"[..])
67/// # );
68/// // Set a value
69/// mapper.set(&owner);
70///
71/// // Check if empty
72/// assert!(!mapper.is_empty());
73///
74/// // Get the value
75/// let current_owner = mapper.get();
76/// assert_eq!(current_owner, owner);
77///
78/// // Conditional set (only if empty)
79/// mapper.set_if_empty(&owner); // Does nothing, already set
80///
81/// // Update in place
82/// mapper.update(|addr| {
83/// // Modify the value
84/// *addr = owner.clone();
85/// });
86///
87/// // Replace and get old value
88/// # let new_owner = owner.clone();
89/// let old_owner = mapper.replace(&new_owner);
90/// assert_eq!(old_owner, owner);
91///
92/// // Take value (get and clear)
93/// let taken = mapper.take();
94/// assert!(mapper.is_empty());
95///
96/// // Clear storage
97/// mapper.set(&owner);
98/// mapper.clear();
99/// assert!(mapper.is_empty());
100/// # }
101/// ```
102///
103/// # Numeric Counter Example
104///
105/// ```rust
106/// # use multiversx_sc::storage::mappers::{StorageMapper, SingleValueMapper};
107/// # fn counter_example<SA: multiversx_sc::api::StorageMapperApi>() {
108/// # let counter = SingleValueMapper::<SA, u64>::new(
109/// # multiversx_sc::storage::StorageKey::new(&b"counter"[..])
110/// # );
111/// // Initialize counter
112/// counter.set(0u64);
113///
114/// // Increment using update
115/// counter.update(|value| *value += 1);
116/// assert_eq!(counter.get(), 1);
117///
118/// // Increment and return new value
119/// let new_value = counter.update(|value| {
120/// *value += 1;
121/// *value
122/// });
123/// assert_eq!(new_value, 2);
124/// # }
125/// ```
126pub struct SingleValueMapper<SA, T, A = CurrentStorage>
127where
128 SA: StorageMapperApi,
129 A: StorageAddress<SA>,
130 T: TopEncode + TopDecode + 'static,
131{
132 address: A,
133 key: StorageKey<SA>,
134 _phantom_api: PhantomData<SA>,
135 _phantom_item: PhantomData<T>,
136}
137
138impl<SA, T> StorageMapper<SA> for SingleValueMapper<SA, T, CurrentStorage>
139where
140 SA: StorageMapperApi,
141 T: TopEncode + TopDecode,
142{
143 #[inline]
144 fn new(base_key: StorageKey<SA>) -> Self {
145 SingleValueMapper {
146 address: CurrentStorage,
147 key: base_key,
148 _phantom_api: PhantomData,
149 _phantom_item: PhantomData,
150 }
151 }
152}
153
154impl<SA, T> StorageMapperFromAddress<SA> for SingleValueMapper<SA, T, ManagedAddress<SA>>
155where
156 SA: StorageMapperApi,
157 T: TopEncode + TopDecode,
158{
159 #[inline]
160 fn new_from_address(address: ManagedAddress<SA>, base_key: StorageKey<SA>) -> Self {
161 SingleValueMapper {
162 address,
163 key: base_key,
164 _phantom_api: PhantomData,
165 _phantom_item: PhantomData,
166 }
167 }
168}
169
170impl<SA, T, A> SingleValueMapper<SA, T, A>
171where
172 SA: StorageMapperApi,
173 A: StorageAddress<SA>,
174 T: TopEncode + TopDecode,
175{
176 /// Retrieves current value from storage.
177 pub fn get(&self) -> T {
178 self.address.address_storage_get(self.key.as_ref())
179 }
180
181 /// Returns whether the storage managed by this mapper is empty.
182 pub fn is_empty(&self) -> bool {
183 self.raw_byte_length() == 0
184 }
185
186 pub fn raw_byte_length(&self) -> usize {
187 self.address.address_storage_get_len(self.key.as_ref())
188 }
189}
190
191impl<SA, T> SingleValueMapper<SA, T, CurrentStorage>
192where
193 SA: StorageMapperApi,
194 T: TopEncode + TopDecode,
195{
196 /// Saves argument to storage.
197 ///
198 /// Accepts owned item of type `T`, or any borrowed form of it, such as `&T`.
199 #[inline]
200 pub fn set<BT>(&self, new_value: BT)
201 where
202 BT: Borrow<T>,
203 {
204 storage_set(self.key.as_ref(), new_value.borrow());
205 }
206
207 /// Saves argument to storage only if the storage is empty.
208 /// Does nothing otherwise.
209 pub fn set_if_empty<BT>(&self, value: BT)
210 where
211 BT: Borrow<T>,
212 {
213 if self.is_empty() {
214 self.set(value);
215 }
216 }
217
218 /// Clears the storage for this mapper.
219 pub fn clear(&self) {
220 storage_clear(self.key.as_ref());
221 }
222
223 /// Syntactic sugar, to more compactly express a get, update and set in one line.
224 /// Takes whatever lies in storage, apples the given closure and saves the final value back to storage.
225 /// Propagates the return value of the given function.
226 pub fn update<R, F: FnOnce(&mut T) -> R>(&self, f: F) -> R {
227 let mut value = self.get();
228 let result = f(&mut value);
229 self.set(value);
230 result
231 }
232
233 /// Takes the value out of the storage, clearing it in the process.
234 pub fn take(&self) -> T {
235 let value = self.get();
236 self.clear();
237 value
238 }
239
240 // Replaces the actual value in the storage by the value given in parameter, returning the old value.
241 pub fn replace<BT>(&self, new_value: BT) -> T
242 where
243 BT: Borrow<T>,
244 {
245 let value = self.get();
246 self.set(new_value);
247 value
248 }
249}
250
251impl<SA, T> TopEncodeMulti for SingleValueMapper<SA, T, CurrentStorage>
252where
253 SA: StorageMapperApi,
254 T: TopEncode + TopDecode,
255{
256 fn multi_encode_or_handle_err<O, H>(&self, output: &mut O, h: H) -> Result<(), H::HandledErr>
257 where
258 O: TopEncodeMultiOutput,
259 H: EncodeErrorHandler,
260 {
261 output.push_single_value(&self.get(), h)
262 }
263}
264
265/// Intermediary type for deserializing the result of an endpoint that returns a `SingleValueMapper`.
266///
267/// Necessary because we cannot implement `TypeAbiFrom` directly on `T`.
268pub struct SingleValue<T: TopDecode>(T);
269
270impl<T: TopEncode + TopDecode> TopEncode for SingleValue<T> {
271 fn top_encode_or_handle_err<O, H>(&self, output: O, h: H) -> Result<(), H::HandledErr>
272 where
273 O: TopEncodeOutput,
274 H: EncodeErrorHandler,
275 {
276 self.0.top_encode_or_handle_err(output, h)
277 }
278}
279
280impl<T: TopDecode> TopDecode for SingleValue<T> {
281 fn top_decode_or_handle_err<I, H>(input: I, h: H) -> Result<Self, H::HandledErr>
282 where
283 I: TopDecodeInput,
284 H: DecodeErrorHandler,
285 {
286 Ok(SingleValue::<T>(T::top_decode_or_handle_err(input, h)?))
287 }
288}
289
290impl<T: TopDecode> From<T> for SingleValue<T> {
291 fn from(value: T) -> Self {
292 SingleValue::<T>(value)
293 }
294}
295
296impl<T: TopDecode> SingleValue<T> {
297 #[inline]
298 pub fn into(self) -> T {
299 self.0
300 }
301}
302
303impl<SA, T, R> TypeAbiFrom<SingleValueMapper<SA, T, CurrentStorage>> for SingleValue<R>
304where
305 SA: StorageMapperApi,
306 T: TopEncode + TopDecode,
307 R: TopDecode + TypeAbiFrom<T>,
308{
309}
310
311impl<SA, T> TypeAbiFrom<SingleValueMapper<SA, T>> for PlaceholderOutput
312where
313 SA: StorageMapperApi,
314 T: TopEncode + TopDecode,
315{
316}
317
318impl<SA, T> TypeAbiFrom<Self> for SingleValueMapper<SA, T, CurrentStorage>
319where
320 SA: StorageMapperApi,
321 T: TopEncode + TopDecode + TypeAbi,
322{
323}
324
325impl<SA, T> TypeAbi for SingleValueMapper<SA, T, CurrentStorage>
326where
327 SA: StorageMapperApi,
328 T: TopEncode + TopDecode + TypeAbi,
329{
330 type Unmanaged = T::Unmanaged;
331
332 fn type_name() -> TypeName {
333 T::type_name()
334 }
335
336 fn type_name_rust() -> TypeName {
337 T::type_name_rust()
338 }
339
340 fn provide_type_descriptions<TDC: TypeDescriptionContainer>(accumulator: &mut TDC) {
341 T::provide_type_descriptions(accumulator)
342 }
343}