use crate::{
storage::{
generator::StorageMap as _,
types::{
OptionQuery, QueryKindTrait, StorageEntryMetadataBuilder, StorageMap, StorageValue,
ValueQuery,
},
StorageAppend, StorageDecodeLength, StorageTryAppend,
},
traits::{Get, GetDefault, StorageInfo, StorageInfoTrait, StorageInstance},
Never,
};
use alloc::{vec, vec::Vec};
use codec::{Decode, Encode, EncodeLike, FullCodec, MaxEncodedLen, Ref};
use sp_io::MultiRemovalResults;
use sp_metadata_ir::StorageEntryMetadataIR;
use sp_runtime::traits::Saturating;
#[doc = docify::embed!("src/storage/types/counted_map.rs", test_simple_count_works)]
pub struct CountedStorageMap<
Prefix,
Hasher,
Key,
Value,
QueryKind = OptionQuery,
OnEmpty = GetDefault,
MaxValues = GetDefault,
>(core::marker::PhantomData<(Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues)>);
pub trait CountedStorageMapInstance: StorageInstance {
type CounterPrefix: StorageInstance;
}
trait MapWrapper {
type Map;
}
impl<P: CountedStorageMapInstance, H, K, V, Q, O, M> MapWrapper
for CountedStorageMap<P, H, K, V, Q, O, M>
{
type Map = StorageMap<P, H, K, V, Q, O, M>;
}
pub type Counter = u32;
type CounterFor<P> =
StorageValue<<P as CountedStorageMapInstance>::CounterPrefix, Counter, ValueQuery>;
pub struct OnRemovalCounterUpdate<Prefix>(core::marker::PhantomData<Prefix>);
impl<Prefix: CountedStorageMapInstance> crate::storage::PrefixIteratorOnRemoval
for OnRemovalCounterUpdate<Prefix>
{
fn on_removal(_key: &[u8], _value: &[u8]) {
CounterFor::<Prefix>::mutate(|value| value.saturating_dec());
}
}
impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues>
CountedStorageMap<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: CountedStorageMapInstance,
Hasher: crate::hash::StorageHasher,
Key: FullCodec,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
pub fn counter_storage_final_key() -> [u8; 32] {
CounterFor::<Prefix>::hashed_key()
}
pub fn map_storage_final_prefix() -> Vec<u8> {
use crate::storage::generator::StorageMap;
<Self as MapWrapper>::Map::prefix_hash().to_vec()
}
pub fn hashed_key_for<KeyArg: EncodeLike<Key>>(key: KeyArg) -> Vec<u8> {
<Self as MapWrapper>::Map::hashed_key_for(key)
}
pub fn contains_key<KeyArg: EncodeLike<Key>>(key: KeyArg) -> bool {
<Self as MapWrapper>::Map::contains_key(key)
}
pub fn get<KeyArg: EncodeLike<Key>>(key: KeyArg) -> QueryKind::Query {
<Self as MapWrapper>::Map::get(key)
}
pub fn try_get<KeyArg: EncodeLike<Key>>(key: KeyArg) -> Result<Value, ()> {
<Self as MapWrapper>::Map::try_get(key)
}
pub fn set<KeyArg: EncodeLike<Key>>(key: KeyArg, q: QueryKind::Query) {
match QueryKind::from_query_to_optional_value(q) {
Some(v) => Self::insert(key, v),
None => Self::remove(key),
}
}
pub fn swap<KeyArg1: EncodeLike<Key>, KeyArg2: EncodeLike<Key>>(key1: KeyArg1, key2: KeyArg2) {
<Self as MapWrapper>::Map::swap(key1, key2)
}
pub fn insert<KeyArg: EncodeLike<Key>, ValArg: EncodeLike<Value>>(key: KeyArg, val: ValArg) {
if !<Self as MapWrapper>::Map::contains_key(Ref::from(&key)) {
CounterFor::<Prefix>::mutate(|value| value.saturating_inc());
}
<Self as MapWrapper>::Map::insert(key, val)
}
pub fn remove<KeyArg: EncodeLike<Key>>(key: KeyArg) {
if <Self as MapWrapper>::Map::contains_key(Ref::from(&key)) {
CounterFor::<Prefix>::mutate(|value| value.saturating_dec());
}
<Self as MapWrapper>::Map::remove(key)
}
pub fn mutate<KeyArg: EncodeLike<Key>, R, F: FnOnce(&mut QueryKind::Query) -> R>(
key: KeyArg,
f: F,
) -> R {
Self::try_mutate(key, |v| Ok::<R, Never>(f(v)))
.expect("`Never` can not be constructed; qed")
}
pub fn try_mutate<KeyArg, R, E, F>(key: KeyArg, f: F) -> Result<R, E>
where
KeyArg: EncodeLike<Key>,
F: FnOnce(&mut QueryKind::Query) -> Result<R, E>,
{
Self::try_mutate_exists(key, |option_value_ref| {
let option_value = core::mem::replace(option_value_ref, None);
let mut query = <Self as MapWrapper>::Map::from_optional_value_to_query(option_value);
let res = f(&mut query);
let option_value = <Self as MapWrapper>::Map::from_query_to_optional_value(query);
let _ = core::mem::replace(option_value_ref, option_value);
res
})
}
pub fn mutate_exists<KeyArg: EncodeLike<Key>, R, F: FnOnce(&mut Option<Value>) -> R>(
key: KeyArg,
f: F,
) -> R {
Self::try_mutate_exists(key, |v| Ok::<R, Never>(f(v)))
.expect("`Never` can not be constructed; qed")
}
pub fn try_mutate_exists<KeyArg, R, E, F>(key: KeyArg, f: F) -> Result<R, E>
where
KeyArg: EncodeLike<Key>,
F: FnOnce(&mut Option<Value>) -> Result<R, E>,
{
<Self as MapWrapper>::Map::try_mutate_exists(key, |option_value| {
let existed = option_value.is_some();
let res = f(option_value);
let exist = option_value.is_some();
if res.is_ok() {
if existed && !exist {
CounterFor::<Prefix>::mutate(|value| value.saturating_dec());
} else if !existed && exist {
CounterFor::<Prefix>::mutate(|value| value.saturating_inc());
}
}
res
})
}
pub fn take<KeyArg: EncodeLike<Key>>(key: KeyArg) -> QueryKind::Query {
let removed_value = <Self as MapWrapper>::Map::mutate_exists(key, |value| value.take());
if removed_value.is_some() {
CounterFor::<Prefix>::mutate(|value| value.saturating_dec());
}
<Self as MapWrapper>::Map::from_optional_value_to_query(removed_value)
}
pub fn append<Item, EncodeLikeItem, EncodeLikeKey>(key: EncodeLikeKey, item: EncodeLikeItem)
where
EncodeLikeKey: EncodeLike<Key>,
Item: Encode,
EncodeLikeItem: EncodeLike<Item>,
Value: StorageAppend<Item>,
{
if !<Self as MapWrapper>::Map::contains_key(Ref::from(&key)) {
CounterFor::<Prefix>::mutate(|value| value.saturating_inc());
}
<Self as MapWrapper>::Map::append(key, item)
}
pub fn decode_len<KeyArg: EncodeLike<Key>>(key: KeyArg) -> Option<usize>
where
Value: StorageDecodeLength,
{
<Self as MapWrapper>::Map::decode_len(key)
}
pub fn migrate_key<OldHasher: crate::hash::StorageHasher, KeyArg: EncodeLike<Key>>(
key: KeyArg,
) -> Option<Value> {
<Self as MapWrapper>::Map::migrate_key::<OldHasher, _>(key)
}
#[deprecated = "Use `clear` instead"]
pub fn remove_all() {
#[allow(deprecated)]
<Self as MapWrapper>::Map::remove_all(None);
CounterFor::<Prefix>::kill();
}
pub fn clear(limit: u32, maybe_cursor: Option<&[u8]>) -> MultiRemovalResults {
let result = <Self as MapWrapper>::Map::clear(limit, maybe_cursor);
match result.maybe_cursor {
None => CounterFor::<Prefix>::kill(),
Some(_) => CounterFor::<Prefix>::mutate(|x| x.saturating_reduce(result.unique)),
}
result
}
pub fn iter_values() -> crate::storage::PrefixIterator<Value, OnRemovalCounterUpdate<Prefix>> {
<Self as MapWrapper>::Map::iter_values().convert_on_removal()
}
pub fn translate_values<OldValue: Decode, F: FnMut(OldValue) -> Option<Value>>(mut f: F) {
<Self as MapWrapper>::Map::translate_values(|old_value| {
let res = f(old_value);
if res.is_none() {
CounterFor::<Prefix>::mutate(|value| value.saturating_dec());
}
res
})
}
pub fn try_append<KArg, Item, EncodeLikeItem>(key: KArg, item: EncodeLikeItem) -> Result<(), ()>
where
KArg: EncodeLike<Key>,
Item: Encode,
EncodeLikeItem: EncodeLike<Item>,
Value: StorageTryAppend<Item>,
{
let bound = Value::bound();
let current = <Self as MapWrapper>::Map::decode_len(Ref::from(&key)).unwrap_or_default();
if current < bound {
CounterFor::<Prefix>::mutate(|value| value.saturating_inc());
let key = <Self as MapWrapper>::Map::hashed_key_for(key);
sp_io::storage::append(&key, item.encode());
Ok(())
} else {
Err(())
}
}
pub fn initialize_counter() -> Counter {
let count = Self::iter_values().count() as Counter;
CounterFor::<Prefix>::set(count);
count
}
pub fn count() -> Counter {
CounterFor::<Prefix>::get()
}
}
impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues>
CountedStorageMap<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: CountedStorageMapInstance,
Hasher: crate::hash::StorageHasher + crate::ReversibleStorageHasher,
Key: FullCodec,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
pub fn iter() -> crate::storage::PrefixIterator<(Key, Value), OnRemovalCounterUpdate<Prefix>> {
<Self as MapWrapper>::Map::iter().convert_on_removal()
}
pub fn drain() -> crate::storage::PrefixIterator<(Key, Value), OnRemovalCounterUpdate<Prefix>> {
<Self as MapWrapper>::Map::drain().convert_on_removal()
}
pub fn translate<O: Decode, F: FnMut(Key, O) -> Option<Value>>(mut f: F) {
<Self as MapWrapper>::Map::translate(|key, old_value| {
let res = f(key, old_value);
if res.is_none() {
CounterFor::<Prefix>::mutate(|value| value.saturating_dec());
}
res
})
}
pub fn iter_from(
starting_raw_key: Vec<u8>,
) -> crate::storage::PrefixIterator<(Key, Value), OnRemovalCounterUpdate<Prefix>> {
<Self as MapWrapper>::Map::iter_from(starting_raw_key).convert_on_removal()
}
pub fn iter_keys() -> crate::storage::KeyPrefixIterator<Key> {
<Self as MapWrapper>::Map::iter_keys()
}
}
impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues> StorageEntryMetadataBuilder
for CountedStorageMap<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: CountedStorageMapInstance,
Hasher: crate::hash::StorageHasher,
Key: FullCodec + scale_info::StaticTypeInfo,
Value: FullCodec + scale_info::StaticTypeInfo,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
fn build_metadata(
deprecation_status: sp_metadata_ir::DeprecationStatusIR,
docs: Vec<&'static str>,
entries: &mut Vec<StorageEntryMetadataIR>,
) {
<Self as MapWrapper>::Map::build_metadata(deprecation_status.clone(), docs, entries);
CounterFor::<Prefix>::build_metadata(
deprecation_status,
if cfg!(feature = "no-metadata-docs") {
vec![]
} else {
vec!["Counter for the related counted storage map"]
},
entries,
);
}
}
impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues> crate::traits::StorageInfoTrait
for CountedStorageMap<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: CountedStorageMapInstance,
Hasher: crate::hash::StorageHasher,
Key: FullCodec + MaxEncodedLen,
Value: FullCodec + MaxEncodedLen,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
fn storage_info() -> Vec<StorageInfo> {
[<Self as MapWrapper>::Map::storage_info(), CounterFor::<Prefix>::storage_info()].concat()
}
}
impl<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues>
crate::traits::PartialStorageInfoTrait
for CountedStorageMap<Prefix, Hasher, Key, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: CountedStorageMapInstance,
Hasher: crate::hash::StorageHasher,
Key: FullCodec,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
fn partial_storage_info() -> Vec<StorageInfo> {
[<Self as MapWrapper>::Map::partial_storage_info(), CounterFor::<Prefix>::storage_info()]
.concat()
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::{
hash::*,
storage::{bounded_vec::BoundedVec, types::ValueQuery},
traits::ConstU32,
};
use sp_io::{hashing::twox_128, TestExternalities};
use sp_metadata_ir::{StorageEntryModifierIR, StorageEntryTypeIR, StorageHasherIR};
struct Prefix;
impl StorageInstance for Prefix {
fn pallet_prefix() -> &'static str {
"test"
}
const STORAGE_PREFIX: &'static str = "foo";
}
struct CounterPrefix;
impl StorageInstance for CounterPrefix {
fn pallet_prefix() -> &'static str {
"test"
}
const STORAGE_PREFIX: &'static str = "counter_for_foo";
}
impl CountedStorageMapInstance for Prefix {
type CounterPrefix = CounterPrefix;
}
struct ADefault;
impl crate::traits::Get<u32> for ADefault {
fn get() -> u32 {
97
}
}
#[crate::storage_alias]
type ExampleCountedMap = CountedStorageMap<Prefix, Twox64Concat, u16, u32>;
#[test]
fn storage_alias_works() {
TestExternalities::default().execute_with(|| {
assert_eq!(ExampleCountedMap::count(), 0);
ExampleCountedMap::insert(3, 10);
})
}
#[test]
fn test_value_query() {
type A = CountedStorageMap<Prefix, Twox64Concat, u16, u32, ValueQuery, ADefault>;
TestExternalities::default().execute_with(|| {
let mut k: Vec<u8> = vec![];
k.extend(&twox_128(b"test"));
k.extend(&twox_128(b"foo"));
k.extend(&3u16.twox_64_concat());
assert_eq!(A::hashed_key_for(3).to_vec(), k);
assert_eq!(A::contains_key(3), false);
assert_eq!(A::get(3), ADefault::get());
assert_eq!(A::try_get(3), Err(()));
assert_eq!(A::count(), 0);
A::insert(3, 10);
assert_eq!(A::contains_key(3), true);
assert_eq!(A::get(3), 10);
assert_eq!(A::try_get(3), Ok(10));
assert_eq!(A::count(), 1);
A::swap(4, 3);
assert_eq!(A::contains_key(3), false);
assert_eq!(A::get(3), ADefault::get());
assert_eq!(A::try_get(3), Err(()));
assert_eq!(A::contains_key(4), true);
assert_eq!(A::get(4), 10);
assert_eq!(A::try_get(4), Ok(10));
assert_eq!(A::count(), 1);
A::swap(4, 3);
assert_eq!(A::try_get(3), Ok(10));
assert_eq!(A::contains_key(4), false);
assert_eq!(A::get(4), ADefault::get());
assert_eq!(A::try_get(4), Err(()));
assert_eq!(A::count(), 1);
A::insert(4, 11);
assert_eq!(A::try_get(3), Ok(10));
assert_eq!(A::try_get(4), Ok(11));
assert_eq!(A::count(), 2);
A::swap(3, 4);
assert_eq!(A::try_get(3), Ok(11));
assert_eq!(A::try_get(4), Ok(10));
assert_eq!(A::count(), 2);
A::insert(3, 12);
assert_eq!(A::try_get(3), Ok(12));
assert_eq!(A::count(), 2);
A::remove(2);
assert_eq!(A::contains_key(2), false);
assert_eq!(A::count(), 2);
A::remove(3);
assert_eq!(A::try_get(3), Err(()));
assert_eq!(A::count(), 1);
A::mutate(3, |query| {
assert_eq!(*query, ADefault::get());
*query = 40;
});
assert_eq!(A::try_get(3), Ok(40));
assert_eq!(A::count(), 2);
A::mutate(3, |query| {
assert_eq!(*query, 40);
*query = 40;
});
assert_eq!(A::try_get(3), Ok(40));
assert_eq!(A::count(), 2);
A::try_mutate(2, |query| {
assert_eq!(*query, ADefault::get());
*query = 4;
Result::<(), ()>::Err(())
})
.err()
.unwrap();
assert_eq!(A::try_get(2), Err(()));
assert_eq!(A::count(), 2);
A::try_mutate(2, |query| {
assert_eq!(*query, ADefault::get());
*query = 41;
Result::<(), ()>::Ok(())
})
.unwrap();
assert_eq!(A::try_get(2), Ok(41));
assert_eq!(A::count(), 3);
A::try_mutate(2, |query| {
assert_eq!(*query, 41);
*query = 41;
Result::<(), ()>::Ok(())
})
.unwrap();
assert_eq!(A::try_get(2), Ok(41));
assert_eq!(A::count(), 3);
A::try_mutate_exists(1, |query| {
assert_eq!(*query, None);
*query = Some(4);
Result::<(), ()>::Err(())
})
.err()
.unwrap();
assert_eq!(A::try_get(1), Err(()));
assert_eq!(A::count(), 3);
A::try_mutate_exists(1, |query| {
assert_eq!(*query, None);
*query = Some(43);
Result::<(), ()>::Ok(())
})
.unwrap();
assert_eq!(A::try_get(1), Ok(43));
assert_eq!(A::count(), 4);
A::try_mutate_exists(1, |query| {
assert_eq!(*query, Some(43));
*query = Some(45);
Result::<(), ()>::Ok(())
})
.unwrap();
assert_eq!(A::try_get(1), Ok(45));
assert_eq!(A::count(), 4);
A::try_mutate_exists(1, |query| {
assert_eq!(*query, Some(45));
*query = None;
Result::<(), ()>::Ok(())
})
.unwrap();
assert_eq!(A::try_get(1), Err(()));
assert_eq!(A::count(), 3);
assert_eq!(A::take(4), 10);
assert_eq!(A::try_get(4), Err(()));
assert_eq!(A::count(), 2);
assert_eq!(A::take(4), ADefault::get());
assert_eq!(A::try_get(4), Err(()));
assert_eq!(A::count(), 2);
let _ = A::clear(u32::max_value(), None);
assert_eq!(A::count(), 0);
assert_eq!(A::initialize_counter(), 0);
A::insert(1, 1);
A::insert(2, 2);
assert_eq!(A::iter_values().collect::<Vec<_>>(), vec![2, 1]);
assert_eq!(A::iter_values().drain().collect::<Vec<_>>(), vec![2, 1]);
assert_eq!(A::count(), 0);
A::insert(1, 1);
A::insert(2, 2);
assert_eq!(A::initialize_counter(), 2);
A::set(30, 100);
assert_eq!(A::contains_key(30), true);
assert_eq!(A::get(30), 100);
assert_eq!(A::try_get(30), Ok(100));
assert_eq!(A::count(), 3);
A::set(30, 101);
assert_eq!(A::contains_key(30), true);
assert_eq!(A::get(30), 101);
assert_eq!(A::try_get(30), Ok(101));
assert_eq!(A::count(), 3);
})
}
#[test]
fn test_option_query() {
type B = CountedStorageMap<Prefix, Twox64Concat, u16, u32>;
TestExternalities::default().execute_with(|| {
let mut k: Vec<u8> = vec![];
k.extend(&twox_128(b"test"));
k.extend(&twox_128(b"foo"));
k.extend(&3u16.twox_64_concat());
assert_eq!(B::hashed_key_for(3).to_vec(), k);
assert_eq!(B::contains_key(3), false);
assert_eq!(B::get(3), None);
assert_eq!(B::try_get(3), Err(()));
assert_eq!(B::count(), 0);
B::insert(3, 10);
assert_eq!(B::contains_key(3), true);
assert_eq!(B::get(3), Some(10));
assert_eq!(B::try_get(3), Ok(10));
assert_eq!(B::count(), 1);
B::swap(4, 3);
assert_eq!(B::contains_key(3), false);
assert_eq!(B::get(3), None);
assert_eq!(B::try_get(3), Err(()));
assert_eq!(B::contains_key(4), true);
assert_eq!(B::get(4), Some(10));
assert_eq!(B::try_get(4), Ok(10));
assert_eq!(B::count(), 1);
B::swap(4, 3);
assert_eq!(B::try_get(3), Ok(10));
assert_eq!(B::contains_key(4), false);
assert_eq!(B::get(4), None);
assert_eq!(B::try_get(4), Err(()));
assert_eq!(B::count(), 1);
B::insert(4, 11);
assert_eq!(B::try_get(3), Ok(10));
assert_eq!(B::try_get(4), Ok(11));
assert_eq!(B::count(), 2);
B::swap(3, 4);
assert_eq!(B::try_get(3), Ok(11));
assert_eq!(B::try_get(4), Ok(10));
assert_eq!(B::count(), 2);
B::insert(3, 11);
assert_eq!(B::count(), 2);
B::remove(2);
assert_eq!(B::contains_key(2), false);
assert_eq!(B::count(), 2);
B::remove(3);
assert_eq!(B::try_get(3), Err(()));
assert_eq!(B::count(), 1);
B::mutate(3, |query| {
assert_eq!(*query, None);
*query = Some(40)
});
assert_eq!(B::try_get(3), Ok(40));
assert_eq!(B::count(), 2);
B::mutate(3, |query| {
assert_eq!(*query, Some(40));
*query = Some(40)
});
assert_eq!(B::try_get(3), Ok(40));
assert_eq!(B::count(), 2);
B::mutate(3, |query| {
assert_eq!(*query, Some(40));
*query = None
});
assert_eq!(B::try_get(3), Err(()));
assert_eq!(B::count(), 1);
B::insert(3, 40);
B::try_mutate(2, |query| {
assert_eq!(*query, None);
*query = Some(4);
Result::<(), ()>::Err(())
})
.err()
.unwrap();
assert_eq!(B::try_get(2), Err(()));
assert_eq!(B::count(), 2);
B::try_mutate(2, |query| {
assert_eq!(*query, None);
*query = Some(41);
Result::<(), ()>::Ok(())
})
.unwrap();
assert_eq!(B::try_get(2), Ok(41));
assert_eq!(B::count(), 3);
B::try_mutate(2, |query| {
assert_eq!(*query, Some(41));
*query = Some(41);
Result::<(), ()>::Ok(())
})
.unwrap();
assert_eq!(B::try_get(2), Ok(41));
assert_eq!(B::count(), 3);
B::try_mutate(2, |query| {
assert_eq!(*query, Some(41));
*query = None;
Result::<(), ()>::Ok(())
})
.unwrap();
assert_eq!(B::try_get(2), Err(()));
assert_eq!(B::count(), 2);
B::insert(2, 41);
B::try_mutate_exists(1, |query| {
assert_eq!(*query, None);
*query = Some(4);
Result::<(), ()>::Err(())
})
.err()
.unwrap();
assert_eq!(B::try_get(1), Err(()));
assert_eq!(B::count(), 3);
B::try_mutate_exists(1, |query| {
assert_eq!(*query, None);
*query = Some(43);
Result::<(), ()>::Ok(())
})
.unwrap();
assert_eq!(B::try_get(1), Ok(43));
assert_eq!(B::count(), 4);
B::try_mutate_exists(1, |query| {
assert_eq!(*query, Some(43));
*query = Some(43);
Result::<(), ()>::Ok(())
})
.unwrap();
assert_eq!(B::try_get(1), Ok(43));
assert_eq!(B::count(), 4);
B::try_mutate_exists(1, |query| {
assert_eq!(*query, Some(43));
*query = None;
Result::<(), ()>::Ok(())
})
.unwrap();
assert_eq!(B::try_get(1), Err(()));
assert_eq!(B::count(), 3);
assert_eq!(B::take(4), Some(10));
assert_eq!(B::try_get(4), Err(()));
assert_eq!(B::count(), 2);
assert_eq!(B::take(4), None);
assert_eq!(B::try_get(4), Err(()));
assert_eq!(B::count(), 2);
let _ = B::clear(u32::max_value(), None);
assert_eq!(B::count(), 0);
assert_eq!(B::initialize_counter(), 0);
B::insert(1, 1);
B::insert(2, 2);
assert_eq!(B::iter_values().collect::<Vec<_>>(), vec![2, 1]);
assert_eq!(B::iter_values().drain().collect::<Vec<_>>(), vec![2, 1]);
assert_eq!(B::count(), 0);
B::insert(1, 1);
B::insert(2, 2);
assert_eq!(B::initialize_counter(), 2);
B::set(30, Some(100));
assert_eq!(B::contains_key(30), true);
assert_eq!(B::get(30), Some(100));
assert_eq!(B::try_get(30), Ok(100));
assert_eq!(B::count(), 3);
B::set(30, Some(101));
assert_eq!(B::contains_key(30), true);
assert_eq!(B::get(30), Some(101));
assert_eq!(B::try_get(30), Ok(101));
assert_eq!(B::count(), 3);
B::set(30, None);
assert_eq!(B::contains_key(30), false);
assert_eq!(B::get(30), None);
assert_eq!(B::try_get(30), Err(()));
assert_eq!(B::count(), 2);
B::set(31, None);
assert_eq!(B::contains_key(31), false);
assert_eq!(B::get(31), None);
assert_eq!(B::try_get(31), Err(()));
assert_eq!(B::count(), 2);
})
}
#[test]
fn append_decode_len_works() {
type B = CountedStorageMap<Prefix, Twox64Concat, u16, Vec<u32>>;
TestExternalities::default().execute_with(|| {
assert_eq!(B::decode_len(0), None);
B::append(0, 3);
assert_eq!(B::decode_len(0), Some(1));
B::append(0, 3);
assert_eq!(B::decode_len(0), Some(2));
B::append(0, 3);
assert_eq!(B::decode_len(0), Some(3));
})
}
#[test]
fn try_append_decode_len_works() {
type B = CountedStorageMap<Prefix, Twox64Concat, u16, BoundedVec<u32, ConstU32<3u32>>>;
TestExternalities::default().execute_with(|| {
assert_eq!(B::decode_len(0), None);
B::try_append(0, 3).unwrap();
assert_eq!(B::decode_len(0), Some(1));
B::try_append(0, 3).unwrap();
assert_eq!(B::decode_len(0), Some(2));
B::try_append(0, 3).unwrap();
assert_eq!(B::decode_len(0), Some(3));
B::try_append(0, 3).err().unwrap();
assert_eq!(B::decode_len(0), Some(3));
})
}
#[test]
fn migrate_keys_works() {
type A = CountedStorageMap<Prefix, Twox64Concat, u16, u32>;
type B = CountedStorageMap<Prefix, Blake2_128Concat, u16, u32>;
TestExternalities::default().execute_with(|| {
A::insert(1, 1);
assert_eq!(B::migrate_key::<Twox64Concat, _>(1), Some(1));
assert_eq!(B::get(1), Some(1));
})
}
#[test]
fn translate_values() {
type A = CountedStorageMap<Prefix, Twox64Concat, u16, u32>;
TestExternalities::default().execute_with(|| {
A::insert(1, 1);
A::insert(2, 2);
A::translate_values::<u32, _>(|old_value| if old_value == 1 { None } else { Some(1) });
assert_eq!(A::count(), 1);
assert_eq!(A::get(2), Some(1));
})
}
#[test]
fn test_iter_drain_translate() {
type A = CountedStorageMap<Prefix, Twox64Concat, u16, u32>;
TestExternalities::default().execute_with(|| {
A::insert(1, 1);
A::insert(2, 2);
assert_eq!(A::iter().collect::<Vec<_>>(), vec![(2, 2), (1, 1)]);
assert_eq!(A::count(), 2);
A::translate::<u32, _>(
|key, value| if key == 1 { None } else { Some(key as u32 * value) },
);
assert_eq!(A::count(), 1);
assert_eq!(A::drain().collect::<Vec<_>>(), vec![(2, 4)]);
assert_eq!(A::count(), 0);
})
}
#[test]
fn test_iter_from() {
type A = CountedStorageMap<Prefix, Twox64Concat, u16, u32>;
TestExternalities::default().execute_with(|| {
A::insert(1, 1);
A::insert(2, 2);
A::insert(3, 3);
A::insert(4, 4);
assert_eq!(A::iter_from(vec![]).collect::<Vec<_>>(), A::iter().collect::<Vec<_>>());
let iter_all = A::iter().collect::<Vec<_>>();
let (before, after) = iter_all.split_at(2);
let last_key = before.last().map(|(k, _)| k).unwrap();
assert_eq!(A::iter_from(A::hashed_key_for(last_key)).collect::<Vec<_>>(), after);
})
}
#[test]
fn test_metadata() {
type A = CountedStorageMap<Prefix, Twox64Concat, u16, u32, ValueQuery, ADefault>;
let mut entries = vec![];
A::build_metadata(sp_metadata_ir::DeprecationStatusIR::NotDeprecated, vec![], &mut entries);
assert_eq!(
entries,
vec![
StorageEntryMetadataIR {
name: "foo",
modifier: StorageEntryModifierIR::Default,
ty: StorageEntryTypeIR::Map {
hashers: vec![StorageHasherIR::Twox64Concat],
key: scale_info::meta_type::<u16>(),
value: scale_info::meta_type::<u32>(),
},
default: 97u32.encode(),
docs: vec![],
deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated,
},
StorageEntryMetadataIR {
name: "counter_for_foo",
modifier: StorageEntryModifierIR::Default,
ty: StorageEntryTypeIR::Plain(scale_info::meta_type::<Counter>()),
default: vec![0, 0, 0, 0],
docs: if cfg!(feature = "no-metadata-docs") {
vec![]
} else {
vec!["Counter for the related counted storage map"]
},
deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated,
},
]
);
}
#[docify::export]
#[test]
fn test_simple_count_works() {
type FooCountedMap = CountedStorageMap<Prefix, Twox64Concat, u16, u32>;
TestExternalities::default().execute_with(|| {
FooCountedMap::insert(1, 1);
FooCountedMap::insert(2, 2);
assert_eq!(FooCountedMap::count(), 2);
});
}
}