topsoil_core/traits/stored_map.rs
1// This file is part of Soil.
2
3// Copyright (C) Soil contributors.
4// Copyright (C) Parity Technologies (UK) Ltd.
5// SPDX-License-Identifier: Apache-2.0 OR GPL-3.0-or-later WITH Classpath-exception-2.0
6
7//! Traits and associated datatypes for managing abstract stored values.
8
9use crate::storage::StorageMap;
10use codec::FullCodec;
11use subsoil::runtime::DispatchError;
12
13/// An abstraction of a value stored within storage, but possibly as part of a larger composite
14/// item.
15pub trait StoredMap<K, T: Default> {
16 /// Get the item, or its default if it doesn't yet exist; we make no distinction between the
17 /// two.
18 fn get(k: &K) -> T;
19
20 /// Maybe mutate the item only if an `Ok` value is returned from `f`. Do nothing if an `Err` is
21 /// returned. It is removed or reset to default value if it has been mutated to `None`.
22 /// `f` will always be called with an option representing if the storage item exists (`Some<V>`)
23 /// or if the storage item does not exist (`None`), independent of the `QueryType`.
24 fn try_mutate_exists<R, E: From<DispatchError>>(
25 k: &K,
26 f: impl FnOnce(&mut Option<T>) -> Result<R, E>,
27 ) -> Result<R, E>;
28
29 // Everything past here has a default implementation.
30
31 /// Mutate the item.
32 fn mutate<R>(k: &K, f: impl FnOnce(&mut T) -> R) -> Result<R, DispatchError> {
33 Self::mutate_exists(k, |maybe_account| match maybe_account {
34 Some(ref mut account) => f(account),
35 x @ None => {
36 let mut account = Default::default();
37 let r = f(&mut account);
38 *x = Some(account);
39 r
40 },
41 })
42 }
43
44 /// Mutate the item, removing or resetting to default value if it has been mutated to `None`.
45 ///
46 /// This is infallible as long as the value does not get destroyed.
47 fn mutate_exists<R>(k: &K, f: impl FnOnce(&mut Option<T>) -> R) -> Result<R, DispatchError> {
48 Self::try_mutate_exists(k, |x| -> Result<R, DispatchError> { Ok(f(x)) })
49 }
50
51 /// Set the item to something new.
52 fn insert(k: &K, t: T) -> Result<(), DispatchError> {
53 Self::mutate(k, |i| *i = t)
54 }
55
56 /// Remove the item or otherwise replace it with its default value; we don't care which.
57 fn remove(k: &K) -> Result<(), DispatchError> {
58 Self::mutate_exists(k, |x| *x = None)
59 }
60}
61
62/// A shim for placing around a storage item in order to use it as a `StoredValue`. Ideally this
63/// wouldn't be needed as `StorageValue`s should blanket implement `StoredValue`s, however this
64/// would break the ability to have custom impls of `StoredValue`. The other workaround is to
65/// implement it directly in the macro.
66///
67/// This form has the advantage that two additional types are provides, `Created` and `Removed`,
68/// which are both generic events that can be tied to handlers to do something in the case of being
69/// about to create an account where one didn't previously exist (at all; not just where it used to
70/// be the default value), or where the account is being removed or reset back to the default value
71/// where previously it did exist (though may have been in a default state). This works well with
72/// system module's `CallOnCreatedAccount` and `CallKillAccount`.
73pub struct StorageMapShim<S, K, T>(core::marker::PhantomData<(S, K, T)>);
74impl<S: StorageMap<K, T, Query = T>, K: FullCodec, T: FullCodec + Default> StoredMap<K, T>
75 for StorageMapShim<S, K, T>
76{
77 fn get(k: &K) -> T {
78 S::get(k)
79 }
80 fn insert(k: &K, t: T) -> Result<(), DispatchError> {
81 S::insert(k, t);
82 Ok(())
83 }
84 fn remove(k: &K) -> Result<(), DispatchError> {
85 if S::contains_key(&k) {
86 S::remove(k);
87 }
88 Ok(())
89 }
90 fn mutate<R>(k: &K, f: impl FnOnce(&mut T) -> R) -> Result<R, DispatchError> {
91 Ok(S::mutate(k, f))
92 }
93 fn mutate_exists<R>(k: &K, f: impl FnOnce(&mut Option<T>) -> R) -> Result<R, DispatchError> {
94 S::try_mutate_exists(k, |maybe_value| {
95 let r = f(maybe_value);
96 Ok(r)
97 })
98 }
99 fn try_mutate_exists<R, E: From<DispatchError>>(
100 k: &K,
101 f: impl FnOnce(&mut Option<T>) -> Result<R, E>,
102 ) -> Result<R, E> {
103 S::try_mutate_exists(k, |maybe_value| {
104 let r = f(maybe_value)?;
105 Ok(r)
106 })
107 }
108}