gear_common/storage/primitives/
map.rs

1// This file is part of Gear.
2
3// Copyright (C) 2022-2025 Gear Technologies Inc.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! Module for map storing primitive.
20//!
21//! This primitive defines interface of interaction
22//! with globally stored single-key map (Key -> Value).
23
24use sp_runtime::codec::{Encode, EncodeAppend, EncodeLike};
25/// Represents logic of managing globally stored
26/// single-key map for more complicated logic.
27///
28/// In fact, represents custom implementation/wrapper
29/// around of Substrate's `StorageMap` with `OptionQuery`.
30pub trait MapStorage {
31    /// Map's key type.
32    type Key;
33    /// Map's stored value type.
34    type Value;
35
36    /// Returns bool, defining does map contain value under given key.
37    fn contains_key(key: &Self::Key) -> bool;
38
39    /// Gets value stored under given key, if present.
40    fn get(key: &Self::Key) -> Option<Self::Value>;
41
42    /// Inserts value with given key.
43    fn insert(key: Self::Key, value: Self::Value);
44
45    /// Mutates value by `Option` reference, which stored (or not
46    /// in `None` case) under given key with given function.
47    ///
48    /// May return generic type value.
49    fn mutate<R, F: FnOnce(&mut Option<Self::Value>) -> R>(key: Self::Key, f: F) -> R;
50
51    /// Works the same as `Self::mutate`, but triggers if value present.
52    fn mutate_exists<R, F: FnOnce(&mut Self::Value) -> R>(key: Self::Key, f: F) -> Option<R> {
53        Self::mutate(key, |opt_val| opt_val.as_mut().map(f))
54    }
55
56    /// Mutates all stored values with given convert function.
57    fn mutate_values<F: FnMut(Self::Value) -> Self::Value>(f: F);
58
59    /// Removes value stored under the given key.
60    fn remove(key: Self::Key);
61
62    /// Removes all values.
63    fn clear();
64
65    /// Gets value stored under given key, if present,
66    /// and removes it from storage.
67    fn take(key: Self::Key) -> Option<Self::Value>;
68}
69
70pub trait AppendMapStorage<Item, Key, Value>: MapStorage<Key = Key, Value = Value>
71where
72    Item: Encode,
73    Key: Encode,
74    Value: EncodeAppend<Item = Item>,
75{
76    fn append<EncodeLikeKey, EncodeLikeItem>(key: EncodeLikeKey, item: EncodeLikeItem)
77    where
78        EncodeLikeKey: EncodeLike<Key>,
79        EncodeLikeItem: EncodeLike<Item>;
80}
81
82/// Creates new type with specified name and key-value types and implements
83/// `MapStorage` for it based on specified storage,
84/// which is a `Substrate`'s `StorageMap`.
85///
86/// This macro main purpose is to follow newtype pattern
87/// and avoid `Substrate` dependencies in `gear_common`.
88///
89/// Requires `PhantomData` be in scope: from `std`, `core` or `sp_std`.
90///
91/// Requires `Config` be in scope of the crate root where it called.
92///
93/// Has two implementations to provide auto addition of `Counted` logic
94/// (for `Substrate`'s `CountedStorageMap`) due to storage's
95/// arguments difference.
96#[allow(clippy::crate_in_macro_def)]
97#[macro_export]
98macro_rules! wrap_storage_map {
99    (storage: $storage: ident, name: $name: ident, key: $key: ty, value: $val: ty) => {
100        pub struct $name<T>(PhantomData<T>);
101
102        impl<T: crate::Config> MapStorage for $name<T> {
103            type Key = $key;
104            type Value = $val;
105
106            fn contains_key(key: &Self::Key) -> bool {
107                $storage::<T>::contains_key(key)
108            }
109
110            fn get(key: &Self::Key) -> Option<Self::Value> {
111                $storage::<T>::get(key)
112            }
113
114            fn insert(key: Self::Key, value: Self::Value) {
115                $storage::<T>::insert(key, value)
116            }
117
118            fn mutate<R, F: FnOnce(&mut Option<Self::Value>) -> R>(key: Self::Key, f: F) -> R {
119                $storage::<T>::mutate(key, f)
120            }
121
122            fn mutate_values<F: FnMut(Self::Value) -> Self::Value>(mut f: F) {
123                let f = |v| Some(f(v));
124                $storage::<T>::translate_values(f)
125            }
126
127            fn remove(key: Self::Key) {
128                $storage::<T>::remove(key)
129            }
130
131            fn clear() {
132                let _ = $storage::<T>::clear(u32::MAX, None);
133            }
134
135            fn take(key: Self::Key) -> Option<Self::Value> {
136                $storage::<T>::take(key)
137            }
138        }
139    };
140}
141
142/// Same as `wrap_storage_map!`, but with length type parameter
143/// to auto-impl `Counted` trait of `gear_common` storage primitives.
144///
145/// Better to use Rust's numeric types as `Length`.
146#[allow(clippy::crate_in_macro_def)]
147#[macro_export]
148macro_rules! wrap_counted_storage_map {
149    (storage: $storage: ident, name: $name: ident, key: $key: ty, value: $val: ty, length: $len: ty) => {
150        $crate::wrap_storage_map!(storage: $storage, name: $name, key: $key, value: $val);
151
152        impl<T: crate::Config> Counted for $name<T> {
153            type Length = $len;
154
155            fn len() -> Self::Length {
156                $storage::<T>::count() as Self::Length
157            }
158        }
159    };
160}