mod genesis_config;
mod getters;
mod instance_trait;
mod metadata;
mod parse;
mod print_pallet_upgrade;
mod storage_info;
mod storage_struct;
mod store_trait;
pub(crate) use instance_trait::INHERENT_INSTANCE_NAME;
use frame_support_procedural_tools::{
generate_crate_access, generate_hidden_includes, syn_ext as ext,
};
use quote::quote;
pub struct DeclStorageDef {
generate_storage_info: bool,
hidden_crate: Option<syn::Ident>,
visibility: syn::Visibility,
store_trait: syn::Ident,
module_name: syn::Ident,
module_runtime_generic: syn::Ident,
module_runtime_trait: syn::Path,
module_instance: Option<ModuleInstanceDef>,
where_clause: Option<syn::WhereClause>,
extra_genesis_build: Option<syn::Expr>,
extra_genesis_config_lines: Vec<ExtraGenesisLineDef>,
storage_lines: Vec<StorageLineDef>,
crate_name: syn::Ident,
}
impl syn::parse::Parse for DeclStorageDef {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
parse::parse(input)
}
}
pub struct DeclStorageDefExt {
generate_storage_info: bool,
hidden_crate: proc_macro2::TokenStream,
hidden_imports: proc_macro2::TokenStream,
visibility: syn::Visibility,
store_trait: syn::Ident,
#[allow(unused)]
module_name: syn::Ident,
module_runtime_generic: syn::Ident,
module_runtime_trait: syn::Path,
module_instance: Option<ModuleInstanceDef>,
where_clause: Option<syn::WhereClause>,
extra_genesis_build: Option<syn::Expr>,
extra_genesis_config_lines: Vec<ExtraGenesisLineDef>,
storage_lines: Vec<StorageLineDefExt>,
crate_name: syn::Ident,
module_struct: proc_macro2::TokenStream,
module_impl: proc_macro2::TokenStream,
optional_instance: Option<proc_macro2::TokenStream>,
optional_instance_bound: Option<proc_macro2::TokenStream>,
optional_instance_bound_optional_default: Option<proc_macro2::TokenStream>,
}
impl From<DeclStorageDef> for DeclStorageDefExt {
fn from(mut def: DeclStorageDef) -> Self {
let hidden_crate_name = def
.hidden_crate
.as_ref()
.map(|i| i.to_string())
.unwrap_or_else(|| "decl_storage".to_string());
let hidden_crate = generate_crate_access(&hidden_crate_name, "frame-support");
let hidden_imports = generate_hidden_includes(&hidden_crate_name, "frame-support");
let storage_lines = def.storage_lines.drain(..).collect::<Vec<_>>();
let storage_lines = storage_lines
.into_iter()
.map(|line| StorageLineDefExt::from_def(line, &def, &hidden_crate))
.collect();
let (optional_instance, optional_instance_bound, optional_instance_bound_optional_default) =
if let Some(instance) = def.module_instance.as_ref() {
let instance_generic = &instance.instance_generic;
let instance_trait = &instance.instance_trait;
let optional_equal_instance_default =
instance.instance_default.as_ref().map(|d| quote!( = #d ));
(
Some(quote!(#instance_generic)),
Some(quote!(#instance_generic: #instance_trait)),
Some(
quote!(#instance_generic: #instance_trait #optional_equal_instance_default),
),
)
} else {
(None, None, None)
};
let module_runtime_generic = &def.module_runtime_generic;
let module_runtime_trait = &def.module_runtime_trait;
let module_name = &def.module_name;
let module_struct = quote!(
#module_name<#module_runtime_generic, #optional_instance>
);
let module_impl = quote!(
<#module_runtime_generic: #module_runtime_trait + 'static, #optional_instance_bound>
);
Self {
hidden_crate,
hidden_imports,
generate_storage_info: def.generate_storage_info,
visibility: def.visibility,
store_trait: def.store_trait,
module_name: def.module_name,
module_runtime_generic: def.module_runtime_generic,
module_runtime_trait: def.module_runtime_trait,
module_instance: def.module_instance,
where_clause: def.where_clause,
extra_genesis_build: def.extra_genesis_build,
extra_genesis_config_lines: def.extra_genesis_config_lines,
crate_name: def.crate_name,
storage_lines,
module_struct,
module_impl,
optional_instance,
optional_instance_bound,
optional_instance_bound_optional_default,
}
}
}
pub struct ModuleInstanceDef {
instance_generic: syn::Ident,
instance_trait: syn::Ident,
instance_default: Option<syn::Ident>,
}
pub struct StorageLineDef {
attrs: Vec<syn::Attribute>,
visibility: syn::Visibility,
name: syn::Ident,
getter: Option<syn::Ident>,
config: Option<syn::Ident>,
max_values: Option<syn::Expr>,
build: Option<syn::Expr>,
default_value: Option<syn::Expr>,
storage_type: StorageLineTypeDef,
}
pub struct StorageLineDefExt {
#[allow(unused)]
attrs: Vec<syn::Attribute>,
visibility: syn::Visibility,
name: syn::Ident,
getter: Option<syn::Ident>,
config: Option<syn::Ident>,
max_values: Option<syn::Expr>,
build: Option<syn::Expr>,
default_value: Option<syn::Expr>,
storage_type: StorageLineTypeDef,
doc_attrs: Vec<syn::Meta>,
query_type: syn::Type,
value_type: syn::Type,
storage_struct: proc_macro2::TokenStream,
optional_storage_runtime_comma: Option<proc_macro2::TokenStream>,
optional_storage_runtime_bound_comma: Option<proc_macro2::TokenStream>,
optional_storage_where_clause: Option<proc_macro2::TokenStream>,
storage_trait: proc_macro2::TokenStream,
storage_generator_trait: proc_macro2::TokenStream,
is_generic: bool,
is_option: bool,
}
impl StorageLineDefExt {
fn from_def(
storage_def: StorageLineDef,
def: &DeclStorageDef,
hidden_crate: &proc_macro2::TokenStream,
) -> Self {
let is_generic = match &storage_def.storage_type {
StorageLineTypeDef::Simple(value) =>
ext::type_contains_ident(value, &def.module_runtime_generic),
StorageLineTypeDef::Map(map) =>
ext::type_contains_ident(&map.key, &def.module_runtime_generic) ||
ext::type_contains_ident(&map.value, &def.module_runtime_generic),
StorageLineTypeDef::DoubleMap(map) =>
ext::type_contains_ident(&map.key1, &def.module_runtime_generic) ||
ext::type_contains_ident(&map.key2, &def.module_runtime_generic) ||
ext::type_contains_ident(&map.value, &def.module_runtime_generic),
StorageLineTypeDef::NMap(map) =>
map.keys
.iter()
.any(|key| ext::type_contains_ident(key, &def.module_runtime_generic)) ||
ext::type_contains_ident(&map.value, &def.module_runtime_generic),
};
let query_type = match &storage_def.storage_type {
StorageLineTypeDef::Simple(value) => value.clone(),
StorageLineTypeDef::Map(map) => map.value.clone(),
StorageLineTypeDef::DoubleMap(map) => map.value.clone(),
StorageLineTypeDef::NMap(map) => map.value.clone(),
};
let is_option = ext::extract_type_option(&query_type).is_some();
let value_type =
ext::extract_type_option(&query_type).unwrap_or_else(|| query_type.clone());
let module_runtime_generic = &def.module_runtime_generic;
let module_runtime_trait = &def.module_runtime_trait;
let optional_storage_runtime_comma =
if is_generic { Some(quote!( #module_runtime_generic, )) } else { None };
let optional_storage_runtime_bound_comma = if is_generic {
Some(quote!( #module_runtime_generic: #module_runtime_trait, ))
} else {
None
};
let storage_name = &storage_def.name;
let optional_instance_generic = def.module_instance.as_ref().map(|i| {
let instance_generic = &i.instance_generic;
quote!( #instance_generic )
});
let storage_struct = quote!(
#storage_name<#optional_storage_runtime_comma #optional_instance_generic>
);
let optional_storage_where_clause =
if is_generic { def.where_clause.as_ref().map(|w| quote!( #w )) } else { None };
let storage_trait_truncated = match &storage_def.storage_type {
StorageLineTypeDef::Simple(_) => {
quote!( StorageValue<#value_type> )
},
StorageLineTypeDef::Map(map) => {
let key = &map.key;
quote!( StorageMap<#key, #value_type> )
},
StorageLineTypeDef::DoubleMap(map) => {
let key1 = &map.key1;
let key2 = &map.key2;
quote!( StorageDoubleMap<#key1, #key2, #value_type> )
},
StorageLineTypeDef::NMap(map) => {
let keygen = map.to_keygen_struct(hidden_crate);
quote!( StorageNMap<#keygen, #value_type> )
},
};
let storage_trait = quote!( storage::#storage_trait_truncated );
let storage_generator_trait = quote!( storage::generator::#storage_trait_truncated );
let doc_attrs = storage_def
.attrs
.iter()
.filter_map(|a| a.parse_meta().ok())
.filter(|m| m.path().is_ident("doc"))
.collect();
Self {
attrs: storage_def.attrs,
visibility: storage_def.visibility,
name: storage_def.name,
getter: storage_def.getter,
config: storage_def.config,
max_values: storage_def.max_values,
build: storage_def.build,
default_value: storage_def.default_value,
storage_type: storage_def.storage_type,
doc_attrs,
query_type,
value_type,
storage_struct,
optional_storage_runtime_comma,
optional_storage_runtime_bound_comma,
optional_storage_where_clause,
storage_trait,
storage_generator_trait,
is_generic,
is_option,
}
}
}
pub enum StorageLineTypeDef {
Map(MapDef),
DoubleMap(Box<DoubleMapDef>),
NMap(NMapDef),
Simple(syn::Type),
}
pub struct MapDef {
pub hasher: HasherKind,
pub key: syn::Type,
pub value: syn::Type,
}
pub struct DoubleMapDef {
pub hasher1: HasherKind,
pub hasher2: HasherKind,
pub key1: syn::Type,
pub key2: syn::Type,
pub value: syn::Type,
}
pub struct NMapDef {
pub hashers: Vec<HasherKind>,
pub keys: Vec<syn::Type>,
pub value: syn::Type,
}
impl NMapDef {
fn to_keygen_struct(&self, scrate: &proc_macro2::TokenStream) -> proc_macro2::TokenStream {
if self.keys.len() == 1 {
let hasher = &self.hashers[0].to_storage_hasher_struct();
let key = &self.keys[0];
return quote!( #scrate::storage::types::Key<#scrate::#hasher, #key> )
}
let key_hasher = self
.keys
.iter()
.zip(&self.hashers)
.map(|(key, hasher)| {
let hasher = hasher.to_storage_hasher_struct();
quote!( #scrate::storage::types::Key<#scrate::#hasher, #key> )
})
.collect::<Vec<_>>();
quote!(( #(#key_hasher,)* ))
}
fn to_key_tuple(&self) -> proc_macro2::TokenStream {
if self.keys.len() == 1 {
let key = &self.keys[0];
return quote!(#key)
}
let tuple = self.keys.iter().map(|key| quote!(#key)).collect::<Vec<_>>();
quote!(( #(#tuple,)* ))
}
}
pub struct ExtraGenesisLineDef {
attrs: Vec<syn::Attribute>,
name: syn::Ident,
typ: syn::Type,
default: Option<syn::Expr>,
}
#[derive(Debug, Clone)]
pub enum HasherKind {
Blake2_256,
Blake2_128,
Blake2_128Concat,
Twox256,
Twox128,
Twox64Concat,
Identity,
}
impl HasherKind {
fn to_storage_hasher_struct(&self) -> proc_macro2::TokenStream {
match self {
HasherKind::Blake2_256 => quote!(Blake2_256),
HasherKind::Blake2_128 => quote!(Blake2_128),
HasherKind::Blake2_128Concat => quote!(Blake2_128Concat),
HasherKind::Twox256 => quote!(Twox256),
HasherKind::Twox128 => quote!(Twox128),
HasherKind::Twox64Concat => quote!(Twox64Concat),
HasherKind::Identity => quote!(Identity),
}
}
fn into_metadata(&self) -> proc_macro2::TokenStream {
match self {
HasherKind::Blake2_256 => quote!(StorageHasher::Blake2_256),
HasherKind::Blake2_128 => quote!(StorageHasher::Blake2_128),
HasherKind::Blake2_128Concat => quote!(StorageHasher::Blake2_128Concat),
HasherKind::Twox256 => quote!(StorageHasher::Twox256),
HasherKind::Twox128 => quote!(StorageHasher::Twox128),
HasherKind::Twox64Concat => quote!(StorageHasher::Twox64Concat),
HasherKind::Identity => quote!(StorageHasher::Identity),
}
}
}
pub fn decl_storage_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let def = syn::parse_macro_input!(input as DeclStorageDef);
let def_ext = DeclStorageDefExt::from(def);
print_pallet_upgrade::maybe_print_pallet_upgrade(&def_ext);
let scrate = &def_ext.hidden_crate;
let scrate_decl = &def_ext.hidden_imports;
let store_trait = store_trait::decl_and_impl(&def_ext);
let getters = getters::impl_getters(&def_ext);
let metadata = metadata::impl_metadata(&def_ext);
let instance_trait = instance_trait::decl_and_impl(&def_ext);
let genesis_config = genesis_config::genesis_config_and_build_storage(&def_ext);
let storage_struct = storage_struct::decl_and_impl(&def_ext);
let storage_info = storage_info::impl_storage_info(&def_ext);
quote!(
use #scrate::{
StorageValue as _,
StorageMap as _,
StorageDoubleMap as _,
StorageNMap as _,
StoragePrefixedMap as _,
IterableStorageMap as _,
IterableStorageNMap as _,
IterableStorageDoubleMap as _,
};
#scrate_decl
#store_trait
#getters
#metadata
#instance_trait
#genesis_config
#storage_struct
#storage_info
)
.into()
}