multiversx_sc/storage/mappers/
whitelist_mapper.rs

1use core::marker::PhantomData;
2
3use multiversx_sc_codec::{TopDecode, TopEncode};
4
5use super::{
6    source::{CurrentStorage, StorageAddress},
7    SingleValueMapper, StorageMapper, StorageMapperFromAddress,
8};
9use crate::{
10    api::{ErrorApiImpl, StorageMapperApi},
11    codec::NestedEncode,
12    storage::StorageKey,
13    types::ManagedAddress,
14};
15
16type FlagMapper<SA, A> = SingleValueMapper<SA, bool, A>;
17
18static ITEM_NOT_WHITELISTED_ERR_MSG: &[u8] = b"Item not whitelisted";
19
20/// A non-iterable whitelist mapper.
21/// Very efficient for storing a whitelist, as each item requires only one storage key.
22/// If you need to iterate over the keys, use UnorderedSetMapper or SetMapper instead.
23pub struct WhitelistMapper<SA, T, A = CurrentStorage>
24where
25    SA: StorageMapperApi,
26    A: StorageAddress<SA>,
27    T: NestedEncode + 'static,
28{
29    address: A,
30    base_key: StorageKey<SA>,
31    _phantom: PhantomData<T>,
32}
33
34impl<SA, T> StorageMapper<SA> for WhitelistMapper<SA, T, CurrentStorage>
35where
36    SA: StorageMapperApi,
37    T: NestedEncode + 'static,
38{
39    fn new(base_key: StorageKey<SA>) -> Self {
40        Self {
41            address: CurrentStorage,
42            base_key,
43            _phantom: PhantomData,
44        }
45    }
46}
47
48impl<SA, T> StorageMapperFromAddress<SA> for WhitelistMapper<SA, T, ManagedAddress<SA>>
49where
50    SA: StorageMapperApi,
51    T: NestedEncode + 'static,
52{
53    fn new_from_address(address: ManagedAddress<SA>, base_key: StorageKey<SA>) -> Self {
54        Self {
55            address,
56            base_key,
57            _phantom: PhantomData,
58        }
59    }
60}
61
62impl<SA, T> WhitelistMapper<SA, T, ManagedAddress<SA>>
63where
64    SA: StorageMapperApi,
65    T: TopDecode + TopEncode + NestedEncode + 'static,
66{
67    pub fn contains(&self, item: &T) -> bool {
68        let mapper = self.build_mapper_for_item(item);
69        !mapper.is_empty()
70    }
71
72    pub fn require_whitelisted(&self, item: &T) {
73        if !self.contains(item) {
74            SA::error_api_impl().signal_error(ITEM_NOT_WHITELISTED_ERR_MSG);
75        }
76    }
77
78    fn build_mapper_for_item(&self, item: &T) -> FlagMapper<SA, ManagedAddress<SA>> {
79        let mut key = self.base_key.clone();
80        key.append_item(item);
81
82        FlagMapper::<SA, ManagedAddress<SA>>::new_from_address(self.address.clone(), key)
83    }
84}
85
86impl<SA, T> WhitelistMapper<SA, T, CurrentStorage>
87where
88    SA: StorageMapperApi,
89    T: TopDecode + TopEncode + NestedEncode + 'static,
90{
91    pub fn contains(&self, item: &T) -> bool {
92        let mapper = self.build_mapper_for_item(item);
93        !mapper.is_empty()
94    }
95
96    pub fn require_whitelisted(&self, item: &T) {
97        if !self.contains(item) {
98            SA::error_api_impl().signal_error(ITEM_NOT_WHITELISTED_ERR_MSG);
99        }
100    }
101
102    pub fn add(&self, item: &T) {
103        let mapper = self.build_mapper_for_item(item);
104        mapper.set(true);
105    }
106
107    pub fn remove(&self, item: &T) {
108        let mapper = self.build_mapper_for_item(item);
109        mapper.clear();
110    }
111
112    fn build_mapper_for_item(&self, item: &T) -> FlagMapper<SA, CurrentStorage> {
113        let mut key = self.base_key.clone();
114        key.append_item(item);
115
116        FlagMapper::<SA, CurrentStorage>::new(key)
117    }
118}