use crate::{
storage::{
types::{OptionQuery, QueryKindTrait, StorageEntryMetadataBuilder},
KeyLenOf, StorageAppend, StorageDecodeLength, StoragePrefixedMap, StorageTryAppend,
},
traits::{Get, GetDefault, StorageInfo, StorageInstance},
StorageHasher, Twox128,
};
use alloc::{vec, vec::Vec};
use codec::{Decode, Encode, EncodeLike, FullCodec, MaxEncodedLen};
use frame_support::storage::StorageDecodeNonDedupLength;
use sp_arithmetic::traits::SaturatedConversion;
use sp_metadata_ir::{StorageEntryMetadataIR, StorageEntryTypeIR};
#[doc = docify::embed!("src/storage/types/double_map.rs", example_double_map_partial_operations)]
pub struct StorageDoubleMap<
Prefix,
Hasher1,
Key1,
Hasher2,
Key2,
Value,
QueryKind = OptionQuery,
OnEmpty = GetDefault,
MaxValues = GetDefault,
>(
core::marker::PhantomData<(
Prefix,
Hasher1,
Key1,
Hasher2,
Key2,
Value,
QueryKind,
OnEmpty,
MaxValues,
)>,
);
impl<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty, MaxValues> Get<u32>
for KeyLenOf<
StorageDoubleMap<
Prefix,
Hasher1,
Key1,
Hasher2,
Key2,
Value,
QueryKind,
OnEmpty,
MaxValues,
>,
>
where
Prefix: StorageInstance,
Hasher1: crate::hash::StorageHasher,
Hasher2: crate::hash::StorageHasher,
Key1: MaxEncodedLen,
Key2: MaxEncodedLen,
{
fn get() -> u32 {
let z =
Hasher1::max_len::<Key1>() + Hasher2::max_len::<Key2>() + Twox128::max_len::<()>() * 2;
z as u32
}
}
impl<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty, MaxValues>
crate::storage::generator::StorageDoubleMap<Key1, Key2, Value>
for StorageDoubleMap<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
Hasher1: crate::hash::StorageHasher,
Hasher2: crate::hash::StorageHasher,
Key1: FullCodec,
Key2: FullCodec,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
type Query = QueryKind::Query;
type Hasher1 = Hasher1;
type Hasher2 = Hasher2;
fn pallet_prefix() -> &'static [u8] {
Prefix::pallet_prefix().as_bytes()
}
fn storage_prefix() -> &'static [u8] {
Prefix::STORAGE_PREFIX.as_bytes()
}
fn prefix_hash() -> [u8; 32] {
Prefix::prefix_hash()
}
fn from_optional_value_to_query(v: Option<Value>) -> Self::Query {
QueryKind::from_optional_value_to_query(v)
}
fn from_query_to_optional_value(v: Self::Query) -> Option<Value> {
QueryKind::from_query_to_optional_value(v)
}
}
impl<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty, MaxValues>
StoragePrefixedMap<Value>
for StorageDoubleMap<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
Hasher1: crate::hash::StorageHasher,
Hasher2: crate::hash::StorageHasher,
Key1: FullCodec,
Key2: FullCodec,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
fn pallet_prefix() -> &'static [u8] {
<Self as crate::storage::generator::StorageDoubleMap<Key1, Key2, Value>>::pallet_prefix()
}
fn storage_prefix() -> &'static [u8] {
<Self as crate::storage::generator::StorageDoubleMap<Key1, Key2, Value>>::storage_prefix()
}
}
impl<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty, MaxValues>
StorageDoubleMap<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
Hasher1: crate::hash::StorageHasher,
Hasher2: crate::hash::StorageHasher,
Key1: FullCodec,
Key2: FullCodec,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
pub fn hashed_key_for<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Vec<u8>
where
KArg1: EncodeLike<Key1>,
KArg2: EncodeLike<Key2>,
{
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::hashed_key_for(k1, k2)
}
pub fn contains_key<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> bool
where
KArg1: EncodeLike<Key1>,
KArg2: EncodeLike<Key2>,
{
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::contains_key(k1, k2)
}
pub fn get<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> QueryKind::Query
where
KArg1: EncodeLike<Key1>,
KArg2: EncodeLike<Key2>,
{
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::get(k1, k2)
}
pub fn try_get<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> Result<Value, ()>
where
KArg1: EncodeLike<Key1>,
KArg2: EncodeLike<Key2>,
{
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::try_get(k1, k2)
}
pub fn set<KArg1: EncodeLike<Key1>, KArg2: EncodeLike<Key2>>(
k1: KArg1,
k2: KArg2,
q: QueryKind::Query,
) {
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::set(k1, k2, q)
}
pub fn take<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> QueryKind::Query
where
KArg1: EncodeLike<Key1>,
KArg2: EncodeLike<Key2>,
{
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::take(k1, k2)
}
pub fn swap<XKArg1, XKArg2, YKArg1, YKArg2>(
x_k1: XKArg1,
x_k2: XKArg2,
y_k1: YKArg1,
y_k2: YKArg2,
) where
XKArg1: EncodeLike<Key1>,
XKArg2: EncodeLike<Key2>,
YKArg1: EncodeLike<Key1>,
YKArg2: EncodeLike<Key2>,
{
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::swap(x_k1, x_k2, y_k1, y_k2)
}
pub fn insert<KArg1, KArg2, VArg>(k1: KArg1, k2: KArg2, val: VArg)
where
KArg1: EncodeLike<Key1>,
KArg2: EncodeLike<Key2>,
VArg: EncodeLike<Value>,
{
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::insert(k1, k2, val)
}
pub fn remove<KArg1, KArg2>(k1: KArg1, k2: KArg2)
where
KArg1: EncodeLike<Key1>,
KArg2: EncodeLike<Key2>,
{
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::remove(k1, k2)
}
#[deprecated = "Use `clear_prefix` instead"]
pub fn remove_prefix<KArg1>(k1: KArg1, limit: Option<u32>) -> sp_io::KillStorageResult
where
KArg1: ?Sized + EncodeLike<Key1>,
{
#[allow(deprecated)]
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::remove_prefix(k1, limit)
}
pub fn clear_prefix<KArg1>(
first_key: KArg1,
limit: u32,
maybe_cursor: Option<&[u8]>,
) -> sp_io::MultiRemovalResults
where
KArg1: ?Sized + EncodeLike<Key1>,
{
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::clear_prefix(
first_key,
limit,
maybe_cursor,
)
}
pub fn iter_prefix_values<KArg1>(k1: KArg1) -> crate::storage::PrefixIterator<Value>
where
KArg1: ?Sized + EncodeLike<Key1>,
{
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::iter_prefix_values(k1)
}
pub fn mutate<KArg1, KArg2, R, F>(k1: KArg1, k2: KArg2, f: F) -> R
where
KArg1: EncodeLike<Key1>,
KArg2: EncodeLike<Key2>,
F: FnOnce(&mut QueryKind::Query) -> R,
{
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::mutate(k1, k2, f)
}
pub fn try_mutate<KArg1, KArg2, R, E, F>(k1: KArg1, k2: KArg2, f: F) -> Result<R, E>
where
KArg1: EncodeLike<Key1>,
KArg2: EncodeLike<Key2>,
F: FnOnce(&mut QueryKind::Query) -> Result<R, E>,
{
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::try_mutate(k1, k2, f)
}
pub fn mutate_exists<KArg1, KArg2, R, F>(k1: KArg1, k2: KArg2, f: F) -> R
where
KArg1: EncodeLike<Key1>,
KArg2: EncodeLike<Key2>,
F: FnOnce(&mut Option<Value>) -> R,
{
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::mutate_exists(k1, k2, f)
}
pub fn try_mutate_exists<KArg1, KArg2, R, E, F>(k1: KArg1, k2: KArg2, f: F) -> Result<R, E>
where
KArg1: EncodeLike<Key1>,
KArg2: EncodeLike<Key2>,
F: FnOnce(&mut Option<Value>) -> Result<R, E>,
{
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::try_mutate_exists(k1, k2, f)
}
pub fn append<Item, EncodeLikeItem, KArg1, KArg2>(k1: KArg1, k2: KArg2, item: EncodeLikeItem)
where
KArg1: EncodeLike<Key1>,
KArg2: EncodeLike<Key2>,
Item: Encode,
EncodeLikeItem: EncodeLike<Item>,
Value: StorageAppend<Item>,
{
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::append(k1, k2, item)
}
pub fn decode_len<KArg1, KArg2>(key1: KArg1, key2: KArg2) -> Option<usize>
where
KArg1: EncodeLike<Key1>,
KArg2: EncodeLike<Key2>,
Value: StorageDecodeLength,
{
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::decode_len(key1, key2)
}
pub fn decode_non_dedup_len<KArg1, KArg2>(key1: KArg1, key2: KArg2) -> Option<usize>
where
KArg1: EncodeLike<Key1>,
KArg2: EncodeLike<Key2>,
Value: StorageDecodeNonDedupLength,
{
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::decode_non_dedup_len(
key1, key2,
)
}
pub fn migrate_keys<
OldHasher1: crate::StorageHasher,
OldHasher2: crate::StorageHasher,
KeyArg1: EncodeLike<Key1>,
KeyArg2: EncodeLike<Key2>,
>(
key1: KeyArg1,
key2: KeyArg2,
) -> Option<Value> {
<Self as crate::storage::StorageDoubleMap<Key1, Key2, Value>>::migrate_keys::<
OldHasher1,
OldHasher2,
_,
_,
>(key1, key2)
}
#[deprecated = "Use `clear` instead"]
pub fn remove_all(limit: Option<u32>) -> sp_io::KillStorageResult {
#[allow(deprecated)]
<Self as crate::storage::StoragePrefixedMap<Value>>::remove_all(limit)
}
pub fn clear(limit: u32, maybe_cursor: Option<&[u8]>) -> sp_io::MultiRemovalResults {
<Self as crate::storage::StoragePrefixedMap<Value>>::clear(limit, maybe_cursor)
}
pub fn iter_values() -> crate::storage::PrefixIterator<Value> {
<Self as crate::storage::StoragePrefixedMap<Value>>::iter_values()
}
pub fn translate_values<OldValue: Decode, F: FnMut(OldValue) -> Option<Value>>(f: F) {
<Self as crate::storage::StoragePrefixedMap<Value>>::translate_values(f)
}
pub fn try_append<KArg1, KArg2, Item, EncodeLikeItem>(
key1: KArg1,
key2: KArg2,
item: EncodeLikeItem,
) -> Result<(), ()>
where
KArg1: EncodeLike<Key1> + Clone,
KArg2: EncodeLike<Key2> + Clone,
Item: Encode,
EncodeLikeItem: EncodeLike<Item>,
Value: StorageTryAppend<Item>,
{
<Self as crate::storage::TryAppendDoubleMap<Key1, Key2, Value, Item>>::try_append(
key1, key2, item,
)
}
}
impl<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty, MaxValues>
StorageDoubleMap<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
Hasher1: crate::hash::StorageHasher + crate::ReversibleStorageHasher,
Hasher2: crate::hash::StorageHasher + crate::ReversibleStorageHasher,
Key1: FullCodec,
Key2: FullCodec,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
pub fn iter_prefix(k1: impl EncodeLike<Key1>) -> crate::storage::PrefixIterator<(Key2, Value)> {
<Self as crate::storage::IterableStorageDoubleMap<Key1, Key2, Value>>::iter_prefix(k1)
}
pub fn iter_prefix_from(
k1: impl EncodeLike<Key1>,
starting_raw_key: Vec<u8>,
) -> crate::storage::PrefixIterator<(Key2, Value)> {
<Self as crate::storage::IterableStorageDoubleMap<Key1, Key2, Value>>::iter_prefix_from(
k1,
starting_raw_key,
)
}
pub fn iter_key_prefix(k1: impl EncodeLike<Key1>) -> crate::storage::KeyPrefixIterator<Key2> {
<Self as crate::storage::IterableStorageDoubleMap<Key1, Key2, Value>>::iter_key_prefix(k1)
}
pub fn iter_key_prefix_from(
k1: impl EncodeLike<Key1>,
starting_raw_key: Vec<u8>,
) -> crate::storage::KeyPrefixIterator<Key2> {
<Self as crate::storage::IterableStorageDoubleMap<Key1, Key2, Value>>::iter_key_prefix_from(
k1,
starting_raw_key,
)
}
pub fn drain_prefix(
k1: impl EncodeLike<Key1>,
) -> crate::storage::PrefixIterator<(Key2, Value)> {
<Self as crate::storage::IterableStorageDoubleMap<Key1, Key2, Value>>::drain_prefix(k1)
}
pub fn iter() -> crate::storage::PrefixIterator<(Key1, Key2, Value)> {
<Self as crate::storage::IterableStorageDoubleMap<Key1, Key2, Value>>::iter()
}
pub fn iter_from(
starting_raw_key: Vec<u8>,
) -> crate::storage::PrefixIterator<(Key1, Key2, Value)> {
<Self as crate::storage::IterableStorageDoubleMap<Key1, Key2, Value>>::iter_from(
starting_raw_key,
)
}
pub fn iter_keys() -> crate::storage::KeyPrefixIterator<(Key1, Key2)> {
<Self as crate::storage::IterableStorageDoubleMap<Key1, Key2, Value>>::iter_keys()
}
pub fn iter_keys_from(
starting_raw_key: Vec<u8>,
) -> crate::storage::KeyPrefixIterator<(Key1, Key2)> {
<Self as crate::storage::IterableStorageDoubleMap<Key1, Key2, Value>>::iter_keys_from(
starting_raw_key,
)
}
pub fn drain() -> crate::storage::PrefixIterator<(Key1, Key2, Value)> {
<Self as crate::storage::IterableStorageDoubleMap<Key1, Key2, Value>>::drain()
}
pub fn translate<O: Decode, F: FnMut(Key1, Key2, O) -> Option<Value>>(f: F) {
<Self as crate::storage::IterableStorageDoubleMap<Key1, Key2, Value>>::translate(f)
}
}
impl<Prefix, Hasher1, Hasher2, Key1, Key2, Value, QueryKind, OnEmpty, MaxValues>
StorageEntryMetadataBuilder
for StorageDoubleMap<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
Hasher1: crate::hash::StorageHasher,
Hasher2: crate::hash::StorageHasher,
Key1: FullCodec + scale_info::StaticTypeInfo,
Key2: 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>,
) {
let docs = if cfg!(feature = "no-metadata-docs") { vec![] } else { docs };
let entry = StorageEntryMetadataIR {
name: Prefix::STORAGE_PREFIX,
modifier: QueryKind::METADATA,
ty: StorageEntryTypeIR::Map {
hashers: vec![Hasher1::METADATA, Hasher2::METADATA],
key: scale_info::meta_type::<(Key1, Key2)>(),
value: scale_info::meta_type::<Value>(),
},
default: OnEmpty::get().encode(),
docs,
deprecation_info: deprecation_status,
};
entries.push(entry);
}
}
impl<Prefix, Hasher1, Hasher2, Key1, Key2, Value, QueryKind, OnEmpty, MaxValues>
crate::traits::StorageInfoTrait
for StorageDoubleMap<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
Hasher1: crate::hash::StorageHasher,
Hasher2: crate::hash::StorageHasher,
Key1: FullCodec + MaxEncodedLen,
Key2: FullCodec + MaxEncodedLen,
Value: FullCodec + MaxEncodedLen,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
fn storage_info() -> Vec<StorageInfo> {
vec![StorageInfo {
pallet_name: Self::pallet_prefix().to_vec(),
storage_name: Self::storage_prefix().to_vec(),
prefix: Self::final_prefix().to_vec(),
max_values: MaxValues::get(),
max_size: Some(
Hasher1::max_len::<Key1>()
.saturating_add(Hasher2::max_len::<Key2>())
.saturating_add(Value::max_encoded_len())
.saturated_into(),
),
}]
}
}
impl<Prefix, Hasher1, Hasher2, Key1, Key2, Value, QueryKind, OnEmpty, MaxValues>
crate::traits::PartialStorageInfoTrait
for StorageDoubleMap<Prefix, Hasher1, Key1, Hasher2, Key2, Value, QueryKind, OnEmpty, MaxValues>
where
Prefix: StorageInstance,
Hasher1: crate::hash::StorageHasher,
Hasher2: crate::hash::StorageHasher,
Key1: FullCodec,
Key2: FullCodec,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: Get<QueryKind::Query> + 'static,
MaxValues: Get<Option<u32>>,
{
fn partial_storage_info() -> Vec<StorageInfo> {
vec![StorageInfo {
pallet_name: Self::pallet_prefix().to_vec(),
storage_name: Self::storage_prefix().to_vec(),
prefix: Self::final_prefix().to_vec(),
max_values: MaxValues::get(),
max_size: None,
}]
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::{hash::*, storage::types::ValueQuery};
use sp_io::{hashing::twox_128, TestExternalities};
use sp_metadata_ir::{StorageEntryModifierIR, StorageEntryTypeIR, StorageHasherIR};
use std::collections::BTreeSet;
struct Prefix;
impl StorageInstance for Prefix {
fn pallet_prefix() -> &'static str {
"test"
}
const STORAGE_PREFIX: &'static str = "foo";
}
struct ADefault;
impl crate::traits::Get<u32> for ADefault {
fn get() -> u32 {
97
}
}
#[test]
fn keylenof_works() {
type A = StorageDoubleMap<Prefix, Blake2_128Concat, u64, Twox64Concat, u32, u32>;
let size = 16 * 2 + 16 + 8 + 8 + 4; assert_eq!(KeyLenOf::<A>::get(), size);
}
#[test]
fn test() {
type A =
StorageDoubleMap<Prefix, Blake2_128Concat, u16, Twox64Concat, u8, u32, OptionQuery>;
type AValueQueryWithAnOnEmpty = StorageDoubleMap<
Prefix,
Blake2_128Concat,
u16,
Twox64Concat,
u8,
u32,
ValueQuery,
ADefault,
>;
type B = StorageDoubleMap<Prefix, Blake2_256, u16, Twox128, u8, u32, ValueQuery>;
type C = StorageDoubleMap<Prefix, Blake2_128Concat, u16, Twox64Concat, u8, u8, ValueQuery>;
type WithLen = StorageDoubleMap<Prefix, Blake2_128Concat, u16, Twox64Concat, u8, Vec<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.blake2_128_concat());
k.extend(&30u8.twox_64_concat());
assert_eq!(A::hashed_key_for(3, 30).to_vec(), k);
assert_eq!(A::contains_key(3, 30), false);
assert_eq!(A::get(3, 30), None);
assert_eq!(AValueQueryWithAnOnEmpty::get(3, 30), 97);
A::insert(3, 30, 10);
assert_eq!(A::contains_key(3, 30), true);
assert_eq!(A::get(3, 30), Some(10));
assert_eq!(AValueQueryWithAnOnEmpty::get(3, 30), 10);
A::swap(3, 30, 2, 20);
assert_eq!(A::contains_key(3, 30), false);
assert_eq!(A::contains_key(2, 20), true);
assert_eq!(A::get(3, 30), None);
assert_eq!(AValueQueryWithAnOnEmpty::get(3, 30), 97);
assert_eq!(A::get(2, 20), Some(10));
assert_eq!(AValueQueryWithAnOnEmpty::get(2, 20), 10);
A::remove(2, 20);
assert_eq!(A::contains_key(2, 20), false);
assert_eq!(A::get(2, 20), None);
AValueQueryWithAnOnEmpty::mutate(2, 20, |v| *v = *v * 2);
AValueQueryWithAnOnEmpty::mutate(2, 20, |v| *v = *v * 2);
assert_eq!(A::contains_key(2, 20), true);
assert_eq!(A::get(2, 20), Some(97 * 4));
A::remove(2, 20);
let _: Result<(), ()> = AValueQueryWithAnOnEmpty::try_mutate(2, 20, |v| {
*v = *v * 2;
Ok(())
});
let _: Result<(), ()> = AValueQueryWithAnOnEmpty::try_mutate(2, 20, |v| {
*v = *v * 2;
Ok(())
});
assert_eq!(A::contains_key(2, 20), true);
assert_eq!(A::get(2, 20), Some(97 * 4));
A::remove(2, 20);
let _: Result<(), ()> = AValueQueryWithAnOnEmpty::try_mutate(2, 20, |v| {
*v = *v * 2;
Err(())
});
assert_eq!(A::contains_key(2, 20), false);
A::remove(2, 20);
AValueQueryWithAnOnEmpty::mutate_exists(2, 20, |v| {
assert!(v.is_none());
*v = Some(10);
});
assert_eq!(A::contains_key(2, 20), true);
assert_eq!(A::get(2, 20), Some(10));
AValueQueryWithAnOnEmpty::mutate_exists(2, 20, |v| {
*v = Some(v.unwrap() * 10);
});
assert_eq!(A::contains_key(2, 20), true);
assert_eq!(A::get(2, 20), Some(100));
A::remove(2, 20);
let _: Result<(), ()> = AValueQueryWithAnOnEmpty::try_mutate_exists(2, 20, |v| {
assert!(v.is_none());
*v = Some(10);
Ok(())
});
assert_eq!(A::contains_key(2, 20), true);
assert_eq!(A::get(2, 20), Some(10));
let _: Result<(), ()> = AValueQueryWithAnOnEmpty::try_mutate_exists(2, 20, |v| {
*v = Some(v.unwrap() * 10);
Ok(())
});
assert_eq!(A::contains_key(2, 20), true);
assert_eq!(A::get(2, 20), Some(100));
assert_eq!(A::try_get(2, 20), Ok(100));
let _: Result<(), ()> = AValueQueryWithAnOnEmpty::try_mutate_exists(2, 20, |v| {
*v = Some(v.unwrap() * 10);
Err(())
});
assert_eq!(A::contains_key(2, 20), true);
assert_eq!(A::get(2, 20), Some(100));
A::insert(2, 20, 10);
assert_eq!(A::take(2, 20), Some(10));
assert_eq!(A::contains_key(2, 20), false);
assert_eq!(AValueQueryWithAnOnEmpty::take(2, 20), 97);
assert_eq!(A::contains_key(2, 20), false);
assert_eq!(A::try_get(2, 20), Err(()));
B::insert(2, 20, 10);
assert_eq!(A::migrate_keys::<Blake2_256, Twox128, _, _>(2, 20), Some(10));
assert_eq!(A::contains_key(2, 20), true);
assert_eq!(A::get(2, 20), Some(10));
A::insert(3, 30, 10);
A::insert(4, 40, 10);
let _ = A::clear(u32::max_value(), None);
assert_eq!(A::contains_key(3, 30), false);
assert_eq!(A::contains_key(4, 40), false);
A::insert(3, 30, 10);
A::insert(4, 40, 10);
assert_eq!(A::iter_values().collect::<Vec<_>>(), vec![10, 10]);
C::insert(3, 30, 10);
C::insert(4, 40, 10);
A::translate_values::<u8, _>(|v| Some((v * 2).into()));
assert_eq!(A::iter().collect::<Vec<_>>(), vec![(4, 40, 20), (3, 30, 20)]);
A::insert(3, 30, 10);
A::insert(4, 40, 10);
assert_eq!(A::iter().collect::<Vec<_>>(), vec![(4, 40, 10), (3, 30, 10)]);
assert_eq!(A::drain().collect::<Vec<_>>(), vec![(4, 40, 10), (3, 30, 10)]);
assert_eq!(A::iter().collect::<Vec<_>>(), vec![]);
C::insert(3, 30, 10);
C::insert(4, 40, 10);
A::translate::<u8, _>(|k1, k2, v| Some((k1 * k2 as u16 * v as u16).into()));
assert_eq!(A::iter().collect::<Vec<_>>(), vec![(4, 40, 1600), (3, 30, 900)]);
let mut entries = vec![];
A::build_metadata(
sp_metadata_ir::DeprecationStatusIR::NotDeprecated,
vec![],
&mut entries,
);
AValueQueryWithAnOnEmpty::build_metadata(
sp_metadata_ir::DeprecationStatusIR::NotDeprecated,
vec![],
&mut entries,
);
assert_eq!(
entries,
vec![
StorageEntryMetadataIR {
name: "foo",
modifier: StorageEntryModifierIR::Optional,
ty: StorageEntryTypeIR::Map {
hashers: vec![
StorageHasherIR::Blake2_128Concat,
StorageHasherIR::Twox64Concat
],
key: scale_info::meta_type::<(u16, u8)>(),
value: scale_info::meta_type::<u32>(),
},
default: Option::<u32>::None.encode(),
docs: vec![],
deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated
},
StorageEntryMetadataIR {
name: "foo",
modifier: StorageEntryModifierIR::Default,
ty: StorageEntryTypeIR::Map {
hashers: vec![
StorageHasherIR::Blake2_128Concat,
StorageHasherIR::Twox64Concat
],
key: scale_info::meta_type::<(u16, u8)>(),
value: scale_info::meta_type::<u32>(),
},
default: 97u32.encode(),
docs: vec![],
deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated
}
]
);
let _ = WithLen::clear(u32::max_value(), None);
assert_eq!(WithLen::decode_len(3, 30), None);
WithLen::append(0, 100, 10);
assert_eq!(WithLen::decode_len(0, 100), Some(1));
A::insert(3, 30, 11);
A::insert(3, 31, 12);
A::insert(4, 40, 13);
A::insert(4, 41, 14);
assert_eq!(A::iter_prefix_values(3).collect::<Vec<_>>(), vec![12, 11]);
assert_eq!(A::iter_prefix(3).collect::<Vec<_>>(), vec![(31, 12), (30, 11)]);
assert_eq!(A::iter_prefix_values(4).collect::<Vec<_>>(), vec![13, 14]);
assert_eq!(A::iter_prefix(4).collect::<Vec<_>>(), vec![(40, 13), (41, 14)]);
let _ = A::clear_prefix(3, u32::max_value(), None);
assert_eq!(A::iter_prefix(3).collect::<Vec<_>>(), vec![]);
assert_eq!(A::iter_prefix(4).collect::<Vec<_>>(), vec![(40, 13), (41, 14)]);
assert_eq!(A::drain_prefix(4).collect::<Vec<_>>(), vec![(40, 13), (41, 14)]);
assert_eq!(A::iter_prefix(4).collect::<Vec<_>>(), vec![]);
assert_eq!(A::drain_prefix(4).collect::<Vec<_>>(), vec![]);
})
}
#[docify::export]
#[test]
fn example_double_map_partial_operations() {
type FooDoubleMap =
StorageDoubleMap<Prefix, Blake2_128Concat, u32, Blake2_128Concat, u32, u32, ValueQuery>;
TestExternalities::default().execute_with(|| {
FooDoubleMap::insert(0, 0, 42);
FooDoubleMap::insert(0, 1, 43);
FooDoubleMap::insert(1, 0, 314);
let collected_k2_keys: BTreeSet<_> = FooDoubleMap::iter_key_prefix(0).collect();
assert_eq!(collected_k2_keys, [0, 1].iter().copied().collect::<BTreeSet<_>>());
let collected_k2_values: BTreeSet<_> = FooDoubleMap::iter_prefix_values(0).collect();
assert_eq!(collected_k2_values, [42, 43].iter().copied().collect::<BTreeSet<_>>());
let _ = FooDoubleMap::clear_prefix(0, u32::max_value(), None);
assert_eq!(FooDoubleMap::iter_prefix(0).collect::<Vec<_>>(), vec![]);
});
}
}