mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-27 23:18:01 +00:00
Implement parameterisable modules (#1800)
* first implementation
* remove done comment
* origin done
* impl log for instance
* impl inherent for instance
* Fix wasm build + full example build
this requires parity codec implements codec for core::marker::PhantomData
* patch parity-codec link to github branch
* improve internal names and fix instance prefix
* Fix in macros
* add test modules for support
this allow to test for construct_runtime as well.
The reason to have put that in another crate is:
* if we put test in `tests/` dir of srml/support then decl_storage fails to get
srml-support access because it believes it is inside srml-support
crate and so derive access to `quote!{ crate }` but this is wrong
(and I don't see any way to prevent that, and it only bother us so I
don't think that matters that much)
* if we put test inside lib.rs then contruct_runtime cannot be used
because it call some macros that are defined with macros
(decl_outer_event and decl_outer_origin) and thus rustc complains.
* defaultinstance to its own struct to avoid errors
* enforce <T, I> for Event and Config, impl test
* add origin, log, inherent to test
* test more code generation
* basic storage test
* fix typo
* rename a few imports and field
* delete wip test in example and runtime
* change default prefix to make it backward compatible with test
* rename Instance to I and Instantiable to Instance
note: the name of generic parameter I is only enforce by decl_module!
and this could be rewritten
* doc
* clean old TODOs
* update parity-codec to 3.2
* update node impl version + builds
* fix warning
* fix unrelated grandpa test
* refactor code
This commit is contained in:
@@ -58,6 +58,18 @@ use proc_macro::TokenStream;
|
||||
/// ```
|
||||
/// or when at least one storage field requires default initialization (both `get` and `config` or `build`).
|
||||
/// This struct can be expose as `Config` by `decl_runtime` macro.
|
||||
///
|
||||
/// ### Module with instances
|
||||
///
|
||||
/// `decl_storage!` macro support building modules with instances with the following syntax: (DefaultInstance type
|
||||
/// is optionnal)
|
||||
/// ```nocompile
|
||||
/// trait Store for Module<T: Trait<I>, I: Instance=DefaultInstance> as Example {}
|
||||
/// ```
|
||||
///
|
||||
/// Then the genesis config is generated with two generic parameter `GenesisConfig<T, I>`
|
||||
/// and storages are now accessible using two generic parameters like:
|
||||
/// `<Dummy<T, I>>::get()` or `Dummy::<T, I>::get()`
|
||||
#[proc_macro]
|
||||
pub fn decl_storage(input: TokenStream) -> TokenStream {
|
||||
storage::transformation::decl_storage_impl(input)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use syn;
|
||||
use quote::quote;
|
||||
use crate::storage::transformation::DeclStorageTypeInfos;
|
||||
use crate::storage::transformation::{DeclStorageTypeInfos, InstanceOpts};
|
||||
|
||||
pub fn option_unwrap(is_option: bool) -> TokenStream2 {
|
||||
if !is_option {
|
||||
@@ -34,9 +34,11 @@ pub(crate) struct Impls<'a, I: Iterator<Item=syn::Meta>> {
|
||||
pub visibility: &'a syn::Visibility,
|
||||
pub traitinstance: &'a syn::Ident,
|
||||
pub traittype: &'a syn::TypeParamBound,
|
||||
pub instance_opts: &'a InstanceOpts,
|
||||
pub type_infos: DeclStorageTypeInfos<'a>,
|
||||
pub fielddefault: TokenStream2,
|
||||
pub prefix: String,
|
||||
pub cratename: &'a syn::Ident,
|
||||
pub name: &'a syn::Ident,
|
||||
pub attrs: I,
|
||||
}
|
||||
@@ -48,11 +50,13 @@ impl<'a, I: Iterator<Item=syn::Meta>> Impls<'a, I> {
|
||||
visibility,
|
||||
traitinstance,
|
||||
traittype,
|
||||
instance_opts,
|
||||
type_infos,
|
||||
fielddefault,
|
||||
prefix,
|
||||
name,
|
||||
attrs,
|
||||
..
|
||||
} = self;
|
||||
let DeclStorageTypeInfos { typ, value_type, is_option, .. } = type_infos;
|
||||
let option_simple_1 = option_unwrap(is_option);
|
||||
@@ -70,17 +74,32 @@ impl<'a, I: Iterator<Item=syn::Meta>> Impls<'a, I> {
|
||||
}
|
||||
};
|
||||
|
||||
let InstanceOpts {
|
||||
comma_instance,
|
||||
equal_default_instance,
|
||||
bound_instantiable,
|
||||
instance,
|
||||
..
|
||||
} = instance_opts;
|
||||
|
||||
let final_prefix = if let Some(instance) = instance {
|
||||
let method_name = syn::Ident::new(&format!("build_prefix_once_for_{}", name.to_string()), proc_macro2::Span::call_site());
|
||||
quote!{ #instance::#method_name(#prefix.as_bytes()) }
|
||||
} else {
|
||||
quote!{ #prefix.as_bytes() }
|
||||
};
|
||||
|
||||
// generator for value
|
||||
quote!{
|
||||
#( #[ #attrs ] )*
|
||||
#visibility struct #name<#traitinstance: #traittype>(#scrate::storage::generator::PhantomData<#traitinstance>);
|
||||
#visibility struct #name<#traitinstance: #traittype, #instance #bound_instantiable #equal_default_instance>(#scrate::storage::generator::PhantomData<(#traitinstance #comma_instance)>);
|
||||
|
||||
impl<#traitinstance: #traittype> #scrate::storage::generator::StorageValue<#typ> for #name<#traitinstance> {
|
||||
impl<#traitinstance: #traittype, #instance #bound_instantiable> #scrate::storage::generator::StorageValue<#typ> for #name<#traitinstance, #instance> {
|
||||
type Query = #value_type;
|
||||
|
||||
/// Get the storage key.
|
||||
fn key() -> &'static [u8] {
|
||||
#prefix.as_bytes()
|
||||
#final_prefix
|
||||
}
|
||||
|
||||
/// Load the value from the provided storage instance.
|
||||
@@ -113,11 +132,13 @@ impl<'a, I: Iterator<Item=syn::Meta>> Impls<'a, I> {
|
||||
visibility,
|
||||
traitinstance,
|
||||
traittype,
|
||||
instance_opts,
|
||||
type_infos,
|
||||
fielddefault,
|
||||
prefix,
|
||||
name,
|
||||
attrs,
|
||||
..
|
||||
} = self;
|
||||
let DeclStorageTypeInfos { typ, value_type, is_option, .. } = type_infos;
|
||||
let option_simple_1 = option_unwrap(is_option);
|
||||
@@ -134,22 +155,38 @@ impl<'a, I: Iterator<Item=syn::Meta>> Impls<'a, I> {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let InstanceOpts {
|
||||
comma_instance,
|
||||
equal_default_instance,
|
||||
bound_instantiable,
|
||||
instance,
|
||||
..
|
||||
} = instance_opts;
|
||||
|
||||
let final_prefix = if let Some(instance) = instance {
|
||||
let method_name = syn::Ident::new(&format!("build_prefix_once_for_{}", name.to_string()), proc_macro2::Span::call_site());
|
||||
quote!{ #instance::#method_name(#prefix.as_bytes()) }
|
||||
} else {
|
||||
quote!{ #prefix.as_bytes() }
|
||||
};
|
||||
|
||||
// generator for map
|
||||
quote!{
|
||||
#( #[ #attrs ] )*
|
||||
#visibility struct #name<#traitinstance: #traittype>(#scrate::storage::generator::PhantomData<#traitinstance>);
|
||||
#visibility struct #name<#traitinstance: #traittype, #instance #bound_instantiable #equal_default_instance>(#scrate::storage::generator::PhantomData<(#traitinstance #comma_instance)>);
|
||||
|
||||
impl<#traitinstance: #traittype> #scrate::storage::generator::StorageMap<#kty, #typ> for #name<#traitinstance> {
|
||||
impl<#traitinstance: #traittype, #instance #bound_instantiable> #scrate::storage::generator::StorageMap<#kty, #typ> for #name<#traitinstance, #instance> {
|
||||
type Query = #value_type;
|
||||
|
||||
/// Get the prefix key in storage.
|
||||
fn prefix() -> &'static [u8] {
|
||||
#prefix.as_bytes()
|
||||
#final_prefix
|
||||
}
|
||||
|
||||
/// Get the storage key used to fetch a value corresponding to a specific key.
|
||||
fn key_for(x: &#kty) -> #scrate::rstd::vec::Vec<u8> {
|
||||
let mut key = #prefix.as_bytes().to_vec();
|
||||
let mut key = <Self as #scrate::storage::generator::StorageMap<#kty, #typ>>::prefix().to_vec();
|
||||
#scrate::codec::Encode::encode_to(x, &mut key);
|
||||
key
|
||||
}
|
||||
@@ -185,16 +222,41 @@ impl<'a, I: Iterator<Item=syn::Meta>> Impls<'a, I> {
|
||||
visibility,
|
||||
traitinstance,
|
||||
traittype,
|
||||
instance_opts,
|
||||
type_infos,
|
||||
fielddefault,
|
||||
prefix,
|
||||
name,
|
||||
attrs,
|
||||
..
|
||||
} = self;
|
||||
|
||||
let InstanceOpts {
|
||||
comma_instance,
|
||||
equal_default_instance,
|
||||
bound_instantiable,
|
||||
instance,
|
||||
..
|
||||
} = instance_opts;
|
||||
|
||||
let final_prefix = if let Some(instance) = instance {
|
||||
let method_name = syn::Ident::new(&format!("build_prefix_once_for_{}", name.to_string()), proc_macro2::Span::call_site());
|
||||
quote!{ #instance::#method_name(#prefix.as_bytes()) }
|
||||
} else {
|
||||
quote!{ #prefix.as_bytes() }
|
||||
};
|
||||
|
||||
// make sure to use different prefix for head and elements.
|
||||
let final_head_key = if let Some(instance) = instance {
|
||||
let method_name = syn::Ident::new(&format!("build_head_key_once_for_{}", name.to_string()), proc_macro2::Span::call_site());
|
||||
quote!{ #instance::#method_name(#prefix.as_bytes()) }
|
||||
} else {
|
||||
let final_head_key = format!("head of {}", prefix);
|
||||
quote!{ #final_head_key.as_bytes() }
|
||||
};
|
||||
|
||||
let DeclStorageTypeInfos { typ, value_type, is_option, .. } = type_infos;
|
||||
let option_simple_1 = option_unwrap(is_option);
|
||||
// make sure to use different prefix for head and elements.
|
||||
let head_key = format!("head of {}", prefix);
|
||||
let name_lowercase = name.to_string().to_lowercase();
|
||||
let inner_module = syn::Ident::new(&format!("__linked_map_details_for_{}_do_not_use", name_lowercase), name.span());
|
||||
let linkage = syn::Ident::new(&format!("__LinkageFor{}DoNotUse", name), name.span());
|
||||
@@ -250,30 +312,22 @@ impl<'a, I: Iterator<Item=syn::Meta>> Impls<'a, I> {
|
||||
pub _data: #phantom_data<V>,
|
||||
}
|
||||
|
||||
impl<'a, S: #scrate::GenericStorage, K, V> Iterator for Enumerator<'a, S, K, V> where
|
||||
K: 'a + #scrate::codec::Codec,
|
||||
V: 'a + #scrate::codec::Decode,
|
||||
impl<'a, S: #scrate::GenericStorage, #traitinstance: #traittype, #instance #bound_instantiable> Iterator for Enumerator<'a, S, #kty, (#typ, #traitinstance, #instance)>
|
||||
where #traitinstance: 'a
|
||||
{
|
||||
type Item = (K, V);
|
||||
type Item = (#kty, #typ);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let next = self.next.take()?;
|
||||
let key_for = key_for(&next);
|
||||
let (val, linkage): (V, Linkage<K>) = self.storage.get(&*key_for)
|
||||
let key_for = <super::#name<#traitinstance, #instance> as #scrate::storage::generator::StorageMap<#kty, #typ>>::key_for(&next);
|
||||
let (val, linkage): (#typ, Linkage<#kty>) = self.storage.get(&*key_for)
|
||||
.expect("previous/next only contain existing entires; we enumerate using next; entry exists; qed");
|
||||
self.next = linkage.next;
|
||||
Some((next, val))
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a storage key for given item.
|
||||
pub(crate) fn key_for<Key: #scrate::codec::Encode>(key: &Key) -> #scrate::rstd::vec::Vec<u8> {
|
||||
let mut key_for = #prefix.as_bytes().to_vec();
|
||||
#scrate::codec::Encode::encode_to(&key, &mut key_for);
|
||||
key_for
|
||||
}
|
||||
|
||||
pub(crate) trait Utils<#traitinstance: #traittype> {
|
||||
pub(crate) trait Utils<#traitinstance: #traittype, #instance #bound_instantiable> {
|
||||
/// Update linkage when this element is removed.
|
||||
///
|
||||
/// Takes care of updating previous and next elements points
|
||||
@@ -304,17 +358,17 @@ impl<'a, I: Iterator<Item=syn::Meta>> Impls<'a, I> {
|
||||
|
||||
let structure = quote! {
|
||||
#( #[ #attrs ] )*
|
||||
#visibility struct #name<#traitinstance: #traittype>(#phantom_data<#traitinstance>);
|
||||
#visibility struct #name<#traitinstance: #traittype, #instance #bound_instantiable #equal_default_instance>(#phantom_data<(#traitinstance #comma_instance)>);
|
||||
|
||||
impl<#traitinstance: #traittype> self::#inner_module::Utils<#traitinstance> for #name<#traitinstance> {
|
||||
impl<#traitinstance: #traittype, #instance #bound_instantiable> self::#inner_module::Utils<#traitinstance, #instance> for #name<#traitinstance, #instance> {
|
||||
fn remove_linkage<S: #scrate::GenericStorage>(
|
||||
linkage: self::#inner_module::Linkage<#kty>,
|
||||
storage: &S,
|
||||
) {
|
||||
use self::#inner_module::{key_for, Utils};
|
||||
use self::#inner_module::Utils;
|
||||
|
||||
let next_key = linkage.next.as_ref().map(|x| key_for(x));
|
||||
let prev_key = linkage.previous.as_ref().map(|x| key_for(x));
|
||||
let next_key = linkage.next.as_ref().map(|x| #as_map::key_for(x));
|
||||
let prev_key = linkage.previous.as_ref().map(|x| #as_map::key_for(x));
|
||||
|
||||
if let Some(prev_key) = prev_key {
|
||||
// Retrieve previous element and update `next`
|
||||
@@ -347,12 +401,12 @@ impl<'a, I: Iterator<Item=syn::Meta>> Impls<'a, I> {
|
||||
storage: &S,
|
||||
key: &#kty,
|
||||
) -> self::#inner_module::Linkage<#kty> {
|
||||
use self::#inner_module::{key_for, Utils};
|
||||
use self::#inner_module::Utils;
|
||||
|
||||
if let Some(head) = Self::read_head(storage) {
|
||||
// update previous head predecessor
|
||||
{
|
||||
let head_key = key_for(&head);
|
||||
let head_key = #as_map::key_for(&head);
|
||||
let (data, linkage) = Self::read_with_linkage(storage, &*head_key).expect(r#"
|
||||
head is set when first element is inserted and unset when last element is removed;
|
||||
if head is Some then it points to existing key; qed
|
||||
@@ -376,13 +430,13 @@ impl<'a, I: Iterator<Item=syn::Meta>> Impls<'a, I> {
|
||||
}
|
||||
|
||||
fn read_head<S: #scrate::GenericStorage>(storage: &S) -> Option<#kty> {
|
||||
storage.get(#head_key.as_bytes())
|
||||
storage.get(#final_head_key)
|
||||
}
|
||||
|
||||
fn write_head<S: #scrate::GenericStorage>(storage: &S, head: Option<&#kty>) {
|
||||
match head {
|
||||
Some(head) => storage.put(#head_key.as_bytes(), head),
|
||||
None => storage.kill(#head_key.as_bytes()),
|
||||
Some(head) => storage.put(#final_head_key, head),
|
||||
None => storage.kill(#final_head_key),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -393,17 +447,19 @@ impl<'a, I: Iterator<Item=syn::Meta>> Impls<'a, I> {
|
||||
|
||||
#structure
|
||||
|
||||
impl<#traitinstance: #traittype> #scrate::storage::generator::StorageMap<#kty, #typ> for #name<#traitinstance> {
|
||||
impl<#traitinstance: #traittype, #instance #bound_instantiable> #scrate::storage::generator::StorageMap<#kty, #typ> for #name<#traitinstance, #instance> {
|
||||
type Query = #value_type;
|
||||
|
||||
/// Get the prefix key in storage.
|
||||
fn prefix() -> &'static [u8] {
|
||||
#prefix.as_bytes()
|
||||
#final_prefix
|
||||
}
|
||||
|
||||
/// Get the storage key used to fetch a value corresponding to a specific key.
|
||||
fn key_for(x: &#kty) -> #scrate::rstd::vec::Vec<u8> {
|
||||
self::#inner_module::key_for(x)
|
||||
fn key_for(key: &#kty) -> #scrate::rstd::vec::Vec<u8> {
|
||||
let mut key_for = #as_map::prefix().to_vec();
|
||||
#scrate::codec::Encode::encode_to(&key, &mut key_for);
|
||||
key_for
|
||||
}
|
||||
|
||||
/// Load the value associated with the given key from the map.
|
||||
@@ -413,9 +469,9 @@ impl<'a, I: Iterator<Item=syn::Meta>> Impls<'a, I> {
|
||||
|
||||
/// Take the value, reading and removing it.
|
||||
fn take<S: #scrate::GenericStorage>(key: &#kty, storage: &S) -> Self::Query {
|
||||
use self::#inner_module::{Utils, key_for};
|
||||
use self::#inner_module::Utils;
|
||||
|
||||
let res: Option<(#value_type, self::#inner_module::Linkage<#kty>)> = storage.take(&*key_for(key));
|
||||
let res: Option<(#value_type, self::#inner_module::Linkage<#kty>)> = storage.take(&*#as_map::key_for(key));
|
||||
match res {
|
||||
Some((data, linkage)) => {
|
||||
Self::remove_linkage(linkage, storage);
|
||||
@@ -432,9 +488,9 @@ impl<'a, I: Iterator<Item=syn::Meta>> Impls<'a, I> {
|
||||
|
||||
/// Store a value to be associated with the given key from the map.
|
||||
fn insert<S: #scrate::GenericStorage>(key: &#kty, val: &#typ, storage: &S) {
|
||||
use self::#inner_module::{Utils, key_for};
|
||||
use self::#inner_module::Utils;
|
||||
|
||||
let key_for = &*key_for(key);
|
||||
let key_for = &*#as_map::key_for(key);
|
||||
let linkage = match Self::read_with_linkage(storage, key_for) {
|
||||
// overwrite but reuse existing linkage
|
||||
Some((_data, linkage)) => linkage,
|
||||
@@ -446,9 +502,9 @@ impl<'a, I: Iterator<Item=syn::Meta>> Impls<'a, I> {
|
||||
|
||||
/// Mutate the value under a key
|
||||
fn mutate<R, F: FnOnce(&mut Self::Query) -> R, S: #scrate::GenericStorage>(key: &#kty, f: F, storage: &S) -> R {
|
||||
use self::#inner_module::{Utils, key_for};
|
||||
use self::#inner_module::Utils;
|
||||
|
||||
let key_for = &*key_for(key);
|
||||
let key_for = &*#as_map::key_for(key);
|
||||
let (mut val, linkage) = Self::read_with_linkage(storage, key_for)
|
||||
.map(|(data, linkage)| (data, Some(linkage)))
|
||||
.unwrap_or_else(|| (#fielddefault, None));
|
||||
@@ -459,7 +515,7 @@ impl<'a, I: Iterator<Item=syn::Meta>> Impls<'a, I> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<#traitinstance: #traittype> #scrate::storage::generator::EnumerableStorageMap<#kty, #typ> for #name<#traitinstance> {
|
||||
impl<#traitinstance: 'static + #traittype, #instance #bound_instantiable> #scrate::storage::generator::EnumerableStorageMap<#kty, #typ> for #name<#traitinstance, #instance> {
|
||||
fn head<S: #scrate::GenericStorage>(storage: &S) -> Option<#kty> {
|
||||
use self::#inner_module::Utils;
|
||||
|
||||
@@ -475,7 +531,7 @@ impl<'a, I: Iterator<Item=syn::Meta>> Impls<'a, I> {
|
||||
#scrate::storage::generator::Box::new(Enumerator {
|
||||
next: Self::read_head(storage),
|
||||
storage,
|
||||
_data: #phantom_data::<#typ>::default(),
|
||||
_data: #phantom_data::<(#typ, #traitinstance, #instance)>::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,14 +39,20 @@ struct StorageDefinition {
|
||||
pub module_ident: Ident,
|
||||
pub mod_lt_token: Token![<],
|
||||
pub mod_param: syn::GenericParam,
|
||||
pub mod_instance_param_token: Option<Token![,]>,
|
||||
pub mod_instance: Option<syn::Ident>,
|
||||
pub mod_instantiable_token: Option<Token![:]>,
|
||||
pub mod_instantiable: Option<syn::Ident>,
|
||||
pub mod_default_instance_token: Option<Token![=]>,
|
||||
pub mod_default_instance: Option<syn::Ident>,
|
||||
pub mod_gt_token: Token![>],
|
||||
pub as_token: Token![as],
|
||||
pub crate_ident: Ident,
|
||||
pub content: ext::Braces<ext::Punctuated<DeclStorageLine, Token![;]>>,
|
||||
pub extra_genesis: Option<AddExtraGenesis>,
|
||||
pub extra_genesis_skip_phantom_data_field: Option<ExtraGenesisSkipPhantomDataField>,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Parse, ToTokens, Debug)]
|
||||
struct SpecificHiddenCrate {
|
||||
pub keyword: ext::CustomToken<SpecificHiddenCrate>,
|
||||
@@ -59,6 +65,12 @@ struct AddExtraGenesis {
|
||||
pub content: ext::Braces<AddExtraGenesisContent>,
|
||||
}
|
||||
|
||||
#[derive(Parse, ToTokens, Debug)]
|
||||
struct ExtraGenesisSkipPhantomDataField {
|
||||
pub genesis_phantom_keyword: ext::CustomToken<ExtraGenesisSkipPhantomDataField>,
|
||||
pub token: Token![;],
|
||||
}
|
||||
|
||||
#[derive(Parse, ToTokens, Debug)]
|
||||
struct AddExtraGenesisContent {
|
||||
pub lines: ext::Punctuated<AddExtraGenesisLineEnum, Token![;]>,
|
||||
@@ -153,3 +165,4 @@ custom_keyword_impl!(AddExtraGenesis, "add_extra_genesis", "storage extra genesi
|
||||
custom_keyword_impl!(DeclStorageGetter, "get", "storage getter");
|
||||
custom_keyword!(MapKeyword, "map", "map as keyword");
|
||||
custom_keyword!(LinkedMapKeyword, "linked_map", "linked_map as keyword");
|
||||
custom_keyword_impl!(ExtraGenesisSkipPhantomDataField, "extra_genesis_skip_phantom_data_field", "extra_genesis_skip_phantom_data_field as keyword");
|
||||
|
||||
@@ -38,6 +38,8 @@ use quote::quote;
|
||||
|
||||
use super::*;
|
||||
|
||||
const NUMBER_OF_INSTANCE: usize = 16;
|
||||
|
||||
// try macro but returning tokenized error
|
||||
macro_rules! try_tok(( $expre : expr ) => {
|
||||
match $expre {
|
||||
@@ -57,11 +59,21 @@ pub fn decl_storage_impl(input: TokenStream) -> TokenStream {
|
||||
ident: storetype,
|
||||
module_ident,
|
||||
mod_param: strait,
|
||||
mod_instance,
|
||||
mod_instantiable,
|
||||
mod_default_instance,
|
||||
crate_ident: cratename,
|
||||
content: ext::Braces { content: storage_lines, ..},
|
||||
extra_genesis,
|
||||
extra_genesis_skip_phantom_data_field,
|
||||
..
|
||||
} = def;
|
||||
|
||||
let instance_opts = match get_instance_opts(mod_instance, mod_instantiable, mod_default_instance) {
|
||||
Ok(opts) => opts,
|
||||
Err(err) => return err.to_compile_error().into(),
|
||||
};
|
||||
|
||||
let hidden_crate_name = hidden_crate.map(|rc| rc.ident.content).map(|i| i.to_string())
|
||||
.unwrap_or_else(|| "decl_storage".to_string());
|
||||
let scrate = generate_crate_access(&hidden_crate_name, "srml-support");
|
||||
@@ -89,13 +101,16 @@ pub fn decl_storage_impl(input: TokenStream) -> TokenStream {
|
||||
&scrate,
|
||||
&traitinstance,
|
||||
&traittype,
|
||||
&instance_opts,
|
||||
&storage_lines,
|
||||
&extra_genesis,
|
||||
extra_genesis_skip_phantom_data_field.is_some(),
|
||||
));
|
||||
let decl_storage_items = decl_storage_items(
|
||||
&scrate,
|
||||
&traitinstance,
|
||||
&traittype,
|
||||
&instance_opts,
|
||||
&cratename,
|
||||
&storage_lines,
|
||||
);
|
||||
@@ -104,19 +119,29 @@ pub fn decl_storage_impl(input: TokenStream) -> TokenStream {
|
||||
);
|
||||
let impl_store_items = impl_store_items(
|
||||
&traitinstance,
|
||||
&instance_opts.instance,
|
||||
&storage_lines,
|
||||
);
|
||||
let impl_store_fns = impl_store_fns(
|
||||
&scrate,
|
||||
&traitinstance,
|
||||
&instance_opts.instance,
|
||||
&storage_lines,
|
||||
);
|
||||
let (store_default_struct, store_functions_to_metadata) = store_functions_to_metadata(
|
||||
&scrate,
|
||||
&traitinstance,
|
||||
&traittype,
|
||||
&instance_opts,
|
||||
&storage_lines,
|
||||
);
|
||||
|
||||
let InstanceOpts {
|
||||
instance,
|
||||
bound_instantiable,
|
||||
..
|
||||
} = instance_opts;
|
||||
|
||||
let cratename_string = cratename.to_string();
|
||||
let expanded = quote! {
|
||||
#scrate_decl
|
||||
@@ -125,10 +150,10 @@ pub fn decl_storage_impl(input: TokenStream) -> TokenStream {
|
||||
#decl_store_items
|
||||
}
|
||||
#store_default_struct
|
||||
impl<#traitinstance: #traittype> #storetype for #module_ident<#traitinstance> {
|
||||
impl<#traitinstance: #traittype, #instance #bound_instantiable> #storetype for #module_ident<#traitinstance, #instance> {
|
||||
#impl_store_items
|
||||
}
|
||||
impl<#traitinstance: 'static + #traittype> #module_ident<#traitinstance> {
|
||||
impl<#traitinstance: 'static + #traittype, #instance #bound_instantiable> #module_ident<#traitinstance, #instance> {
|
||||
#impl_store_fns
|
||||
#[doc(hidden)]
|
||||
pub fn store_metadata() -> #scrate::storage::generator::StorageMetadata {
|
||||
@@ -157,10 +182,20 @@ fn decl_store_extra_genesis(
|
||||
scrate: &TokenStream2,
|
||||
traitinstance: &Ident,
|
||||
traittype: &syn::TypeParamBound,
|
||||
instance_opts: &InstanceOpts,
|
||||
storage_lines: &ext::Punctuated<DeclStorageLine, Token![;]>,
|
||||
extra_genesis: &Option<AddExtraGenesis>,
|
||||
extra_genesis_skip_phantom_data_field: bool,
|
||||
) -> Result<TokenStream2> {
|
||||
|
||||
let InstanceOpts {
|
||||
comma_instance,
|
||||
equal_default_instance,
|
||||
bound_instantiable,
|
||||
instance,
|
||||
..
|
||||
} = instance_opts;
|
||||
|
||||
let mut is_trait_needed = false;
|
||||
let mut has_trait_field = false;
|
||||
let mut serde_complete_bound = std::collections::HashSet::new();
|
||||
@@ -222,7 +257,7 @@ fn decl_store_extra_genesis(
|
||||
},
|
||||
});
|
||||
opt_build = Some(build.as_ref().map(|b| &b.expr.content).map(|b|quote!( #b ))
|
||||
.unwrap_or_else(|| quote!( (|config: &GenesisConfig<#traitinstance>| config.#ident.clone()) )));
|
||||
.unwrap_or_else(|| quote!( (|config: &GenesisConfig<#traitinstance, #instance>| config.#ident.clone()) )));
|
||||
|
||||
let fielddefault = default_value.inner.as_ref().map(|d| &d.expr).map(|d|
|
||||
if type_infos.is_option {
|
||||
@@ -246,8 +281,7 @@ fn decl_store_extra_genesis(
|
||||
use #scrate::codec::{Encode, Decode};
|
||||
|
||||
let v = (#builder)(&self);
|
||||
<#name<#traitinstance> as #scrate::storage::generator::StorageValue<#typ>>::put(&v, &storage);
|
||||
|
||||
<#name<#traitinstance, #instance> as #scrate::storage::generator::StorageValue<#typ>>::put(&v, &storage);
|
||||
}}
|
||||
},
|
||||
DeclStorageTypeInfosKind::Map { key_type, .. } => {
|
||||
@@ -257,7 +291,7 @@ fn decl_store_extra_genesis(
|
||||
|
||||
let data = (#builder)(&self);
|
||||
for (k, v) in data.into_iter() {
|
||||
<#name<#traitinstance> as #scrate::storage::generator::StorageMap<#key_type, #typ>>::insert(&k, &v, &storage);
|
||||
<#name<#traitinstance, #instance> as #scrate::storage::generator::StorageMap<#key_type, #typ>>::insert(&k, &v, &storage);
|
||||
}
|
||||
}}
|
||||
},
|
||||
@@ -335,24 +369,26 @@ fn decl_store_extra_genesis(
|
||||
|| !genesis_extrafields.is_empty()
|
||||
|| !builders.is_empty();
|
||||
Ok(if is_extra_genesis_needed {
|
||||
let (fparam, sparam, ph_field, ph_default) = if is_trait_needed {
|
||||
if has_trait_field {
|
||||
let (fparam_struct, fparam_impl, sparam, ph_field, ph_default) = if is_trait_needed {
|
||||
if (has_trait_field && instance.is_none()) || extra_genesis_skip_phantom_data_field {
|
||||
// no phantom data required
|
||||
(
|
||||
quote!(<#traitinstance: #traittype>),
|
||||
quote!(<#traitinstance>),
|
||||
quote!(<#traitinstance: #traittype, #instance #bound_instantiable #equal_default_instance>),
|
||||
quote!(<#traitinstance: #traittype, #instance #bound_instantiable>),
|
||||
quote!(<#traitinstance, #instance>),
|
||||
quote!(),
|
||||
quote!(),
|
||||
)
|
||||
} else {
|
||||
// need phantom data
|
||||
(
|
||||
quote!(<#traitinstance: #traittype>),
|
||||
quote!(<#traitinstance>),
|
||||
quote!(<#traitinstance: #traittype, #instance #bound_instantiable #equal_default_instance>),
|
||||
quote!(<#traitinstance: #traittype, #instance #bound_instantiable>),
|
||||
quote!(<#traitinstance, #instance>),
|
||||
|
||||
quote!{
|
||||
#[serde(skip)]
|
||||
pub _genesis_phantom_data: #scrate::storage::generator::PhantomData<#traitinstance>,
|
||||
pub _genesis_phantom_data: #scrate::storage::generator::PhantomData<(#traitinstance #comma_instance)>,
|
||||
},
|
||||
quote!{
|
||||
_genesis_phantom_data: Default::default(),
|
||||
@@ -361,7 +397,7 @@ fn decl_store_extra_genesis(
|
||||
}
|
||||
} else {
|
||||
// do not even need type parameter
|
||||
(quote!(), quote!(), quote!(), quote!())
|
||||
(quote!(), quote!(), quote!(), quote!(), quote!())
|
||||
};
|
||||
quote!{
|
||||
|
||||
@@ -370,14 +406,14 @@ fn decl_store_extra_genesis(
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#serde_bug_bound
|
||||
pub struct GenesisConfig#fparam {
|
||||
pub struct GenesisConfig#fparam_struct {
|
||||
#ph_field
|
||||
#config_field
|
||||
#genesis_extrafields
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl#fparam Default for GenesisConfig#sparam {
|
||||
impl#fparam_impl Default for GenesisConfig#sparam {
|
||||
fn default() -> Self {
|
||||
GenesisConfig {
|
||||
#ph_default
|
||||
@@ -388,7 +424,7 @@ fn decl_store_extra_genesis(
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl#fparam #scrate::runtime_primitives::BuildStorage for GenesisConfig#sparam {
|
||||
impl#fparam_impl #scrate::runtime_primitives::BuildStorage for GenesisConfig#sparam {
|
||||
fn assimilate_storage(self, r: &mut #scrate::runtime_primitives::StorageOverlay, c: &mut #scrate::runtime_primitives::ChildrenStorageOverlay) -> ::std::result::Result<(), String> {
|
||||
use #scrate::rstd::{cell::RefCell, marker::PhantomData};
|
||||
let storage = (RefCell::new(r), PhantomData::<Self>::default());
|
||||
@@ -412,11 +448,96 @@ fn decl_storage_items(
|
||||
scrate: &TokenStream2,
|
||||
traitinstance: &Ident,
|
||||
traittype: &syn::TypeParamBound,
|
||||
instance_opts: &InstanceOpts,
|
||||
cratename: &Ident,
|
||||
storage_lines: &ext::Punctuated<DeclStorageLine, Token![;]>,
|
||||
) -> TokenStream2 {
|
||||
|
||||
let mut impls = TokenStream2::new();
|
||||
|
||||
let InstanceOpts {
|
||||
instance,
|
||||
default_instance,
|
||||
instantiable,
|
||||
..
|
||||
} = instance_opts;
|
||||
|
||||
// Build Instantiable trait
|
||||
if instance.is_some() {
|
||||
let mut method_defs = TokenStream2::new();
|
||||
let mut method_impls = TokenStream2::new();
|
||||
for sline in storage_lines.inner.iter() {
|
||||
let DeclStorageLine {
|
||||
storage_type,
|
||||
name,
|
||||
..
|
||||
} = sline;
|
||||
|
||||
let type_infos = get_type_infos(storage_type);
|
||||
|
||||
let method_name = syn::Ident::new(&format!("build_prefix_once_for_{}", name.to_string()), proc_macro2::Span::call_site());
|
||||
|
||||
method_defs.extend(quote!{ fn #method_name(prefix: &'static [u8]) -> &'static [u8]; });
|
||||
method_impls.extend(quote!{
|
||||
fn #method_name(prefix: &'static [u8]) -> &'static [u8] {
|
||||
static LAZY: #scrate::lazy::Lazy<#scrate::rstd::vec::Vec<u8>> = #scrate::lazy::Lazy::INIT;
|
||||
LAZY.get(|| {
|
||||
let mut final_prefix = #scrate::rstd::vec::Vec::new();
|
||||
final_prefix.extend_from_slice(prefix);
|
||||
final_prefix.extend_from_slice(Self::INSTANCE_PREFIX.as_bytes());
|
||||
final_prefix
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
if let DeclStorageTypeInfosKind::Map { is_linked: true, .. } = type_infos.kind {
|
||||
let method_name = syn::Ident::new(&format!("build_head_key_once_for_{}", name.to_string()), proc_macro2::Span::call_site());
|
||||
|
||||
method_defs.extend(quote!{ fn #method_name(prefix: &'static [u8]) -> &'static [u8]; });
|
||||
method_impls.extend(quote!{
|
||||
fn #method_name(prefix: &'static [u8]) -> &'static [u8] {
|
||||
static LAZY: #scrate::lazy::Lazy<#scrate::rstd::vec::Vec<u8>> = #scrate::lazy::Lazy::INIT;
|
||||
LAZY.get(|| {
|
||||
let mut final_prefix = #scrate::rstd::vec::Vec::new();
|
||||
final_prefix.extend_from_slice("head of ".as_bytes());
|
||||
final_prefix.extend_from_slice(prefix);
|
||||
final_prefix.extend_from_slice(Self::INSTANCE_PREFIX.as_bytes());
|
||||
final_prefix
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impls.extend(quote! {
|
||||
pub trait #instantiable: 'static {
|
||||
const INSTANCE_PREFIX: &'static str;
|
||||
#method_defs
|
||||
}
|
||||
});
|
||||
|
||||
let instances = (0..NUMBER_OF_INSTANCE)
|
||||
.map(|i| {
|
||||
let name = format!("Instance{}", i);
|
||||
let ident = syn::Ident::new(&name, proc_macro2::Span::call_site());
|
||||
(name, ident)
|
||||
})
|
||||
.chain(default_instance.clone().map(|ident| (String::new(), ident)));
|
||||
|
||||
for (prefix, ident) in instances {
|
||||
impls.extend(quote! {
|
||||
// Those trait are derived because of wrong bounds for generics
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
#[derive(Clone, Eq, PartialEq, #scrate::codec::Encode, #scrate::codec::Decode)]
|
||||
pub struct #ident;
|
||||
impl #instantiable for #ident {
|
||||
const INSTANCE_PREFIX: &'static str = #prefix;
|
||||
#method_impls
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for sline in storage_lines.inner.iter() {
|
||||
let DeclStorageLine {
|
||||
attrs,
|
||||
@@ -435,8 +556,10 @@ fn decl_storage_items(
|
||||
let i = impls::Impls {
|
||||
scrate,
|
||||
visibility,
|
||||
cratename,
|
||||
traitinstance,
|
||||
traittype,
|
||||
instance_opts,
|
||||
type_infos,
|
||||
fielddefault: default_value.inner.as_ref().map(|d| &d.expr).map(|d| quote!( #d ))
|
||||
.unwrap_or_else(|| quote!{ Default::default() }),
|
||||
@@ -474,13 +597,14 @@ fn decl_store_items(
|
||||
|
||||
fn impl_store_items(
|
||||
traitinstance: &Ident,
|
||||
instance: &Option<syn::Ident>,
|
||||
storage_lines: &ext::Punctuated<DeclStorageLine, Token![;]>,
|
||||
) -> TokenStream2 {
|
||||
storage_lines.inner.iter().map(|sline| &sline.name)
|
||||
.fold(TokenStream2::new(), |mut items, name| {
|
||||
items.extend(
|
||||
quote!(
|
||||
type #name = #name<#traitinstance>;
|
||||
type #name = #name<#traitinstance, #instance>;
|
||||
)
|
||||
);
|
||||
items
|
||||
@@ -490,6 +614,7 @@ fn impl_store_items(
|
||||
fn impl_store_fns(
|
||||
scrate: &TokenStream2,
|
||||
traitinstance: &Ident,
|
||||
instance: &Option<syn::Ident>,
|
||||
storage_lines: &ext::Punctuated<DeclStorageLine, Token![;]>,
|
||||
) -> TokenStream2 {
|
||||
let mut items = TokenStream2::new();
|
||||
@@ -517,7 +642,7 @@ fn impl_store_fns(
|
||||
quote!{
|
||||
#( #[ #attrs ] )*
|
||||
pub fn #get_fn() -> #value_type {
|
||||
<#name<#traitinstance> as #scrate::storage::generator::StorageValue<#typ>> :: get(&#scrate::storage::RuntimeStorage)
|
||||
<#name<#traitinstance, #instance> as #scrate::storage::generator::StorageValue<#typ>> :: get(&#scrate::storage::RuntimeStorage)
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -525,7 +650,7 @@ fn impl_store_fns(
|
||||
quote!{
|
||||
#( #[ #attrs ] )*
|
||||
pub fn #get_fn<K: #scrate::storage::generator::Borrow<#key_type>>(key: K) -> #value_type {
|
||||
<#name<#traitinstance> as #scrate::storage::generator::StorageMap<#key_type, #typ>> :: get(key.borrow(), &#scrate::storage::RuntimeStorage)
|
||||
<#name<#traitinstance, #instance> as #scrate::storage::generator::StorageMap<#key_type, #typ>> :: get(key.borrow(), &#scrate::storage::RuntimeStorage)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -540,9 +665,18 @@ fn store_functions_to_metadata (
|
||||
scrate: &TokenStream2,
|
||||
traitinstance: &Ident,
|
||||
traittype: &syn::TypeParamBound,
|
||||
instance_opts: &InstanceOpts,
|
||||
storage_lines: &ext::Punctuated<DeclStorageLine, Token![;]>,
|
||||
) -> (TokenStream2, TokenStream2) {
|
||||
|
||||
let InstanceOpts {
|
||||
comma_instance,
|
||||
equal_default_instance,
|
||||
bound_instantiable,
|
||||
instance,
|
||||
..
|
||||
} = instance_opts;
|
||||
|
||||
let mut items = TokenStream2::new();
|
||||
let mut default_getter_struct_def = TokenStream2::new();
|
||||
for sline in storage_lines.inner.iter() {
|
||||
@@ -613,7 +747,7 @@ fn store_functions_to_metadata (
|
||||
ty: #stype,
|
||||
default: #scrate::storage::generator::DecodeDifferent::Encode(
|
||||
#scrate::storage::generator::DefaultByteGetter(
|
||||
&#struct_name::<#traitinstance>(#scrate::rstd::marker::PhantomData)
|
||||
&#struct_name::<#traitinstance, #instance>(#scrate::rstd::marker::PhantomData)
|
||||
)
|
||||
),
|
||||
documentation: #scrate::storage::generator::DecodeDifferent::Encode(&[ #docs ]),
|
||||
@@ -622,12 +756,12 @@ fn store_functions_to_metadata (
|
||||
items.extend(item);
|
||||
let def_get = quote! {
|
||||
#[doc(hidden)]
|
||||
pub struct #struct_name<#traitinstance>(pub #scrate::rstd::marker::PhantomData<#traitinstance>);
|
||||
pub struct #struct_name<#traitinstance, #instance #bound_instantiable #equal_default_instance>(pub #scrate::rstd::marker::PhantomData<(#traitinstance #comma_instance)>);
|
||||
#[cfg(feature = "std")]
|
||||
#[allow(non_upper_case_globals)]
|
||||
static #cache_name: #scrate::once_cell::sync::OnceCell<#scrate::rstd::vec::Vec<u8>> = #scrate::once_cell::sync::OnceCell::INIT;
|
||||
#[cfg(feature = "std")]
|
||||
impl<#traitinstance: #traittype> #scrate::storage::generator::DefaultByte for #struct_name<#traitinstance> {
|
||||
impl<#traitinstance: #traittype, #instance #bound_instantiable> #scrate::storage::generator::DefaultByte for #struct_name<#traitinstance, #instance> {
|
||||
fn default_byte(&self) -> #scrate::rstd::vec::Vec<u8> {
|
||||
use #scrate::codec::Encode;
|
||||
#cache_name.get_or_init(|| {
|
||||
@@ -637,7 +771,7 @@ fn store_functions_to_metadata (
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "std"))]
|
||||
impl<#traitinstance: #traittype> #scrate::storage::generator::DefaultByte for #struct_name<#traitinstance> {
|
||||
impl<#traitinstance: #traittype, #instance #bound_instantiable> #scrate::storage::generator::DefaultByte for #struct_name<#traitinstance, #instance> {
|
||||
fn default_byte(&self) -> #scrate::rstd::vec::Vec<u8> {
|
||||
use #scrate::codec::Encode;
|
||||
let def_val: #value_type = #default;
|
||||
@@ -708,3 +842,44 @@ fn get_type_infos(storage_type: &DeclStorageType) -> DeclStorageTypeInfos {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct InstanceOpts {
|
||||
pub instance: Option<syn::Ident>,
|
||||
pub default_instance: Option<syn::Ident>,
|
||||
pub instantiable: Option<syn::Ident>,
|
||||
pub comma_instance: TokenStream2,
|
||||
pub equal_default_instance: TokenStream2,
|
||||
pub bound_instantiable: TokenStream2,
|
||||
}
|
||||
|
||||
fn get_instance_opts(
|
||||
instance: Option<syn::Ident>,
|
||||
instantiable: Option<syn::Ident>,
|
||||
default_instance: Option<syn::Ident>,
|
||||
) -> syn::Result<InstanceOpts> {
|
||||
|
||||
let right_syntax = "Should be $Instance: $Instantiable = $DefaultInstance";
|
||||
|
||||
match (instance, instantiable, default_instance) {
|
||||
(Some(instance), Some(instantiable), default_instance_def) => {
|
||||
let (equal_default_instance, default_instance) = if let Some(default_instance) = default_instance_def {
|
||||
(quote!{= #default_instance}, Some(default_instance))
|
||||
} else {
|
||||
(quote!{}, None)
|
||||
};
|
||||
Ok(InstanceOpts {
|
||||
comma_instance: quote!{, #instance},
|
||||
equal_default_instance,
|
||||
bound_instantiable: quote!{: #instantiable},
|
||||
instance: Some(instance),
|
||||
default_instance,
|
||||
instantiable: Some(instantiable),
|
||||
})
|
||||
},
|
||||
(None, None, None) => Ok(Default::default()),
|
||||
(Some(instance), None, _) => Err(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))),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user