Skip to main content

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}