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)>
This commit is contained in:
thiolliere
2019-10-16 15:47:46 +02:00
committed by GitHub
parent dc92631180
commit f4e36f0d74
15 changed files with 1982 additions and 1862 deletions
+2 -2
View File
@@ -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,
};
+1 -1
View File
@@ -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)
}
@@ -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,
}
}
}
@@ -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
}
}
@@ -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!()
}
}
@@ -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
}
)
}
@@ -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
}
}
}
}
}
@@ -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
}
}
}
@@ -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(&quote!( #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(&quote!(#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(&quote!(#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(&quote!(#key1).to_string());
let key2 = &map.key2;
let key2 = clean_type_string(&quote!(#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
}
}
)
}
@@ -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;
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)]
struct AddExtraGenesisContent {
pub lines: ext::Punctuated<AddExtraGenesisLineEnum, Token![;]>,
/// 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)]
enum AddExtraGenesisLineEnum {
AddExtraGenesisLine(AddExtraGenesisLine),
AddExtraGenesisBuild(DeclStorageBuild),
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 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 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,
}
#[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>,
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)
}
};
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());
let module_runtime_generic = &def.module_runtime_generic;
let module_runtime_trait = &def.module_runtime_trait;
let optional_storage_runtime_comma = if is_generic {
Some(quote!( #module_runtime_generic, ))
} else {
None
};
let optional_storage_runtime_bound_comma = if is_generic {
Some(quote!( #module_runtime_generic: #module_runtime_trait, ))
} else {
None
};
let storage_name = &storage_def.name;
let optional_instance_generic = def.module_instance.as_ref().map(|i| {
let instance_generic = &i.instance_generic;
quote!( #instance_generic )
});
let storage_struct = quote!(
#storage_name<#optional_storage_runtime_comma #optional_instance_generic>
);
let optional_storage_where_clause = if is_generic {
def.where_clause.as_ref().map(|w| quote!( #w ))
} else {
None
};
let storage_trait_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 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),
pub enum StorageLineTypeDef {
Map(MapDef),
LinkedMap(MapDef),
DoubleMap(DoubleMapDef),
Simple(syn::Type),
}
#[derive(Parse, ToTokens, Debug)]
struct DeclStorageMap {
pub map_keyword: keyword::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 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 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()
}
@@ -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,
})
}
@@ -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
}
@@ -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
}
)
}
File diff suppressed because it is too large Load Diff
@@ -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
}
}