use crate::{
storage::{
generator::StorageValue as StorageValueT,
types::{OptionQuery, QueryKindTrait, StorageEntryMetadataBuilder},
StorageAppend, StorageDecodeLength, StorageTryAppend,
},
traits::{Get, GetDefault, StorageInfo, StorageInstance},
};
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};
pub struct StorageValue<Prefix, Value, QueryKind = OptionQuery, OnEmpty = GetDefault>(
core::marker::PhantomData<(Prefix, Value, QueryKind, OnEmpty)>,
);
impl<Prefix, Value, QueryKind, OnEmpty> crate::storage::generator::StorageValue<Value>
for StorageValue<Prefix, Value, QueryKind, OnEmpty>
where
Prefix: StorageInstance,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: Get<QueryKind::Query> + 'static,
{
type Query = QueryKind::Query;
fn pallet_prefix() -> &'static [u8] {
Prefix::pallet_prefix().as_bytes()
}
fn storage_prefix() -> &'static [u8] {
Prefix::STORAGE_PREFIX.as_bytes()
}
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)
}
fn storage_value_final_key() -> [u8; 32] {
Prefix::prefix_hash()
}
}
impl<Prefix, Value, QueryKind, OnEmpty> StorageValue<Prefix, Value, QueryKind, OnEmpty>
where
Prefix: StorageInstance,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static,
{
pub fn hashed_key() -> [u8; 32] {
<Self as crate::storage::StorageValue<Value>>::hashed_key()
}
pub fn exists() -> bool {
<Self as crate::storage::StorageValue<Value>>::exists()
}
pub fn get() -> QueryKind::Query {
<Self as crate::storage::StorageValue<Value>>::get()
}
pub fn try_get() -> Result<Value, ()> {
<Self as crate::storage::StorageValue<Value>>::try_get()
}
pub fn translate<O: Decode, F: FnOnce(Option<O>) -> Option<Value>>(
f: F,
) -> Result<Option<Value>, ()> {
<Self as crate::storage::StorageValue<Value>>::translate(f)
}
pub fn put<Arg: EncodeLike<Value>>(val: Arg) {
<Self as crate::storage::StorageValue<Value>>::put(val)
}
pub fn set(val: QueryKind::Query) {
<Self as crate::storage::StorageValue<Value>>::set(val)
}
pub fn mutate<R, F: FnOnce(&mut QueryKind::Query) -> R>(f: F) -> R {
<Self as crate::storage::StorageValue<Value>>::mutate(f)
}
pub fn mutate_extant<R: Default, F: FnOnce(&mut Value) -> R>(f: F) -> R {
<Self as crate::storage::StorageValue<Value>>::mutate_extant(f)
}
pub fn try_mutate<R, E, F: FnOnce(&mut QueryKind::Query) -> Result<R, E>>(
f: F,
) -> Result<R, E> {
<Self as crate::storage::StorageValue<Value>>::try_mutate(f)
}
pub fn mutate_exists<R, F: FnOnce(&mut Option<Value>) -> R>(f: F) -> R {
<Self as crate::storage::StorageValue<Value>>::mutate_exists(f)
}
pub fn try_mutate_exists<R, E, F: FnOnce(&mut Option<Value>) -> Result<R, E>>(
f: F,
) -> Result<R, E> {
<Self as crate::storage::StorageValue<Value>>::try_mutate_exists(f)
}
pub fn kill() {
<Self as crate::storage::StorageValue<Value>>::kill()
}
pub fn take() -> QueryKind::Query {
<Self as crate::storage::StorageValue<Value>>::take()
}
pub fn append<Item, EncodeLikeItem>(item: EncodeLikeItem)
where
Item: Encode,
EncodeLikeItem: EncodeLike<Item>,
Value: StorageAppend<Item>,
{
<Self as crate::storage::StorageValue<Value>>::append(item)
}
pub fn decode_len() -> Option<usize>
where
Value: StorageDecodeLength,
{
<Self as crate::storage::StorageValue<Value>>::decode_len()
}
pub fn decode_non_dedup_len() -> Option<usize>
where
Value: StorageDecodeNonDedupLength,
{
<Self as crate::storage::StorageValue<Value>>::decode_non_dedup_len()
}
pub fn try_append<Item, EncodeLikeItem>(item: EncodeLikeItem) -> Result<(), ()>
where
Item: Encode,
EncodeLikeItem: EncodeLike<Item>,
Value: StorageTryAppend<Item>,
{
<Self as crate::storage::TryAppendValue<Value, Item>>::try_append(item)
}
}
impl<Prefix, Value, QueryKind, OnEmpty> StorageEntryMetadataBuilder
for StorageValue<Prefix, Value, QueryKind, OnEmpty>
where
Prefix: StorageInstance,
Value: FullCodec + scale_info::StaticTypeInfo,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static,
{
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::Plain(scale_info::meta_type::<Value>()),
default: OnEmpty::get().encode(),
docs,
deprecation_info: deprecation_status,
};
entries.push(entry);
}
}
impl<Prefix, Value, QueryKind, OnEmpty> crate::traits::StorageInfoTrait
for StorageValue<Prefix, Value, QueryKind, OnEmpty>
where
Prefix: StorageInstance,
Value: FullCodec + MaxEncodedLen,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static,
{
fn storage_info() -> Vec<StorageInfo> {
vec![StorageInfo {
pallet_name: Self::pallet_prefix().to_vec(),
storage_name: Self::storage_prefix().to_vec(),
prefix: Self::hashed_key().to_vec(),
max_values: Some(1),
max_size: Some(Value::max_encoded_len().saturated_into()),
}]
}
}
impl<Prefix, Value, QueryKind, OnEmpty> crate::traits::PartialStorageInfoTrait
for StorageValue<Prefix, Value, QueryKind, OnEmpty>
where
Prefix: StorageInstance,
Value: FullCodec,
QueryKind: QueryKindTrait<Value, OnEmpty>,
OnEmpty: crate::traits::Get<QueryKind::Query> + 'static,
{
fn partial_storage_info() -> Vec<StorageInfo> {
vec![StorageInfo {
pallet_name: Self::pallet_prefix().to_vec(),
storage_name: Self::storage_prefix().to_vec(),
prefix: Self::hashed_key().to_vec(),
max_values: Some(1),
max_size: None,
}]
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::storage::types::ValueQuery;
use sp_io::{hashing::twox_128, TestExternalities};
use sp_metadata_ir::StorageEntryModifierIR;
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 test() {
type A = StorageValue<Prefix, u32, OptionQuery>;
type AValueQueryWithAnOnEmpty = StorageValue<Prefix, u32, ValueQuery, ADefault>;
type B = StorageValue<Prefix, u16, ValueQuery>;
type WithLen = StorageValue<Prefix, Vec<u32>>;
TestExternalities::default().execute_with(|| {
assert_eq!(A::hashed_key().to_vec(), [twox_128(b"test"), twox_128(b"foo")].concat());
assert_eq!(A::exists(), false);
assert_eq!(A::get(), None);
assert_eq!(AValueQueryWithAnOnEmpty::get(), 97);
assert_eq!(A::try_get(), Err(()));
A::put(2);
assert_eq!(A::exists(), true);
assert_eq!(A::get(), Some(2));
assert_eq!(AValueQueryWithAnOnEmpty::get(), 2);
assert_eq!(A::try_get(), Ok(2));
assert_eq!(A::try_get(), Ok(2));
B::put(4);
A::translate::<u16, _>(|v| v.map(Into::into)).unwrap();
assert_eq!(A::try_get(), Ok(4));
A::set(None);
assert_eq!(A::try_get(), Err(()));
A::set(Some(2));
assert_eq!(A::try_get(), Ok(2));
A::mutate(|v| *v = Some(v.unwrap() * 2));
assert_eq!(A::try_get(), Ok(4));
A::set(Some(4));
let _: Result<(), ()> = A::try_mutate(|v| {
*v = Some(v.unwrap() * 2);
Ok(())
});
assert_eq!(A::try_get(), Ok(8));
let _: Result<(), ()> = A::try_mutate(|v| {
*v = Some(v.unwrap() * 2);
Err(())
});
assert_eq!(A::try_get(), Ok(8));
A::kill();
AValueQueryWithAnOnEmpty::mutate(|v| *v = *v * 2);
assert_eq!(AValueQueryWithAnOnEmpty::try_get(), Ok(97 * 2));
AValueQueryWithAnOnEmpty::kill();
let _: Result<(), ()> = AValueQueryWithAnOnEmpty::try_mutate(|v| {
*v = *v * 2;
Ok(())
});
assert_eq!(AValueQueryWithAnOnEmpty::try_get(), Ok(97 * 2));
A::kill();
assert_eq!(A::try_get(), Err(()));
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::Plain(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::Plain(scale_info::meta_type::<u32>()),
default: 97u32.encode(),
docs: vec![],
deprecation_info: sp_metadata_ir::DeprecationStatusIR::NotDeprecated
}
]
);
WithLen::kill();
assert_eq!(WithLen::decode_len(), None);
WithLen::append(3);
assert_eq!(WithLen::decode_len(), Some(1));
});
}
}