From f4e36f0d74ac21c6cdee9a6bc85efce2e5ad45cd Mon Sep 17 00:00:00 2001 From: thiolliere <gui.thiolliere@gmail.com> Date: Wed, 16 Oct 2019 15:47:46 +0200 Subject: [PATCH] Refactor decl storage (#3765) * split implementation in multiple files: * transformation -> genesis_config/ getters.rs instance_trait.rs metadata.rs mod.rs store_trait.rs * mod.rs -> parser.rs * impl.rs -> storage_struct.rs * parser is isolated into parse module, it could be improved as well but this can be done in another PR * modules contains a defintion of decl_storage input which must be ok to work with. * implementation change: * T: Trait might be more often bound to 'static (anyway we only use static one and it is needed for metadata current implementation). * GenesisConfig no longer requires its fields to be Clone (possible since to EncodeLike feature) * builder for map storages must return precise type Vec<(key, value)> --- substrate/node/runtime/src/lib.rs | 4 +- substrate/srml/support/procedural/src/lib.rs | 2 +- .../src/storage/genesis_config/builder_def.rs | 111 ++ .../genesis_config/genesis_config_def.rs | 144 ++ .../src/storage/genesis_config/mod.rs | 203 +++ .../support/procedural/src/storage/getters.rs | 80 ++ .../support/procedural/src/storage/impls.rs | 418 ------ .../procedural/src/storage/instance_trait.rs | 196 +++ .../procedural/src/storage/metadata.rs | 230 +++ .../support/procedural/src/storage/mod.rs | 521 ++++--- .../support/procedural/src/storage/parse.rs | 377 +++++ .../procedural/src/storage/storage_struct.rs | 220 +++ .../procedural/src/storage/store_trait.rs | 54 + .../procedural/src/storage/transformation.rs | 1264 ----------------- .../support/procedural/tools/src/syn_ext.rs | 12 +- 15 files changed, 1978 insertions(+), 1858 deletions(-) create mode 100644 substrate/srml/support/procedural/src/storage/genesis_config/builder_def.rs create mode 100644 substrate/srml/support/procedural/src/storage/genesis_config/genesis_config_def.rs create mode 100644 substrate/srml/support/procedural/src/storage/genesis_config/mod.rs create mode 100644 substrate/srml/support/procedural/src/storage/getters.rs delete mode 100644 substrate/srml/support/procedural/src/storage/impls.rs create mode 100644 substrate/srml/support/procedural/src/storage/instance_trait.rs create mode 100644 substrate/srml/support/procedural/src/storage/metadata.rs create mode 100644 substrate/srml/support/procedural/src/storage/parse.rs create mode 100644 substrate/srml/support/procedural/src/storage/storage_struct.rs create mode 100644 substrate/srml/support/procedural/src/storage/store_trait.rs delete mode 100644 substrate/srml/support/procedural/src/storage/transformation.rs diff --git a/substrate/node/runtime/src/lib.rs b/substrate/node/runtime/src/lib.rs index fd31969efa0..63fc8560138 100644 --- a/substrate/node/runtime/src/lib.rs +++ b/substrate/node/runtime/src/lib.rs @@ -84,8 +84,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to equal spec_version. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 176, - impl_version: 176, + spec_version: 177, + impl_version: 177, apis: RUNTIME_API_VERSIONS, }; diff --git a/substrate/srml/support/procedural/src/lib.rs b/substrate/srml/support/procedural/src/lib.rs index a9cfa8d30d1..38d404712ed 100644 --- a/substrate/srml/support/procedural/src/lib.rs +++ b/substrate/srml/support/procedural/src/lib.rs @@ -213,5 +213,5 @@ use proc_macro::TokenStream; /// #[proc_macro] pub fn decl_storage(input: TokenStream) -> TokenStream { - storage::transformation::decl_storage_impl(input) + storage::decl_storage_impl(input) } diff --git a/substrate/srml/support/procedural/src/storage/genesis_config/builder_def.rs b/substrate/srml/support/procedural/src/storage/genesis_config/builder_def.rs new file mode 100644 index 00000000000..60f094e9fe4 --- /dev/null +++ b/substrate/srml/support/procedural/src/storage/genesis_config/builder_def.rs @@ -0,0 +1,111 @@ +// Copyright 2017-2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see <http://www.gnu.org/licenses/>. + +//! Builder logic definition used to build genesis storage. + +use srml_support_procedural_tools::syn_ext as ext; +use proc_macro2::TokenStream; +use syn::spanned::Spanned; +use quote::{quote, quote_spanned}; +use super::super::{DeclStorageDefExt, StorageLineTypeDef}; + +/// Definition of builder blocks, each block insert some value in the storage. +/// They must be called inside externalities, and with `self` being the genesis config. +pub struct BuilderDef { + /// Contains: + /// * build block for storage with build attribute. + /// * build block for storage with config attribute and no build attribute. + /// * build block for extra genesis build expression. + pub blocks: Vec<TokenStream>, + /// The build blocks requires generic traits. + pub is_generic: bool, +} + +impl BuilderDef { + pub fn from_def(scrate: &TokenStream, def: &DeclStorageDefExt) -> Self { + let mut blocks = Vec::new(); + let mut is_generic = false; + + for line in def.storage_lines.iter() { + let storage_struct = &line.storage_struct; + let storage_trait = &line.storage_trait; + let value_type = &line.value_type; + + // Contains the data to inset at genesis either from build or config. + let mut data = None; + + if let Some(builder) = &line.build { + is_generic |= ext::expr_contains_ident(&builder, &def.module_runtime_generic); + is_generic |= line.is_generic; + + data = Some(quote_spanned!(builder.span() => &(#builder)(&self))); + } else if let Some(config) = &line.config { + is_generic |= line.is_generic; + + data = Some(quote!(&self.#config;)); + }; + + if let Some(data) = data { + blocks.push(match &line.storage_type { + StorageLineTypeDef::Simple(_) => { + quote!{{ + let v: &#value_type = #data; + <#storage_struct as #scrate::#storage_trait>::put::<&#value_type>(v); + }} + }, + StorageLineTypeDef::Map(map) | StorageLineTypeDef::LinkedMap(map) => { + let key = &map.key; + quote!{{ + let data: &#scrate::rstd::vec::Vec<(#key, #value_type)> = #data; + data.iter().for_each(|(k, v)| { + <#storage_struct as #scrate::#storage_trait>::insert::< + &#key, &#value_type + >(k, v); + }); + }} + }, + StorageLineTypeDef::DoubleMap(map) => { + let key1 = &map.key1; + let key2 = &map.key2; + quote!{{ + let data: &#scrate::rstd::vec::Vec<(#key1, #key2, #value_type)> = #data; + data.iter().for_each(|(k1, k2, v)| { + <#storage_struct as #scrate::#storage_trait>::insert::< + &#key1, &#key2, &#value_type + >(k1, k2, v); + }); + }} + }, + }); + } + } + + if let Some(builder) = def.extra_genesis_build.as_ref() { + is_generic |= ext::expr_contains_ident(&builder, &def.module_runtime_generic); + + blocks.push(quote_spanned! { builder.span() => + let extra_genesis_builder: fn(&Self) = #builder; + extra_genesis_builder(&self); + }); + } + + + Self { + blocks, + is_generic, + } + } +} diff --git a/substrate/srml/support/procedural/src/storage/genesis_config/genesis_config_def.rs b/substrate/srml/support/procedural/src/storage/genesis_config/genesis_config_def.rs new file mode 100644 index 00000000000..f9d2f8abe80 --- /dev/null +++ b/substrate/srml/support/procedural/src/storage/genesis_config/genesis_config_def.rs @@ -0,0 +1,144 @@ +// Copyright 2017-2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see <http://www.gnu.org/licenses/>. + +//! Genesis config defintion. + +use srml_support_procedural_tools::syn_ext as ext; +use proc_macro2::TokenStream; +use syn::parse_quote; +use quote::quote; +use super::super::{DeclStorageDefExt, StorageLineTypeDef}; + +pub struct GenesisConfigFieldDef { + pub doc: Vec<syn::Meta>, + pub name: syn::Ident, + pub typ: syn::Type, + pub default: TokenStream, +} + +pub struct GenesisConfigDef { + pub is_generic: bool, + pub fields: Vec<GenesisConfigFieldDef>, + /// For example: `<T: Trait<I>, I: Instance=DefaultInstance>`. + pub genesis_struct_decl: TokenStream, + /// For example: `<T, I>`. + pub genesis_struct: TokenStream, + /// For example: `<T: Trait<I>, I: Instance>`. + pub genesis_impl: TokenStream, + /// The where clause to use to constrain generics if genesis config is generic. + pub genesis_where_clause: Option<syn::WhereClause>, +} + +impl GenesisConfigDef { + pub fn from_def(def: &DeclStorageDefExt) -> Self { + let fields = Self::get_genesis_config_field_defs(def); + + let is_generic = fields.iter() + .any(|field| ext::type_contains_ident(&field.typ, &def.module_runtime_generic)); + + let ( + genesis_struct_decl, + genesis_impl, + genesis_struct, + genesis_where_clause + ) = if is_generic { + let runtime_generic = &def.module_runtime_generic; + let runtime_trait = &def.module_runtime_trait; + let optional_instance = &def.optional_instance; + let optional_instance_bound = &def.optional_instance_bound; + let optional_instance_bound_optional_default = &def.optional_instance_bound_optional_default; + let where_clause = &def.where_clause; + ( + quote!(<#runtime_generic: #runtime_trait, #optional_instance_bound_optional_default>), + quote!(<#runtime_generic: #runtime_trait, #optional_instance_bound>), + quote!(<#runtime_generic, #optional_instance>), + where_clause.clone(), + ) + } else { + (quote!(), quote!(), quote!(), None) + }; + + Self { + is_generic, + fields, + genesis_struct_decl, + genesis_struct, + genesis_impl, + genesis_where_clause, + } + } + + fn get_genesis_config_field_defs(def: &DeclStorageDefExt) -> Vec<GenesisConfigFieldDef> { + let mut config_field_defs = Vec::new(); + + for (config_field, line) in def.storage_lines.iter() + .filter_map(|line| line.config.as_ref().map(|config_field| (config_field.clone(), line))) + { + let value_type = &line.value_type; + + let typ = match &line.storage_type { + StorageLineTypeDef::Simple(_) => (*value_type).clone(), + StorageLineTypeDef::Map(map) | StorageLineTypeDef::LinkedMap(map) => { + let key = &map.key; + parse_quote!( Vec<(#key, #value_type)> ) + }, + StorageLineTypeDef::DoubleMap(map) => { + let key1 = &map.key1; + let key2 = &map.key2; + + parse_quote!( Vec<(#key1, #key2, #value_type)> ) + }, + }; + + let default = line.default_value.as_ref() + .map(|d| { + if line.is_option { + quote!( #d.unwrap_or_default() ) + } else { + quote!( #d ) + } + }) + .unwrap_or_else(|| quote!( Default::default() )); + + config_field_defs.push(GenesisConfigFieldDef { + doc: line.doc_attrs.clone(), + name: config_field, + typ, + default, + }); + } + + for line in &def.extra_genesis_config_lines { + let doc = line.attrs.iter() + .filter_map(|a| a.parse_meta().ok()) + .filter(|m| m.name() == "doc") + .collect(); + + let default = line.default.as_ref().map(|e| quote!( #e )) + .unwrap_or_else(|| quote!( Default::default() )); + + + config_field_defs.push(GenesisConfigFieldDef { + doc, + name: line.name.clone(), + typ: line.typ.clone(), + default, + }); + } + + config_field_defs + } +} diff --git a/substrate/srml/support/procedural/src/storage/genesis_config/mod.rs b/substrate/srml/support/procedural/src/storage/genesis_config/mod.rs new file mode 100644 index 00000000000..57de1e34a8d --- /dev/null +++ b/substrate/srml/support/procedural/src/storage/genesis_config/mod.rs @@ -0,0 +1,203 @@ +// Copyright 2017-2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see <http://www.gnu.org/licenses/>. + +//! Declaration of genesis config structure and implementation of build storage trait and +//! functions. + +use proc_macro2::{TokenStream, Span}; +use quote::quote; +use super::{DeclStorageDefExt, instance_trait::DEFAULT_INSTANTIABLE_TRAIT_NAME}; +use genesis_config_def::GenesisConfigDef; +use builder_def::BuilderDef; + +mod genesis_config_def; +mod builder_def; + +const DEFAULT_INSTANCE_NAME: &str = "__GeneratedInstance"; + +fn decl_genesis_config_and_impl_default( + scrate: &TokenStream, + genesis_config: &GenesisConfigDef, +) -> TokenStream { + let config_fields = genesis_config.fields.iter().map(|field| { + let (name, typ, doc) = (&field.name, &field.typ, &field.doc); + quote!( #( #[ #doc] )* pub #name: #typ, ) + }); + + let config_field_defaults = genesis_config.fields.iter().map(|field| { + let (name, default, doc) = (&field.name, &field.default, &field.doc); + quote!( #( #[ #doc] )* #name: #default, ) + }); + + let serde_bug_bound = if !genesis_config.fields.is_empty() { + let mut b_ser = String::new(); + let mut b_dser = String::new(); + + for typ in genesis_config.fields.iter().map(|c| &c.typ) { + let typ = quote!( #typ ); + b_ser.push_str(&format!("{} : {}::serde::Serialize, ", typ, scrate)); + b_dser.push_str(&format!("{} : {}::serde::de::DeserializeOwned, ", typ, scrate)); + } + + quote! { + #[serde(bound(serialize = #b_ser))] + #[serde(bound(deserialize = #b_dser))] + } + } else { + quote!() + }; + + let genesis_struct_decl = &genesis_config.genesis_struct_decl; + let genesis_struct = &genesis_config.genesis_struct; + let genesis_impl = &genesis_config.genesis_impl; + let genesis_where_clause = &genesis_config.genesis_where_clause; + + quote!( + #[derive(#scrate::Serialize, #scrate::Deserialize)] + #[cfg(feature = "std")] + #[serde(rename_all = "camelCase")] + #[serde(deny_unknown_fields)] + #serde_bug_bound + pub struct GenesisConfig#genesis_struct_decl #genesis_where_clause { + #( #config_fields )* + } + + #[cfg(feature = "std")] + impl#genesis_impl Default for GenesisConfig#genesis_struct #genesis_where_clause { + fn default() -> Self { + GenesisConfig { + #( #config_field_defaults )* + } + } + } + ) +} + +fn impl_build_storage( + scrate: &TokenStream, + def: &DeclStorageDefExt, + genesis_config: &GenesisConfigDef, + builders: &BuilderDef, +) -> TokenStream { + let runtime_generic = &def.module_runtime_generic; + let runtime_trait = &def.module_runtime_trait; + let optional_instance = &def.optional_instance; + let optional_instance_bound = &def.optional_instance_bound; + let where_clause = &def.where_clause; + + let inherent_instance = def.optional_instance.clone().unwrap_or_else(|| { + let name = syn::Ident::new(DEFAULT_INSTANCE_NAME, Span::call_site()); + quote!( #name ) + }); + let inherent_instance_bound = def.optional_instance_bound.clone().unwrap_or_else(|| { + let bound = syn::Ident::new(DEFAULT_INSTANTIABLE_TRAIT_NAME, Span::call_site()); + quote!( #inherent_instance: #bound ) + }); + + let build_storage_impl = quote!( + <#runtime_generic: #runtime_trait, #inherent_instance_bound> + ); + + let genesis_struct = &genesis_config.genesis_struct; + let genesis_impl = &genesis_config.genesis_impl; + let genesis_where_clause = &genesis_config.genesis_where_clause; + + let ( + fn_generic, + fn_traitinstance, + fn_where_clause + ) = if !genesis_config.is_generic && builders.is_generic { + ( + quote!( <#runtime_generic: #runtime_trait, #optional_instance_bound> ), + quote!( #runtime_generic, #optional_instance ), + Some(&def.where_clause), + ) + } else { + (quote!(), quote!(), None) + }; + + let builder_blocks = &builders.blocks; + + let build_storage_impl_trait = quote!( + #scrate::sr_primitives::BuildModuleGenesisStorage<#runtime_generic, #inherent_instance> + ); + + quote!{ + #[cfg(feature = "std")] + impl#genesis_impl GenesisConfig#genesis_struct #genesis_where_clause { + pub fn build_storage #fn_generic (self) -> std::result::Result< + ( + #scrate::sr_primitives::StorageOverlay, + #scrate::sr_primitives::ChildrenStorageOverlay, + ), + String + > #fn_where_clause { + let mut storage = (Default::default(), Default::default()); + self.assimilate_storage::<#fn_traitinstance>(&mut storage)?; + Ok(storage) + } + + /// Assimilate the storage for this module into pre-existing overlays. + pub fn assimilate_storage #fn_generic ( + self, + tuple_storage: &mut ( + #scrate::sr_primitives::StorageOverlay, + #scrate::sr_primitives::ChildrenStorageOverlay, + ), + ) -> std::result::Result<(), String> #fn_where_clause { + #scrate::with_storage(tuple_storage, || { + #( #builder_blocks )* + Ok(()) + }) + } + } + + #[cfg(feature = "std")] + impl#build_storage_impl #build_storage_impl_trait for GenesisConfig#genesis_struct + #where_clause + { + fn build_module_genesis_storage( + self, + storage: &mut ( + #scrate::sr_primitives::StorageOverlay, + #scrate::sr_primitives::ChildrenStorageOverlay, + ), + ) -> std::result::Result<(), String> { + self.assimilate_storage::<#fn_traitinstance> (storage) + } + } + } +} + +pub fn genesis_config_and_build_storage( + scrate: &TokenStream, + def: &DeclStorageDefExt, +) -> TokenStream { + let builders = BuilderDef::from_def(scrate, def); + if !builders.blocks.is_empty() { + let genesis_config = &GenesisConfigDef::from_def(def); + let decl_genesis_config_and_impl_default = + decl_genesis_config_and_impl_default(scrate, genesis_config); + let impl_build_storage = impl_build_storage(scrate, def, genesis_config, &builders); + + quote!{ + #decl_genesis_config_and_impl_default + #impl_build_storage + } + } else { + quote!() + } +} diff --git a/substrate/srml/support/procedural/src/storage/getters.rs b/substrate/srml/support/procedural/src/storage/getters.rs new file mode 100644 index 00000000000..f30e489eb58 --- /dev/null +++ b/substrate/srml/support/procedural/src/storage/getters.rs @@ -0,0 +1,80 @@ +// Copyright 2017-2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see <http://www.gnu.org/licenses/>. + +//! Implementation of getters on module structure. + +use proc_macro2::TokenStream; +use quote::quote; +use super::{DeclStorageDefExt, StorageLineTypeDef}; + +pub fn impl_getters(scrate: &TokenStream, def: &DeclStorageDefExt) -> TokenStream { + let mut getters = TokenStream::new(); + + for (get_fn, line) in def.storage_lines.iter() + .filter_map(|line| line.getter.as_ref().map(|get_fn| (get_fn, line))) + { + let attrs = &line.doc_attrs; + + let storage_struct = &line.storage_struct; + let storage_trait = &line.storage_trait; + + let getter = match &line.storage_type { + StorageLineTypeDef::Simple(value) => { + quote!{ + #( #[ #attrs ] )* + pub fn #get_fn() -> #value { + <#storage_struct as #scrate::#storage_trait>::get() + } + } + }, + StorageLineTypeDef::Map(map) | StorageLineTypeDef::LinkedMap(map) => { + let key = &map.key; + let value = &map.value; + quote!{ + #( #[ #attrs ] )* + pub fn #get_fn<K: #scrate::codec::EncodeLike<#key>>(key: K) -> #value { + <#storage_struct as #scrate::#storage_trait>::get(key) + } + } + }, + StorageLineTypeDef::DoubleMap(map) => { + let key1 = &map.key1; + let key2 = &map.key2; + let value = &map.value; + quote!{ + pub fn #get_fn<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> #value + where + KArg1: #scrate::codec::EncodeLike<#key1>, + KArg2: #scrate::codec::EncodeLike<#key2>, + { + <#storage_struct as #scrate::#storage_trait>::get(k1, k2) + } + } + }, + }; + getters.extend(getter); + } + + let module_struct = &def.module_struct; + let module_impl = &def.module_impl; + let where_clause = &def.where_clause; + + quote!( + impl#module_impl #module_struct #where_clause { + #getters + } + ) +} diff --git a/substrate/srml/support/procedural/src/storage/impls.rs b/substrate/srml/support/procedural/src/storage/impls.rs deleted file mode 100644 index ee6fa9a1642..00000000000 --- a/substrate/srml/support/procedural/src/storage/impls.rs +++ /dev/null @@ -1,418 +0,0 @@ -// Copyright 2019 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see <http://www.gnu.org/licenses/>. - -use crate::storage::transformation::{DeclStorageTypeInfos, InstanceOpts}; - -use srml_support_procedural_tools::syn_ext as ext; -use proc_macro2::TokenStream as TokenStream2; -use syn::Ident; -use quote::quote; - -fn from_optional_value_to_query(is_option: bool, fielddefault: TokenStream2) -> TokenStream2 { - if !is_option { - // raw type case - quote!( v.unwrap_or_else(|| #fielddefault ) ) - } else { - // Option<> type case - quote!( v.or_else(|| #fielddefault ) ) - } -} - -fn from_query_to_optional_value(is_option: bool) -> TokenStream2 { - if !is_option { - // raw type case - quote!( Some(v) ) - } else { - // Option<> type case - quote!( v ) - } -} - -// prefix for consts in trait Instance -pub(crate) const PREFIX_FOR: &str = "PREFIX_FOR_"; -pub(crate) const HEAD_KEY_FOR: &str = "HEAD_KEY_FOR_"; - -pub(crate) struct Impls<'a, I: Iterator<Item=syn::Meta>> { - pub scrate: &'a TokenStream2, - pub visibility: &'a syn::Visibility, - pub traitinstance: &'a syn::Ident, - pub traittype: &'a syn::TypeParamBound, - pub instance_opts: &'a InstanceOpts, - pub type_infos: DeclStorageTypeInfos<'a>, - pub fielddefault: TokenStream2, - pub prefix: String, - pub cratename: &'a syn::Ident, - pub name: &'a syn::Ident, - pub attrs: I, - pub where_clause: &'a Option<syn::WhereClause>, -} - -impl<'a, I: Iterator<Item=syn::Meta>> Impls<'a, I> { - pub fn simple_value(self) -> TokenStream2 { - let Self { - scrate, - visibility, - traitinstance, - traittype, - instance_opts, - type_infos, - fielddefault, - prefix, - name, - attrs, - where_clause, - .. - } = self; - let DeclStorageTypeInfos { typ, value_type, is_option, .. } = type_infos; - - let from_optional_value_to_query = from_optional_value_to_query(is_option, fielddefault); - let from_query_to_optional_value = from_query_to_optional_value(is_option); - - let InstanceOpts { - equal_default_instance, - bound_instantiable, - instance, - .. - } = instance_opts; - - let final_prefix = if let Some(instance) = instance { - let const_name = Ident::new(&format!("{}{}", PREFIX_FOR, name.to_string()), proc_macro2::Span::call_site()); - quote!{ #instance::#const_name.as_bytes() } - } else { - quote!{ #prefix.as_bytes() } - }; - - let (struct_trait, impl_trait, trait_and_instance, where_clause) = if ext::type_contains_ident( - value_type, traitinstance - ) { - ( - quote!(#traitinstance: #traittype, #instance #bound_instantiable #equal_default_instance), - quote!(#traitinstance: #traittype, #instance #bound_instantiable), - quote!(#traitinstance, #instance), - where_clause.clone(), - ) - } else { - ( - quote!(#instance #bound_instantiable #equal_default_instance), - quote!(#instance #bound_instantiable), - quote!(#instance), - None, - ) - }; - - // generator for value - quote! { - #( #[ #attrs ] )* - #visibility struct #name<#struct_trait>( - #scrate::rstd::marker::PhantomData<(#trait_and_instance)> - ) #where_clause; - - impl<#impl_trait> #scrate::storage::generator::StorageValue<#typ> - for #name<#trait_and_instance> #where_clause - { - type Query = #value_type; - - fn unhashed_key() -> &'static [u8] { - #final_prefix - } - - fn from_optional_value_to_query(v: Option<#typ>) -> Self::Query { - #from_optional_value_to_query - } - - fn from_query_to_optional_value(v: Self::Query) -> Option<#typ> { - #from_query_to_optional_value - } - } - } - } - - pub fn map(self, hasher: TokenStream2, kty: &syn::Type) -> TokenStream2 { - let Self { - scrate, - visibility, - traitinstance, - traittype, - instance_opts, - type_infos, - fielddefault, - prefix, - name, - attrs, - where_clause, - .. - } = self; - let DeclStorageTypeInfos { typ, value_type, is_option, .. } = type_infos; - - let from_optional_value_to_query = from_optional_value_to_query(is_option, fielddefault); - let from_query_to_optional_value = from_query_to_optional_value(is_option); - - let InstanceOpts { - equal_default_instance, - bound_instantiable, - instance, - .. - } = instance_opts; - - let final_prefix = if let Some(instance) = instance { - let const_name = syn::Ident::new( - &format!("{}{}", PREFIX_FOR, name.to_string()), - proc_macro2::Span::call_site(), - ); - quote! { #instance::#const_name.as_bytes() } - } else { - quote! { #prefix.as_bytes() } - }; - - let trait_required = ext::type_contains_ident(value_type, traitinstance) - || ext::type_contains_ident(kty, traitinstance); - - let (struct_trait, impl_trait, trait_and_instance, where_clause) = if trait_required { - ( - quote!(#traitinstance: #traittype, #instance #bound_instantiable #equal_default_instance), - quote!(#traitinstance: #traittype, #instance #bound_instantiable), - quote!(#traitinstance, #instance), - where_clause.clone(), - ) - } else { - ( - quote!(#instance #bound_instantiable #equal_default_instance), - quote!(#instance #bound_instantiable), - quote!(#instance), - None, - ) - }; - - // generator for map - quote!{ - #( #[ #attrs ] )* - #visibility struct #name<#struct_trait>( - #scrate::rstd::marker::PhantomData<(#trait_and_instance)> - ) #where_clause; - - impl<#impl_trait> #scrate::storage::generator::StorageMap<#kty, #typ> - for #name<#trait_and_instance> #where_clause - { - type Query = #value_type; - type Hasher = #scrate::#hasher; - - fn prefix() -> &'static [u8] { - #final_prefix - } - - fn from_optional_value_to_query(v: Option<#typ>) -> Self::Query { - #from_optional_value_to_query - } - - fn from_query_to_optional_value(v: Self::Query) -> Option<#typ> { - #from_query_to_optional_value - } - } - } - } - - pub fn linked_map(self, hasher: TokenStream2, kty: &syn::Type) -> TokenStream2 { - let Self { - scrate, - visibility, - traitinstance, - traittype, - instance_opts, - type_infos, - fielddefault, - prefix, - name, - attrs, - where_clause, - .. - } = self; - - let InstanceOpts { - equal_default_instance, - bound_instantiable, - instance, - .. - } = instance_opts; - - let final_prefix = if let Some(instance) = instance { - let const_name = Ident::new( - &format!("{}{}", PREFIX_FOR, name.to_string()), proc_macro2::Span::call_site() - ); - quote!{ #instance::#const_name.as_bytes() } - } else { - quote!{ #prefix.as_bytes() } - }; - - // make sure to use different prefix for head and elements. - let head_key = if let Some(instance) = instance { - let const_name = Ident::new( - &format!("{}{}", HEAD_KEY_FOR, name.to_string()), proc_macro2::Span::call_site() - ); - quote!{ #instance::#const_name.as_bytes() } - } else { - let head_key = format!("head of {}", prefix); - quote!{ #head_key.as_bytes() } - }; - - let DeclStorageTypeInfos { typ, value_type, is_option, .. } = type_infos; - - let from_optional_value_to_query = from_optional_value_to_query(is_option, fielddefault); - let from_query_to_optional_value = from_query_to_optional_value(is_option); - - let trait_required = ext::type_contains_ident(value_type, traitinstance) - || ext::type_contains_ident(kty, traitinstance); - - let (struct_trait, impl_trait, trait_and_instance, where_clause) = if trait_required { - ( - quote!(#traitinstance: #traittype, #instance #bound_instantiable #equal_default_instance), - quote!(#traitinstance: #traittype, #instance #bound_instantiable), - quote!(#traitinstance, #instance), - where_clause.clone(), - ) - } else { - ( - quote!(#instance #bound_instantiable #equal_default_instance), - quote!(#instance #bound_instantiable), - quote!(#instance), - None, - ) - }; - - // generator for linked map - quote! { - #( #[ #attrs ] )* - #visibility struct #name<#struct_trait>( - #scrate::rstd::marker::PhantomData<(#trait_and_instance)> - ) #where_clause; - - impl<#impl_trait> #scrate::storage::generator::StorageLinkedMap<#kty, #typ> - for #name<#trait_and_instance> #where_clause - { - type Query = #value_type; - type Hasher = #scrate::#hasher; - - fn prefix() -> &'static [u8] { - #final_prefix - } - - fn head_key() -> &'static [u8] { - #head_key - } - - fn from_optional_value_to_query(v: Option<#typ>) -> Self::Query { - #from_optional_value_to_query - } - - fn from_query_to_optional_value(v: Self::Query) -> Option<#typ> { - #from_query_to_optional_value - } - } - } - } - - pub fn double_map( - self, - hasher: TokenStream2, - k1ty: &syn::Type, - k2ty: &syn::Type, - k2_hasher: TokenStream2, - ) -> TokenStream2 { - let Self { - scrate, - visibility, - traitinstance, - traittype, - type_infos, - fielddefault, - prefix, - name, - attrs, - instance_opts, - where_clause, - .. - } = self; - - let DeclStorageTypeInfos { typ, value_type, is_option, .. } = type_infos; - - let from_optional_value_to_query = from_optional_value_to_query(is_option, fielddefault); - let from_query_to_optional_value = from_query_to_optional_value(is_option); - - let InstanceOpts { - equal_default_instance, - bound_instantiable, - instance, - .. - } = instance_opts; - - let final_prefix = if let Some(instance) = instance { - let const_name = Ident::new( - &format!("{}{}", PREFIX_FOR, name.to_string()), proc_macro2::Span::call_site() - ); - quote!{ #instance::#const_name.as_bytes() } - } else { - quote!{ #prefix.as_bytes() } - }; - - let (struct_trait, impl_trait, trait_and_instance, where_clause) = if ext::type_contains_ident( - value_type, traitinstance - ) || ext::type_contains_ident(k1ty, traitinstance) || ext::type_contains_ident(k2ty, traitinstance) - { - ( - quote!(#traitinstance: #traittype, #instance #bound_instantiable #equal_default_instance), - quote!(#traitinstance: #traittype, #instance #bound_instantiable), - quote!(#traitinstance, #instance), - where_clause.clone(), - ) - } else { - ( - quote!(#instance #bound_instantiable #equal_default_instance), - quote!(#instance #bound_instantiable), - quote!(#instance), - None, - ) - }; - - // generator for double map - quote!{ - #( #[ #attrs ] )* - #visibility struct #name<#struct_trait> ( - #scrate::rstd::marker::PhantomData<(#trait_and_instance)> - ) #where_clause; - - impl<#impl_trait> #scrate::storage::generator::StorageDoubleMap<#k1ty, #k2ty, #typ> - for #name<#trait_and_instance> #where_clause - { - type Query = #value_type; - - type Hasher1 = #scrate::#hasher; - - type Hasher2 = #scrate::#k2_hasher; - - fn key1_prefix() -> &'static [u8] { - #final_prefix - } - - fn from_optional_value_to_query(v: Option<#typ>) -> Self::Query { - #from_optional_value_to_query - } - - fn from_query_to_optional_value(v: Self::Query) -> Option<#typ> { - #from_query_to_optional_value - } - } - } - } -} diff --git a/substrate/srml/support/procedural/src/storage/instance_trait.rs b/substrate/srml/support/procedural/src/storage/instance_trait.rs new file mode 100644 index 00000000000..fe81dfe0db3 --- /dev/null +++ b/substrate/srml/support/procedural/src/storage/instance_trait.rs @@ -0,0 +1,196 @@ +// Copyright 2017-2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see <http://www.gnu.org/licenses/>. + +//! Implementation of the trait instance and the instance structures implementing it. +//! (For not instantiable traits there is still the inherent instance implemented). + +use proc_macro2::{TokenStream, Span}; +use quote::quote; +use super::{DeclStorageDefExt, StorageLineTypeDef}; + +const NUMBER_OF_INSTANCE: usize = 16; +const INHERENT_INSTANCE_NAME: &str = "__InherentHiddenInstance"; +pub(crate) const DEFAULT_INSTANTIABLE_TRAIT_NAME: &str = "__GeneratedInstantiable"; + +// prefix for consts in trait Instance +pub(crate) const PREFIX_FOR: &str = "PREFIX_FOR_"; +pub(crate) const HEAD_KEY_FOR: &str = "HEAD_KEY_FOR_"; + +// Used to generate the const: +// `const $name: &'static str = $value_prefix ++ instance_prefix ++ $value_suffix` +struct InstanceConstDef { + name: syn::Ident, + value_prefix: String, + value_suffix: String, +} + +// Used to generate an instance implementation. +struct InstanceDef { + prefix: String, + instance_struct: syn::Ident, + doc: TokenStream, +} + +pub fn decl_and_impl(scrate: &TokenStream, def: &DeclStorageDefExt) -> TokenStream { + let mut impls = TokenStream::new(); + + let mut const_defs = vec![]; + + for line in def.storage_lines.iter() { + let storage_prefix = format!("{} {}", def.crate_name, line.name); + + let const_name = syn::Ident::new( + &format!("{}{}", PREFIX_FOR, line.name.to_string()), proc_macro2::Span::call_site() + ); + const_defs.push(InstanceConstDef { + name: const_name, + value_prefix: String::new(), + value_suffix: storage_prefix.clone(), + }); + + if let StorageLineTypeDef::LinkedMap(_) = line.storage_type { + let const_name = syn::Ident::new( + &format!("{}{}", HEAD_KEY_FOR, line.name.to_string()), proc_macro2::Span::call_site() + ); + const_defs.push(InstanceConstDef { + name: const_name, + value_prefix: "head of ".into(), + value_suffix: storage_prefix, + }); + } + } + + impls.extend(create_instance_trait(&const_defs, def)); + + // Implementation of instances. + if let Some(module_instance) = &def.module_instance { + let instance_defs = (0..NUMBER_OF_INSTANCE) + .map(|i| { + let name = format!("Instance{}", i); + InstanceDef { + instance_struct: syn::Ident::new(&name, proc_macro2::Span::call_site()), + prefix: name, + doc: quote!(#[doc=r"Module instance"]), + } + }) + .chain( + module_instance.instance_default.as_ref().map(|ident| InstanceDef { + prefix: String::new(), + instance_struct: ident.clone(), + doc: quote!(#[doc=r"Default module instance"]), + }) + ); + + for instance_def in instance_defs { + impls.extend(create_and_impl_instance_struct(scrate, &instance_def, &const_defs, def)); + } + } + + // The name of the inherently available instance. + let inherent_instance = syn::Ident::new(INHERENT_INSTANCE_NAME, Span::call_site()); + + // Implementation of inherent instance. + if let Some(default_instance) = def.module_instance.as_ref() + .and_then(|i| i.instance_default.as_ref()) + { + impls.extend(quote! { + #[doc(hidden)] + pub type #inherent_instance = #default_instance; + }); + } else { + let instance_def = InstanceDef { + prefix: String::new(), + instance_struct: inherent_instance, + doc: quote!(#[doc(hidden)]), + }; + impls.extend(create_and_impl_instance_struct(scrate, &instance_def, &const_defs, def)); + } + + impls +} + +fn create_instance_trait( + const_defs: &[InstanceConstDef], + def: &DeclStorageDefExt, +) -> TokenStream { + let instance_trait = def.module_instance.as_ref().map(|i| i.instance_trait.clone()) + .unwrap_or_else(|| syn::Ident::new(DEFAULT_INSTANTIABLE_TRAIT_NAME, Span::call_site())); + + let mut const_impls = TokenStream::new(); + for const_def in const_defs { + let const_name = &const_def.name; + const_impls.extend(quote! { + const #const_name: &'static str; + }); + } + + let optional_hide = if def.module_instance.is_some() { + quote!() + } else { + quote!(#[doc(hidden)]) + }; + + quote! { + /// Tag a type as an instance of a module. + /// + /// Defines storage prefixes, they must be unique. + #optional_hide + pub trait #instance_trait: 'static { + /// The prefix used by any storage entry of an instance. + const PREFIX: &'static str; + #const_impls + } + } +} + +fn create_and_impl_instance_struct( + scrate: &TokenStream, + instance_def: &InstanceDef, + const_defs: &[InstanceConstDef], + def: &DeclStorageDefExt, +) -> TokenStream { + let mut const_impls = TokenStream::new(); + + for const_def in const_defs { + let const_value = format!( + "{}{}{}", const_def.value_prefix, instance_def.prefix, const_def.value_suffix + ); + let const_name = &const_def.name; + + const_impls.extend(quote! { + const #const_name: &'static str = #const_value; + }); + } + + let instance_trait = def.module_instance.as_ref().map(|i| i.instance_trait.clone()) + .unwrap_or_else(|| syn::Ident::new(DEFAULT_INSTANTIABLE_TRAIT_NAME, Span::call_site())); + + let instance_struct = &instance_def.instance_struct; + let prefix = format!("{}{}", instance_def.prefix, def.crate_name.to_string()); + let doc = &instance_def.doc; + + quote! { + // Those trait are derived because of wrong bounds for generics + #[cfg_attr(feature = "std", derive(Debug))] + #[derive(Clone, Eq, PartialEq, #scrate::codec::Encode, #scrate::codec::Decode)] + #doc + pub struct #instance_struct; + impl #instance_trait for #instance_struct { + const PREFIX: &'static str = #prefix; + #const_impls + } + } +} diff --git a/substrate/srml/support/procedural/src/storage/metadata.rs b/substrate/srml/support/procedural/src/storage/metadata.rs new file mode 100644 index 00000000000..e280c7d8a20 --- /dev/null +++ b/substrate/srml/support/procedural/src/storage/metadata.rs @@ -0,0 +1,230 @@ +// Copyright 2017-2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see <http://www.gnu.org/licenses/>. + +//! Implementation of `storage_metadata` on module structure, used by construct_runtime. + +use srml_support_procedural_tools::clean_type_string; +use proc_macro2::TokenStream; +use quote::quote; +use super::{DeclStorageDefExt, StorageLineDefExt, StorageLineTypeDef}; + +fn storage_line_metadata_type(scrate: &TokenStream, line: &StorageLineDefExt) -> TokenStream { + let value_type = &line.value_type; + let value_type = clean_type_string("e!( #value_type ).to_string()); + match &line.storage_type { + StorageLineTypeDef::Simple(_) => { + quote!{ + #scrate::metadata::StorageEntryType::Plain( + #scrate::metadata::DecodeDifferent::Encode(#value_type), + ) + } + }, + StorageLineTypeDef::Map(map) => { + let hasher = map.hasher.into_metadata(); + let key = &map.key; + let key = clean_type_string("e!(#key).to_string()); + quote!{ + #scrate::metadata::StorageEntryType::Map { + hasher: #scrate::metadata::#hasher, + key: #scrate::metadata::DecodeDifferent::Encode(#key), + value: #scrate::metadata::DecodeDifferent::Encode(#value_type), + is_linked: false, + } + } + }, + StorageLineTypeDef::LinkedMap(map) => { + let hasher = map.hasher.into_metadata(); + let key = &map.key; + let key = clean_type_string("e!(#key).to_string()); + quote!{ + #scrate::metadata::StorageEntryType::Map { + hasher: #scrate::metadata::#hasher, + key: #scrate::metadata::DecodeDifferent::Encode(#key), + value: #scrate::metadata::DecodeDifferent::Encode(#value_type), + is_linked: true, + } + } + }, + StorageLineTypeDef::DoubleMap(map) => { + let hasher1 = map.hasher1.into_metadata(); + let hasher2 = map.hasher2.into_metadata(); + let key1 = &map.key1; + let key1 = clean_type_string("e!(#key1).to_string()); + let key2 = &map.key2; + let key2 = clean_type_string("e!(#key2).to_string()); + quote!{ + #scrate::metadata::StorageEntryType::DoubleMap { + hasher: #scrate::metadata::#hasher1, + key1: #scrate::metadata::DecodeDifferent::Encode(#key1), + key2: #scrate::metadata::DecodeDifferent::Encode(#key2), + value: #scrate::metadata::DecodeDifferent::Encode(#value_type), + key2_hasher: #scrate::metadata::#hasher2, + } + } + }, + } +} + +fn default_byte_getter( + scrate: &TokenStream, + line: &StorageLineDefExt, + def: &DeclStorageDefExt, +) -> (TokenStream, TokenStream) { + let default = line.default_value.as_ref().map(|d| quote!( #d )) + .unwrap_or_else(|| quote!( Default::default() )); + + let str_name = line.name.to_string(); + let struct_name = syn::Ident::new(&("__GetByteStruct".to_string() + &str_name), line.name.span()); + let cache_name = syn::Ident::new(&("__CACHE_GET_BYTE_STRUCT_".to_string() + &str_name), line.name.span()); + + let runtime_generic = &def.module_runtime_generic; + let runtime_trait = &def.module_runtime_trait; + let optional_instance_bound_optional_default = &def.optional_instance_bound_optional_default; + let optional_instance_bound = &def.optional_instance_bound; + let optional_instance = &def.optional_instance; + let optional_comma_instance = optional_instance.as_ref().map(|i| quote!(, #i)); + let where_clause = &def.where_clause; + + let query_type = &line.query_type; + + let struct_def = quote! { + #[doc(hidden)] + pub struct #struct_name< + #runtime_generic, #optional_instance_bound_optional_default + >(pub #scrate::rstd::marker::PhantomData<(#runtime_generic #optional_comma_instance)>); + + #[cfg(feature = "std")] + #[allow(non_upper_case_globals)] + static #cache_name: #scrate::once_cell::sync::OnceCell<#scrate::rstd::vec::Vec<u8>> = + #scrate::once_cell::sync::OnceCell::new(); + + #[cfg(feature = "std")] + impl<#runtime_generic: #runtime_trait, #optional_instance_bound> + #scrate::metadata::DefaultByte + for #struct_name<#runtime_generic, #optional_instance> + #where_clause + { + fn default_byte(&self) -> #scrate::rstd::vec::Vec<u8> { + use #scrate::codec::Encode; + #cache_name.get_or_init(|| { + let def_val: #query_type = #default; + <#query_type as Encode>::encode(&def_val) + }).clone() + } + } + + unsafe impl<#runtime_generic: #runtime_trait, #optional_instance_bound> Send + for #struct_name<#runtime_generic, #optional_instance> #where_clause {} + + unsafe impl<#runtime_generic: #runtime_trait, #optional_instance_bound> Sync + for #struct_name<#runtime_generic, #optional_instance> #where_clause {} + + #[cfg(not(feature = "std"))] + impl<#runtime_generic: #runtime_trait, #optional_instance_bound> + #scrate::metadata::DefaultByte + for #struct_name<#runtime_generic, #optional_instance> + #where_clause + { + fn default_byte(&self) -> #scrate::rstd::vec::Vec<u8> { + use #scrate::codec::Encode; + let def_val: #query_type = #default; + <#query_type as Encode>::encode(&def_val) + } + } + }; + let struct_instance = quote!( + #struct_name::<#runtime_generic, #optional_instance>(#scrate::rstd::marker::PhantomData) + ); + + (struct_def, struct_instance) +} + +pub fn impl_metadata(scrate: &TokenStream, def: &DeclStorageDefExt) -> TokenStream { + let mut entries = TokenStream::new(); + let mut default_byte_getter_struct_defs = TokenStream::new(); + + for line in def.storage_lines.iter() { + let str_name = line.name.to_string(); + + let modifier = if line.is_option { + quote!(#scrate::metadata::StorageEntryModifier::Optional) + } else { + quote!(#scrate::metadata::StorageEntryModifier::Default) + }; + + let ty = storage_line_metadata_type(scrate, line); + + let ( + default_byte_getter_struct_def, + default_byte_getter_struct_instance, + ) = default_byte_getter(scrate, line, def); + + let mut docs = TokenStream::new(); + for attr in line.attrs.iter().filter_map(|v| v.parse_meta().ok()) { + if let syn::Meta::NameValue(meta) = attr { + if meta.ident == "doc" { + let lit = meta.lit; + docs.extend(quote!(#lit,)); + } + } + } + + let entry = quote! { + #scrate::metadata::StorageEntryMetadata { + name: #scrate::metadata::DecodeDifferent::Encode(#str_name), + modifier: #modifier, + ty: #ty, + default: #scrate::metadata::DecodeDifferent::Encode( + #scrate::metadata::DefaultByteGetter(&#default_byte_getter_struct_instance) + ), + documentation: #scrate::metadata::DecodeDifferent::Encode(&[ #docs ]), + }, + }; + + default_byte_getter_struct_defs.extend(default_byte_getter_struct_def); + entries.extend(entry); + } + + let prefix = if let Some(instance) = &def.module_instance { + let instance_generic = &instance.instance_generic; + quote!(#instance_generic::PREFIX) + } else { + let prefix = def.crate_name.to_string(); + quote!(#prefix) + }; + + let store_metadata = quote!( + #scrate::metadata::StorageMetadata { + prefix: #scrate::metadata::DecodeDifferent::Encode(#prefix), + entries: #scrate::metadata::DecodeDifferent::Encode(&[ #entries ][..]), + } + ); + + let module_struct = &def.module_struct; + let module_impl = &def.module_impl; + let where_clause = &def.where_clause; + + quote!( + #default_byte_getter_struct_defs + + impl#module_impl #module_struct #where_clause { + #[doc(hidden)] + pub fn storage_metadata() -> #scrate::metadata::StorageMetadata { + #store_metadata + } + } + ) +} diff --git a/substrate/srml/support/procedural/src/storage/mod.rs b/substrate/srml/support/procedural/src/storage/mod.rs index 4253206f44d..9a6931d87e9 100644 --- a/substrate/srml/support/procedural/src/storage/mod.rs +++ b/substrate/srml/support/procedural/src/storage/mod.rs @@ -14,191 +14,358 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see <http://www.gnu.org/licenses/>. -// tag::description[] -//! `decl_storage` macro -// end::description[] +//! `decl_storage` input definition and expansion. + +mod storage_struct; +mod parse; +mod store_trait; +mod getters; +mod metadata; +mod instance_trait; +mod genesis_config; -use srml_support_procedural_tools::{ToTokens, Parse, syn_ext as ext}; -use syn::{Ident, Token}; -use proc_macro2::TokenStream as TokenStream2; use quote::quote; +use srml_support_procedural_tools::{ + generate_crate_access, generate_hidden_includes, syn_ext as ext +}; -mod impls; - -pub mod transformation; - -mod keyword { - syn::custom_keyword!(hiddencrate); - syn::custom_keyword!(add_extra_genesis); - syn::custom_keyword!(extra_genesis_skip_phantom_data_field); - syn::custom_keyword!(config); - syn::custom_keyword!(build); - syn::custom_keyword!(get); - syn::custom_keyword!(map); - syn::custom_keyword!(linked_map); - syn::custom_keyword!(double_map); - syn::custom_keyword!(blake2_256); - syn::custom_keyword!(blake2_128); - syn::custom_keyword!(twox_256); - syn::custom_keyword!(twox_128); - syn::custom_keyword!(twox_64_concat); - syn::custom_keyword!(hasher); +/// All informations contained in input of decl_storage +pub struct DeclStorageDef { + /// Name of the module used to import hidden imports. + hidden_crate: Option<syn::Ident>, + /// Visibility of store trait. + visibility: syn::Visibility, + /// Name of store trait: usually `Store`. + store_trait: syn::Ident, + /// Module name used by construct_runtime: usually `Module`. + module_name: syn::Ident, + /// Usually `T`. + module_runtime_generic: syn::Ident, + /// Usually `Trait` + module_runtime_trait: syn::Path, + /// For instantiable module: usually `I: Instance=DefaultInstance`. + module_instance: Option<ModuleInstanceDef>, + /// Where claused used to constrain T and I even more. + where_clause: Option<syn::WhereClause>, + /// The extra build function used to build storage at genesis. + extra_genesis_build: Option<syn::Expr>, + /// The extra genesis config fields. + extra_genesis_config_lines: Vec<ExtraGenesisLineDef>, + /// Definition of storages. + storage_lines: Vec<StorageLineDef>, + /// Name of the crate, used for storage prefixes. + crate_name: syn::Ident, } -/// Parsing usage only -#[derive(Parse, ToTokens, Debug)] -struct StorageDefinition { - pub hidden_crate: ext::Opt<SpecificHiddenCrate>, - pub visibility: syn::Visibility, - pub trait_token: Token![trait], - pub ident: Ident, - pub for_token: Token![for], - pub module_ident: Ident, - pub mod_lt_token: Token![<], - pub mod_param: syn::GenericParam, - pub mod_instance_param_token: Option<Token![,]>, - pub mod_instance: Option<syn::Ident>, - pub mod_instantiable_token: Option<Token![:]>, - pub mod_instantiable: Option<syn::Ident>, - pub mod_default_instance_token: Option<Token![=]>, - pub mod_default_instance: Option<syn::Ident>, - pub mod_gt_token: Token![>], - pub as_token: Token![as], - pub crate_ident: Ident, - pub where_clause: Option<syn::WhereClause>, - pub content: ext::Braces<ext::Punctuated<DeclStorageLine, Token![;]>>, - pub extra_genesis: ext::Opt<AddExtraGenesis>, +impl syn::parse::Parse for DeclStorageDef { + fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { + parse::parse(input) + } } -#[derive(Parse, ToTokens, Debug)] -struct SpecificHiddenCrate { - pub keyword: keyword::hiddencrate, - pub ident: ext::Parens<Ident>, +/// Extended version of `DeclStorageDef` with useful precomputed value. +pub struct DeclStorageDefExt { + /// Name of the module used to import hidden imports. + hidden_crate: Option<syn::Ident>, + /// Visibility of store trait. + visibility: syn::Visibility, + /// Name of store trait: usually `Store`. + store_trait: syn::Ident, + /// Module name used by construct_runtime: usually `Module`. + #[allow(unused)] + module_name: syn::Ident, + /// Usually `T`. + module_runtime_generic: syn::Ident, + /// Usually `Trait`. + module_runtime_trait: syn::Path, + /// For instantiable module: usually `I: Instance=DefaultInstance`. + module_instance: Option<ModuleInstanceDef>, + /// Where claused used to constrain T and I even more. + where_clause: Option<syn::WhereClause>, + /// The extra build function used to build storage at genesis. + extra_genesis_build: Option<syn::Expr>, + /// The extra genesis config fields. + extra_genesis_config_lines: Vec<ExtraGenesisLineDef>, + /// Definition of storages. + storage_lines: Vec<StorageLineDefExt>, + /// Name of the crate, used for storage prefixes. + crate_name: syn::Ident, + /// Full struct expansion: `Module<T, I>`. + module_struct: proc_macro2::TokenStream, + /// Impl block for module: `<T: Trait, I: Instance>`. + module_impl: proc_macro2::TokenStream, + /// For instantiable: `I`. + optional_instance: Option<proc_macro2::TokenStream>, + /// For instantiable: `I: Instance`. + optional_instance_bound: Option<proc_macro2::TokenStream>, + /// For instantiable: `I: Instance = DefaultInstance`. + optional_instance_bound_optional_default: Option<proc_macro2::TokenStream>, } -#[derive(Parse, ToTokens, Debug)] -struct AddExtraGenesis { - pub extragenesis_keyword: keyword::add_extra_genesis, - pub content: ext::Braces<AddExtraGenesisContent>, -} +impl From<DeclStorageDef> for DeclStorageDefExt { + fn from(mut def: DeclStorageDef) -> Self { + let storage_lines = def.storage_lines.drain(..).collect::<Vec<_>>(); + let storage_lines = storage_lines.into_iter() + .map(|line| StorageLineDefExt::from_def(line, &def)) + .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; -#[derive(Parse, ToTokens, Debug)] -struct AddExtraGenesisContent { - pub lines: ext::Punctuated<AddExtraGenesisLineEnum, Token![;]>, + 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: def.hidden_crate, + 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, + } + } } -#[derive(Parse, ToTokens, Debug)] -enum AddExtraGenesisLineEnum { - AddExtraGenesisLine(AddExtraGenesisLine), - AddExtraGenesisBuild(DeclStorageBuild), +/// Usually `I: Instance=DefaultInstance`. +pub struct ModuleInstanceDef { + /// Usually: `I`. + instance_generic: syn::Ident, + /// Usually: `Instance`. + instance_trait: syn::Ident, + /// Usually: `DefaultInstance`. + instance_default: Option<syn::Ident>, } -#[derive(Parse, ToTokens, Debug)] -struct AddExtraGenesisLine { - pub attrs: ext::OuterAttributes, - pub config_keyword: keyword::config, - pub extra_field: ext::Parens<Ident>, - pub coldot_token: Token![:], - pub extra_type: syn::Type, - pub default_value: ext::Opt<DeclStorageDefault>, +pub struct StorageLineDef { + attrs: Vec<syn::Attribute>, + /// Visibility of the storage struct. + visibility: syn::Visibility, + name: syn::Ident, + /// The name of getter function to be implemented on Module struct. + getter: Option<syn::Ident>, + /// The name of the field to be used in genesis config if any. + config: Option<syn::Ident>, + /// The build function of the storage if any. + build: Option<syn::Expr>, + /// Default value of genesis config field and also for storage when no value available. + default_value: Option<syn::Expr>, + storage_type: StorageLineTypeDef, } -#[derive(Parse, ToTokens, Debug)] -struct DeclStorageLine { - // attrs (main use case is doc) - pub attrs: ext::OuterAttributes, - // visibility (no need to make optional - pub visibility: syn::Visibility, - // name - pub name: Ident, - pub getter: ext::Opt<DeclStorageGetter>, - pub config: ext::Opt<DeclStorageConfig>, - pub build: ext::Opt<DeclStorageBuild>, - pub coldot_token: Token![:], - pub storage_type: DeclStorageType, - pub default_value: ext::Opt<DeclStorageDefault>, +pub struct StorageLineDefExt { + #[allow(unused)] + attrs: Vec<syn::Attribute>, + /// Visibility of the storage struct. + visibility: syn::Visibility, + name: syn::Ident, + /// The name of getter function to be implemented on Module struct. + getter: Option<syn::Ident>, + /// The name of the field to be used in genesis config if any. + config: Option<syn::Ident>, + /// The build function of the storage if any. + build: Option<syn::Expr>, + /// Default value of genesis config field and also for storage when no value available. + default_value: Option<syn::Expr>, + storage_type: StorageLineTypeDef, + doc_attrs: Vec<syn::Meta>, + /// Either the type stored in storage or wrapped in an Option. + query_type: syn::Type, + /// The type stored in storage. + value_type: syn::Type, + /// Full struct, for example: `StorageName<T, I>`. + storage_struct: proc_macro2::TokenStream, + /// If storage is generic over runtime then `T`. + optional_storage_runtime_comma: Option<proc_macro2::TokenStream>, + /// If storage is generic over runtime then `T: Trait`. + optional_storage_runtime_bound_comma: Option<proc_macro2::TokenStream>, + /// The where clause to use to constrain generics if storage is generic over runtime. + optional_storage_where_clause: Option<proc_macro2::TokenStream>, + /// Full trait, for example: `storage::StorageMap<u32, u32>`. + storage_trait: proc_macro2::TokenStream, + /// Full trait, for example: `storage::generator::StorageMap<u32, u32>`. + storage_generator_trait: proc_macro2::TokenStream, + /// Weither the storage is generic. + is_generic: bool, + /// Weither the storage value is an option. + is_option: bool, } +impl StorageLineDefExt { + fn from_def(storage_def: StorageLineDef, def: &DeclStorageDef) -> 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::LinkedMap(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) + } + }; -#[derive(Parse, ToTokens, Debug)] -struct DeclStorageGetter { - pub getter_keyword: keyword::get, - pub getfn: ext::Parens<Ident>, -} + let query_type = match &storage_def.storage_type { + StorageLineTypeDef::Simple(value) => value.clone(), + StorageLineTypeDef::Map(map) => map.value.clone(), + StorageLineTypeDef::LinkedMap(map) => map.value.clone(), + StorageLineTypeDef::DoubleMap(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(query_type.clone()); -#[derive(Parse, ToTokens, Debug)] -struct DeclStorageConfig { - pub config_keyword: keyword::config, - pub expr: ext::Parens<Option<syn::Ident>>, -} + 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 + }; -#[derive(Parse, ToTokens, Debug)] -struct DeclStorageBuild { - pub build_keyword: keyword::build, - pub expr: ext::Parens<syn::Expr>, -} + 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> + ); -#[derive(Parse, ToTokens, Debug)] -enum DeclStorageType { - Map(DeclStorageMap), - LinkedMap(DeclStorageLinkedMap), - DoubleMap(DeclStorageDoubleMap), - Simple(syn::Type), + let optional_storage_where_clause = if is_generic { + def.where_clause.as_ref().map(|w| quote!( #w )) + } else { + None + }; + + let storage_trait_trunkated = match &storage_def.storage_type { + StorageLineTypeDef::Simple(_) => { + quote!( StorageValue<#value_type> ) + }, + StorageLineTypeDef::Map(map) => { + let key = &map.key; + quote!( StorageMap<#key, #value_type> ) + }, + StorageLineTypeDef::LinkedMap(map) => { + let key = &map.key; + quote!( StorageLinkedMap<#key, #value_type> ) + }, + StorageLineTypeDef::DoubleMap(map) => { + let key1 = &map.key1; + let key2 = &map.key2; + quote!( StorageDoubleMap<#key1, #key2, #value_type> ) + }, + }; + + let storage_trait = quote!( storage::#storage_trait_trunkated ); + let storage_generator_trait = quote!( storage::generator::#storage_trait_trunkated ); + + let doc_attrs = storage_def.attrs.iter() + .filter_map(|a| a.parse_meta().ok()) + .filter(|m| m.name() == "doc") + .collect(); + + Self { + attrs: storage_def.attrs, + visibility: storage_def.visibility, + name: storage_def.name, + getter: storage_def.getter, + config: storage_def.config, + 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, + } + } } -#[derive(Parse, ToTokens, Debug)] -struct DeclStorageMap { - pub map_keyword: keyword::map, - pub hasher: ext::Opt<SetHasher>, - pub key: syn::Type, - pub ass_keyword: Token![=>], - pub value: syn::Type, +pub enum StorageLineTypeDef { + Map(MapDef), + LinkedMap(MapDef), + DoubleMap(DoubleMapDef), + Simple(syn::Type), } -#[derive(Parse, ToTokens, Debug)] -struct DeclStorageLinkedMap { - pub map_keyword: keyword::linked_map, - pub hasher: ext::Opt<SetHasher>, +pub struct MapDef { + pub hasher: HasherKind, pub key: syn::Type, - pub ass_keyword: Token![=>], + /// This is the query value not the inner value used in storage trait implementation. pub value: syn::Type, } -#[derive(Parse, ToTokens, Debug)] -struct DeclStorageDoubleMap { - pub map_keyword: keyword::double_map, - pub hasher: ext::Opt<SetHasher>, +pub struct DoubleMapDef { + pub hasher1: HasherKind, + pub hasher2: HasherKind, pub key1: syn::Type, - pub comma_keyword: Token![,], - pub key2_hasher: Hasher, - pub key2: ext::Parens<syn::Type>, - pub ass_keyword: Token![=>], + pub key2: syn::Type, + /// This is the query value not the inner value used in storage trait implementation. pub value: syn::Type, } -#[derive(Parse, ToTokens, Debug)] -enum Hasher { - Blake2_256(keyword::blake2_256), - Blake2_128(keyword::blake2_128), - Twox256(keyword::twox_256), - Twox128(keyword::twox_128), - Twox64Concat(keyword::twox_64_concat), -} - -#[derive(Parse, ToTokens, Debug)] -struct DeclStorageDefault { - pub equal_token: Token![=], - pub expr: syn::Expr, -} - -#[derive(Parse, ToTokens, Debug)] -struct SetHasher { - pub hasher_keyword: keyword::hasher, - pub inner: ext::Parens<Hasher>, +pub struct ExtraGenesisLineDef { + attrs: Vec<syn::Attribute>, + name: syn::Ident, + typ: syn::Type, + default: Option<syn::Expr>, } #[derive(Debug, Clone)] -enum HasherKind { +pub enum HasherKind { Blake2_256, Blake2_128, Twox256, @@ -206,26 +373,8 @@ enum HasherKind { Twox64Concat, } -impl From<&SetHasher> for HasherKind { - fn from(set_hasher: &SetHasher) -> Self { - (&set_hasher.inner.content).into() - } -} - -impl From<&Hasher> for HasherKind { - fn from(hasher: &Hasher) -> Self { - match hasher { - Hasher::Blake2_256(_) => HasherKind::Blake2_256, - Hasher::Blake2_128(_) => HasherKind::Blake2_128, - Hasher::Twox256(_) => HasherKind::Twox256, - Hasher::Twox128(_) => HasherKind::Twox128, - Hasher::Twox64Concat(_) => HasherKind::Twox64Concat, - } - } -} - impl HasherKind { - fn into_storage_hasher_struct(&self) -> TokenStream2 { + fn to_storage_hasher_struct(&self) -> proc_macro2::TokenStream { match self { HasherKind::Blake2_256 => quote!( Blake2_256 ), HasherKind::Blake2_128 => quote!( Blake2_128 ), @@ -235,7 +384,7 @@ impl HasherKind { } } - fn into_metadata(&self) -> TokenStream2 { + fn into_metadata(&self) -> proc_macro2::TokenStream { match self { HasherKind::Blake2_256 => quote!( StorageHasher::Blake2_256 ), HasherKind::Blake2_128 => quote!( StorageHasher::Blake2_128 ), @@ -245,3 +394,39 @@ impl HasherKind { } } } + +/// Full implementation of decl_storage. +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); + + let hidden_crate_name = def_ext.hidden_crate.as_ref().map(|i| i.to_string()) + .unwrap_or_else(|| "decl_storage".to_string()); + + let scrate = generate_crate_access(&hidden_crate_name, "srml-support"); + let scrate_decl = generate_hidden_includes(&hidden_crate_name, "srml-support"); + + let store_trait = store_trait::decl_and_impl(&def_ext); + let getters = getters::impl_getters(&scrate, &def_ext); + let metadata = metadata::impl_metadata(&scrate, &def_ext); + let instance_trait = instance_trait::decl_and_impl(&scrate, &def_ext); + let genesis_config = genesis_config::genesis_config_and_build_storage(&scrate, &def_ext); + let storage_struct = storage_struct::decl_and_impl(&scrate, &def_ext); + + quote!( + use #scrate::{ + StorageValue as _, + StorageMap as _, + StorageLinkedMap as _, + StorageDoubleMap as _ + }; + + #scrate_decl + #store_trait + #getters + #metadata + #instance_trait + #genesis_config + #storage_struct + ).into() +} diff --git a/substrate/srml/support/procedural/src/storage/parse.rs b/substrate/srml/support/procedural/src/storage/parse.rs new file mode 100644 index 00000000000..8464077ca26 --- /dev/null +++ b/substrate/srml/support/procedural/src/storage/parse.rs @@ -0,0 +1,377 @@ +// Copyright 2017-2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see <http://www.gnu.org/licenses/>. + +//! Parsing of decl_storage input. + +use srml_support_procedural_tools::{ToTokens, Parse, syn_ext as ext}; +use syn::{Ident, Token}; + +mod keyword { + syn::custom_keyword!(hiddencrate); + syn::custom_keyword!(add_extra_genesis); + syn::custom_keyword!(extra_genesis_skip_phantom_data_field); + syn::custom_keyword!(config); + syn::custom_keyword!(build); + syn::custom_keyword!(get); + syn::custom_keyword!(map); + syn::custom_keyword!(linked_map); + syn::custom_keyword!(double_map); + syn::custom_keyword!(blake2_256); + syn::custom_keyword!(blake2_128); + syn::custom_keyword!(twox_256); + syn::custom_keyword!(twox_128); + syn::custom_keyword!(twox_64_concat); + syn::custom_keyword!(hasher); +} + +/// Parsing usage only +#[derive(Parse, ToTokens, Debug)] +struct StorageDefinition { + pub hidden_crate: ext::Opt<SpecificHiddenCrate>, + pub visibility: syn::Visibility, + pub trait_token: Token![trait], + pub ident: Ident, + pub for_token: Token![for], + pub module_ident: Ident, + pub mod_lt_token: Token![<], + pub mod_param_generic: syn::Ident, + pub mod_param_bound_token: Option<Token![:]>, + pub mod_param_bound: syn::Path, + pub mod_instance_param_token: Option<Token![,]>, + pub mod_instance: Option<syn::Ident>, + pub mod_instantiable_token: Option<Token![:]>, + pub mod_instantiable: Option<syn::Ident>, + pub mod_default_instance_token: Option<Token![=]>, + pub mod_default_instance: Option<syn::Ident>, + pub mod_gt_token: Token![>], + pub as_token: Token![as], + pub crate_ident: Ident, + pub where_clause: Option<syn::WhereClause>, + pub content: ext::Braces<ext::Punctuated<DeclStorageLine, Token![;]>>, + pub extra_genesis: ext::Opt<AddExtraGenesis>, +} + +#[derive(Parse, ToTokens, Debug)] +struct SpecificHiddenCrate { + pub keyword: keyword::hiddencrate, + pub ident: ext::Parens<Ident>, +} + +#[derive(Parse, ToTokens, Debug)] +struct AddExtraGenesis { + pub extragenesis_keyword: keyword::add_extra_genesis, + pub content: ext::Braces<AddExtraGenesisContent>, +} + +#[derive(Parse, ToTokens, Debug)] +struct AddExtraGenesisContent { + pub lines: ext::Punctuated<AddExtraGenesisLineEnum, Token![;]>, +} + +#[derive(Parse, ToTokens, Debug)] +enum AddExtraGenesisLineEnum { + AddExtraGenesisLine(AddExtraGenesisLine), + AddExtraGenesisBuild(DeclStorageBuild), +} + +#[derive(Parse, ToTokens, Debug)] +struct AddExtraGenesisLine { + pub attrs: ext::OuterAttributes, + pub config_keyword: keyword::config, + pub extra_field: ext::Parens<Ident>, + pub coldot_token: Token![:], + pub extra_type: syn::Type, + pub default_value: ext::Opt<DeclStorageDefault>, +} + +#[derive(Parse, ToTokens, Debug)] +struct DeclStorageLine { + // attrs (main use case is doc) + pub attrs: ext::OuterAttributes, + // visibility (no need to make optional + pub visibility: syn::Visibility, + // name + pub name: Ident, + pub getter: ext::Opt<DeclStorageGetter>, + pub config: ext::Opt<DeclStorageConfig>, + pub build: ext::Opt<DeclStorageBuild>, + pub coldot_token: Token![:], + pub storage_type: DeclStorageType, + pub default_value: ext::Opt<DeclStorageDefault>, +} + + +#[derive(Parse, ToTokens, Debug)] +struct DeclStorageGetter { + pub getter_keyword: keyword::get, + pub getfn: ext::Parens<Ident>, +} + +#[derive(Parse, ToTokens, Debug)] +struct DeclStorageConfig { + pub config_keyword: keyword::config, + pub expr: ext::Parens<Option<syn::Ident>>, +} + +#[derive(Parse, ToTokens, Debug)] +struct DeclStorageBuild { + pub build_keyword: keyword::build, + pub expr: ext::Parens<syn::Expr>, +} + +#[derive(Parse, ToTokens, Debug)] +enum DeclStorageType { + Map(DeclStorageMap), + LinkedMap(DeclStorageLinkedMap), + DoubleMap(DeclStorageDoubleMap), + Simple(syn::Type), +} + +#[derive(Parse, ToTokens, Debug)] +struct DeclStorageMap { + pub map_keyword: keyword::map, + pub hasher: ext::Opt<SetHasher>, + pub key: syn::Type, + pub ass_keyword: Token![=>], + pub value: syn::Type, +} + +#[derive(Parse, ToTokens, Debug)] +struct DeclStorageLinkedMap { + pub map_keyword: keyword::linked_map, + pub hasher: ext::Opt<SetHasher>, + pub key: syn::Type, + pub ass_keyword: Token![=>], + pub value: syn::Type, +} + +#[derive(Parse, ToTokens, Debug)] +struct DeclStorageDoubleMap { + pub map_keyword: keyword::double_map, + pub hasher: ext::Opt<SetHasher>, + pub key1: syn::Type, + pub comma_keyword: Token![,], + pub key2_hasher: Hasher, + pub key2: ext::Parens<syn::Type>, + pub ass_keyword: Token![=>], + pub value: syn::Type, +} + +#[derive(Parse, ToTokens, Debug)] +enum Hasher { + Blake2_256(keyword::blake2_256), + Blake2_128(keyword::blake2_128), + Twox256(keyword::twox_256), + Twox128(keyword::twox_128), + Twox64Concat(keyword::twox_64_concat), +} + +#[derive(Parse, ToTokens, Debug)] +struct DeclStorageDefault { + pub equal_token: Token![=], + pub expr: syn::Expr, +} + +#[derive(Parse, ToTokens, Debug)] +struct SetHasher { + pub hasher_keyword: keyword::hasher, + pub inner: ext::Parens<Hasher>, +} + +impl From<SetHasher> for super::HasherKind { + fn from(set_hasher: SetHasher) -> Self { + set_hasher.inner.content.into() + } +} + +impl From<Hasher> for super::HasherKind { + fn from(hasher: Hasher) -> Self { + match hasher { + Hasher::Blake2_256(_) => super::HasherKind::Blake2_256, + Hasher::Blake2_128(_) => super::HasherKind::Blake2_128, + Hasher::Twox256(_) => super::HasherKind::Twox256, + Hasher::Twox128(_) => super::HasherKind::Twox128, + Hasher::Twox64Concat(_) => super::HasherKind::Twox64Concat, + } + } +} + +fn get_module_instance( + instance: Option<syn::Ident>, + instantiable: Option<syn::Ident>, + default_instance: Option<syn::Ident>, +) -> syn::Result<Option<super::ModuleInstanceDef>> { + let right_syntax = "Should be $Instance: $Instantiable = $DefaultInstance"; + + match (instance, instantiable, default_instance) { + (Some(instance), Some(instantiable), default_instance) => { + Ok(Some(super::ModuleInstanceDef { + instance_generic: instance, + instance_trait: instantiable, + instance_default: default_instance, + })) + }, + (None, None, None) => Ok(None), + (Some(instance), None, _) => Err( + syn::Error::new( + instance.span(), + format!( + "Expect instantiable trait bound for instance: {}. {}", + instance, + right_syntax, + ) + ) + ), + (None, Some(instantiable), _) => Err( + syn::Error::new( + instantiable.span(), + format!( + "Expect instance generic for bound instantiable: {}. {}", + instantiable, + right_syntax, + ) + ) + ), + (None, _, Some(default_instance)) => Err( + syn::Error::new( + default_instance.span(), + format!( + "Expect instance generic for default instance: {}. {}", + default_instance, + right_syntax, + ) + ) + ), + } +} + +pub fn parse(input: syn::parse::ParseStream) -> syn::Result<super::DeclStorageDef> { + use syn::parse::Parse; + use syn::spanned::Spanned; + + let def = StorageDefinition::parse(input)?; + + let module_instance = get_module_instance( + def.mod_instance, + def.mod_instantiable, + def.mod_default_instance, + )?; + + let mut extra_genesis_config_lines = vec![]; + let mut extra_genesis_build = None; + + for line in def.extra_genesis.inner.into_iter() + .flat_map(|o| o.content.content.lines.inner.into_iter()) + { + match line { + AddExtraGenesisLineEnum::AddExtraGenesisLine(def) => { + extra_genesis_config_lines.push(super::ExtraGenesisLineDef{ + attrs: def.attrs.inner, + name: def.extra_field.content, + typ: def.extra_type, + default: def.default_value.inner.map(|o| o.expr), + }); + } + AddExtraGenesisLineEnum::AddExtraGenesisBuild(def) => { + if extra_genesis_build.is_some() { + return Err(syn::Error::new( + def.span(), + "Only one build expression allowed for extra genesis" + )) + } + + extra_genesis_build = Some(def.expr.content); + } + } + } + + let mut storage_lines = vec![]; + + for line in def.content.content.inner.into_iter() { + let getter = line.getter.inner.map(|o| o.getfn.content); + let config = if let Some(config) = line.config.inner { + if let Some(ident) = config.expr.content { + Some(ident) + } else if let Some(ident) = getter.clone() { + Some(ident) + } else { + return Err(syn::Error::new( + config.span(), + "Invalid storage definiton, couldn't find config identifier: storage must either have a get \ + identifier `get(ident)` or a defined config identifier `config(ident)`" + )) + } + } else { + None + }; + + let storage_type = match line.storage_type { + DeclStorageType::Map(map) => super::StorageLineTypeDef::Map( + super::MapDef { + hasher: map.hasher.inner.map(Into::into) + .unwrap_or(super::HasherKind::Blake2_256), + key: map.key, + value: map.value, + } + ), + DeclStorageType::LinkedMap(map) => super::StorageLineTypeDef::LinkedMap( + super::MapDef { + hasher: map.hasher.inner.map(Into::into) + .unwrap_or(super::HasherKind::Blake2_256), + key: map.key, + value: map.value, + } + ), + DeclStorageType::DoubleMap(map) => super::StorageLineTypeDef::DoubleMap( + super::DoubleMapDef { + hasher1: map.hasher.inner.map(Into::into) + .unwrap_or(super::HasherKind::Blake2_256), + hasher2: map.key2_hasher.into(), + key1: map.key1, + key2: map.key2.content, + value: map.value, + } + ), + DeclStorageType::Simple(expr) => super::StorageLineTypeDef::Simple(expr), + }; + + storage_lines.push(super::StorageLineDef { + attrs: line.attrs.inner, + visibility: line.visibility, + name: line.name, + getter, + config, + build: line.build.inner.map(|o| o.expr.content), + default_value: line.default_value.inner.map(|o| o.expr), + storage_type, + }) + } + + Ok(super::DeclStorageDef { + hidden_crate: def.hidden_crate.inner.map(|i| i.ident.content), + visibility: def.visibility, + module_name: def.module_ident, + store_trait: def.ident, + module_runtime_generic: def.mod_param_generic, + module_runtime_trait: def.mod_param_bound, + where_clause: def.where_clause, + crate_name: def.crate_ident, + module_instance, + extra_genesis_build, + extra_genesis_config_lines, + storage_lines, + }) +} diff --git a/substrate/srml/support/procedural/src/storage/storage_struct.rs b/substrate/srml/support/procedural/src/storage/storage_struct.rs new file mode 100644 index 00000000000..e195fb53e8a --- /dev/null +++ b/substrate/srml/support/procedural/src/storage/storage_struct.rs @@ -0,0 +1,220 @@ +// Copyright 2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see <http://www.gnu.org/licenses/>. + +//! Implementation of storage structures and implementation of storage traits on them. + +use proc_macro2::TokenStream; +use quote::quote; +use super::{ + DeclStorageDefExt, StorageLineTypeDef, + instance_trait::{PREFIX_FOR, HEAD_KEY_FOR}, +}; + +fn from_optional_value_to_query(is_option: bool, default: &Option<syn::Expr>) -> TokenStream { + let default = default.as_ref().map(|d| quote!( #d )) + .unwrap_or_else(|| quote!( Default::default() )); + + if !is_option { + // raw type case + quote!( v.unwrap_or_else(|| #default ) ) + } else { + // Option<> type case + quote!( v.or_else(|| #default ) ) + } +} + +fn from_query_to_optional_value(is_option: bool) -> TokenStream { + if !is_option { + // raw type case + quote!( Some(v) ) + } else { + // Option<> type case + quote!( v ) + } +} + +pub fn decl_and_impl(scrate: &TokenStream, def: &DeclStorageDefExt) -> TokenStream { + let mut impls = TokenStream::new(); + + for line in &def.storage_lines { + + // Propagate doc attributes. + let attrs = &line.doc_attrs; + + let visibility = &line.visibility; + let optional_storage_runtime_comma = &line.optional_storage_runtime_comma; + let optional_storage_runtime_bound_comma = &line.optional_storage_runtime_bound_comma; + let optional_storage_where_clause = &line.optional_storage_where_clause; + let optional_instance_bound_optional_default = &def.optional_instance_bound_optional_default; + let optional_instance_bound = &def.optional_instance_bound; + let optional_instance = &def.optional_instance; + let name = &line.name; + + let struct_decl = quote!( + #( #[ #attrs ] )* + #visibility struct #name< + #optional_storage_runtime_bound_comma #optional_instance_bound_optional_default + >( + #scrate::rstd::marker::PhantomData< + (#optional_storage_runtime_comma #optional_instance) + > + ) #optional_storage_where_clause; + ); + + let from_query_to_optional_value = from_query_to_optional_value(line.is_option); + let from_optional_value_to_query = + from_optional_value_to_query(line.is_option, &line.default_value); + + let final_prefix = if let Some(instance) = def.module_instance.as_ref() { + let instance = &instance.instance_generic; + let const_name = syn::Ident::new( + &format!("{}{}", PREFIX_FOR, line.name.to_string()), proc_macro2::Span::call_site() + ); + quote!( #instance::#const_name.as_bytes() ) + } else { + let prefix = format!("{} {}", def.crate_name, line.name); + quote!( #prefix.as_bytes() ) + }; + + + let storage_generator_trait = &line.storage_generator_trait; + let storage_struct = &line.storage_struct; + let impl_trait = quote!( #optional_storage_runtime_bound_comma #optional_instance_bound ); + let value_type = &line.value_type; + let query_type = &line.query_type; + + let struct_impl = match &line.storage_type { + StorageLineTypeDef::Simple(_) => { + quote!( + impl<#impl_trait> #scrate::#storage_generator_trait for #storage_struct + #optional_storage_where_clause + { + type Query = #query_type; + + fn unhashed_key() -> &'static [u8] { + #final_prefix + } + + fn from_optional_value_to_query(v: Option<#value_type>) -> Self::Query { + #from_optional_value_to_query + } + + fn from_query_to_optional_value(v: Self::Query) -> Option<#value_type> { + #from_query_to_optional_value + } + } + ) + }, + StorageLineTypeDef::Map(map) => { + let hasher = map.hasher.to_storage_hasher_struct(); + quote!( + impl<#impl_trait> #scrate::#storage_generator_trait for #storage_struct + #optional_storage_where_clause + { + type Query = #query_type; + type Hasher = #scrate::#hasher; + + fn prefix() -> &'static [u8] { + #final_prefix + } + + fn from_optional_value_to_query(v: Option<#value_type>) -> Self::Query { + #from_optional_value_to_query + } + + fn from_query_to_optional_value(v: Self::Query) -> Option<#value_type> { + #from_query_to_optional_value + } + } + ) + }, + StorageLineTypeDef::LinkedMap(map) => { + let hasher = map.hasher.to_storage_hasher_struct(); + + // make sure to use different prefix for head and elements. + let head_key = if let Some(instance) = def.module_instance.as_ref() { + let instance = &instance.instance_generic; + let const_name = syn::Ident::new( + &format!("{}{}", HEAD_KEY_FOR, line.name.to_string()), proc_macro2::Span::call_site() + ); + quote!( #instance::#const_name.as_bytes() ) + } else { + let prefix = format!("head of {} {}", def.crate_name, line.name); + quote!( #prefix.as_bytes() ) + }; + + quote!( + impl<#impl_trait> #scrate::#storage_generator_trait for #storage_struct + #optional_storage_where_clause + { + type Query = #query_type; + type Hasher = #scrate::#hasher; + + fn prefix() -> &'static [u8] { + #final_prefix + } + + fn head_key() -> &'static [u8] { + #head_key + } + + fn from_optional_value_to_query(v: Option<#value_type>) -> Self::Query { + #from_optional_value_to_query + } + + fn from_query_to_optional_value(v: Self::Query) -> Option<#value_type> { + #from_query_to_optional_value + } + } + ) + }, + StorageLineTypeDef::DoubleMap(map) => { + let hasher1 = map.hasher1.to_storage_hasher_struct(); + let hasher2 = map.hasher2.to_storage_hasher_struct(); + quote!( + impl<#impl_trait> #scrate::#storage_generator_trait for #storage_struct + #optional_storage_where_clause + { + type Query = #query_type; + + type Hasher1 = #scrate::#hasher1; + + type Hasher2 = #scrate::#hasher2; + + fn key1_prefix() -> &'static [u8] { + #final_prefix + } + + fn from_optional_value_to_query(v: Option<#value_type>) -> Self::Query { + #from_optional_value_to_query + } + + fn from_query_to_optional_value(v: Self::Query) -> Option<#value_type> { + #from_query_to_optional_value + } + } + ) + } + }; + + impls.extend(quote!( + #struct_decl + #struct_impl + )) + } + + impls +} diff --git a/substrate/srml/support/procedural/src/storage/store_trait.rs b/substrate/srml/support/procedural/src/storage/store_trait.rs new file mode 100644 index 00000000000..4c9d96b6bb2 --- /dev/null +++ b/substrate/srml/support/procedural/src/storage/store_trait.rs @@ -0,0 +1,54 @@ +// Copyright 2017-2019 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see <http://www.gnu.org/licenses/>. + +//! Declaration of store trait and implementation on module structure. + +use proc_macro2::TokenStream; +use quote::quote; +use super::DeclStorageDefExt; + +pub fn decl_and_impl(def: &DeclStorageDefExt) -> TokenStream { + let decl_store_items = def.storage_lines.iter() + .map(|sline| &sline.name) + .fold(TokenStream::new(), |mut items, name| { + items.extend(quote!(type #name;)); + items + }); + + let impl_store_items = def.storage_lines.iter() + .fold(TokenStream::new(), |mut items, line| { + let name = &line.name; + let storage_struct = &line.storage_struct; + + items.extend(quote!(type #name = #storage_struct;)); + items + }); + + let visibility = &def.visibility; + let store_trait = &def.store_trait; + let module_struct = &def.module_struct; + let module_impl = &def.module_impl; + let where_clause = &def.where_clause; + + quote!( + #visibility trait #store_trait { + #decl_store_items + } + impl#module_impl #store_trait for #module_struct #where_clause { + #impl_store_items + } + ) +} diff --git a/substrate/srml/support/procedural/src/storage/transformation.rs b/substrate/srml/support/procedural/src/storage/transformation.rs deleted file mode 100644 index c6a3a1f668c..00000000000 --- a/substrate/srml/support/procedural/src/storage/transformation.rs +++ /dev/null @@ -1,1264 +0,0 @@ -// Copyright 2017-2019 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see <http://www.gnu.org/licenses/>. - -//! `decl_storage` macro transformation - -use srml_support_procedural_tools::syn_ext as ext; -use srml_support_procedural_tools::{ - generate_crate_access, generate_hidden_includes, clean_type_string -}; - -use proc_macro::TokenStream; -use proc_macro2::{TokenStream as TokenStream2, Span}; - -use syn::{ - Ident, - GenericParam, - WhereClause, - spanned::Spanned, - parse::{ - Error, - Result, - }, - parse_macro_input, -}; -use quote::{quote, quote_spanned}; - -use super::*; - -const NUMBER_OF_INSTANCE: usize = 16; -const DEFAULT_INSTANTIABLE_TRAIT_NAME: &str = "__GeneratedInstantiable"; -const DEFAULT_INSTANCE_NAME: &str = "__GeneratedInstance"; -const INHERENT_INSTANCE_NAME: &str = "__InherentHiddenInstance"; - -// try macro but returning tokenized error -macro_rules! try_tok(( $expre : expr ) => { - match $expre { - Ok(r) => r, - Err (err) => { - return err.to_compile_error().into() - } - } -}); - -pub fn decl_storage_impl(input: TokenStream) -> TokenStream { - let def = parse_macro_input!(input as StorageDefinition); - - let StorageDefinition { - hidden_crate, - visibility, - ident: storetype, - module_ident, - mod_param: strait, - mod_instance, - mod_instantiable, - mod_default_instance, - crate_ident: cratename, - content: ext::Braces { content: storage_lines, ..}, - extra_genesis, - where_clause, - .. - } = def; - - let instance_opts = match get_instance_opts( - mod_instance, - mod_instantiable, - mod_default_instance - ) { - Ok(opts) => opts, - Err(err) => return err.to_compile_error().into(), - }; - - let hidden_crate_name = hidden_crate.inner.map(|rc| rc.ident.content).map(|i| i.to_string()) - .unwrap_or_else(|| "decl_storage".to_string()); - let scrate = generate_crate_access(&hidden_crate_name, "srml-support"); - let scrate_decl = generate_hidden_includes( - &hidden_crate_name, - "srml-support", - ); - - let ( - traitinstance, - traittypes, - ) = if let GenericParam::Type(syn::TypeParam {ident, bounds, ..}) = strait { - (ident, bounds) - } else { - return try_tok!(Err(Error::new(strait.span(), "Missing declare store generic params"))); - }; - - let traittype = if let Some(traittype) = traittypes.first() { - traittype.into_value() - } else { - return try_tok!(Err(Error::new(traittypes.span(), "Trait bound expected"))); - }; - - let extra_genesis = try_tok!(decl_store_extra_genesis( - &scrate, - &traitinstance, - &traittype, - &instance_opts, - &storage_lines, - &extra_genesis.inner, - &where_clause, - )); - let decl_storage_items = decl_storage_items( - &scrate, - &traitinstance, - &traittype, - &instance_opts, - &cratename, - &storage_lines, - &where_clause, - ).unwrap_or_else(|err| err.to_compile_error()); - - let decl_store_items = decl_store_items( - &storage_lines, - ); - let impl_store_items = impl_store_items( - &traitinstance, - &instance_opts.instance, - &storage_lines, - ); - let impl_store_fns = impl_store_fns( - &scrate, - &traitinstance, - &instance_opts.instance, - &storage_lines, - ); - let (store_default_struct, store_metadata) = store_functions_to_metadata( - &scrate, - &traitinstance, - &traittype, - &instance_opts, - &storage_lines, - &where_clause, - &cratename, - ); - - let InstanceOpts { - instance, - bound_instantiable, - .. - } = instance_opts; - - let expanded = quote! { - use #scrate::{ - StorageValue as _, - StorageMap as _, - StorageLinkedMap as _, - StorageDoubleMap as _ - }; - - #scrate_decl - #decl_storage_items - #visibility trait #storetype { - #decl_store_items - } - #store_default_struct - impl<#traitinstance: #traittype, #instance #bound_instantiable> #storetype - for #module_ident<#traitinstance, #instance> #where_clause - { - #impl_store_items - } - impl<#traitinstance: 'static + #traittype, #instance #bound_instantiable> - #module_ident<#traitinstance, #instance> #where_clause - { - #impl_store_fns - #[doc(hidden)] - pub fn storage_metadata() -> #scrate::metadata::StorageMetadata { - #store_metadata - } - } - - #extra_genesis - }; - - expanded.into() -} - -fn decl_store_extra_genesis( - scrate: &TokenStream2, - traitinstance: &Ident, - traittype: &syn::TypeParamBound, - instance_opts: &InstanceOpts, - storage_lines: &ext::Punctuated<DeclStorageLine, Token![;]>, - extra_genesis: &Option<AddExtraGenesis>, - where_clause: &Option<syn::WhereClause>, -) -> Result<TokenStream2> { - - let InstanceOpts { - equal_default_instance, - bound_instantiable, - instance, - .. - } = instance_opts; - - let mut is_trait_needed = false; - let mut serde_complete_bound = Vec::new(); - let mut config_field = TokenStream2::new(); - let mut config_field_default = TokenStream2::new(); - let mut builders = TokenStream2::new(); - let mut assimilate_require_generic = instance.is_some(); - let mut builders_clone_bound = Vec::new(); - - for sline in storage_lines.inner.iter() { - let DeclStorageLine { - attrs, - name, - getter, - config, - build, - storage_type, - default_value, - .. - } = sline; - - let type_infos = get_type_infos(storage_type); - - let opt_build = build - .inner - .as_ref() - .map(|b| { - assimilate_require_generic |= ext::expr_contains_ident(&b.expr.content, traitinstance); - &b.expr.content - }) - .map(|b| quote!( #b )); - - // need build line - let builder = if let Some(ref config) = config.inner { - let ident = if let Some(ident) = config.expr.content.as_ref() { - quote!( #ident ) - } else if let Some(ref getter) = getter.inner { - let ident = &getter.getfn.content; - quote!( #ident ) - } else { - return Err( - Error::new_spanned( - name, - "Invalid storage definiton, couldn't find config identifier: storage must either have a get identifier \ - `get(ident)` or a defined config identifier `config(ident)`" - ) - ); - }; - - if ext::type_contains_ident(type_infos.value_type, traitinstance) { - is_trait_needed = true; - } - - if opt_build.is_none() { - builders_clone_bound.push(type_infos.value_type.clone()); - } - - let value_type = &type_infos.value_type; - serde_complete_bound.push(quote!( #value_type )); - match type_infos.kind { - DeclStorageTypeInfosKind::Map { key_type, .. } => { - serde_complete_bound.push(quote!( #key_type )); - is_trait_needed = is_trait_needed - || ext::type_contains_ident(key_type, traitinstance); - - if opt_build.is_none() { - builders_clone_bound.push(key_type.clone()); - } - }, - DeclStorageTypeInfosKind::DoubleMap { key1_type, key2_type, .. } => { - serde_complete_bound.push(quote!( #key1_type )); - serde_complete_bound.push(quote!( #key2_type )); - is_trait_needed = is_trait_needed - || ext::type_contains_ident(key1_type, traitinstance) - || ext::type_contains_ident(key2_type, traitinstance); - if opt_build.is_none() { - builders_clone_bound.push(key1_type.clone()); - builders_clone_bound.push(key2_type.clone()); - } - }, - _ => {}, - } - - if type_infos.is_option { - serde_complete_bound.push(type_infos.typ.clone()); - } - - // Propagate doc attributes. - let attrs = attrs.inner.iter().filter_map(|a| a.parse_meta().ok()).filter(|m| m.name() == "doc"); - - let storage_type = type_infos.typ.clone(); - config_field.extend(match type_infos.kind { - DeclStorageTypeInfosKind::Simple => { - quote!( #( #[ #attrs ] )* pub #ident: #storage_type, ) - }, - DeclStorageTypeInfosKind::Map {key_type, .. } => { - quote!( #( #[ #attrs ] )* pub #ident: Vec<(#key_type, #storage_type)>, ) - }, - DeclStorageTypeInfosKind::DoubleMap {key1_type, key2_type, .. } => { - quote!( #( #[ #attrs ] )* pub #ident: Vec<(#key1_type, #key2_type, #storage_type)>, ) - }, - }); - - let fielddefault = default_value.inner.as_ref().map(|d| &d.expr).map(|d| - if type_infos.is_option { - quote!( #d.unwrap_or_default() ) - } else { - quote!( #d ) - }).unwrap_or_else(|| quote!( Default::default() )); - - config_field_default.extend(quote!( #ident: #fielddefault, )); - - opt_build.or_else(|| Some(quote!( (|config: &Self| config.#ident.clone()) ))) - } else { - opt_build - }; - - let typ = type_infos.typ; - if let Some(builder) = builder { - builders.extend(match type_infos.kind { - DeclStorageTypeInfosKind::Simple => { - let struct_trait = if ext::type_contains_ident(&type_infos.value_type, traitinstance) { - assimilate_require_generic = true; - quote!(#traitinstance,) - } else { - quote!() - }; - - quote!{{ - let v = (#builder)(&self); - < - #name<#struct_trait #instance> as - #scrate::storage::StorageValue<#typ> - >::put::<#typ>(v); - }} - }, - DeclStorageTypeInfosKind::Map { key_type, is_linked, .. } => { - let struct_trait = if ext::type_contains_ident(&type_infos.value_type, traitinstance) - || ext::type_contains_ident(key_type, traitinstance) - { - assimilate_require_generic = true; - quote!(#traitinstance,) - } else { - quote!() - }; - - let map = if is_linked { - quote! { StorageLinkedMap } - } else { - quote! { StorageMap } - }; - - quote!{{ - let data = (#builder)(&self); - data.into_iter().for_each(|(k, v)| { - < - #name<#struct_trait #instance> as - #scrate::storage::#map<#key_type, #typ> - >::insert::<#key_type, #typ>(k, v); - }); - }} - }, - DeclStorageTypeInfosKind::DoubleMap { key1_type, key2_type, .. } => { - let struct_trait = if ext::type_contains_ident(&type_infos.value_type, traitinstance) - || ext::type_contains_ident(key1_type, traitinstance) - || ext::type_contains_ident(key2_type, traitinstance) - { - assimilate_require_generic = true; - quote!(#traitinstance,) - } else { - quote!() - }; - - quote!{{ - let data = (#builder)(&self); - data.into_iter().for_each(|(k1, k2, v)| { - < - #name<#struct_trait #instance> as - #scrate::storage::StorageDoubleMap<#key1_type, #key2_type, #typ> - >::insert::<#key1_type, #key2_type, #typ>(k1, k2, v); - }); - }} - }, - }); - } - } - - let mut has_scall = false; - let mut scall = quote!{ let scall: fn(&Self) = |_| {}; scall }; - let mut genesis_extrafields = TokenStream2::new(); - let mut genesis_extrafields_default = TokenStream2::new(); - - // extra genesis - if let Some(eg) = extra_genesis { - for ex_content in eg.content.content.lines.inner.iter() { - match ex_content { - AddExtraGenesisLineEnum::AddExtraGenesisLine(AddExtraGenesisLine { - attrs, - extra_field, - extra_type, - default_value, - .. - }) => { - if ext::type_contains_ident(&extra_type, traitinstance) { - is_trait_needed = true; - } - - serde_complete_bound.push(quote!( #extra_type )); - - let extrafield = &extra_field.content; - genesis_extrafields.extend(quote!{ - #attrs pub #extrafield: #extra_type, - }); - let extra_default = default_value.inner.as_ref().map(|d| &d.expr).map(|e| quote!{ #e }) - .unwrap_or_else(|| quote!( Default::default() )); - genesis_extrafields_default.extend(quote!{ - #extrafield: #extra_default, - }); - }, - AddExtraGenesisLineEnum::AddExtraGenesisBuild(DeclStorageBuild{ expr, .. }) => { - if has_scall { - return Err(Error::new(expr.span(), "Only one build expression allowed for extra genesis")); - } - assimilate_require_generic |= ext::expr_contains_ident(&expr.content, traitinstance); - let content = &expr.content; - scall = quote_spanned! { expr.span() => - let scall: fn(&Self) = #content; scall - }; - has_scall = true; - }, - } - } - } - - let serde_bug_bound = if !serde_complete_bound.is_empty() { - let mut b_ser = String::new(); - let mut b_dser = String::new(); - - serde_complete_bound.into_iter().for_each(|bound| { - let stype = quote!(#bound); - b_ser.push_str(&format!("{} : {}::serde::Serialize, ", stype, scrate)); - b_dser.push_str(&format!("{} : {}::serde::de::DeserializeOwned, ", stype, scrate)); - }); - - quote! { - #[serde(bound(serialize = #b_ser))] - #[serde(bound(deserialize = #b_dser))] - } - } else { - quote!() - }; - - let is_extra_genesis_needed = has_scall - || !config_field.is_empty() - || !genesis_extrafields.is_empty() - || !builders.is_empty(); - if is_extra_genesis_needed { - let (inherent_instance, inherent_bound_instantiable) = if instance.is_some() { - (instance.clone(), bound_instantiable.clone()) - } else { - let instantiable = Ident::new(DEFAULT_INSTANTIABLE_TRAIT_NAME, Span::call_site()); - ( - Some(Ident::new(DEFAULT_INSTANCE_NAME, Span::call_site())), - quote!(: #instantiable), - ) - }; - - let (fparam_struct, fparam_impl, sparam, build_storage_impl) = if is_trait_needed { - ( - quote!(<#traitinstance: #traittype, #instance #bound_instantiable #equal_default_instance>), - quote!(<#traitinstance: #traittype, #instance #bound_instantiable>), - quote!(<#traitinstance, #instance>), - quote!(<#traitinstance: #traittype, #inherent_instance #inherent_bound_instantiable>), - ) - } else { - // do not even need type parameter - ( - quote!(), - quote!(), - quote!(), - quote!(<#traitinstance: #traittype, #inherent_instance #inherent_bound_instantiable>), - ) - }; - - let (fn_generic, fn_traitinstance) = if !is_trait_needed && assimilate_require_generic { - ( - quote!( <#traitinstance: #traittype, #instance #bound_instantiable> ), - quote!( #traitinstance, #instance ) - ) - } else { - (quote!(), quote!()) - }; - - let impl_trait = quote!(BuildModuleGenesisStorage<#traitinstance, #inherent_instance>); - - let extend_where_clause = |to_extend: &mut WhereClause| { - if let Some(where_clause) = where_clause { - to_extend.predicates.extend(where_clause.predicates.iter().cloned()); - } - }; - - let mut genesis_where_clause: WhereClause = syn::parse_quote!( - where #( #builders_clone_bound: Clone ),* - ); - let mut fn_where_clause = genesis_where_clause.clone(); - - - let mut build_storage_where_clause = genesis_where_clause.clone(); - extend_where_clause(&mut build_storage_where_clause); - - if is_trait_needed { - extend_where_clause(&mut genesis_where_clause); - } else if assimilate_require_generic { - extend_where_clause(&mut fn_where_clause); - } - - let res = quote!{ - #[derive(#scrate::Serialize, #scrate::Deserialize)] - #[cfg(feature = "std")] - #[serde(rename_all = "camelCase")] - #[serde(deny_unknown_fields)] - #serde_bug_bound - pub struct GenesisConfig#fparam_struct #genesis_where_clause { - #config_field - #genesis_extrafields - } - - #[cfg(feature = "std")] - impl#fparam_impl Default for GenesisConfig#sparam #genesis_where_clause { - fn default() -> Self { - GenesisConfig { - #config_field_default - #genesis_extrafields_default - } - } - } - - #[cfg(feature = "std")] - impl#fparam_impl GenesisConfig#sparam #genesis_where_clause { - pub fn build_storage #fn_generic (self) -> std::result::Result< - ( - #scrate::sr_primitives::StorageOverlay, - #scrate::sr_primitives::ChildrenStorageOverlay, - ), - String - > #fn_where_clause { - let mut storage = (Default::default(), Default::default()); - self.assimilate_storage::<#fn_traitinstance>(&mut storage)?; - Ok(storage) - } - - /// Assimilate the storage for this module into pre-existing overlays. - pub fn assimilate_storage #fn_generic ( - self, - tuple_storage: &mut ( - #scrate::sr_primitives::StorageOverlay, - #scrate::sr_primitives::ChildrenStorageOverlay, - ), - ) -> std::result::Result<(), String> #fn_where_clause { - #scrate::with_storage(tuple_storage, || { - #builders - - #scall(&self); - - Ok(()) - }) - } - } - - #[cfg(feature = "std")] - impl#build_storage_impl #scrate::sr_primitives::#impl_trait - for GenesisConfig#sparam #build_storage_where_clause - { - fn build_module_genesis_storage( - self, - storage: &mut ( - #scrate::sr_primitives::StorageOverlay, - #scrate::sr_primitives::ChildrenStorageOverlay, - ), - ) -> std::result::Result<(), String> { - self.assimilate_storage::<#fn_traitinstance> (storage) - } - } - }; - - Ok(res) - } else { - Ok(quote!()) - } -} - -fn create_and_impl_instance( - instance_prefix: &str, - ident: &Ident, - doc: &TokenStream2, - const_names: &[(Ident, String, String)], - scrate: &TokenStream2, - instantiable: &Ident, - cratename: &Ident, -) -> TokenStream2 { - let mut const_impls = TokenStream2::new(); - - for (const_name, const_value_prefix, const_value_suffix) in const_names { - let const_value = format!("{}{}{}", const_value_prefix, instance_prefix, const_value_suffix); - const_impls.extend(quote! { - const #const_name: &'static str = #const_value; - }); - } - - let prefix = format!("{}{}", instance_prefix, cratename.to_string()); - - quote! { - // Those trait are derived because of wrong bounds for generics - #[cfg_attr(feature = "std", derive(Debug))] - #[derive(Clone, Eq, PartialEq, #scrate::codec::Encode, #scrate::codec::Decode)] - #doc - pub struct #ident; - impl #instantiable for #ident { - const PREFIX: &'static str = #prefix; - #const_impls - } - } -} - -fn decl_storage_items( - scrate: &TokenStream2, - traitinstance: &Ident, - traittype: &syn::TypeParamBound, - instance_opts: &InstanceOpts, - cratename: &Ident, - storage_lines: &ext::Punctuated<DeclStorageLine, Token![;]>, - where_clause: &Option<WhereClause>, -) -> syn::Result<TokenStream2> { - let mut impls = TokenStream2::new(); - - let InstanceOpts { - instance, - default_instance, - instantiable, - .. - } = instance_opts; - - let build_prefix = |cratename, name| format!("{} {}", cratename, name); - - // Build Instantiable trait - let mut const_names = vec![]; - - for sline in storage_lines.inner.iter() { - let DeclStorageLine { - storage_type, - name, - .. - } = sline; - - let prefix = build_prefix(cratename, name); - - let type_infos = get_type_infos(storage_type); - - let const_name = syn::Ident::new( - &format!("{}{}", impls::PREFIX_FOR, name.to_string()), proc_macro2::Span::call_site() - ); - const_names.push((const_name, String::new(), prefix.clone())); - - if let DeclStorageTypeInfosKind::Map { is_linked: true, .. } = type_infos.kind { - let const_name = syn::Ident::new( - &format!("{}{}", impls::HEAD_KEY_FOR, name.to_string()), proc_macro2::Span::call_site() - ); - const_names.push((const_name, "head of ".into(), prefix)); - } - } - - let instantiable = instantiable - .clone() - .unwrap_or_else(|| Ident::new(DEFAULT_INSTANTIABLE_TRAIT_NAME, Span::call_site())); - - // Declare Instance trait - { - let mut const_impls = TokenStream2::new(); - for (const_name, ..) in &const_names { - const_impls.extend(quote! { - const #const_name: &'static str; - }); - } - - let hide = if instance.is_some() { - quote!() - } else { - quote!(#[doc(hidden)]) - }; - - impls.extend(quote! { - /// Tag a type as an instance of a module. - /// - /// Defines storage prefixes, they must be unique. - #hide - pub trait #instantiable: 'static { - /// The prefix used by any storage entry of an instance. - const PREFIX: &'static str; - #const_impls - } - }); - } - - if instance.is_some() { - let instances = (0..NUMBER_OF_INSTANCE) - .map(|i| { - let name = format!("Instance{}", i); - let ident = Ident::new(&name, proc_macro2::Span::call_site()); - (name, ident, quote! {#[doc=r"Module instance"]}) - }) - .chain( - default_instance - .clone() - .map(|ident| - (String::new(), ident, quote! {#[doc=r"Default module instance"]}) - ) - ); - - // Impl Instance trait for instances - for (instance_prefix, ident, doc) in instances { - impls.extend( - create_and_impl_instance( - &instance_prefix, &ident, &doc, &const_names, scrate, &instantiable, cratename - ) - ); - } - } - - // The name of the inherently available instance. - let inherent_instance = Ident::new(INHERENT_INSTANCE_NAME, Span::call_site()); - - if default_instance.is_some() { - impls.extend(quote! { - #[doc(hidden)] - pub type #inherent_instance = #default_instance; - }); - } else { - impls.extend( - create_and_impl_instance( - "", - &inherent_instance, - "e!(#[doc(hidden)]), - &const_names, - scrate, - &instantiable, - cratename, - ) - ); - } - - for sline in storage_lines.inner.iter() { - let DeclStorageLine { - attrs, - name, - storage_type, - default_value, - visibility, - .. - } = sline; - - let type_infos = get_type_infos(storage_type); - - if type_infos.is_option && default_value.inner.is_some() { - return Err(syn::Error::new_spanned( - default_value, - "Default values for Option types are not supported" - )); - } - - let fielddefault = default_value.inner - .as_ref() - .map(|d| &d.expr) - .map(|d| quote!( #d )) - .unwrap_or_else(|| quote!{ Default::default() }); - let kind = type_infos.kind.clone(); - // Propagate doc attributes. - let attrs = attrs.inner.iter().filter_map(|a| a.parse_meta().ok()).filter(|m| m.name() == "doc"); - - let i = impls::Impls { - scrate, - visibility, - cratename, - traitinstance, - traittype, - instance_opts, - type_infos, - fielddefault, - prefix: build_prefix(cratename, name), - name, - attrs, - where_clause, - }; - - let implementation = match kind { - DeclStorageTypeInfosKind::Simple => { - i.simple_value() - }, - DeclStorageTypeInfosKind::Map { key_type, is_linked: false, hasher } => { - i.map(hasher.into_storage_hasher_struct(), key_type) - }, - DeclStorageTypeInfosKind::Map { key_type, is_linked: true, hasher } => { - i.linked_map(hasher.into_storage_hasher_struct(), key_type) - }, - DeclStorageTypeInfosKind::DoubleMap { key1_type, key2_type, key2_hasher, hasher } => { - i.double_map(hasher.into_storage_hasher_struct(), key1_type, key2_type, key2_hasher.into_storage_hasher_struct()) - }, - }; - impls.extend(implementation) - } - - Ok(impls) -} - -fn decl_store_items(storage_lines: &ext::Punctuated<DeclStorageLine, Token![;]>) -> TokenStream2 { - storage_lines.inner.iter().map(|sline| &sline.name) - .fold(TokenStream2::new(), |mut items, name| { - items.extend(quote!(type #name;)); - items - }) -} - -fn impl_store_items( - traitinstance: &Ident, - instance: &Option<syn::Ident>, - storage_lines: &ext::Punctuated<DeclStorageLine, Token![;]>, -) -> TokenStream2 { - storage_lines.inner - .iter() - .fold(TokenStream2::new(), |mut items, line| { - let name = &line.name; - let type_infos = get_type_infos(&line.storage_type); - let requires_trait = match type_infos.kind { - DeclStorageTypeInfosKind::Simple => { - ext::type_contains_ident(&type_infos.value_type, traitinstance) - }, - DeclStorageTypeInfosKind::Map { key_type, .. } => { - ext::type_contains_ident(&type_infos.value_type, traitinstance) - || ext::type_contains_ident(key_type, traitinstance) - } - DeclStorageTypeInfosKind::DoubleMap { key1_type, key2_type, .. } => { - ext::type_contains_ident(&type_infos.value_type, traitinstance) - || ext::type_contains_ident(key1_type, traitinstance) - || ext::type_contains_ident(key2_type, traitinstance) - } - }; - - let struct_trait = if requires_trait { - quote!(#traitinstance,) - } else { - quote!() - }; - - items.extend( - quote!( - type #name = #name<#struct_trait #instance>; - ) - ); - items - }) -} - -fn impl_store_fns( - scrate: &TokenStream2, - traitinstance: &Ident, - instance: &Option<syn::Ident>, - storage_lines: &ext::Punctuated<DeclStorageLine, Token![;]>, -) -> TokenStream2 { - let mut items = TokenStream2::new(); - for sline in storage_lines.inner.iter() { - let DeclStorageLine { - attrs, - name, - getter, - storage_type, - .. - } = sline; - - if let Some(getter) = getter.inner.as_ref() { - let get_fn = &getter.getfn.content; - - let type_infos = get_type_infos(storage_type); - let value_type = type_infos.value_type; - - // Propagate doc attributes. - let attrs = attrs.inner.iter().filter_map(|a| a.parse_meta().ok()).filter(|m| m.name() == "doc"); - - let typ = type_infos.typ; - let item = match type_infos.kind { - DeclStorageTypeInfosKind::Simple => { - let struct_trait = if ext::type_contains_ident(&type_infos.value_type, traitinstance) { - quote!(#traitinstance,) - } else { - quote!() - }; - - quote!{ - #( #[ #attrs ] )* - pub fn #get_fn() -> #value_type { - < - #name<#struct_trait #instance> as - #scrate::storage::StorageValue<#typ> - >::get() - } - } - }, - DeclStorageTypeInfosKind::Map { key_type, is_linked, .. } => { - let struct_trait = if ext::type_contains_ident(&type_infos.value_type, traitinstance) - || ext::type_contains_ident(key_type, traitinstance) - { - quote!(#traitinstance,) - } else { - quote!() - }; - - let map = if is_linked { - quote! { StorageLinkedMap } - } else { - quote! { StorageMap } - }; - - quote!{ - #( #[ #attrs ] )* - pub fn #get_fn<K: #scrate::codec::EncodeLike<#key_type>>(key: K) -> #value_type { - < - #name<#struct_trait #instance> as - #scrate::storage::#map<#key_type, #typ> - >::get(key) - } - } - } - DeclStorageTypeInfosKind::DoubleMap { key1_type, key2_type, .. } => { - let struct_trait = if ext::type_contains_ident(&type_infos.value_type, traitinstance) - || ext::type_contains_ident(key1_type, traitinstance) - || ext::type_contains_ident(key2_type, traitinstance) - { - quote!(#traitinstance,) - } else { - quote!() - }; - - quote!{ - pub fn #get_fn<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> #value_type - where - KArg1: #scrate::codec::EncodeLike<#key1_type>, - KArg2: #scrate::codec::EncodeLike<#key2_type>, - { - < - #name<#struct_trait #instance> as - #scrate::storage::StorageDoubleMap<#key1_type, #key2_type, #typ> - >::get(k1, k2) - } - } - } - }; - items.extend(item); - } - } - items -} - -fn store_functions_to_metadata ( - scrate: &TokenStream2, - traitinstance: &Ident, - traittype: &syn::TypeParamBound, - instance_opts: &InstanceOpts, - storage_lines: &ext::Punctuated<DeclStorageLine, Token![;]>, - where_clause: &Option<WhereClause>, - cratename: &Ident, -) -> (TokenStream2, TokenStream2) { - let InstanceOpts { - comma_instance, - equal_default_instance, - bound_instantiable, - instance, - .. - } = instance_opts; - - let mut items = TokenStream2::new(); - let mut default_getter_struct_def = TokenStream2::new(); - for sline in storage_lines.inner.iter() { - let DeclStorageLine { - attrs, - name, - storage_type, - default_value, - .. - } = sline; - - let type_infos = get_type_infos(storage_type); - let value_type = type_infos.value_type; - - let typ = type_infos.typ; - let styp = clean_type_string(&typ.to_string()); - let stype = match type_infos.kind { - DeclStorageTypeInfosKind::Simple => { - quote!{ - #scrate::metadata::StorageEntryType::Plain( - #scrate::metadata::DecodeDifferent::Encode(#styp), - ) - } - }, - DeclStorageTypeInfosKind::Map { key_type, is_linked, hasher } => { - let hasher = hasher.into_metadata(); - let kty = clean_type_string("e!(#key_type).to_string()); - quote!{ - #scrate::metadata::StorageEntryType::Map { - hasher: #scrate::metadata::#hasher, - key: #scrate::metadata::DecodeDifferent::Encode(#kty), - value: #scrate::metadata::DecodeDifferent::Encode(#styp), - is_linked: #is_linked, - } - } - }, - DeclStorageTypeInfosKind::DoubleMap { key1_type, key2_type, key2_hasher, hasher } => { - let hasher = hasher.into_metadata(); - let k1ty = clean_type_string("e!(#key1_type).to_string()); - let k2ty = clean_type_string("e!(#key2_type).to_string()); - let k2_hasher = key2_hasher.into_metadata(); - quote!{ - #scrate::metadata::StorageEntryType::DoubleMap { - hasher: #scrate::metadata::#hasher, - key1: #scrate::metadata::DecodeDifferent::Encode(#k1ty), - key2: #scrate::metadata::DecodeDifferent::Encode(#k2ty), - value: #scrate::metadata::DecodeDifferent::Encode(#styp), - key2_hasher: #scrate::metadata::#k2_hasher, - } - } - }, - }; - let modifier = if type_infos.is_option { - quote!{ - #scrate::metadata::StorageEntryModifier::Optional - } - } else { - quote!{ - #scrate::metadata::StorageEntryModifier::Default - } - }; - let default = default_value.inner.as_ref().map(|d| &d.expr) - .map(|d| { - quote!( #d ) - }) - .unwrap_or_else(|| quote!( Default::default() )); - let mut docs = TokenStream2::new(); - for attr in attrs.inner.iter().filter_map(|v| v.parse_meta().ok()) { - if let syn::Meta::NameValue(syn::MetaNameValue{ - ref ident, - ref lit, - .. - }) = attr { - if ident == "doc" { - docs.extend(quote!(#lit,)); - } - } - } - let str_name = name.to_string(); - let struct_name = proc_macro2::Ident::new(&("__GetByteStruct".to_string() + &str_name), name.span()); - let cache_name = proc_macro2::Ident::new(&("__CACHE_GET_BYTE_STRUCT_".to_string() + &str_name), name.span()); - - let item = quote! { - #scrate::metadata::StorageEntryMetadata { - name: #scrate::metadata::DecodeDifferent::Encode(#str_name), - modifier: #modifier, - ty: #stype, - default: #scrate::metadata::DecodeDifferent::Encode( - #scrate::metadata::DefaultByteGetter( - &#struct_name::<#traitinstance, #instance>(#scrate::rstd::marker::PhantomData) - ) - ), - documentation: #scrate::metadata::DecodeDifferent::Encode(&[ #docs ]), - }, - }; - items.extend(item); - - let def_get = quote! { - #[doc(hidden)] - pub struct #struct_name< - #traitinstance, #instance #bound_instantiable #equal_default_instance - >(pub #scrate::rstd::marker::PhantomData<(#traitinstance #comma_instance)>); - - #[cfg(feature = "std")] - #[allow(non_upper_case_globals)] - static #cache_name: #scrate::once_cell::sync::OnceCell< - #scrate::rstd::vec::Vec<u8> - > = #scrate::once_cell::sync::OnceCell::new(); - - #[cfg(feature = "std")] - impl<#traitinstance: #traittype, #instance #bound_instantiable> #scrate::metadata::DefaultByte - for #struct_name<#traitinstance, #instance> #where_clause - { - fn default_byte(&self) -> #scrate::rstd::vec::Vec<u8> { - use #scrate::codec::Encode; - #cache_name.get_or_init(|| { - let def_val: #value_type = #default; - <#value_type as Encode>::encode(&def_val) - }).clone() - } - } - - unsafe impl<#traitinstance: #traittype, #instance #bound_instantiable> Send - for #struct_name<#traitinstance, #instance> #where_clause {} - - unsafe impl<#traitinstance: #traittype, #instance #bound_instantiable> Sync - for #struct_name<#traitinstance, #instance> #where_clause {} - - #[cfg(not(feature = "std"))] - impl<#traitinstance: #traittype, #instance #bound_instantiable> #scrate::metadata::DefaultByte - for #struct_name<#traitinstance, #instance> #where_clause - { - fn default_byte(&self) -> #scrate::rstd::vec::Vec<u8> { - use #scrate::codec::Encode; - let def_val: #value_type = #default; - <#value_type as Encode>::encode(&def_val) - } - } - }; - - default_getter_struct_def.extend(def_get); - } - - let prefix = cratename.to_string(); - let prefix = instance.as_ref().map_or_else(|| quote!(#prefix), |i| quote!(#i::PREFIX)); - - (default_getter_struct_def, quote!{ - #scrate::metadata::StorageMetadata { - prefix: #scrate::metadata::DecodeDifferent::Encode(#prefix), - entries: #scrate::metadata::DecodeDifferent::Encode(&[ #items ][..]), - } - }) -} - - -#[derive(Debug, Clone)] -pub(crate) struct DeclStorageTypeInfos<'a> { - pub is_option: bool, - pub typ: TokenStream2, - pub value_type: &'a syn::Type, - kind: DeclStorageTypeInfosKind<'a>, -} - -#[derive(Debug, Clone)] -enum DeclStorageTypeInfosKind<'a> { - Simple, - Map { - hasher: HasherKind, - key_type: &'a syn::Type, - is_linked: bool, - }, - DoubleMap { - hasher: HasherKind, - key1_type: &'a syn::Type, - key2_type: &'a syn::Type, - key2_hasher: HasherKind, - } -} - -fn get_type_infos(storage_type: &DeclStorageType) -> DeclStorageTypeInfos { - let (value_type, kind) = match storage_type { - DeclStorageType::Simple(ref st) => (st, DeclStorageTypeInfosKind::Simple), - DeclStorageType::Map(ref map) => (&map.value, DeclStorageTypeInfosKind::Map { - hasher: map.hasher.inner.as_ref().map(|h| h.into()).unwrap_or(HasherKind::Blake2_256), - key_type: &map.key, - is_linked: false, - }), - DeclStorageType::LinkedMap(ref map) => (&map.value, DeclStorageTypeInfosKind::Map { - hasher: map.hasher.inner.as_ref().map(|h| h.into()).unwrap_or(HasherKind::Blake2_256), - key_type: &map.key, - is_linked: true, - }), - DeclStorageType::DoubleMap(ref map) => (&map.value, DeclStorageTypeInfosKind::DoubleMap { - hasher: map.hasher.inner.as_ref().map(|h| h.into()).unwrap_or(HasherKind::Blake2_256), - key1_type: &map.key1, - key2_type: &map.key2.content, - key2_hasher: (&map.key2_hasher).into(), - }), - }; - - let extracted_type = ext::extract_type_option(value_type); - let is_option = extracted_type.is_some(); - let typ = extracted_type.unwrap_or(quote!( #value_type )); - - DeclStorageTypeInfos { - is_option, - typ, - value_type, - kind, - } - -} - -#[derive(Default)] -pub(crate) struct InstanceOpts { - pub instance: Option<Ident>, - pub default_instance: Option<Ident>, - pub instantiable: Option<Ident>, - pub comma_instance: TokenStream2, - pub equal_default_instance: TokenStream2, - pub bound_instantiable: TokenStream2, -} - -fn get_instance_opts( - instance: Option<Ident>, - instantiable: Option<Ident>, - default_instance: Option<Ident>, -) -> Result<InstanceOpts> { - let right_syntax = "Should be $Instance: $Instantiable = $DefaultInstance"; - - match (instance, instantiable, default_instance) { - (Some(instance), Some(instantiable), default_instance) => { - let (equal_default_instance, default_instance) = if let Some(def) = default_instance { - (quote!{= #def}, Some(def)) - } else { - (quote!(), None) - }; - - Ok(InstanceOpts { - comma_instance: quote!{, #instance}, - equal_default_instance, - bound_instantiable: quote!{: #instantiable}, - instance: Some(instance), - default_instance, - instantiable: Some(instantiable), - }) - }, - (None, None, None) => Ok(Default::default()), - (Some(instance), None, _) => Err( - Error::new( - instance.span(), - format!( - "Expect instantiable trait bound for instance: {}. {}", - instance, - right_syntax, - ) - ) - ), - (None, Some(instantiable), _) => Err( - Error::new( - instantiable.span(), - format!( - "Expect instance generic for bound instantiable: {}. {}", - instantiable, - right_syntax, - ) - ) - ), - (None, _, Some(default_instance)) => Err( - Error::new( - default_instance.span(), - format!( - "Expect instance generic for default instance: {}. {}", - default_instance, - right_syntax, - ) - ) - ), - } -} diff --git a/substrate/srml/support/procedural/tools/src/syn_ext.rs b/substrate/srml/support/procedural/tools/src/syn_ext.rs index 1033ebcce2d..1658a6b4aea 100644 --- a/substrate/srml/support/procedural/tools/src/syn_ext.rs +++ b/substrate/srml/support/procedural/tools/src/syn_ext.rs @@ -184,13 +184,15 @@ impl<P: ToTokens> ToTokens for Opt<P> { } } -pub fn extract_type_option(typ: &syn::Type) -> Option<TokenStream> { +pub fn extract_type_option(typ: &syn::Type) -> Option<syn::Type> { if let syn::Type::Path(ref path) = typ { let v = path.path.segments.last()?; if v.value().ident == "Option" { - if let syn::PathArguments::AngleBracketed(ref a) = v.value().arguments { - let args = &a.args; - return Some(quote!{ #args }) + // Option has only one type argument in angle bracket. + if let syn::PathArguments::AngleBracketed(a) = &v.value().arguments { + if let syn::GenericArgument::Type(typ) = a.args.last()?.value() { + return Some(typ.clone()) + } } } } @@ -253,4 +255,4 @@ pub fn expr_contains_ident(expr: &syn::Expr, ident: &Ident) -> bool { visit::visit_expr(&mut visit, expr); visit.result -} \ No newline at end of file +} -- GitLab