decl_storage as a proc_macro (#1176)

* `decl_storage` parsing of the macro (TODO change tool crate structure)

* Start formatting, for now use inner macro.
Still missing optional formating last part (genesis ...).

* Calling extra genesis macro

* decl_storage lines parsing.

* genesis macro as quote (need some cleaning reorg)

* dirty $crate substitute

* proc crate reorg.

* PR impl : skip usage of phantom data, it only applies in test and
council (others required it).

* Remaining macro of decl_storage, warning stringify behave sometime
oddly.

* Formatting code and some cleaning.

* Include line parsing to main struct (cannot use existing macro anymore).

* Remove genesis phantom data when there is already a field with type
parameter.

* Revert wasm files

* Remove old version of `decl_storage`.

* Fix false positive for phantom trait (additional type check on config
build).

* slight changes:
- return token errors instead of panic
- do not use useless intermediate vec

* Update srml/support/procedural/tools/derive/src/lib.rs

remove indent

Co-Authored-By: cheme <emericchevalier.pro@gmail.com>

* Switch iterations to fold, remove unused import.
This commit is contained in:
cheme
2018-12-09 21:09:39 +01:00
committed by Bastian Köcher
parent 3ffb45e3c8
commit ed5b78eaf0
34 changed files with 1681 additions and 1427 deletions
@@ -0,0 +1,59 @@
// Copyright 2017-2018 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/>.
// tag::description[]
//! Proc macro of Support code for the runtime.
// end::description[]
#![recursion_limit="256"]
extern crate proc_macro;
extern crate proc_macro2;
#[macro_use]
extern crate syn;
#[macro_use]
extern crate quote;
#[macro_use]
extern crate srml_support_procedural_tools;
mod storage;
use proc_macro::TokenStream;
/// Declares strongly-typed wrappers around codec-compatible types in storage.
///
/// ## Example
///
/// ```compile_fail
/// decl_storage! {
/// trait Store for Module<T: Trait> as Example {
/// Dummy get(dummy) config(): Option<T::Balance>;
/// Foo get(foo) config(): T::Balance;
/// }
/// }
/// ```
///
/// For now we implement a convenience trait with pre-specialised associated types, one for each
/// storage item. This allows you to gain access to publicly visible storage items from a
/// module type. Currently you must disambiguate by using `<Module as Store>::Item` rather than
/// the simpler `Module::Item`. Hopefully the rust guys with fix this soon.
#[proc_macro]
pub fn decl_storage(input: TokenStream) -> TokenStream {
storage::transformation::decl_storage_impl(input)
}
@@ -0,0 +1,144 @@
// Copyright 2017-2018 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/>.
// tag::description[]
//! `decl_storage` macro
// end::description[]
use srml_support_procedural_tools::syn_ext as ext;
use syn::Ident;
use syn::token::CustomKeyword;
pub mod transformation;
/// Parsing usage only
#[derive(ParseStruct, ToTokensStruct, Debug)]
struct StorageDefinition {
pub hidden_crate: Option<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_gt_token: Token![>],
pub as_token: Token![as],
pub crate_ident: Ident,
pub content: ext::Braces<ext::Punctuated<DeclStorageLine, Token![;]>>,
pub extra_genesis: Option<AddExtraGenesis>,
}
#[derive(ParseStruct, ToTokensStruct, Debug)]
struct SpecificHiddenCrate {
pub keyword: ext::CustomToken<SpecificHiddenCrate>,
pub ident: ext::Parens<Ident>,
}
#[derive(ParseStruct, ToTokensStruct, Debug)]
struct AddExtraGenesis {
pub extragenesis_keyword: ext::CustomToken<AddExtraGenesis>,
pub content: ext::Braces<AddExtraGenesisContent>,
}
#[derive(ParseStruct, ToTokensStruct, Debug)]
struct AddExtraGenesisContent {
pub lines: ext::Punctuated<AddExtraGenesisLineEnum, Token![;]>,
}
#[derive(ParseEnum, ToTokensEnum, Debug)]
enum AddExtraGenesisLineEnum {
AddExtraGenesisLine(AddExtraGenesisLine),
AddExtraGenesisBuild(DeclStorageBuild),
}
#[derive(ParseStruct, ToTokensStruct, Debug)]
struct AddExtraGenesisLine {
pub attrs: ext::OuterAttributes,
pub config_keyword: ext::CustomToken<ConfigKeyword>,
pub extra_field: ext::Parens<Ident>,
pub coldot_token: Token![:],
pub extra_type: syn::Type,
// TODO use a custom ext::Option instead (syn option on '=' fails)
pub default_value: ext::Seq<DeclStorageDefault>,
}
#[derive(ParseStruct, ToTokensStruct, 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: Option<DeclStorageGetter>,
pub config: Option<DeclStorageConfig>,
pub build: Option<DeclStorageBuild>,
pub coldot_token: Token![:],
pub storage_type: DeclStorageType,
// TODO use a custom ext::Option instead (syn option on '=' fails)
pub default_value: ext::Seq<DeclStorageDefault>,
}
#[derive(ParseStruct, ToTokensStruct, Debug)]
struct DeclStorageGetter {
pub getter_keyword: ext::CustomToken<DeclStorageGetter>,
pub getfn: ext::Parens<Ident>,
}
#[derive(ParseStruct, ToTokensStruct, Debug)]
struct DeclStorageConfig {
pub config_keyword: ext::CustomToken<DeclStorageConfig>,
pub expr: ext::Parens<Option<syn::Ident>>,
}
#[derive(ParseStruct, ToTokensStruct, Debug)]
struct DeclStorageBuild {
pub build_keyword: ext::CustomToken<DeclStorageBuild>,
pub expr: ext::Parens<syn::Expr>,
}
#[derive(ParseEnum, ToTokensEnum, Debug)]
enum DeclStorageType {
Map(DeclStorageMap),
Simple(syn::Type),
}
#[derive(ParseStruct, ToTokensStruct, Debug)]
struct DeclStorageMap {
pub map_keyword: ext::CustomToken<MapKeyword>,
pub key: syn::Type,
pub ass_keyword: Token![=>],
pub value: syn::Type,
}
#[derive(ParseStruct, ToTokensStruct, Debug)]
struct DeclStorageDefault {
pub equal_token: Token![=],
pub expr: syn::Expr,
}
custom_keyword_impl!(SpecificHiddenCrate, "hiddencrate", "hiddencrate as keyword");
custom_keyword_impl!(DeclStorageConfig, "config", "build as keyword");
custom_keyword!(ConfigKeyword, "config", "config as keyword");
custom_keyword!(BuildKeyword, "build", "build as keyword");
custom_keyword_impl!(DeclStorageBuild, "build", "storage build config");
custom_keyword_impl!(AddExtraGenesis, "add_extra_genesis", "storage extra genesis");
custom_keyword_impl!(DeclStorageGetter, "get", "storage getter");
custom_keyword!(MapKeyword, "map", "map as keyword");
@@ -0,0 +1,636 @@
// Copyright 2017-2018 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/>.
// tag::description[]
//! `decl_storage` macro transformation
// end::description[]
use srml_support_procedural_tools::syn_ext as ext;
use srml_support_procedural_tools::{generate_crate_access, generate_hidden_includes};
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use syn::{
Ident,
GenericParam,
spanned::Spanned,
parse::{
Error,
Result,
}
};
use super::*;
// 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);
// old macro naming convention (s replaces $)
let StorageDefinition {
hidden_crate,
visibility,
ident: storetype,
module_ident,
mod_param: strait,
crate_ident: cratename,
content: ext::Braces { content: storage_lines, ..},
extra_genesis,
..
} = def;
let hidden_crate_name = hidden_crate.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",
"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,
&storage_lines,
&extra_genesis,
));
let decl_storage_items = decl_storage_items(
&scrate,
&traitinstance,
&traittype,
&cratename,
&storage_lines,
);
let decl_store_items = decl_store_items(
&storage_lines,
);
let impl_store_items = impl_store_items(
&traitinstance,
&storage_lines,
);
let impl_store_fns = impl_store_fns(
&scrate,
&traitinstance,
&storage_lines,
);
let store_functions_to_metadata = store_functions_to_metadata(
&scrate,
&storage_lines,
);
let cratename_string = cratename.to_string();
let expanded = quote! {
#scrate_decl
#decl_storage_items
#visibility trait #storetype {
#decl_store_items
}
impl<#traitinstance: #traittype> #storetype for #module_ident<#traitinstance> {
#impl_store_items
}
impl<#traitinstance: #traittype> #module_ident<#traitinstance> {
#impl_store_fns
pub fn store_metadata() -> #scrate::storage::generator::StorageMetadata {
#scrate::storage::generator::StorageMetadata {
prefix: #scrate::storage::generator::DecodeDifferent::Encode(#cratename_string),
functions: #store_functions_to_metadata ,
}
}
}
#extra_genesis
};
expanded.into()
}
fn decl_store_extra_genesis(
scrate: &TokenStream2,
traitinstance: &Ident,
traittype: &syn::TypeParamBound,
storage_lines: &ext::Punctuated<DeclStorageLine, Token![;]>,
extra_genesis: &Option<AddExtraGenesis>,
) -> Result<TokenStream2> {
let mut is_trait_needed = false;
let mut has_trait_field = false;
let mut config_field = TokenStream2::new();
let mut config_field_default = TokenStream2::new();
let mut builders = TokenStream2::new();
for sline in storage_lines.inner.iter() {
let DeclStorageLine {
name,
getter,
config,
build,
storage_type,
default_value,
..
} = sline;
let is_simple = if let DeclStorageType::Simple(..) = storage_type { true } else { false };
let mut opt_build;
// need build line
if let (Some(ref getter), Some(ref config)) = (getter, config) {
let ident = if let Some(ident) = config.expr.content.as_ref() {
quote!( #ident )
} else {
let ident = &getter.getfn.content;
quote!( #ident )
};
let option_extracteed = if let DeclStorageType::Simple(ref st) = storage_type {
if ext::has_parametric_type(st, traitinstance) {
is_trait_needed = true;
has_trait_field = true;
}
ext::extract_type_option(st)
} else { None };
let is_option = option_extracteed.is_some();
let storage_type = option_extracteed.unwrap_or_else(|| quote!( #storage_type ));
config_field.extend(quote!( pub #ident: #storage_type, ));
opt_build = Some(build.as_ref().map(|b| &b.expr.content).map(|b|quote!( #b ))
.unwrap_or_else(|| quote!( (|config: &GenesisConfig<#traitinstance>| config.#ident.clone()) )));
let fielddefault = default_value.inner.get(0).as_ref().map(|d| &d.expr).map(|d|
if is_option {
quote!( #d.unwrap_or_default() )
} else {
quote!( #d )
}).unwrap_or_else(|| quote!( Default::default() ));
config_field_default.extend(quote!( #ident: #fielddefault, ));
} else {
opt_build = build.as_ref().map(|b| &b.expr.content).map(|b| quote!( #b ));
}
if let Some(builder) = opt_build {
is_trait_needed = true;
if is_simple {
builders.extend(quote!{{
use #scrate::codec::Encode;
let v = (#builder)(&self);
r.insert(Self::hash(<#name<#traitinstance>>::key()).to_vec(), v.encode());
}});
} else {
builders.extend(quote!{{
use #scrate::codec::Encode;
let data = (#builder)(&self);
for (k, v) in data.into_iter() {
r.insert(Self::hash(&<#name<#traitinstance>>::key_for(k)).to_vec(), v.encode());
}
}});
}
}
}
let mut has_scall = false;
let mut scall = quote!{ ( |_, _, _| {} ) };
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::has_parametric_type(&extra_type, traitinstance) {
is_trait_needed = true;
has_trait_field = true;
}
let extrafield = &extra_field.content;
genesis_extrafields.extend(quote!{
#attrs pub #extrafield: #extra_type,
});
let extra_default = default_value.inner.get(0).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"));
}
let content = &expr.content;
scall = quote!( ( #content ) );
has_scall = true;
},
}
}
}
let is_extra_genesis_needed = has_scall
|| !config_field.is_empty()
|| !genesis_extrafields.is_empty()
|| !builders.is_empty();
Ok(if is_extra_genesis_needed {
let (fparam, sparam, ph_field, ph_default) = if is_trait_needed {
if has_trait_field {
// no phantom data required
(
quote!(<#traitinstance: #traittype>),
quote!(<#traitinstance>),
quote!(),
quote!(),
)
} else {
// need phantom data
(
quote!(<#traitinstance: #traittype>),
quote!(<#traitinstance>),
quote!{
#[serde(skip)]
pub _genesis_phantom_data: #scrate::storage::generator::PhantomData<#traitinstance>,
},
quote!{
_genesis_phantom_data: Default::default(),
},
)
}
} else {
// do not even need type parameter
(quote!(), quote!(), quote!(), quote!())
};
quote!{
#[derive(Serialize, Deserialize)]
#[cfg(feature = "std")]
#[serde(rename_all = "camelCase")]
#[serde(deny_unknown_fields)]
pub struct GenesisConfig#fparam {
#ph_field
#config_field
#genesis_extrafields
}
#[cfg(feature = "std")]
impl#fparam Default for GenesisConfig#sparam {
fn default() -> Self {
GenesisConfig {
#ph_default
#config_field_default
#genesis_extrafields_default
}
}
}
#[cfg(feature = "std")]
impl#fparam #scrate::runtime_primitives::BuildStorage for GenesisConfig#sparam {
fn build_storage(self) -> ::std::result::Result<(#scrate::runtime_primitives::StorageMap, #scrate::runtime_primitives::ChildrenStorageMap), String> {
let mut r: #scrate::runtime_primitives::StorageMap = Default::default();
let mut c: #scrate::runtime_primitives::ChildrenStorageMap = Default::default();
#builders
#scall(&mut r, &mut c, &self);
Ok((r, c))
}
}
}
} else {
quote!()
})
}
fn decl_storage_items(
scrate: &TokenStream2,
traitinstance: &Ident,
traittype: &syn::TypeParamBound,
cratename: &Ident,
storage_lines: &ext::Punctuated<DeclStorageLine, Token![;]>,
) -> TokenStream2 {
let mut impls = TokenStream2::new();
for sline in storage_lines.inner.iter() {
let DeclStorageLine {
name,
storage_type,
default_value,
visibility,
..
} = sline;
let (is_simple, extracted_opt, stk, gettype) = match storage_type {
DeclStorageType::Simple(ref st) => (true, ext::extract_type_option(st), None, st),
DeclStorageType::Map(ref map) => (false, ext::extract_type_option(&map.value), Some(&map.key), &map.value),
};
let is_option = extracted_opt.is_some();
let fielddefault = default_value.inner.get(0).as_ref().map(|d| &d.expr).map(|d| quote!( #d ))
.unwrap_or_else(|| quote!{ Default::default() });
let typ = extracted_opt.unwrap_or(quote!( #gettype ));
let option_simple_1 = if !is_option {
// raw type case
quote!( unwrap_or_else )
} else {
// Option<> type case
quote!( or_else )
};
let implementation = if is_simple {
let mutate_impl = if !is_option {
quote!{
<Self as #scrate::storage::generator::StorageValue<#typ>>::put(&val, storage)
}
} else {
quote!{
match val {
Some(ref val) => <Self as #scrate::storage::generator::StorageValue<#typ>>::put(&val, storage),
None => <Self as #scrate::storage::generator::StorageValue<#typ>>::kill(storage),
}
}
};
let key_string = cratename.to_string() + " " + &name.to_string();
// generator for value
quote!{
#visibility struct #name<#traitinstance: #traittype>(#scrate::storage::generator::PhantomData<#traitinstance>);
impl<#traitinstance: #traittype> #scrate::storage::generator::StorageValue<#typ> for #name<#traitinstance> {
type Query = #gettype;
/// Get the storage key.
fn key() -> &'static [u8] {
#key_string.as_bytes()
}
/// Load the value from the provided storage instance.
fn get<S: #scrate::GenericStorage>(storage: &S) -> Self::Query {
storage.get(<#name<#traitinstance> as #scrate::storage::generator::StorageValue<#typ>>::key())
.#option_simple_1(|| #fielddefault)
}
/// Take a value from storage, removing it afterwards.
fn take<S: #scrate::GenericStorage>(storage: &S) -> Self::Query {
storage.take(<#name<#traitinstance> as #scrate::storage::generator::StorageValue<#typ>>::key())
.#option_simple_1(|| #fielddefault)
}
/// Mutate the value under a key.
fn mutate<R, F: FnOnce(&mut Self::Query) -> R, S: #scrate::GenericStorage>(f: F, storage: &S) -> R {
let mut val = <Self as #scrate::storage::generator::StorageValue<#typ>>::get(storage);
let ret = f(&mut val);
#mutate_impl ;
ret
}
}
}
} else {
let kty = stk.expect("is not simple; qed");
let mutate_impl = if !is_option {
quote!{
<Self as #scrate::storage::generator::StorageMap<#kty, #typ>>::insert(key, &val, storage)
}
} else {
quote!{
match val {
Some(ref val) => <Self as #scrate::storage::generator::StorageMap<#kty, #typ>>::insert(key, &val, storage),
None => <Self as #scrate::storage::generator::StorageMap<#kty, #typ>>::remove(key, storage),
}
}
};
let prefix_string = cratename.to_string() + " " + &name.to_string();
// generator for map
quote!{
#visibility struct #name<#traitinstance: #traittype>(#scrate::storage::generator::PhantomData<#traitinstance>);
impl<#traitinstance: #traittype> #scrate::storage::generator::StorageMap<#kty, #typ> for #name<#traitinstance> {
type Query = #gettype;
/// Get the prefix key in storage.
fn prefix() -> &'static [u8] {
#prefix_string.as_bytes()
}
/// Get the storage key used to fetch a value corresponding to a specific key.
fn key_for(x: &#kty) -> #scrate::rstd::vec::Vec<u8> {
let mut key = <#name<#traitinstance> as #scrate::storage::generator::StorageMap<#kty, #typ>>::prefix().to_vec();
#scrate::codec::Encode::encode_to(x, &mut key);
key
}
/// Load the value associated with the given key from the map.
fn get<S: #scrate::GenericStorage>(key: &#kty, storage: &S) -> Self::Query {
let key = <#name<#traitinstance> as #scrate::storage::generator::StorageMap<#kty, #typ>>::key_for(key);
storage.get(&key[..]).#option_simple_1(|| #fielddefault)
}
/// Take the value, reading and removing it.
fn take<S: #scrate::GenericStorage>(key: &#kty, storage: &S) -> Self::Query {
let key = <#name<#traitinstance> as #scrate::storage::generator::StorageMap<#kty, #typ>>::key_for(key);
storage.take(&key[..]).#option_simple_1(|| #fielddefault)
}
/// Mutate the value under a key
fn mutate<R, F: FnOnce(&mut Self::Query) -> R, S: #scrate::GenericStorage>(key: &#kty, f: F, storage: &S) -> R {
let mut val = <Self as #scrate::storage::generator::StorageMap<#kty, #typ>>::take(key, storage);
let ret = f(&mut val);
#mutate_impl ;
ret
}
}
}
};
impls.extend(implementation)
}
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,
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 = #name<#traitinstance>;));
items
})
}
fn impl_store_fns(
scrate: &TokenStream2,
traitinstance: &Ident,
storage_lines: &ext::Punctuated<DeclStorageLine, Token![;]>,
) -> TokenStream2 {
let mut items = TokenStream2::new();
for sline in storage_lines.inner.iter() {
let DeclStorageLine {
name,
getter,
storage_type,
..
} = sline;
if let Some(getter) = getter {
let get_fn = &getter.getfn.content;
let (is_simple, extracted_opt, stk, gettype) = match storage_type {
DeclStorageType::Simple(ref st) => (true, ext::extract_type_option(st), None, st),
DeclStorageType::Map(ref map) => (false, ext::extract_type_option(&map.value), Some(&map.key), &map.value),
};
let typ = extracted_opt.unwrap_or(quote!(#gettype));
let item = if is_simple {
quote!{
pub fn #get_fn() -> #gettype {
<#name<#traitinstance> as #scrate::storage::generator::StorageValue<#typ>> :: get(&#scrate::storage::RuntimeStorage)
}
}
} else {
let kty = stk.expect("is not simple; qed");
// map
quote!{
pub fn #get_fn<K: #scrate::storage::generator::Borrow<#kty>>(key: K) -> #gettype {
<#name<#traitinstance> as #scrate::storage::generator::StorageMap<#kty, #typ>> :: get(key.borrow(), &#scrate::storage::RuntimeStorage)
}
}
};
items.extend(item);
}
}
items
}
fn store_functions_to_metadata (
scrate: &TokenStream2,
storage_lines: &ext::Punctuated<DeclStorageLine, Token![;]>,
) -> TokenStream2 {
let mut items = TokenStream2::new();
for sline in storage_lines.inner.iter() {
let DeclStorageLine {
attrs,
name,
storage_type,
..
} = sline;
let (is_simple, extracted_opt, stk, gettype) = match storage_type {
DeclStorageType::Simple(ref st) => (true, ext::extract_type_option(st), None, st),
DeclStorageType::Map(ref map) => (false, ext::extract_type_option(&map.value), Some(&map.key), &map.value),
};
let is_option = extracted_opt.is_some();
let typ = extracted_opt.unwrap_or(quote!( #gettype ));
let stype = if is_simple {
let styp = typ.to_string().replace(" ","");
quote!{
#scrate::storage::generator::StorageFunctionType::Plain(
#scrate::storage::generator::DecodeDifferent::Encode(#styp),
)
}
} else {
let kty = stk.expect("is not simple; qed");
let kty = quote!(#kty).to_string();
let styp = typ.to_string().replace(" ","");
quote!{
#scrate::storage::generator::StorageFunctionType::Map {
key: #scrate::storage::generator::DecodeDifferent::Encode(#kty),
value: #scrate::storage::generator::DecodeDifferent::Encode(#styp),
}
}
};
let modifier = if !is_option {
quote!{
#scrate::storage::generator::StorageFunctionModifier::Default
}
} else {
quote!{
#scrate::storage::generator::StorageFunctionModifier::Optional
}
};
let mut docs = TokenStream2::new();
for attr in attrs.inner.iter().filter_map(|v| v.interpret_meta()) {
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 item = quote! {
#scrate::storage::generator::StorageFunctionMetadata {
name: #scrate::storage::generator::DecodeDifferent::Encode(#str_name),
modifier: #modifier,
ty: #stype,
documentation: #scrate::storage::generator::DecodeDifferent::Encode(&[ #docs ]),
},
};
items.extend(item);
}
quote!{
#scrate::storage::generator::DecodeDifferent::Encode(&[
#items
])
}
}