mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-20 06:55:42 +00:00
[Feature] Introduce storage_alias for CountedStorageMap (#13366)
* [Feature] Introduce storagage_alias for CountedStorageMap * bit more dry * bit more dry * address review comments * some tests and fixes * fix ui tests * Apply suggestions from code review Co-authored-by: Bastian Köcher <git@kchr.de> * compare metadata --------- Co-authored-by: parity-processbot <> Co-authored-by: Bastian Köcher <git@kchr.de>
This commit is contained in:
@@ -69,6 +69,12 @@ fn get_cargo_env_var<T: FromStr>(version_env: &str) -> std::result::Result<T, ()
|
|||||||
T::from_str(&version).map_err(drop)
|
T::from_str(&version).map_err(drop)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generate the counter_prefix related to the storage.
|
||||||
|
/// counter_prefix is used by counted storage map.
|
||||||
|
fn counter_prefix(prefix: &str) -> String {
|
||||||
|
format!("CounterFor{}", prefix)
|
||||||
|
}
|
||||||
|
|
||||||
/// Declares strongly-typed wrappers around codec-compatible types in storage.
|
/// Declares strongly-typed wrappers around codec-compatible types in storage.
|
||||||
///
|
///
|
||||||
/// ## Example
|
/// ## Example
|
||||||
|
|||||||
@@ -15,9 +15,12 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use crate::pallet::{
|
use crate::{
|
||||||
parse::storage::{Metadata, QueryKind, StorageDef, StorageGenerics},
|
counter_prefix,
|
||||||
Def,
|
pallet::{
|
||||||
|
parse::storage::{Metadata, QueryKind, StorageDef, StorageGenerics},
|
||||||
|
Def,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
use quote::ToTokens;
|
use quote::ToTokens;
|
||||||
use std::{collections::HashMap, ops::IndexMut};
|
use std::{collections::HashMap, ops::IndexMut};
|
||||||
@@ -39,12 +42,6 @@ fn counter_prefix_ident(storage_ident: &syn::Ident) -> syn::Ident {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate the counter_prefix related to the storage.
|
|
||||||
/// counter_prefix is used by counted storage map.
|
|
||||||
fn counter_prefix(prefix: &str) -> String {
|
|
||||||
format!("CounterFor{}", prefix)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Check for duplicated storage prefixes. This step is necessary since users can specify an
|
/// Check for duplicated storage prefixes. This step is necessary since users can specify an
|
||||||
/// alternative storage prefix using the #[pallet::storage_prefix] syntax, and we need to ensure
|
/// alternative storage prefix using the #[pallet::storage_prefix] syntax, and we need to ensure
|
||||||
/// that the prefix specified by the user is not a duplicate of an existing one.
|
/// that the prefix specified by the user is not a duplicate of an existing one.
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
|
|
||||||
//! Implementation of the `storage_alias` attribute macro.
|
//! Implementation of the `storage_alias` attribute macro.
|
||||||
|
|
||||||
|
use crate::counter_prefix;
|
||||||
use frame_support_procedural_tools::generate_crate_access_2018;
|
use frame_support_procedural_tools::generate_crate_access_2018;
|
||||||
use proc_macro2::{Span, TokenStream};
|
use proc_macro2::{Span, TokenStream};
|
||||||
use quote::{quote, ToTokens};
|
use quote::{quote, ToTokens};
|
||||||
@@ -136,6 +137,7 @@ impl ToTokens for SimpleGenerics {
|
|||||||
mod storage_types {
|
mod storage_types {
|
||||||
syn::custom_keyword!(StorageValue);
|
syn::custom_keyword!(StorageValue);
|
||||||
syn::custom_keyword!(StorageMap);
|
syn::custom_keyword!(StorageMap);
|
||||||
|
syn::custom_keyword!(CountedStorageMap);
|
||||||
syn::custom_keyword!(StorageDoubleMap);
|
syn::custom_keyword!(StorageDoubleMap);
|
||||||
syn::custom_keyword!(StorageNMap);
|
syn::custom_keyword!(StorageNMap);
|
||||||
}
|
}
|
||||||
@@ -168,6 +170,21 @@ enum StorageType {
|
|||||||
_trailing_comma: Option<Token![,]>,
|
_trailing_comma: Option<Token![,]>,
|
||||||
_gt_token: Token![>],
|
_gt_token: Token![>],
|
||||||
},
|
},
|
||||||
|
CountedMap {
|
||||||
|
_kw: storage_types::CountedStorageMap,
|
||||||
|
_lt_token: Token![<],
|
||||||
|
prefix: SimplePath,
|
||||||
|
prefix_generics: Option<TypeGenerics>,
|
||||||
|
_hasher_comma: Token![,],
|
||||||
|
hasher_ty: Type,
|
||||||
|
_key_comma: Token![,],
|
||||||
|
key_ty: Type,
|
||||||
|
_value_comma: Token![,],
|
||||||
|
value_ty: Type,
|
||||||
|
query_type: Option<(Token![,], Type)>,
|
||||||
|
_trailing_comma: Option<Token![,]>,
|
||||||
|
_gt_token: Token![>],
|
||||||
|
},
|
||||||
DoubleMap {
|
DoubleMap {
|
||||||
_kw: storage_types::StorageDoubleMap,
|
_kw: storage_types::StorageDoubleMap,
|
||||||
_lt_token: Token![<],
|
_lt_token: Token![<],
|
||||||
@@ -235,12 +252,22 @@ impl StorageType {
|
|||||||
>;
|
>;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
Self::CountedMap {
|
||||||
|
value_ty, query_type, hasher_ty, key_ty, prefix_generics, ..
|
||||||
|
} |
|
||||||
Self::Map { value_ty, query_type, hasher_ty, key_ty, prefix_generics, .. } => {
|
Self::Map { value_ty, query_type, hasher_ty, key_ty, prefix_generics, .. } => {
|
||||||
let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t));
|
let query_type = query_type.as_ref().map(|(c, t)| quote!(#c #t));
|
||||||
|
let map_type = Ident::new(
|
||||||
|
match self {
|
||||||
|
Self::Map { .. } => "StorageMap",
|
||||||
|
_ => "CountedStorageMap",
|
||||||
|
},
|
||||||
|
Span::call_site(),
|
||||||
|
);
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
#( #attributes )*
|
#( #attributes )*
|
||||||
#visibility type #storage_name #storage_generics = #crate_::storage::types::StorageMap<
|
#visibility type #storage_name #storage_generics = #crate_::storage::types::#map_type<
|
||||||
#storage_instance #prefix_generics,
|
#storage_instance #prefix_generics,
|
||||||
#hasher_ty,
|
#hasher_ty,
|
||||||
#key_ty,
|
#key_ty,
|
||||||
@@ -296,6 +323,7 @@ impl StorageType {
|
|||||||
match self {
|
match self {
|
||||||
Self::Value { prefix, .. } |
|
Self::Value { prefix, .. } |
|
||||||
Self::Map { prefix, .. } |
|
Self::Map { prefix, .. } |
|
||||||
|
Self::CountedMap { prefix, .. } |
|
||||||
Self::NMap { prefix, .. } |
|
Self::NMap { prefix, .. } |
|
||||||
Self::DoubleMap { prefix, .. } => prefix,
|
Self::DoubleMap { prefix, .. } => prefix,
|
||||||
}
|
}
|
||||||
@@ -306,6 +334,7 @@ impl StorageType {
|
|||||||
match self {
|
match self {
|
||||||
Self::Value { prefix_generics, .. } |
|
Self::Value { prefix_generics, .. } |
|
||||||
Self::Map { prefix_generics, .. } |
|
Self::Map { prefix_generics, .. } |
|
||||||
|
Self::CountedMap { prefix_generics, .. } |
|
||||||
Self::NMap { prefix_generics, .. } |
|
Self::NMap { prefix_generics, .. } |
|
||||||
Self::DoubleMap { prefix_generics, .. } => prefix_generics.as_ref(),
|
Self::DoubleMap { prefix_generics, .. } => prefix_generics.as_ref(),
|
||||||
}
|
}
|
||||||
@@ -363,6 +392,22 @@ impl Parse for StorageType {
|
|||||||
_trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?,
|
_trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?,
|
||||||
_gt_token: input.parse()?,
|
_gt_token: input.parse()?,
|
||||||
})
|
})
|
||||||
|
} else if lookahead.peek(storage_types::CountedStorageMap) {
|
||||||
|
Ok(Self::CountedMap {
|
||||||
|
_kw: input.parse()?,
|
||||||
|
_lt_token: input.parse()?,
|
||||||
|
prefix: input.parse()?,
|
||||||
|
prefix_generics: parse_pallet_generics(input)?,
|
||||||
|
_hasher_comma: input.parse()?,
|
||||||
|
hasher_ty: input.parse()?,
|
||||||
|
_key_comma: input.parse()?,
|
||||||
|
key_ty: input.parse()?,
|
||||||
|
_value_comma: input.parse()?,
|
||||||
|
value_ty: input.parse()?,
|
||||||
|
query_type: parse_query_type(input)?,
|
||||||
|
_trailing_comma: input.peek(Token![,]).then(|| input.parse()).transpose()?,
|
||||||
|
_gt_token: input.parse()?,
|
||||||
|
})
|
||||||
} else if lookahead.peek(storage_types::StorageDoubleMap) {
|
} else if lookahead.peek(storage_types::StorageDoubleMap) {
|
||||||
Ok(Self::DoubleMap {
|
Ok(Self::DoubleMap {
|
||||||
_kw: input.parse()?,
|
_kw: input.parse()?,
|
||||||
@@ -476,6 +521,7 @@ pub fn storage_alias(input: TokenStream) -> Result<TokenStream> {
|
|||||||
input.storage_type.prefix(),
|
input.storage_type.prefix(),
|
||||||
input.storage_type.prefix_generics(),
|
input.storage_type.prefix_generics(),
|
||||||
&input.visibility,
|
&input.visibility,
|
||||||
|
matches!(input.storage_type, StorageType::CountedMap { .. }),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let definition = input.storage_type.generate_type_declaration(
|
let definition = input.storage_type.generate_type_declaration(
|
||||||
@@ -511,6 +557,7 @@ fn generate_storage_instance(
|
|||||||
prefix: &SimplePath,
|
prefix: &SimplePath,
|
||||||
prefix_generics: Option<&TypeGenerics>,
|
prefix_generics: Option<&TypeGenerics>,
|
||||||
visibility: &Visibility,
|
visibility: &Visibility,
|
||||||
|
is_counted_map: bool,
|
||||||
) -> Result<StorageInstance> {
|
) -> Result<StorageInstance> {
|
||||||
if let Some(ident) = prefix.get_ident().filter(|i| *i == "_") {
|
if let Some(ident) = prefix.get_ident().filter(|i| *i == "_") {
|
||||||
return Err(Error::new(ident.span(), "`_` is not allowed as prefix by `storage_alias`."))
|
return Err(Error::new(ident.span(), "`_` is not allowed as prefix by `storage_alias`."))
|
||||||
@@ -546,9 +593,37 @@ fn generate_storage_instance(
|
|||||||
|
|
||||||
let where_clause = storage_where_clause.map(|w| quote!(#w)).unwrap_or_default();
|
let where_clause = storage_where_clause.map(|w| quote!(#w)).unwrap_or_default();
|
||||||
|
|
||||||
let name = Ident::new(&format!("{}_Storage_Instance", storage_name), Span::call_site());
|
let name_str = format!("{}_Storage_Instance", storage_name);
|
||||||
|
let name = Ident::new(&name_str, Span::call_site());
|
||||||
let storage_name_str = storage_name.to_string();
|
let storage_name_str = storage_name.to_string();
|
||||||
|
|
||||||
|
let counter_code = is_counted_map.then(|| {
|
||||||
|
let counter_name = Ident::new(&counter_prefix(&name_str), Span::call_site());
|
||||||
|
let counter_storage_name_str = counter_prefix(&storage_name_str);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#visibility struct #counter_name< #impl_generics >(
|
||||||
|
#crate_::sp_std::marker::PhantomData<(#type_generics)>
|
||||||
|
) #where_clause;
|
||||||
|
|
||||||
|
impl<#impl_generics> #crate_::traits::StorageInstance
|
||||||
|
for #counter_name< #type_generics > #where_clause
|
||||||
|
{
|
||||||
|
fn pallet_prefix() -> &'static str {
|
||||||
|
#pallet_prefix
|
||||||
|
}
|
||||||
|
|
||||||
|
const STORAGE_PREFIX: &'static str = #counter_storage_name_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<#impl_generics> #crate_::storage::types::CountedStorageMapInstance
|
||||||
|
for #name< #type_generics > #where_clause
|
||||||
|
{
|
||||||
|
type CounterPrefix = #counter_name < #type_generics >;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Implement `StorageInstance` trait.
|
// Implement `StorageInstance` trait.
|
||||||
let code = quote! {
|
let code = quote! {
|
||||||
#visibility struct #name< #impl_generics >(
|
#visibility struct #name< #impl_generics >(
|
||||||
@@ -564,6 +639,8 @@ fn generate_storage_instance(
|
|||||||
|
|
||||||
const STORAGE_PREFIX: &'static str = #storage_name_str;
|
const STORAGE_PREFIX: &'static str = #storage_name_str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#counter_code
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(StorageInstance { name, code })
|
Ok(StorageInstance { name, code })
|
||||||
|
|||||||
@@ -543,6 +543,16 @@ mod test {
|
|||||||
97
|
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]
|
#[test]
|
||||||
fn test_value_query() {
|
fn test_value_query() {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ use frame_support::{
|
|||||||
dispatch::{
|
dispatch::{
|
||||||
DispatchClass, DispatchInfo, GetDispatchInfo, Parameter, Pays, UnfilteredDispatchable,
|
DispatchClass, DispatchInfo, GetDispatchInfo, Parameter, Pays, UnfilteredDispatchable,
|
||||||
},
|
},
|
||||||
pallet_prelude::ValueQuery,
|
pallet_prelude::{StorageInfoTrait, ValueQuery},
|
||||||
storage::unhashed,
|
storage::unhashed,
|
||||||
traits::{
|
traits::{
|
||||||
ConstU32, GetCallName, GetStorageVersion, OnFinalize, OnGenesis, OnInitialize,
|
ConstU32, GetCallName, GetStorageVersion, OnFinalize, OnGenesis, OnInitialize,
|
||||||
@@ -1906,14 +1906,30 @@ fn assert_type_all_pallets_without_system_reversed_is_correct() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_storage_alias() {
|
fn test_storage_alias() {
|
||||||
|
use frame_support::Twox64Concat;
|
||||||
|
|
||||||
#[frame_support::storage_alias]
|
#[frame_support::storage_alias]
|
||||||
type Value<T: pallet::Config>
|
type Value<T: pallet::Config>
|
||||||
where
|
where
|
||||||
<T as frame_system::Config>::AccountId: From<SomeType1> + SomeAssociation1,
|
<T as frame_system::Config>::AccountId: From<SomeType1> + SomeAssociation1,
|
||||||
= StorageValue<pallet::Pallet<T>, u32, ValueQuery>;
|
= StorageValue<pallet::Pallet<T>, u32, ValueQuery>;
|
||||||
|
|
||||||
|
#[frame_support::storage_alias]
|
||||||
|
type SomeCountedStorageMap<T: pallet2::Config>
|
||||||
|
where
|
||||||
|
<T as frame_system::Config>::AccountId: From<SomeType1> + SomeAssociation1,
|
||||||
|
= CountedStorageMap<pallet2::Pallet<T>, Twox64Concat, u8, u32>;
|
||||||
|
|
||||||
TestExternalities::default().execute_with(|| {
|
TestExternalities::default().execute_with(|| {
|
||||||
pallet::Value::<Runtime>::put(10);
|
pallet::Value::<Runtime>::put(10);
|
||||||
assert_eq!(10, Value::<Runtime>::get());
|
assert_eq!(10, Value::<Runtime>::get());
|
||||||
|
|
||||||
|
pallet2::SomeCountedStorageMap::<Runtime>::insert(10, 100);
|
||||||
|
assert_eq!(Some(100), SomeCountedStorageMap::<Runtime>::get(10));
|
||||||
|
assert_eq!(1, SomeCountedStorageMap::<Runtime>::count());
|
||||||
|
assert_eq!(
|
||||||
|
SomeCountedStorageMap::<Runtime>::storage_info(),
|
||||||
|
pallet2::SomeCountedStorageMap::<Runtime>::storage_info()
|
||||||
|
);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
error: expected one of: `StorageValue`, `StorageMap`, `StorageDoubleMap`, `StorageNMap`
|
error: expected one of: `StorageValue`, `StorageMap`, `CountedStorageMap`, `StorageDoubleMap`, `StorageNMap`
|
||||||
--> tests/storage_alias_ui/forbid_underscore_as_prefix.rs:2:14
|
--> tests/storage_alias_ui/forbid_underscore_as_prefix.rs:2:14
|
||||||
|
|
|
|
||||||
2 | type Ident = CustomStorage<Hello, u32>;
|
2 | type Ident = CustomStorage<Hello, u32>;
|
||||||
|
|||||||
Reference in New Issue
Block a user