Allow to name the generic for storages in #[pallet::storage] (#8751)

* implement named generic for storages

* fix error message on unexpected name for generic
This commit is contained in:
Guillaume Thiolliere
2021-05-19 09:11:11 +02:00
committed by GitHub
parent ed39290f91
commit 0b30049417
18 changed files with 746 additions and 136 deletions
@@ -16,7 +16,7 @@
// limitations under the License.
use crate::pallet::Def;
use crate::pallet::parse::storage::{Metadata, QueryKind};
use crate::pallet::parse::storage::{Metadata, QueryKind, StorageGenerics};
use frame_support_procedural_tools::clean_type_string;
/// Generate the prefix_ident related the the storage.
@@ -25,50 +25,112 @@ fn prefix_ident(storage_ident: &syn::Ident) -> syn::Ident {
syn::Ident::new(&format!("_GeneratedPrefixForStorage{}", storage_ident), storage_ident.span())
}
/// * generate StoragePrefix structs (e.g. for a storage `MyStorage` a struct with the name
/// `_GeneratedPrefixForStorage$NameOfStorage` is generated) and implements StorageInstance trait.
/// * replace the first generic `_` by the generated prefix structure
/// * generate metadatas
pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream {
/// * if generics are unnamed: replace the first generic `_` by the generated prefix structure
/// * if generics are named: reorder the generic, remove their name, and add the missing ones.
/// * Add `#[allow(type_alias_bounds)]`
pub fn process_generics(def: &mut Def) {
let frame_support = &def.frame_support;
let frame_system = &def.frame_system;
let pallet_ident = &def.pallet_struct.pallet;
// Replace first arg `_` by the generated prefix structure.
// Add `#[allow(type_alias_bounds)]`
for storage_def in def.storages.iter_mut() {
let item = &mut def.item.content.as_mut().expect("Checked by def").1[storage_def.index];
let typ_item = if let syn::Item::Type(t) = item {
t
} else {
unreachable!("Checked by def");
let typ_item = match item {
syn::Item::Type(t) => t,
_ => unreachable!("Checked by def"),
};
typ_item.attrs.push(syn::parse_quote!(#[allow(type_alias_bounds)]));
let typ_path = if let syn::Type::Path(p) = &mut *typ_item.ty {
p
} else {
unreachable!("Checked by def");
let typ_path = match &mut *typ_item.ty {
syn::Type::Path(p) => p,
_ => unreachable!("Checked by def"),
};
let args = if let syn::PathArguments::AngleBracketed(args) =
&mut typ_path.path.segments[0].arguments
{
args
} else {
unreachable!("Checked by def");
let args = match &mut typ_path.path.segments[0].arguments {
syn::PathArguments::AngleBracketed(args) => args,
_ => unreachable!("Checked by def"),
};
let prefix_ident = prefix_ident(&storage_def.ident);
let type_use_gen = if def.config.has_instance {
quote::quote_spanned!(storage_def.attr_span => T, I)
} else {
quote::quote_spanned!(storage_def.attr_span => T)
};
let prefix_ident = prefix_ident(&storage_def.ident);
args.args[0] = syn::parse_quote!( #prefix_ident<#type_use_gen> );
let default_query_kind: syn::Type =
syn::parse_quote!(#frame_support::storage::types::OptionQuery);
let default_on_empty: syn::Type =
syn::parse_quote!(#frame_support::traits::GetDefault);
let default_max_values: syn::Type =
syn::parse_quote!(#frame_support::traits::GetDefault);
if let Some(named_generics) = storage_def.named_generics.clone() {
args.args.clear();
args.args.push(syn::parse_quote!( #prefix_ident<#type_use_gen> ));
match named_generics {
StorageGenerics::Value { value, query_kind, on_empty } => {
args.args.push(syn::GenericArgument::Type(value));
let query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone());
args.args.push(syn::GenericArgument::Type(query_kind));
let on_empty = on_empty.unwrap_or_else(|| default_on_empty.clone());
args.args.push(syn::GenericArgument::Type(on_empty));
}
StorageGenerics::Map { hasher, key, value, query_kind, on_empty, max_values } => {
args.args.push(syn::GenericArgument::Type(hasher));
args.args.push(syn::GenericArgument::Type(key));
args.args.push(syn::GenericArgument::Type(value));
let query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone());
args.args.push(syn::GenericArgument::Type(query_kind));
let on_empty = on_empty.unwrap_or_else(|| default_on_empty.clone());
args.args.push(syn::GenericArgument::Type(on_empty));
let max_values = max_values.unwrap_or_else(|| default_max_values.clone());
args.args.push(syn::GenericArgument::Type(max_values));
}
StorageGenerics::DoubleMap {
hasher1, key1, hasher2, key2, value, query_kind, on_empty, max_values,
} => {
args.args.push(syn::GenericArgument::Type(hasher1));
args.args.push(syn::GenericArgument::Type(key1));
args.args.push(syn::GenericArgument::Type(hasher2));
args.args.push(syn::GenericArgument::Type(key2));
args.args.push(syn::GenericArgument::Type(value));
let query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone());
args.args.push(syn::GenericArgument::Type(query_kind));
let on_empty = on_empty.unwrap_or_else(|| default_on_empty.clone());
args.args.push(syn::GenericArgument::Type(on_empty));
let max_values = max_values.unwrap_or_else(|| default_max_values.clone());
args.args.push(syn::GenericArgument::Type(max_values));
}
StorageGenerics::NMap { keygen, value, query_kind, on_empty, max_values, } => {
args.args.push(syn::GenericArgument::Type(keygen));
args.args.push(syn::GenericArgument::Type(value));
let query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone());
args.args.push(syn::GenericArgument::Type(query_kind));
let on_empty = on_empty.unwrap_or_else(|| default_on_empty.clone());
args.args.push(syn::GenericArgument::Type(on_empty));
let max_values = max_values.unwrap_or_else(|| default_max_values.clone());
args.args.push(syn::GenericArgument::Type(max_values));
}
}
} else {
args.args[0] = syn::parse_quote!( #prefix_ident<#type_use_gen> );
}
}
}
/// * generate StoragePrefix structs (e.g. for a storage `MyStorage` a struct with the name
/// `_GeneratedPrefixForStorage$NameOfStorage` is generated) and implements StorageInstance trait.
/// * if generics are unnamed: replace the first generic `_` by the generated prefix structure
/// * if generics are named: reorder the generic, remove their name, and add the missing ones.
/// * Add `#[allow(type_alias_bounds)]` on storages type alias
/// * generate metadatas
pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream {
process_generics(def);
let frame_support = &def.frame_support;
let frame_system = &def.frame_system;
let pallet_ident = &def.pallet_struct.pallet;
let entries = def.storages.iter()
.map(|storage| {