mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-23 22:31:06 +00:00
Rename PAINT to PALETTE (#4161)
* /paint to /palette * rename paint to palette * rename the modules in palette to be pallets * update Structure.adoc * bump impl * fix CI directory * Update docs/Structure.adoc Co-Authored-By: Benjamin Kampmann <ben@gnunicorn.org>
This commit is contained in:
committed by
Benjamin Kampmann
parent
512c86a72f
commit
2783b44207
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "palette-support-procedural"
|
||||
version = "2.0.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
palette-support-procedural-tools = { package = "palette-support-procedural-tools", path = "./tools" }
|
||||
|
||||
proc-macro2 = "1.0.6"
|
||||
quote = "1.0.2"
|
||||
syn = { version = "1.0.7", features = ["full"] }
|
||||
@@ -0,0 +1,221 @@
|
||||
// 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/>.
|
||||
|
||||
// tag::description[]
|
||||
//! Proc macro of Support code for the runtime.
|
||||
// end::description[]
|
||||
|
||||
#![recursion_limit="512"]
|
||||
|
||||
extern crate proc_macro;
|
||||
|
||||
mod storage;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
/// Declares strongly-typed wrappers around codec-compatible types in storage.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```nocompile
|
||||
/// decl_storage! {
|
||||
/// trait Store for Module<T: Trait> as Example {
|
||||
/// Foo get(fn foo) config(): u32=12;
|
||||
/// Bar: map u32 => u32;
|
||||
/// pub Zed build(|config| vec![(0, 0)]): linked_map u32 => u32;
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Declaration is set with the header `(pub) trait Store for Module<T: Trait> as Example`,
|
||||
/// with `Store` a (pub) trait generated associating each storage item to the `Module` and
|
||||
/// `as Example` setting the prefix used for storage items of this module. `Example` must be unique:
|
||||
/// another module with the same name and the same inner storage item name will conflict.
|
||||
/// `Example` is called the module prefix.
|
||||
///
|
||||
/// note: For instantiable modules the module prefix is prepended with instance
|
||||
/// prefix. Instance prefix is "" for default instance and "Instance$n" for instance number $n.
|
||||
/// Thus, instance 3 of module Example has a module prefix of `Instance3Example`
|
||||
///
|
||||
/// Basic storage consists of a name and a type; supported types are:
|
||||
///
|
||||
/// * Value: `Foo: type`: Implements the
|
||||
/// [`StorageValue`](../palette_support/storage/trait.StorageValue.html) trait using the
|
||||
/// [`StorageValue generator`](../palette_support/storage/generator/trait.StorageValue.html).
|
||||
/// The generator `unhashed_key` is `$module_prefix ++ " " ++ $storage_name`
|
||||
///
|
||||
/// * Map: `Foo: map hasher($hash) type => type`: Implements the
|
||||
/// [`StorageMap`](../palette_support/storage/trait.StorageMap.html) trait using the
|
||||
/// [`StorageMap generator`](../palette_support/storage/generator/trait.StorageMap.html).
|
||||
///
|
||||
/// `$hash` representing a choice of hashing algorithms available in the
|
||||
/// [`Hashable`](../palette_support/trait.Hashable.html) trait.
|
||||
///
|
||||
/// `hasher($hash)` is optional and its default is `blake2_256`. One should use another hasher
|
||||
/// with care, see generator documentation.
|
||||
///
|
||||
/// The generator is implemented with:
|
||||
/// * `prefix`: `$module_prefix ++ " " ++ $storage_name`
|
||||
/// * `Hasher`: $hash
|
||||
///
|
||||
/// * Linked map: `Foo: linked_map hasher($hash) type => type`: Implements the
|
||||
/// [`StorageLinkedMap`](../palette_support/storage/trait.StorageLinkedMap.html) trait using the
|
||||
/// [`StorageLinkedMap generator`](../palette_support/storage/generator/trait.StorageLinkedMap.html).
|
||||
///
|
||||
/// `$hash` representing a choice of hashing algorithms available in the
|
||||
/// [`Hashable`](../palette_support/trait.Hashable.html) trait.
|
||||
///
|
||||
/// `hasher($hash)` is optional and its default is `blake2_256`. One should use another hasher
|
||||
/// with care, see generator documentation.
|
||||
///
|
||||
/// The generator is implemented with:
|
||||
/// * `prefix`: `$module_prefix ++ " " ++ $storage_name`
|
||||
/// * `head_key`: `"head of " ++ $module_prefix ++ " " ++ $storage_name`
|
||||
/// * `Hasher`: $hash
|
||||
///
|
||||
/// All key formatting logic can be accessed in a type-agnostic format via the
|
||||
/// [`KeyFormat`](../srml_support/storage/generator/trait.KeyFormat.html) trait, which
|
||||
/// is implemented for the storage linked map type as well.
|
||||
///
|
||||
/// * Double map: `Foo: double_map hasher($hash1) u32, $hash2(u32) => u32`: Implements the
|
||||
/// [`StorageDoubleMap`](../palette_support/storage/trait.StorageDoubleMap.html) trait using the
|
||||
/// [`StorageDoubleMap generator`](../palette_support/storage/generator/trait.StorageDoubleMap.html).
|
||||
///
|
||||
/// `$hash1` and `$hash2` representing choices of hashing algorithms available in the
|
||||
/// [`Hashable`](../palette_support/trait.Hashable.html) trait. They must be choosen with care, see
|
||||
/// generator documentation.
|
||||
///
|
||||
/// `hasher($hash)` is optional and its default is `blake2_256`.
|
||||
///
|
||||
/// `hasher($hash)` is optional and its default is `blake2_256`. One should use another hasher
|
||||
/// with care, see generator documentation.
|
||||
///
|
||||
/// If the first key is untrusted, a cryptographic `hasher` such as `blake2_256` must be used.
|
||||
/// Otherwise, other values of all storage items can be compromised.
|
||||
///
|
||||
/// If the second key is untrusted, a cryptographic `hasher` such as `blake2_256` must be used.
|
||||
/// Otherwise, other items in storage with the same first key can be compromised.
|
||||
///
|
||||
/// The generator is implemented with:
|
||||
/// * `key1_prefix`: `$module_prefix ++ " " ++ $storage_name`
|
||||
/// * `Hasher1`: $hash1
|
||||
/// * `Hasher2`: $hash2
|
||||
///
|
||||
/// Supported hashers (ordered from least to best security):
|
||||
///
|
||||
/// * `twox_64_concat` - TwoX with 64bit + key concatenated.
|
||||
/// * `twox_128` - TwoX with 128bit.
|
||||
/// * `twox_256` - TwoX with with 256bit.
|
||||
/// * `blake2_128` - Blake2 with 128bit.
|
||||
/// * `blake2_256` - Blake2 with 256bit.
|
||||
///
|
||||
/// Basic storage can be extended as such:
|
||||
///
|
||||
/// `#vis #name get(fn #getter) config(#field_name) build(#closure): #type = #default;`
|
||||
///
|
||||
/// * `#vis`: Set the visibility of the structure. `pub` or nothing.
|
||||
/// * `#name`: Name of the storage item, used as a prefix in storage.
|
||||
/// * [optional] `get(fn #getter)`: Implements the function #getter to `Module`.
|
||||
/// * [optional] `config(#field_name)`: `field_name` is optional if get is set.
|
||||
/// Will include the item in `GenesisConfig`.
|
||||
/// * [optional] `build(#closure)`: Closure called with storage overlays.
|
||||
/// * `#type`: Storage type.
|
||||
/// * [optional] `#default`: Value returned when none.
|
||||
///
|
||||
/// Storage items are accessible in multiple ways:
|
||||
///
|
||||
/// * The structure: `Foo` or `Foo::<T>` depending if the value type is generic or not.
|
||||
/// * The `Store` trait structure: `<Module<T> as Store>::Foo`
|
||||
/// * The getter on the module that calls get on the structure: `Module::<T>::foo()`
|
||||
///
|
||||
/// ## GenesisConfig
|
||||
///
|
||||
/// An optional `GenesisConfig` struct for storage initialization can be defined, either
|
||||
/// when at least one storage field requires default initialization
|
||||
/// (both `get` and `config` or `build`), or specifically as in:
|
||||
///
|
||||
/// ```nocompile
|
||||
/// decl_storage! {
|
||||
/// trait Store for Module<T: Trait> as Example {
|
||||
///
|
||||
/// // Your storage items
|
||||
/// }
|
||||
/// add_extra_genesis {
|
||||
/// config(genesis_field): GenesisFieldType;
|
||||
/// config(genesis_field2): GenesisFieldType;
|
||||
/// ...
|
||||
/// build(|_: &Self| {
|
||||
/// // Modification of storage
|
||||
/// })
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This struct can be exposed as `ExampleConfig` by the `construct_runtime!` macro like follows:
|
||||
///
|
||||
/// ```nocompile
|
||||
/// construct_runtime!(
|
||||
/// pub enum Runtume with ... {
|
||||
/// ...,
|
||||
/// Example: example::{Module, Storage, ..., Config<T>},
|
||||
/// ...,
|
||||
/// }
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// ### Module with Instances
|
||||
///
|
||||
/// The `decl_storage!` macro supports building modules with instances with the following syntax
|
||||
/// (`DefaultInstance` type is optional):
|
||||
///
|
||||
/// ```nocompile
|
||||
/// trait Store for Module<T: Trait<I>, I: Instance=DefaultInstance> as Example {}
|
||||
/// ```
|
||||
///
|
||||
/// Accessing the structure no requires the instance as generic parameter:
|
||||
/// * `Foo::<I>` if the value type is not generic
|
||||
/// * `Foo::<T, I>` if the value type is generic
|
||||
///
|
||||
/// ## Where clause
|
||||
///
|
||||
/// This macro supports a where clause which will be replicated to all generated types.
|
||||
///
|
||||
/// ```nocompile
|
||||
/// trait Store for Module<T: Trait> as Example where T::AccountId: std::fmt::Display {}
|
||||
/// ```
|
||||
///
|
||||
/// ## Limitations
|
||||
///
|
||||
/// # Instancing and generic `GenesisConfig`
|
||||
///
|
||||
/// If your module supports instancing and you see an error like `parameter `I` is never used` for
|
||||
/// your `decl_storage!`, you are hitting a limitation of the current implementation. You probably
|
||||
/// try to use an associated type of a non-instantiable trait. To solve this, add the following to
|
||||
/// your macro call:
|
||||
///
|
||||
/// ```nocompile
|
||||
/// add_extra_genesis {
|
||||
/// config(phantom): std::marker::PhantomData<I>,
|
||||
/// }
|
||||
/// ...
|
||||
///
|
||||
/// This adds a field to your `GenesisConfig` with the name `phantom` that you can initialize with
|
||||
/// `Default::default()`.
|
||||
///
|
||||
#[proc_macro]
|
||||
pub fn decl_storage(input: TokenStream) -> TokenStream {
|
||||
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 palette_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,
|
||||
}
|
||||
}
|
||||
}
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
// 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 palette_support_procedural_tools::syn_ext as ext;
|
||||
use proc_macro2::TokenStream;
|
||||
use syn::{spanned::Spanned, parse_quote};
|
||||
use quote::quote;
|
||||
use super::super::{DeclStorageDefExt, StorageLineTypeDef};
|
||||
|
||||
pub struct GenesisConfigFieldDef {
|
||||
pub name: syn::Ident,
|
||||
pub typ: syn::Type,
|
||||
pub attrs: Vec<syn::Meta>,
|
||||
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) -> syn::Result<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)
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
is_generic,
|
||||
fields,
|
||||
genesis_struct_decl,
|
||||
genesis_struct,
|
||||
genesis_impl,
|
||||
genesis_where_clause,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_genesis_config_field_defs(def: &DeclStorageDefExt)
|
||||
-> syn::Result<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 {
|
||||
name: config_field,
|
||||
typ,
|
||||
attrs: line.doc_attrs.clone(),
|
||||
default,
|
||||
});
|
||||
}
|
||||
|
||||
for line in &def.extra_genesis_config_lines {
|
||||
let attrs = line.attrs.iter()
|
||||
.map(|attr| {
|
||||
let meta = attr.parse_meta()?;
|
||||
if meta.path().is_ident("cfg") {
|
||||
return Err(syn::Error::new(
|
||||
meta.span(),
|
||||
"extra genesis config items do not support `cfg` attribute"
|
||||
));
|
||||
}
|
||||
Ok(meta)
|
||||
})
|
||||
.collect::<syn::Result<_>>()?;
|
||||
|
||||
let default = line.default.as_ref().map(|e| quote!( #e ))
|
||||
.unwrap_or_else(|| quote!( Default::default() ));
|
||||
|
||||
|
||||
config_field_defs.push(GenesisConfigFieldDef {
|
||||
name: line.name.clone(),
|
||||
typ: line.typ.clone(),
|
||||
attrs,
|
||||
default,
|
||||
});
|
||||
}
|
||||
|
||||
Ok(config_field_defs)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
// 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, attrs) = (&field.name, &field.typ, &field.attrs);
|
||||
quote!( #( #[ #attrs] )* pub #name: #typ, )
|
||||
});
|
||||
|
||||
let config_field_defaults = genesis_config.fields.iter().map(|field| {
|
||||
let (name, default) = (&field.name, &field.default);
|
||||
quote!( #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::BasicExternalities::execute_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 = match GenesisConfigDef::from_def(def) {
|
||||
Ok(genesis_config) => genesis_config,
|
||||
Err(err) => return err.to_compile_error(),
|
||||
};
|
||||
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
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
// 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
|
||||
#[derive(
|
||||
Clone, Eq, PartialEq,
|
||||
#scrate::codec::Encode,
|
||||
#scrate::codec::Decode,
|
||||
#scrate::RuntimeDebug,
|
||||
)]
|
||||
#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 palette_support_procedural_tools::clean_type_string;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use super::{DeclStorageDefExt, StorageLineDefExt, StorageLineTypeDef};
|
||||
|
||||
fn storage_line_metadata_type(scrate: &TokenStream, line: &StorageLineDefExt) -> TokenStream {
|
||||
let value_type = &line.value_type;
|
||||
let value_type = clean_type_string("e!( #value_type ).to_string());
|
||||
match &line.storage_type {
|
||||
StorageLineTypeDef::Simple(_) => {
|
||||
quote!{
|
||||
#scrate::metadata::StorageEntryType::Plain(
|
||||
#scrate::metadata::DecodeDifferent::Encode(#value_type),
|
||||
)
|
||||
}
|
||||
},
|
||||
StorageLineTypeDef::Map(map) => {
|
||||
let hasher = map.hasher.into_metadata();
|
||||
let key = &map.key;
|
||||
let key = clean_type_string("e!(#key).to_string());
|
||||
quote!{
|
||||
#scrate::metadata::StorageEntryType::Map {
|
||||
hasher: #scrate::metadata::#hasher,
|
||||
key: #scrate::metadata::DecodeDifferent::Encode(#key),
|
||||
value: #scrate::metadata::DecodeDifferent::Encode(#value_type),
|
||||
is_linked: false,
|
||||
}
|
||||
}
|
||||
},
|
||||
StorageLineTypeDef::LinkedMap(map) => {
|
||||
let hasher = map.hasher.into_metadata();
|
||||
let key = &map.key;
|
||||
let key = clean_type_string("e!(#key).to_string());
|
||||
quote!{
|
||||
#scrate::metadata::StorageEntryType::Map {
|
||||
hasher: #scrate::metadata::#hasher,
|
||||
key: #scrate::metadata::DecodeDifferent::Encode(#key),
|
||||
value: #scrate::metadata::DecodeDifferent::Encode(#value_type),
|
||||
is_linked: true,
|
||||
}
|
||||
}
|
||||
},
|
||||
StorageLineTypeDef::DoubleMap(map) => {
|
||||
let hasher1 = map.hasher1.into_metadata();
|
||||
let hasher2 = map.hasher2.into_metadata();
|
||||
let key1 = &map.key1;
|
||||
let key1 = clean_type_string("e!(#key1).to_string());
|
||||
let key2 = &map.key2;
|
||||
let key2 = clean_type_string("e!(#key2).to_string());
|
||||
quote!{
|
||||
#scrate::metadata::StorageEntryType::DoubleMap {
|
||||
hasher: #scrate::metadata::#hasher1,
|
||||
key1: #scrate::metadata::DecodeDifferent::Encode(#key1),
|
||||
key2: #scrate::metadata::DecodeDifferent::Encode(#key2),
|
||||
value: #scrate::metadata::DecodeDifferent::Encode(#value_type),
|
||||
key2_hasher: #scrate::metadata::#hasher2,
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn default_byte_getter(
|
||||
scrate: &TokenStream,
|
||||
line: &StorageLineDefExt,
|
||||
def: &DeclStorageDefExt,
|
||||
) -> (TokenStream, TokenStream) {
|
||||
let default = line.default_value.as_ref().map(|d| quote!( #d ))
|
||||
.unwrap_or_else(|| quote!( Default::default() ));
|
||||
|
||||
let str_name = line.name.to_string();
|
||||
let struct_name = syn::Ident::new(&("__GetByteStruct".to_string() + &str_name), line.name.span());
|
||||
let cache_name = syn::Ident::new(&("__CACHE_GET_BYTE_STRUCT_".to_string() + &str_name), line.name.span());
|
||||
|
||||
let runtime_generic = &def.module_runtime_generic;
|
||||
let runtime_trait = &def.module_runtime_trait;
|
||||
let optional_instance_bound_optional_default = &def.optional_instance_bound_optional_default;
|
||||
let optional_instance_bound = &def.optional_instance_bound;
|
||||
let optional_instance = &def.optional_instance;
|
||||
let optional_comma_instance = optional_instance.as_ref().map(|i| quote!(, #i));
|
||||
let where_clause = &def.where_clause;
|
||||
|
||||
let query_type = &line.query_type;
|
||||
|
||||
let struct_def = quote! {
|
||||
#[doc(hidden)]
|
||||
pub struct #struct_name<
|
||||
#runtime_generic, #optional_instance_bound_optional_default
|
||||
>(pub #scrate::rstd::marker::PhantomData<(#runtime_generic #optional_comma_instance)>);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[allow(non_upper_case_globals)]
|
||||
static #cache_name: #scrate::once_cell::sync::OnceCell<#scrate::rstd::vec::Vec<u8>> =
|
||||
#scrate::once_cell::sync::OnceCell::new();
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<#runtime_generic: #runtime_trait, #optional_instance_bound>
|
||||
#scrate::metadata::DefaultByte
|
||||
for #struct_name<#runtime_generic, #optional_instance>
|
||||
#where_clause
|
||||
{
|
||||
fn default_byte(&self) -> #scrate::rstd::vec::Vec<u8> {
|
||||
use #scrate::codec::Encode;
|
||||
#cache_name.get_or_init(|| {
|
||||
let def_val: #query_type = #default;
|
||||
<#query_type as Encode>::encode(&def_val)
|
||||
}).clone()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<#runtime_generic: #runtime_trait, #optional_instance_bound> Send
|
||||
for #struct_name<#runtime_generic, #optional_instance> #where_clause {}
|
||||
|
||||
unsafe impl<#runtime_generic: #runtime_trait, #optional_instance_bound> Sync
|
||||
for #struct_name<#runtime_generic, #optional_instance> #where_clause {}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl<#runtime_generic: #runtime_trait, #optional_instance_bound>
|
||||
#scrate::metadata::DefaultByte
|
||||
for #struct_name<#runtime_generic, #optional_instance>
|
||||
#where_clause
|
||||
{
|
||||
fn default_byte(&self) -> #scrate::rstd::vec::Vec<u8> {
|
||||
use #scrate::codec::Encode;
|
||||
let def_val: #query_type = #default;
|
||||
<#query_type as Encode>::encode(&def_val)
|
||||
}
|
||||
}
|
||||
};
|
||||
let struct_instance = quote!(
|
||||
#struct_name::<#runtime_generic, #optional_instance>(#scrate::rstd::marker::PhantomData)
|
||||
);
|
||||
|
||||
(struct_def, struct_instance)
|
||||
}
|
||||
|
||||
pub fn impl_metadata(scrate: &TokenStream, def: &DeclStorageDefExt) -> TokenStream {
|
||||
let mut entries = TokenStream::new();
|
||||
let mut default_byte_getter_struct_defs = TokenStream::new();
|
||||
|
||||
for line in def.storage_lines.iter() {
|
||||
let str_name = line.name.to_string();
|
||||
|
||||
let modifier = if line.is_option {
|
||||
quote!(#scrate::metadata::StorageEntryModifier::Optional)
|
||||
} else {
|
||||
quote!(#scrate::metadata::StorageEntryModifier::Default)
|
||||
};
|
||||
|
||||
let ty = storage_line_metadata_type(scrate, line);
|
||||
|
||||
let (
|
||||
default_byte_getter_struct_def,
|
||||
default_byte_getter_struct_instance,
|
||||
) = default_byte_getter(scrate, line, def);
|
||||
|
||||
let mut docs = TokenStream::new();
|
||||
for attr in line.attrs.iter().filter_map(|v| v.parse_meta().ok()) {
|
||||
if let syn::Meta::NameValue(meta) = attr {
|
||||
if meta.path.is_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
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,432 @@
|
||||
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Substrate is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Substrate is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! `decl_storage` input definition and expansion.
|
||||
|
||||
mod storage_struct;
|
||||
mod parse;
|
||||
mod store_trait;
|
||||
mod getters;
|
||||
mod metadata;
|
||||
mod instance_trait;
|
||||
mod genesis_config;
|
||||
|
||||
use quote::quote;
|
||||
use palette_support_procedural_tools::{
|
||||
generate_crate_access, generate_hidden_includes, syn_ext as ext
|
||||
};
|
||||
|
||||
/// 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,
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for DeclStorageDef {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
parse::parse(input)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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>,
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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>,
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
pub struct StorageLineDefExt {
|
||||
#[allow(unused)]
|
||||
attrs: Vec<syn::Attribute>,
|
||||
/// Visibility of the storage struct.
|
||||
visibility: syn::Visibility,
|
||||
name: syn::Ident,
|
||||
/// The name of getter function to be implemented on Module struct.
|
||||
getter: Option<syn::Ident>,
|
||||
/// The name of the field to be used in genesis config if any.
|
||||
config: Option<syn::Ident>,
|
||||
/// The build function of the storage if any.
|
||||
build: Option<syn::Expr>,
|
||||
/// Default value of genesis config field and also for storage when no value available.
|
||||
default_value: Option<syn::Expr>,
|
||||
storage_type: StorageLineTypeDef,
|
||||
doc_attrs: Vec<syn::Meta>,
|
||||
/// Either the type stored in storage or wrapped in an Option.
|
||||
query_type: syn::Type,
|
||||
/// The type stored in storage.
|
||||
value_type: syn::Type,
|
||||
/// Full struct, for example: `StorageName<T, I>`.
|
||||
storage_struct: proc_macro2::TokenStream,
|
||||
/// If storage is generic over runtime then `T`.
|
||||
optional_storage_runtime_comma: Option<proc_macro2::TokenStream>,
|
||||
/// If storage is generic over runtime then `T: Trait`.
|
||||
optional_storage_runtime_bound_comma: Option<proc_macro2::TokenStream>,
|
||||
/// The where clause to use to constrain generics if storage is generic over runtime.
|
||||
optional_storage_where_clause: Option<proc_macro2::TokenStream>,
|
||||
/// Full trait, for example: `storage::StorageMap<u32, u32>`.
|
||||
storage_trait: proc_macro2::TokenStream,
|
||||
/// Full trait, for example: `storage::generator::StorageMap<u32, u32>`.
|
||||
storage_generator_trait: proc_macro2::TokenStream,
|
||||
/// Weither the storage is generic.
|
||||
is_generic: bool,
|
||||
/// Weither the storage value is an option.
|
||||
is_option: bool,
|
||||
}
|
||||
|
||||
impl StorageLineDefExt {
|
||||
fn from_def(storage_def: StorageLineDef, def: &DeclStorageDef) -> Self {
|
||||
let is_generic = match &storage_def.storage_type {
|
||||
StorageLineTypeDef::Simple(value) => {
|
||||
ext::type_contains_ident(&value, &def.module_runtime_generic)
|
||||
},
|
||||
StorageLineTypeDef::Map(map) => {
|
||||
ext::type_contains_ident(&map.key, &def.module_runtime_generic)
|
||||
|| ext::type_contains_ident(&map.value, &def.module_runtime_generic)
|
||||
}
|
||||
StorageLineTypeDef::LinkedMap(map) => {
|
||||
ext::type_contains_ident(&map.key, &def.module_runtime_generic)
|
||||
|| ext::type_contains_ident(&map.value, &def.module_runtime_generic)
|
||||
}
|
||||
StorageLineTypeDef::DoubleMap(map) => {
|
||||
ext::type_contains_ident(&map.key1, &def.module_runtime_generic)
|
||||
|| ext::type_contains_ident(&map.key2, &def.module_runtime_generic)
|
||||
|| ext::type_contains_ident(&map.value, &def.module_runtime_generic)
|
||||
}
|
||||
};
|
||||
|
||||
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.path().is_ident("doc"))
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
attrs: storage_def.attrs,
|
||||
visibility: storage_def.visibility,
|
||||
name: storage_def.name,
|
||||
getter: storage_def.getter,
|
||||
config: storage_def.config,
|
||||
build: storage_def.build,
|
||||
default_value: storage_def.default_value,
|
||||
storage_type: storage_def.storage_type,
|
||||
doc_attrs,
|
||||
query_type,
|
||||
value_type,
|
||||
storage_struct,
|
||||
optional_storage_runtime_comma,
|
||||
optional_storage_runtime_bound_comma,
|
||||
optional_storage_where_clause,
|
||||
storage_trait,
|
||||
storage_generator_trait,
|
||||
is_generic,
|
||||
is_option,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum StorageLineTypeDef {
|
||||
Map(MapDef),
|
||||
LinkedMap(MapDef),
|
||||
DoubleMap(DoubleMapDef),
|
||||
Simple(syn::Type),
|
||||
}
|
||||
|
||||
pub struct MapDef {
|
||||
pub hasher: HasherKind,
|
||||
pub key: syn::Type,
|
||||
/// This is the query value not the inner value used in storage trait implementation.
|
||||
pub value: syn::Type,
|
||||
}
|
||||
|
||||
pub struct DoubleMapDef {
|
||||
pub hasher1: HasherKind,
|
||||
pub hasher2: HasherKind,
|
||||
pub key1: syn::Type,
|
||||
pub key2: syn::Type,
|
||||
/// This is the query value not the inner value used in storage trait implementation.
|
||||
pub value: syn::Type,
|
||||
}
|
||||
|
||||
pub struct ExtraGenesisLineDef {
|
||||
attrs: Vec<syn::Attribute>,
|
||||
name: syn::Ident,
|
||||
typ: syn::Type,
|
||||
default: Option<syn::Expr>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum HasherKind {
|
||||
Blake2_256,
|
||||
Blake2_128,
|
||||
Twox256,
|
||||
Twox128,
|
||||
Twox64Concat,
|
||||
}
|
||||
|
||||
impl HasherKind {
|
||||
fn to_storage_hasher_struct(&self) -> proc_macro2::TokenStream {
|
||||
match self {
|
||||
HasherKind::Blake2_256 => quote!( Blake2_256 ),
|
||||
HasherKind::Blake2_128 => quote!( Blake2_128 ),
|
||||
HasherKind::Twox256 => quote!( Twox256 ),
|
||||
HasherKind::Twox128 => quote!( Twox128 ),
|
||||
HasherKind::Twox64Concat => quote!( Twox64Concat ),
|
||||
}
|
||||
}
|
||||
|
||||
fn into_metadata(&self) -> proc_macro2::TokenStream {
|
||||
match self {
|
||||
HasherKind::Blake2_256 => quote!( StorageHasher::Blake2_256 ),
|
||||
HasherKind::Blake2_128 => quote!( StorageHasher::Blake2_128 ),
|
||||
HasherKind::Twox256 => quote!( StorageHasher::Twox256 ),
|
||||
HasherKind::Twox128 => quote!( StorageHasher::Twox128 ),
|
||||
HasherKind::Twox64Concat => quote!( StorageHasher::Twox64Concat ),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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, "palette-support");
|
||||
let scrate_decl = generate_hidden_includes(&hidden_crate_name, "palette-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,404 @@
|
||||
// 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 palette_support_procedural_tools::{ToTokens, Parse, syn_ext as ext};
|
||||
use syn::{Ident, Token, spanned::Spanned};
|
||||
|
||||
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 DeclStorageGetterBody {
|
||||
fn_keyword: Option<Token![fn]>,
|
||||
ident: Ident,
|
||||
}
|
||||
|
||||
#[derive(Parse, ToTokens, Debug)]
|
||||
struct DeclStorageGetter {
|
||||
pub getter_keyword: keyword::get,
|
||||
pub getfn: ext::Parens<DeclStorageGetterBody>,
|
||||
}
|
||||
|
||||
#[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;
|
||||
|
||||
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 storage_lines = parse_storage_line_defs(def.content.content.inner.into_iter())?;
|
||||
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse the `DeclStorageLine` into `StorageLineDef`.
|
||||
fn parse_storage_line_defs(
|
||||
defs: impl Iterator<Item = DeclStorageLine>,
|
||||
) -> syn::Result<Vec<super::StorageLineDef>> {
|
||||
let mut storage_lines = Vec::<super::StorageLineDef>::new();
|
||||
|
||||
for line in defs {
|
||||
let getter = line.getter.inner.map(|o| o.getfn.content.ident);
|
||||
let config = if let Some(config) = line.config.inner {
|
||||
if let Some(ident) = config.expr.content {
|
||||
Some(ident)
|
||||
} else if let Some(ref ident) = getter {
|
||||
Some(ident.clone())
|
||||
} else {
|
||||
return Err(syn::Error::new(
|
||||
config.span(),
|
||||
"Invalid storage definition, couldn't find config identifier: storage must \
|
||||
either have a get identifier `get(fn ident)` or a defined config identifier \
|
||||
`config(ident)`",
|
||||
))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(ref config) = config {
|
||||
storage_lines.iter().filter_map(|sl| sl.config.as_ref()).try_for_each(|other_config| {
|
||||
if other_config == config {
|
||||
Err(syn::Error::new(
|
||||
config.span(),
|
||||
"`config()`/`get()` with the same name already defined.",
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
})?;
|
||||
}
|
||||
|
||||
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(storage_lines)
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
// 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;
|
||||
type KeyFormat = Self;
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
impl<#impl_trait> #scrate::storage::generator::LinkedMapKeyFormat for #storage_struct {
|
||||
type Hasher = #scrate::#hasher;
|
||||
|
||||
fn head_key() -> &'static [u8] {
|
||||
#head_key
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
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
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "palette-support-procedural-tools"
|
||||
version = "2.0.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
palette-support-procedural-tools-derive = { package = "palette-support-procedural-tools-derive", path = "./derive" }
|
||||
proc-macro2 = "1.0.6"
|
||||
quote = "1.0.2"
|
||||
syn = { version = "1.0.7", features = ["full"] }
|
||||
proc-macro-crate = "0.1.4"
|
||||
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "palette-support-procedural-tools-derive"
|
||||
version = "2.0.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0.6"
|
||||
quote = { version = "1.0.2", features = ["proc-macro"] }
|
||||
syn = { version = "1.0.7", features = ["proc-macro" ,"full", "extra-traits", "parsing"] }
|
||||
@@ -0,0 +1,243 @@
|
||||
// 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/>.
|
||||
|
||||
// tag::description[]
|
||||
//! Use to derive parsing for parsing struct.
|
||||
// end::description[]
|
||||
|
||||
#![recursion_limit = "128"]
|
||||
|
||||
extern crate proc_macro;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::Span;
|
||||
use syn::parse_macro_input;
|
||||
use quote::quote;
|
||||
|
||||
pub(crate) fn fields_idents(
|
||||
fields: impl Iterator<Item = syn::Field>,
|
||||
) -> impl Iterator<Item = proc_macro2::TokenStream> {
|
||||
fields.enumerate().map(|(ix, field)| {
|
||||
field.ident.clone().map(|i| quote!{#i}).unwrap_or_else(|| {
|
||||
let f_ix: syn::Ident = syn::Ident::new(&format!("f_{}", ix), Span::call_site());
|
||||
quote!( #f_ix )
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn fields_access(
|
||||
fields: impl Iterator<Item = syn::Field>,
|
||||
) -> impl Iterator<Item = proc_macro2::TokenStream> {
|
||||
fields.enumerate().map(|(ix, field)| {
|
||||
field.ident.clone().map(|i| quote!( #i )).unwrap_or_else(|| {
|
||||
let f_ix: syn::Index = syn::Index {
|
||||
index: ix as u32,
|
||||
span: Span::call_site(),
|
||||
};
|
||||
quote!( #f_ix )
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// self defined parsing struct or enum.
|
||||
/// not meant for any struct/enum, just for fast
|
||||
/// parse implementation.
|
||||
/// For enums:
|
||||
/// variant are tested in order of definition.
|
||||
/// Empty variant is always true.
|
||||
/// Please use carefully, this will fully parse successful variant twice.
|
||||
#[proc_macro_derive(Parse)]
|
||||
pub fn derive_parse(input: TokenStream) -> TokenStream {
|
||||
let item = parse_macro_input!(input as syn::Item);
|
||||
match item {
|
||||
syn::Item::Enum(input) => derive_parse_enum(input),
|
||||
syn::Item::Struct(input) => derive_parse_struct(input),
|
||||
_ => TokenStream::new(), // ignore
|
||||
}
|
||||
}
|
||||
|
||||
fn derive_parse_struct(input: syn::ItemStruct) -> TokenStream {
|
||||
let syn::ItemStruct {
|
||||
ident,
|
||||
generics,
|
||||
fields,
|
||||
..
|
||||
} = input;
|
||||
let field_names = {
|
||||
let name = fields_idents(fields.iter().map(Clone::clone));
|
||||
quote!{
|
||||
#(
|
||||
#name,
|
||||
)*
|
||||
}
|
||||
};
|
||||
let field = fields_idents(fields.iter().map(Clone::clone));
|
||||
let tokens = quote! {
|
||||
impl #generics syn::parse::Parse for #ident #generics {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
|
||||
#(
|
||||
let #field = input.parse()?;
|
||||
)*
|
||||
Ok(Self {
|
||||
#field_names
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
tokens.into()
|
||||
}
|
||||
|
||||
fn derive_parse_enum(input: syn::ItemEnum) -> TokenStream {
|
||||
let syn::ItemEnum {
|
||||
ident,
|
||||
generics,
|
||||
variants,
|
||||
..
|
||||
} = input;
|
||||
let variants = variants.iter().map(|v| {
|
||||
let variant_ident = v.ident.clone();
|
||||
let fields_build = if v.fields.iter().count() > 0 {
|
||||
let fields_id = fields_idents(v.fields.iter().map(Clone::clone));
|
||||
quote!( (#(#fields_id), *) )
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
|
||||
let fields_procs = fields_idents(v.fields.iter().map(Clone::clone))
|
||||
.map(|fident| {
|
||||
quote!{
|
||||
let mut #fident = match fork.parse() {
|
||||
Ok(r) => r,
|
||||
Err(_e) => break,
|
||||
};
|
||||
}
|
||||
});
|
||||
let fields_procs_again = fields_idents(v.fields.iter().map(Clone::clone))
|
||||
.map(|fident| {
|
||||
quote!{
|
||||
#fident = input.parse().expect("was parsed just before");
|
||||
}
|
||||
});
|
||||
|
||||
// double parse to update input cursor position
|
||||
// next syn crate version should be checked for a way
|
||||
// to copy position/state from a fork
|
||||
quote!{
|
||||
let mut fork = input.fork();
|
||||
loop {
|
||||
#(#fields_procs)*
|
||||
#(#fields_procs_again)*
|
||||
return Ok(#ident::#variant_ident#fields_build);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let tokens = quote! {
|
||||
impl #generics syn::parse::Parse for #ident #generics {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
|
||||
#(
|
||||
#variants
|
||||
)*
|
||||
// no early return from any variants
|
||||
Err(
|
||||
syn::parse::Error::new(
|
||||
proc_macro2::Span::call_site(),
|
||||
"derived enum no matching variants"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
tokens.into()
|
||||
}
|
||||
|
||||
/// self defined parsing struct or enum.
|
||||
/// not meant for any struct/enum, just for fast
|
||||
/// parse implementation.
|
||||
/// For enum:
|
||||
/// it only output fields (empty field act as a None).
|
||||
#[proc_macro_derive(ToTokens)]
|
||||
pub fn derive_totokens(input: TokenStream) -> TokenStream {
|
||||
let item = parse_macro_input!(input as syn::Item);
|
||||
match item {
|
||||
syn::Item::Enum(input) => derive_totokens_enum(input),
|
||||
syn::Item::Struct(input) => derive_totokens_struct(input),
|
||||
_ => TokenStream::new(), // ignore
|
||||
}
|
||||
}
|
||||
|
||||
fn derive_totokens_struct(input: syn::ItemStruct) -> TokenStream {
|
||||
let syn::ItemStruct {
|
||||
ident,
|
||||
generics,
|
||||
fields,
|
||||
..
|
||||
} = input;
|
||||
|
||||
let fields = fields_access(fields.iter().map(Clone::clone));
|
||||
let tokens = quote! {
|
||||
|
||||
impl #generics quote::ToTokens for #ident #generics {
|
||||
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||
#(
|
||||
self.#fields.to_tokens(tokens);
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
tokens.into()
|
||||
}
|
||||
|
||||
fn derive_totokens_enum(input: syn::ItemEnum) -> TokenStream {
|
||||
let syn::ItemEnum {
|
||||
ident,
|
||||
generics,
|
||||
variants,
|
||||
..
|
||||
} = input;
|
||||
let variants = variants.iter().map(|v| {
|
||||
let v_ident = v.ident.clone();
|
||||
let fields_build = if v.fields.iter().count() > 0 {
|
||||
let fields_id = fields_idents(v.fields.iter().map(Clone::clone));
|
||||
quote!( (#(#fields_id), *) )
|
||||
} else {
|
||||
quote!()
|
||||
};
|
||||
let field = fields_idents(v.fields.iter().map(Clone::clone));
|
||||
quote! {
|
||||
#ident::#v_ident#fields_build => {
|
||||
#(
|
||||
#field.to_tokens(tokens);
|
||||
)*
|
||||
},
|
||||
}
|
||||
});
|
||||
let tokens = quote! {
|
||||
impl #generics quote::ToTokens for #ident #generics {
|
||||
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||
match self {
|
||||
#(
|
||||
#variants
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
tokens.into()
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
// 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/>.
|
||||
|
||||
// tag::description[]
|
||||
//! Proc macro helpers for procedural macros
|
||||
// end::description[]
|
||||
|
||||
// reexport proc macros
|
||||
pub use palette_support_procedural_tools_derive::*;
|
||||
|
||||
use proc_macro_crate::crate_name;
|
||||
use syn::parse::Error;
|
||||
use quote::quote;
|
||||
|
||||
pub mod syn_ext;
|
||||
|
||||
// FIXME #1569, remove the following functions, which are copied from sr-api-macros
|
||||
use proc_macro2::{TokenStream, Span};
|
||||
use syn::Ident;
|
||||
|
||||
fn generate_hidden_includes_mod_name(unique_id: &str) -> Ident {
|
||||
Ident::new(&format!("sr_api_hidden_includes_{}", unique_id), Span::call_site())
|
||||
}
|
||||
|
||||
/// Generates the access to the `palette-support` crate.
|
||||
pub fn generate_crate_access(unique_id: &str, def_crate: &str) -> TokenStream {
|
||||
if std::env::var("CARGO_PKG_NAME").unwrap() == def_crate {
|
||||
quote::quote!( palette_support )
|
||||
} else {
|
||||
let mod_name = generate_hidden_includes_mod_name(unique_id);
|
||||
quote::quote!( self::#mod_name::hidden_include )
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates the hidden includes that are required to make the macro independent from its scope.
|
||||
pub fn generate_hidden_includes(unique_id: &str, def_crate: &str) -> TokenStream {
|
||||
if ::std::env::var("CARGO_PKG_NAME").unwrap() == def_crate {
|
||||
TokenStream::new()
|
||||
} else {
|
||||
let mod_name = generate_hidden_includes_mod_name(unique_id);
|
||||
|
||||
match crate_name(def_crate) {
|
||||
Ok(name) => {
|
||||
let name = Ident::new(&name, Span::call_site());
|
||||
quote::quote!(
|
||||
#[doc(hidden)]
|
||||
mod #mod_name {
|
||||
pub extern crate #name as hidden_include;
|
||||
}
|
||||
)
|
||||
},
|
||||
Err(e) => {
|
||||
let err = Error::new(Span::call_site(), &e).to_compile_error();
|
||||
quote!( #err )
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// fn to remove white spaces around string types
|
||||
// (basically whitespaces around tokens)
|
||||
pub fn clean_type_string(input: &str) -> String {
|
||||
input
|
||||
.replace(" ::", "::")
|
||||
.replace(":: ", "::")
|
||||
.replace(" ,", ",")
|
||||
.replace(" ;", ";")
|
||||
.replace(" [", "[")
|
||||
.replace("[ ", "[")
|
||||
.replace(" ]", "]")
|
||||
.replace(" (", "(")
|
||||
.replace("( ", "(")
|
||||
.replace(" )", ")")
|
||||
.replace(" <", "<")
|
||||
.replace("< ", "<")
|
||||
.replace(" >", ">")
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
// 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/>.
|
||||
|
||||
// tag::description[]
|
||||
//! Extension to syn types, mainly for parsing
|
||||
// end::description[]
|
||||
|
||||
use syn::{visit::{Visit, self}, parse::{Parse, ParseStream, Result}, Ident};
|
||||
use proc_macro2::{TokenStream, TokenTree};
|
||||
use quote::ToTokens;
|
||||
use std::iter::once;
|
||||
use palette_support_procedural_tools_derive::{ToTokens, Parse};
|
||||
|
||||
/// stop parsing here getting remaining token as content
|
||||
/// Warn duplicate stream (part of)
|
||||
#[derive(Parse, ToTokens, Debug)]
|
||||
pub struct StopParse {
|
||||
pub inner: TokenStream,
|
||||
}
|
||||
|
||||
// inner macro really dependant on syn naming convention, do not export
|
||||
macro_rules! groups_impl {
|
||||
($name:ident, $tok:ident, $deli:ident, $parse:ident) => {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct $name<P> {
|
||||
pub token: syn::token::$tok,
|
||||
pub content: P,
|
||||
}
|
||||
|
||||
impl<P: Parse> Parse for $name<P> {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let syn::group::$name { token, content } = syn::group::$parse(input)?;
|
||||
let content = content.parse()?;
|
||||
Ok($name { token, content, })
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: ToTokens> ToTokens for $name<P> {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let mut inner_stream = TokenStream::new();
|
||||
self.content.to_tokens(&mut inner_stream);
|
||||
let token_tree: proc_macro2::TokenTree =
|
||||
proc_macro2::Group::new(proc_macro2::Delimiter::$deli, inner_stream).into();
|
||||
tokens.extend(once(token_tree));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
groups_impl!(Braces, Brace, Brace, parse_braces);
|
||||
groups_impl!(Brackets, Bracket, Bracket, parse_brackets);
|
||||
groups_impl!(Parens, Paren, Parenthesis, parse_parens);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PunctuatedInner<P,T,V> {
|
||||
pub inner: syn::punctuated::Punctuated<P,T>,
|
||||
pub variant: V,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NoTrailing;
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Trailing;
|
||||
|
||||
pub type Punctuated<P,T> = PunctuatedInner<P,T,NoTrailing>;
|
||||
|
||||
pub type PunctuatedTrailing<P,T> = PunctuatedInner<P,T,Trailing>;
|
||||
|
||||
impl<P: Parse, T: Parse + syn::token::Token> Parse for PunctuatedInner<P,T,Trailing> {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
Ok(PunctuatedInner {
|
||||
inner: syn::punctuated::Punctuated::parse_separated_nonempty(input)?,
|
||||
variant: Trailing,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Parse, T: Parse> Parse for PunctuatedInner<P,T,NoTrailing> {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
Ok(PunctuatedInner {
|
||||
inner: syn::punctuated::Punctuated::parse_terminated(input)?,
|
||||
variant: NoTrailing,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: ToTokens, T: ToTokens, V> ToTokens for PunctuatedInner<P,T,V> {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
self.inner.to_tokens(tokens)
|
||||
}
|
||||
}
|
||||
|
||||
/// Note that syn Meta is almost fine for use case (lacks only `ToToken`)
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Meta {
|
||||
pub inner: syn::Meta,
|
||||
}
|
||||
|
||||
impl Parse for Meta {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
Ok(Meta {
|
||||
inner: syn::Meta::parse(input)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Meta {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
match self.inner {
|
||||
syn::Meta::Path(ref path) => path.to_tokens(tokens),
|
||||
syn::Meta::List(ref l) => l.to_tokens(tokens),
|
||||
syn::Meta::NameValue(ref n) => n.to_tokens(tokens),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct OuterAttributes {
|
||||
pub inner: Vec<syn::Attribute>,
|
||||
}
|
||||
|
||||
impl Parse for OuterAttributes {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let inner = syn::Attribute::parse_outer(input)?;
|
||||
Ok(OuterAttributes {
|
||||
inner,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for OuterAttributes {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
for att in self.inner.iter() {
|
||||
att.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Opt<P> {
|
||||
pub inner: Option<P>,
|
||||
}
|
||||
|
||||
impl<P: Parse> Parse for Opt<P> {
|
||||
// Note that it cost a double parsing (same as enum derive)
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let inner = match input.fork().parse::<P>() {
|
||||
Ok(_item) => Some(input.parse().expect("Same parsing ran before")),
|
||||
Err(_e) => None,
|
||||
};
|
||||
|
||||
Ok(Opt { inner })
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: ToTokens> ToTokens for Opt<P> {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
if let Some(ref p) = self.inner {
|
||||
p.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.ident == "Option" {
|
||||
// Option has only one type argument in angle bracket.
|
||||
if let syn::PathArguments::AngleBracketed(a) = &v.arguments {
|
||||
if let syn::GenericArgument::Type(typ) = a.args.last()? {
|
||||
return Some(typ.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Auxialary structure to check if a given `Ident` is contained in an ast.
|
||||
struct ContainsIdent<'a> {
|
||||
ident: &'a Ident,
|
||||
result: bool,
|
||||
}
|
||||
|
||||
impl<'ast> ContainsIdent<'ast> {
|
||||
fn visit_tokenstream(&mut self, stream: TokenStream) {
|
||||
stream.into_iter().for_each(|tt|
|
||||
match tt {
|
||||
TokenTree::Ident(id) => self.visit_ident(&id),
|
||||
TokenTree::Group(ref group) => self.visit_tokenstream(group.stream()),
|
||||
_ => {}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
fn visit_ident(&mut self, ident: &Ident) {
|
||||
if ident == self.ident {
|
||||
self.result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ast> Visit<'ast> for ContainsIdent<'ast> {
|
||||
fn visit_ident(&mut self, input: &'ast Ident) {
|
||||
self.visit_ident(input);
|
||||
}
|
||||
|
||||
fn visit_macro(&mut self, input: &'ast syn::Macro) {
|
||||
self.visit_tokenstream(input.tokens.clone());
|
||||
visit::visit_macro(self, input);
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if a `Type` contains the given `Ident`.
|
||||
pub fn type_contains_ident(typ: &syn::Type, ident: &Ident) -> bool {
|
||||
let mut visit = ContainsIdent {
|
||||
result: false,
|
||||
ident,
|
||||
};
|
||||
|
||||
visit::visit_type(&mut visit, typ);
|
||||
visit.result
|
||||
}
|
||||
|
||||
/// Check if a `Expr` contains the given `Ident`.
|
||||
pub fn expr_contains_ident(expr: &syn::Expr, ident: &Ident) -> bool {
|
||||
let mut visit = ContainsIdent {
|
||||
result: false,
|
||||
ident,
|
||||
};
|
||||
|
||||
visit::visit_expr(&mut visit, expr);
|
||||
visit.result
|
||||
}
|
||||
Reference in New Issue
Block a user