fix: Complete snowbridge pezpallet rebrand and critical bug fixes
- snowbridge-pezpallet-* → pezsnowbridge-pezpallet-* (201 refs) - pallet/ directories → pezpallet/ (4 locations) - Fixed pezpallet.rs self-include recursion bug - Fixed sc-chain-spec hardcoded crate name in derive macro - Reverted .pezpallet_by_name() to .pallet_by_name() (subxt API) - Added BizinikiwiConfig type alias for zombienet tests - Deleted obsolete session state files Verified: pezsnowbridge-pezpallet-*, pezpallet-staking, pezpallet-staking-async, pezframe-benchmarking-cli all pass cargo check
This commit is contained in:
@@ -0,0 +1,614 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::{
|
||||
deprecation::extract_or_return_allow_attrs,
|
||||
pezpallet::{
|
||||
expand::warnings::{weight_constant_warning, weight_witness_warning},
|
||||
parse::{
|
||||
call::{CallVariantDef, CallWeightDef},
|
||||
helper::CallReturnType,
|
||||
},
|
||||
Def,
|
||||
},
|
||||
COUNTER,
|
||||
};
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use proc_macro_warning::Warning;
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
/// Expand the weight to final token stream and accumulate warnings.
|
||||
fn expand_weight(
|
||||
prefix: &str,
|
||||
pezframe_support: &syn::Path,
|
||||
dev_mode: bool,
|
||||
weight_warnings: &mut Vec<Warning>,
|
||||
method: &CallVariantDef,
|
||||
weight: &CallWeightDef,
|
||||
) -> TokenStream2 {
|
||||
match weight {
|
||||
CallWeightDef::DevModeDefault => quote::quote!(
|
||||
#pezframe_support::pezpallet_prelude::Weight::zero()
|
||||
),
|
||||
CallWeightDef::Immediate(e) => {
|
||||
weight_constant_warning(e, dev_mode, weight_warnings);
|
||||
weight_witness_warning(method, dev_mode, weight_warnings);
|
||||
|
||||
e.into_token_stream()
|
||||
},
|
||||
CallWeightDef::Inherited(t) => {
|
||||
// Expand `<<T as Config>::WeightInfo>::$prefix$call_name()`.
|
||||
let n = &syn::Ident::new(&format!("{}{}", prefix, method.name), method.name.span());
|
||||
quote!({ < #t > :: #n () })
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// * Generate enum call and implement various trait on it.
|
||||
/// * Implement Callable and call_function on `Pezpallet`
|
||||
pub fn expand_call(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
let (span, where_clause, methods, docs) = match def.call.as_ref() {
|
||||
Some(call) => {
|
||||
let span = call.attr_span;
|
||||
let where_clause = call.where_clause.clone();
|
||||
let methods = call.methods.clone();
|
||||
let docs = call.docs.clone();
|
||||
|
||||
(span, where_clause, methods, docs)
|
||||
},
|
||||
None => (def.item.span(), def.config.where_clause.clone(), Vec::new(), Vec::new()),
|
||||
};
|
||||
let pezframe_support = &def.pezframe_support;
|
||||
let pezframe_system = &def.pezframe_system;
|
||||
let type_impl_gen = &def.type_impl_generics(span);
|
||||
let type_decl_bounded_gen = &def.type_decl_bounded_generics(span);
|
||||
let type_use_gen = &def.type_use_generics(span);
|
||||
let call_ident = syn::Ident::new("Call", span);
|
||||
let pezpallet_ident = &def.pezpallet_struct.pezpallet;
|
||||
|
||||
let fn_name = methods.iter().map(|method| &method.name).collect::<Vec<_>>();
|
||||
let call_index = methods.iter().map(|method| method.call_index).collect::<Vec<_>>();
|
||||
let new_call_variant_fn_name = fn_name
|
||||
.iter()
|
||||
.map(|fn_name| quote::format_ident!("new_call_variant_{}", fn_name))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let new_call_variant_doc = fn_name
|
||||
.iter()
|
||||
.map(|fn_name| format!("Create a call with the variant `{}`.", fn_name))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut call_index_warnings = Vec::new();
|
||||
// Emit a warning for each call that is missing `call_index` when not in dev-mode.
|
||||
for method in &methods {
|
||||
if method.explicit_call_index || def.dev_mode {
|
||||
continue;
|
||||
}
|
||||
|
||||
let warning = Warning::new_deprecated("ImplicitCallIndex")
|
||||
.index(call_index_warnings.len())
|
||||
.old("use implicit call indices")
|
||||
.new("ensure that all calls have a `pezpallet::call_index` attribute or put the pezpallet into `dev` mode")
|
||||
.help_links(&[
|
||||
"https://github.com/pezkuwichain/kurdistan-sdk/issues/39",
|
||||
"https://github.com/pezkuwichain/kurdistan-sdk/issues/36"
|
||||
])
|
||||
.span(method.name.span())
|
||||
.build_or_panic();
|
||||
call_index_warnings.push(warning);
|
||||
}
|
||||
|
||||
let mut fn_weight = Vec::<TokenStream2>::new();
|
||||
let mut weight_warnings = Vec::new();
|
||||
for method in &methods {
|
||||
let w = expand_weight(
|
||||
"",
|
||||
pezframe_support,
|
||||
def.dev_mode,
|
||||
&mut weight_warnings,
|
||||
method,
|
||||
&method.weight,
|
||||
);
|
||||
fn_weight.push(w);
|
||||
}
|
||||
debug_assert_eq!(fn_weight.len(), methods.len());
|
||||
|
||||
let fn_doc = methods.iter().map(|method| &method.docs).collect::<Vec<_>>();
|
||||
|
||||
let args_name = methods
|
||||
.iter()
|
||||
.map(|method| method.args.iter().map(|(_, name, _)| name.clone()).collect::<Vec<_>>())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let args_name_stripped = methods
|
||||
.iter()
|
||||
.map(|method| {
|
||||
method
|
||||
.args
|
||||
.iter()
|
||||
.map(|(_, name, _)| {
|
||||
syn::Ident::new(name.to_string().trim_start_matches('_'), name.span())
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let make_args_name_pattern = |ref_tok| {
|
||||
args_name
|
||||
.iter()
|
||||
.zip(args_name_stripped.iter())
|
||||
.map(|(args_name, args_name_stripped)| {
|
||||
args_name
|
||||
.iter()
|
||||
.zip(args_name_stripped)
|
||||
.map(|(args_name, args_name_stripped)| {
|
||||
if args_name == args_name_stripped {
|
||||
quote::quote!( #ref_tok #args_name )
|
||||
} else {
|
||||
quote::quote!( #args_name_stripped: #ref_tok #args_name )
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
|
||||
let args_name_pattern = make_args_name_pattern(None);
|
||||
let args_name_pattern_ref = make_args_name_pattern(Some(quote::quote!(ref)));
|
||||
|
||||
let args_type = methods
|
||||
.iter()
|
||||
.map(|method| method.args.iter().map(|(_, _, type_)| type_.clone()).collect::<Vec<_>>())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let args_compact_attr = methods.iter().map(|method| {
|
||||
method
|
||||
.args
|
||||
.iter()
|
||||
.map(|(is_compact, _, type_)| {
|
||||
if *is_compact {
|
||||
quote::quote_spanned!(type_.span() => #[codec(compact)] )
|
||||
} else {
|
||||
quote::quote!()
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
|
||||
let default_docs =
|
||||
[syn::parse_quote!(r"Contains a variant per dispatchable extrinsic that this pezpallet has.")];
|
||||
let docs = if docs.is_empty() { &default_docs[..] } else { &docs[..] };
|
||||
|
||||
let maybe_compile_error = if def.call.is_none() {
|
||||
quote::quote! {
|
||||
compile_error!(concat!(
|
||||
"`",
|
||||
stringify!($pezpallet_name),
|
||||
"` does not have #[pezpallet::call] defined, perhaps you should remove `Call` from \
|
||||
construct_runtime?",
|
||||
));
|
||||
}
|
||||
} else {
|
||||
proc_macro2::TokenStream::new()
|
||||
};
|
||||
|
||||
let count = COUNTER.with(|counter| counter.borrow_mut().inc());
|
||||
let macro_ident = syn::Ident::new(&format!("__is_call_part_defined_{}", count), span);
|
||||
|
||||
let capture_docs = if cfg!(feature = "no-metadata-docs") { "never" } else { "always" };
|
||||
|
||||
// Wrap all calls inside of storage layers
|
||||
if let Some(call) = def.call.as_ref() {
|
||||
let item_impl =
|
||||
&mut def.item.content.as_mut().expect("Checked by def parser").1[call.index];
|
||||
let syn::Item::Impl(item_impl) = item_impl else {
|
||||
unreachable!("Checked by def parser");
|
||||
};
|
||||
|
||||
item_impl.items.iter_mut().enumerate().for_each(|(i, item)| {
|
||||
if let syn::ImplItem::Fn(method) = item {
|
||||
let return_type =
|
||||
&call.methods.get(i).expect("def should be consistent with item").return_type;
|
||||
|
||||
let (ok_type, err_type) = match return_type {
|
||||
CallReturnType::DispatchResult => (
|
||||
quote::quote!(()),
|
||||
quote::quote!(#pezframe_support::pezpallet_prelude::DispatchError),
|
||||
),
|
||||
CallReturnType::DispatchResultWithPostInfo => (
|
||||
quote::quote!(#pezframe_support::dispatch::PostDispatchInfo),
|
||||
quote::quote!(#pezframe_support::dispatch::DispatchErrorWithPostInfo),
|
||||
),
|
||||
};
|
||||
|
||||
let block = &method.block;
|
||||
method.block = syn::parse_quote! {{
|
||||
// We execute all dispatchable in a new storage layer, allowing them
|
||||
// to return an error at any point, and undoing any storage changes.
|
||||
#pezframe_support::storage::with_storage_layer::<#ok_type, #err_type, _>(
|
||||
|| #block
|
||||
)
|
||||
}};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Extracts #[allow] attributes, necessary so that we don't run into compiler warnings
|
||||
let maybe_allow_attrs = methods
|
||||
.iter()
|
||||
.map(|method| {
|
||||
let attrs = extract_or_return_allow_attrs(&method.attrs);
|
||||
quote::quote! {
|
||||
#(#attrs)*
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let cfg_attrs = methods
|
||||
.iter()
|
||||
.map(|method| {
|
||||
let attrs =
|
||||
method.cfg_attrs.iter().map(|attr| attr.to_token_stream()).collect::<Vec<_>>();
|
||||
quote::quote!( #( #attrs )* )
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let feeless_checks = methods.iter().map(|method| &method.feeless_check).collect::<Vec<_>>();
|
||||
let feeless_check =
|
||||
feeless_checks.iter().zip(args_name.iter()).map(|(feeless_check, arg_name)| {
|
||||
if let Some(check) = feeless_check {
|
||||
quote::quote_spanned!(span => #check)
|
||||
} else {
|
||||
quote::quote_spanned!(span => |_origin, #( #arg_name, )*| { false })
|
||||
}
|
||||
});
|
||||
|
||||
let deprecation = match crate::deprecation::get_deprecation_enum(
|
||||
"e::quote! {#pezframe_support},
|
||||
methods.iter().map(|item| (item.call_index as u8, item.attrs.as_ref())),
|
||||
) {
|
||||
Ok(deprecation) => deprecation,
|
||||
Err(e) => return e.into_compile_error(),
|
||||
};
|
||||
|
||||
// Implementation of the authorize function for each call
|
||||
// `authorize_fn_pallet_impl` writes the user-defined authorize function as a function
|
||||
// implementation for the pezpallet.
|
||||
// `authorize_impl` is the call to this former function to implement `Authorize` trait.
|
||||
let (authorize_fn_pallet_impl, authorize_impl) = methods
|
||||
.iter()
|
||||
.zip(args_name.iter())
|
||||
.zip(args_type.iter())
|
||||
.zip(cfg_attrs.iter())
|
||||
.map(|(((method, arg_name), arg_type), cfg_attr)| {
|
||||
if let Some(authorize_def) = &method.authorize {
|
||||
let authorize_fn = &authorize_def.expr;
|
||||
let attr_fn_getter = syn::Ident::new(
|
||||
&format!("__macro_inner_authorize_call_for_{}", method.name),
|
||||
authorize_fn.span(),
|
||||
);
|
||||
let source = syn::Ident::new("source", span);
|
||||
|
||||
let authorize_fn_pallet_impl = quote::quote_spanned!(authorize_fn.span() =>
|
||||
// Closure don't have a writable type. So we fix the authorize token stream to
|
||||
// be any implementation of a specific function.
|
||||
// This allows to have good type inference on the closure.
|
||||
//
|
||||
// Then we wrap this into an implementation for `Pezpallet` in order to get access
|
||||
// to `Self` as `Pezpallet` instead of `Call`.
|
||||
#cfg_attr
|
||||
impl<#type_impl_gen> Pezpallet<#type_use_gen> #where_clause {
|
||||
#[doc(hidden)]
|
||||
fn #attr_fn_getter() -> impl Fn(
|
||||
#pezframe_support::pezpallet_prelude::TransactionSource,
|
||||
#( &#arg_type ),*
|
||||
) -> #pezframe_support::pezpallet_prelude::TransactionValidityWithRefund {
|
||||
#authorize_fn
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// `source` is from outside this block, so we can't use the authorize_fn span.
|
||||
let authorize_impl = quote::quote!(
|
||||
{
|
||||
let authorize_fn = Pezpallet::<#type_use_gen>::#attr_fn_getter();
|
||||
let res = authorize_fn(#source, #( #arg_name, )*);
|
||||
|
||||
Some(res)
|
||||
}
|
||||
);
|
||||
|
||||
(authorize_fn_pallet_impl, authorize_impl)
|
||||
} else {
|
||||
(Default::default(), quote::quote!(None))
|
||||
}
|
||||
})
|
||||
.unzip::<_, _, Vec<TokenStream2>, Vec<TokenStream2>>();
|
||||
|
||||
// Implementation of the authorize function weight for each call
|
||||
let mut authorize_fn_weight = Vec::<TokenStream2>::new();
|
||||
for method in &methods {
|
||||
let w = match &method.authorize {
|
||||
Some(authorize_def) => expand_weight(
|
||||
"authorize_",
|
||||
pezframe_support,
|
||||
def.dev_mode,
|
||||
&mut weight_warnings,
|
||||
method,
|
||||
&authorize_def.weight,
|
||||
),
|
||||
// No authorize logic, weight is negligible
|
||||
None => quote::quote!(#pezframe_support::pezpallet_prelude::Weight::zero()),
|
||||
};
|
||||
authorize_fn_weight.push(w);
|
||||
}
|
||||
assert_eq!(authorize_fn_weight.len(), methods.len());
|
||||
|
||||
quote::quote_spanned!(span =>
|
||||
#[doc(hidden)]
|
||||
mod warnings {
|
||||
#(
|
||||
#call_index_warnings
|
||||
)*
|
||||
#(
|
||||
#weight_warnings
|
||||
)*
|
||||
}
|
||||
|
||||
#[allow(unused_imports)]
|
||||
#[doc(hidden)]
|
||||
pub mod __bizinikiwi_call_check {
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! #macro_ident {
|
||||
($pezpallet_name:ident) => {
|
||||
#maybe_compile_error
|
||||
};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use #macro_ident as is_call_part_defined;
|
||||
}
|
||||
|
||||
#( #[doc = #docs] )*
|
||||
#[derive(
|
||||
#pezframe_support::RuntimeDebugNoBound,
|
||||
#pezframe_support::CloneNoBound,
|
||||
#pezframe_support::EqNoBound,
|
||||
#pezframe_support::PartialEqNoBound,
|
||||
#pezframe_support::__private::codec::Encode,
|
||||
#pezframe_support::__private::codec::Decode,
|
||||
#pezframe_support::__private::codec::DecodeWithMemTracking,
|
||||
#pezframe_support::__private::scale_info::TypeInfo,
|
||||
)]
|
||||
#[codec(encode_bound())]
|
||||
#[codec(decode_bound())]
|
||||
#[scale_info(skip_type_params(#type_use_gen), capture_docs = #capture_docs)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum #call_ident<#type_decl_bounded_gen> #where_clause {
|
||||
#[doc(hidden)]
|
||||
#[codec(skip)]
|
||||
__Ignore(
|
||||
::core::marker::PhantomData<(#type_use_gen,)>,
|
||||
#pezframe_support::Never,
|
||||
),
|
||||
#(
|
||||
#cfg_attrs
|
||||
#( #[doc = #fn_doc] )*
|
||||
#[codec(index = #call_index)]
|
||||
#fn_name {
|
||||
#(
|
||||
#[allow(missing_docs)]
|
||||
#args_compact_attr #args_name_stripped: #args_type
|
||||
),*
|
||||
},
|
||||
)*
|
||||
}
|
||||
|
||||
impl<#type_impl_gen> #call_ident<#type_use_gen> #where_clause {
|
||||
#(
|
||||
#cfg_attrs
|
||||
#[doc = #new_call_variant_doc]
|
||||
pub fn #new_call_variant_fn_name(
|
||||
#( #args_name_stripped: #args_type ),*
|
||||
) -> Self {
|
||||
Self::#fn_name {
|
||||
#( #args_name_stripped ),*
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
|
||||
impl<#type_impl_gen> #pezframe_support::dispatch::GetDispatchInfo
|
||||
for #call_ident<#type_use_gen>
|
||||
#where_clause
|
||||
{
|
||||
fn get_dispatch_info(&self) -> #pezframe_support::dispatch::DispatchInfo {
|
||||
match *self {
|
||||
#(
|
||||
#cfg_attrs
|
||||
Self::#fn_name { #( #args_name_pattern_ref, )* } => {
|
||||
let __pallet_base_weight = #fn_weight;
|
||||
|
||||
let __pallet_weight = <
|
||||
dyn #pezframe_support::dispatch::WeighData<( #( & #args_type, )* )>
|
||||
>::weigh_data(&__pallet_base_weight, ( #( #args_name, )* ));
|
||||
|
||||
let __pallet_class = <
|
||||
dyn #pezframe_support::dispatch::ClassifyDispatch<
|
||||
( #( & #args_type, )* )
|
||||
>
|
||||
>::classify_dispatch(&__pallet_base_weight, ( #( #args_name, )* ));
|
||||
|
||||
let __pallet_pays_fee = <
|
||||
dyn #pezframe_support::dispatch::PaysFee<( #( & #args_type, )* )>
|
||||
>::pays_fee(&__pallet_base_weight, ( #( #args_name, )* ));
|
||||
|
||||
#pezframe_support::dispatch::DispatchInfo {
|
||||
call_weight: __pallet_weight,
|
||||
extension_weight: Default::default(),
|
||||
class: __pallet_class,
|
||||
pays_fee: __pallet_pays_fee,
|
||||
}
|
||||
},
|
||||
)*
|
||||
Self::__Ignore(_, _) => unreachable!("__Ignore cannot be used"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<#type_impl_gen> #pezframe_support::dispatch::CheckIfFeeless for #call_ident<#type_use_gen>
|
||||
#where_clause
|
||||
{
|
||||
type Origin = #pezframe_system::pezpallet_prelude::OriginFor<T>;
|
||||
#[allow(unused_variables)]
|
||||
fn is_feeless(&self, origin: &Self::Origin) -> bool {
|
||||
match *self {
|
||||
#(
|
||||
#cfg_attrs
|
||||
Self::#fn_name { #( #args_name_pattern_ref, )* } => {
|
||||
let feeless_check = #feeless_check;
|
||||
feeless_check(origin, #( #args_name, )*)
|
||||
},
|
||||
)*
|
||||
Self::__Ignore(_, _) => unreachable!("__Ignore cannot be used"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<#type_impl_gen> #pezframe_support::traits::GetCallName for #call_ident<#type_use_gen>
|
||||
#where_clause
|
||||
{
|
||||
fn get_call_name(&self) -> &'static str {
|
||||
match *self {
|
||||
#( #cfg_attrs Self::#fn_name { .. } => stringify!(#fn_name), )*
|
||||
Self::__Ignore(_, _) => unreachable!("__PhantomItem cannot be used."),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_call_names() -> &'static [&'static str] {
|
||||
&[ #( #cfg_attrs stringify!(#fn_name), )* ]
|
||||
}
|
||||
}
|
||||
|
||||
impl<#type_impl_gen> #pezframe_support::traits::GetCallIndex for #call_ident<#type_use_gen>
|
||||
#where_clause
|
||||
{
|
||||
fn get_call_index(&self) -> u8 {
|
||||
match *self {
|
||||
#( #cfg_attrs Self::#fn_name { .. } => #call_index, )*
|
||||
Self::__Ignore(_, _) => unreachable!("__PhantomItem cannot be used."),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_call_indices() -> &'static [u8] {
|
||||
&[ #( #cfg_attrs #call_index, )* ]
|
||||
}
|
||||
}
|
||||
|
||||
impl<#type_impl_gen> #pezframe_support::traits::UnfilteredDispatchable
|
||||
for #call_ident<#type_use_gen>
|
||||
#where_clause
|
||||
{
|
||||
type RuntimeOrigin = #pezframe_system::pezpallet_prelude::OriginFor<T>;
|
||||
fn dispatch_bypass_filter(
|
||||
self,
|
||||
origin: Self::RuntimeOrigin
|
||||
) -> #pezframe_support::dispatch::DispatchResultWithPostInfo {
|
||||
#pezframe_support::dispatch_context::run_in_context(|| {
|
||||
match self {
|
||||
#(
|
||||
#cfg_attrs
|
||||
Self::#fn_name { #( #args_name_pattern, )* } => {
|
||||
#pezframe_support::__private::pezsp_tracing::enter_span!(
|
||||
#pezframe_support::__private::pezsp_tracing::trace_span!(stringify!(#fn_name))
|
||||
);
|
||||
#maybe_allow_attrs
|
||||
#[allow(clippy::useless_conversion)]
|
||||
<#pezpallet_ident<#type_use_gen>>::#fn_name(origin, #( #args_name, )* )
|
||||
.map(Into::into).map_err(Into::into)
|
||||
},
|
||||
)*
|
||||
Self::__Ignore(_, _) => {
|
||||
let _ = origin; // Use origin for empty Call enum
|
||||
unreachable!("__PhantomItem cannot be used.");
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<#type_impl_gen> #pezframe_support::dispatch::Callable<T> for #pezpallet_ident<#type_use_gen>
|
||||
#where_clause
|
||||
{
|
||||
type RuntimeCall = #call_ident<#type_use_gen>;
|
||||
}
|
||||
|
||||
impl<#type_impl_gen> #pezpallet_ident<#type_use_gen> #where_clause {
|
||||
#[allow(dead_code)]
|
||||
#[doc(hidden)]
|
||||
pub fn call_functions() -> #pezframe_support::__private::metadata_ir::PalletCallMetadataIR {
|
||||
#pezframe_support::__private::metadata_ir::PalletCallMetadataIR {
|
||||
ty: #pezframe_support::__private::scale_info::meta_type::<#call_ident<#type_use_gen>>(),
|
||||
deprecation_info: #deprecation,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#( #authorize_fn_pallet_impl )*
|
||||
|
||||
impl<#type_impl_gen> #pezframe_support::traits::Authorize for #call_ident<#type_use_gen>
|
||||
#where_clause
|
||||
{
|
||||
fn authorize(&self, source: #pezframe_support::pezpallet_prelude::TransactionSource) -> ::core::option::Option<::core::result::Result<
|
||||
(
|
||||
#pezframe_support::pezpallet_prelude::ValidTransaction,
|
||||
#pezframe_support::pezpallet_prelude::Weight,
|
||||
),
|
||||
#pezframe_support::pezpallet_prelude::TransactionValidityError
|
||||
>>
|
||||
{
|
||||
match *self {
|
||||
#(
|
||||
#cfg_attrs
|
||||
Self::#fn_name { #( #args_name_pattern_ref, )* } => {
|
||||
#authorize_impl
|
||||
},
|
||||
)*
|
||||
Self::__Ignore(_, _) => {
|
||||
let _ = source;
|
||||
unreachable!("__Ignore cannot be used")
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn weight_of_authorize(&self) -> #pezframe_support::pezpallet_prelude::Weight {
|
||||
match *self {
|
||||
#(
|
||||
#cfg_attrs
|
||||
Self::#fn_name { #( #args_name_pattern_ref, )* } => {
|
||||
#authorize_fn_weight
|
||||
},
|
||||
)*
|
||||
Self::__Ignore(_, _) => unreachable!("__Ignore cannot be used"),
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::pezpallet::Def;
|
||||
use proc_macro2::TokenStream;
|
||||
|
||||
/// Expands `composite_enum` and adds the `VariantCount` implementation for it.
|
||||
pub fn expand_composites(def: &mut Def) -> TokenStream {
|
||||
let mut expand = quote::quote!();
|
||||
let pezframe_support = &def.pezframe_support;
|
||||
|
||||
for composite in &def.composites {
|
||||
let name = &composite.ident;
|
||||
let (impl_generics, ty_generics, where_clause) = composite.generics.split_for_impl();
|
||||
let variants_count = composite.variant_count;
|
||||
|
||||
// add `VariantCount` implementation for `composite_enum`
|
||||
expand.extend(quote::quote_spanned!(composite.attr_span =>
|
||||
impl #impl_generics #pezframe_support::traits::VariantCount for #name #ty_generics #where_clause {
|
||||
const VARIANT_COUNT: u32 = #variants_count;
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
expand
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::pezpallet::{parse::GenericKind, Def};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{parse_quote, Item};
|
||||
|
||||
///
|
||||
/// * Generate default rust doc
|
||||
pub fn expand_config(def: &mut Def) -> TokenStream {
|
||||
let config = &def.config;
|
||||
let config_item = {
|
||||
let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[config.index];
|
||||
if let Item::Trait(item) = item {
|
||||
item
|
||||
} else {
|
||||
unreachable!("Checked by config parser")
|
||||
}
|
||||
};
|
||||
|
||||
config_item.attrs.insert(
|
||||
0,
|
||||
parse_quote!(
|
||||
#[doc = r"
|
||||
Configuration trait of this pezpallet.
|
||||
|
||||
The main purpose of this trait is to act as an interface between this pezpallet and the runtime in
|
||||
which it is embedded in. A type, function, or constant in this trait is essentially left to be
|
||||
configured by the runtime that includes this pezpallet.
|
||||
|
||||
Consequently, a runtime that wants to include this pezpallet must implement this trait."
|
||||
]
|
||||
),
|
||||
);
|
||||
config_item.attrs.retain(|attr| !attr.path().is_ident("deprecated"));
|
||||
|
||||
// insert `pezframe_system::Config` supertrait with `RuntimeEvent: From<Event<Self>>` if neither
|
||||
// associated type nor type bound is defined.
|
||||
if let Some(event) = &def.event {
|
||||
if !def.is_pezframe_system {
|
||||
let pezframe_system = &def.pezframe_system;
|
||||
|
||||
// can't use `type_use_gen()` since it returns `T`, not `Self`
|
||||
let event_use_gen = match event.gen_kind {
|
||||
GenericKind::None => quote!(),
|
||||
GenericKind::Config => quote::quote_spanned! {event.attr_span => Self},
|
||||
GenericKind::ConfigAndInstance => {
|
||||
quote::quote_spanned! {event.attr_span => Self, I}
|
||||
},
|
||||
};
|
||||
|
||||
let supertrait_with_event_bound = syn::parse2::<syn::TypeParamBound>(
|
||||
quote! { #pezframe_system::Config<RuntimeEvent: From<Event<#event_use_gen>>> },
|
||||
)
|
||||
.expect("Parsing super trait doesn't fail; qed");
|
||||
|
||||
config_item.supertraits.push(supertrait_with_event_bound.into());
|
||||
}
|
||||
}
|
||||
|
||||
// we only emit `DefaultConfig` if there are trait items, so an empty `DefaultConfig` is
|
||||
// impossible consequently.
|
||||
match &config.default_sub_trait {
|
||||
Some(default_sub_trait) if default_sub_trait.items.len() > 0 => {
|
||||
let trait_items = &default_sub_trait
|
||||
.items
|
||||
.iter()
|
||||
.map(|item| {
|
||||
if item.1 {
|
||||
if let syn::TraitItem::Type(item) = item.0.clone() {
|
||||
let mut item = item.clone();
|
||||
item.bounds.clear();
|
||||
syn::TraitItem::Type(item)
|
||||
} else {
|
||||
item.0.clone()
|
||||
}
|
||||
} else {
|
||||
item.0.clone()
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let type_param_bounds = if default_sub_trait.has_system {
|
||||
let system = &def.pezframe_system;
|
||||
quote::quote!(: #system::DefaultConfig)
|
||||
} else {
|
||||
quote::quote!()
|
||||
};
|
||||
|
||||
quote!(
|
||||
/// Based on [`Config`]. Auto-generated by
|
||||
/// [`#[pezpallet::config(with_default)]`](`pezframe_support::pezpallet_macros::config`).
|
||||
/// Can be used in tandem with
|
||||
/// [`#[register_default_config]`](`pezframe_support::register_default_config`) and
|
||||
/// [`#[derive_impl]`](`pezframe_support::derive_impl`) to derive test config traits
|
||||
/// based on existing pezpallet config traits in a safe and developer-friendly way.
|
||||
///
|
||||
/// See [here](`pezframe_support::pezpallet_macros::config`) for more information and caveats about
|
||||
/// the auto-generated `DefaultConfig` trait and how it is generated.
|
||||
pub trait DefaultConfig #type_param_bounds {
|
||||
#(#trait_items)*
|
||||
}
|
||||
)
|
||||
},
|
||||
_ => quote!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate the metadata for the associated types of the config trait.
|
||||
///
|
||||
/// Implements the `pezpallet_associated_types_metadata` function for the pezpallet.
|
||||
pub fn expand_config_metadata(def: &Def) -> proc_macro2::TokenStream {
|
||||
let pezframe_support = &def.pezframe_support;
|
||||
let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site());
|
||||
let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site());
|
||||
let pezpallet_ident = &def.pezpallet_struct.pezpallet;
|
||||
let trait_use_gen = &def.trait_use_generics(proc_macro2::Span::call_site());
|
||||
|
||||
let mut where_clauses = vec![&def.config.where_clause];
|
||||
where_clauses.extend(def.extra_constants.iter().map(|d| &d.where_clause));
|
||||
let completed_where_clause = super::merge_where_clauses(&where_clauses);
|
||||
|
||||
let types = def.config.associated_types_metadata.iter().map(|metadata| {
|
||||
let ident = &metadata.ident;
|
||||
let span = ident.span();
|
||||
let ident_str = ident.to_string();
|
||||
let cfgs = &metadata.cfg;
|
||||
|
||||
let no_docs = vec![];
|
||||
let doc = if cfg!(feature = "no-metadata-docs") { &no_docs } else { &metadata.doc };
|
||||
|
||||
quote::quote_spanned!(span => {
|
||||
#( #cfgs ) *
|
||||
#pezframe_support::__private::metadata_ir::PalletAssociatedTypeMetadataIR {
|
||||
name: #ident_str,
|
||||
ty: #pezframe_support::__private::scale_info::meta_type::<
|
||||
<T as Config #trait_use_gen>::#ident
|
||||
>(),
|
||||
docs: #pezframe_support::__private::vec![ #( #doc ),* ],
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
quote::quote!(
|
||||
impl<#type_impl_gen> #pezpallet_ident<#type_use_gen> #completed_where_clause {
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn pezpallet_associated_types_metadata()
|
||||
-> #pezframe_support::__private::vec::Vec<#pezframe_support::__private::metadata_ir::PalletAssociatedTypeMetadataIR>
|
||||
{
|
||||
#pezframe_support::__private::vec![ #( #types ),* ]
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::{deprecation::extract_or_return_allow_attrs, pezpallet::Def};
|
||||
|
||||
struct ConstDef {
|
||||
/// Name of the associated type.
|
||||
pub ident: syn::Ident,
|
||||
/// The type in Get, e.g. `u32` in `type Foo: Get<u32>;`, but `Self` is replaced by `T`
|
||||
pub type_: syn::Type,
|
||||
/// The doc associated
|
||||
pub doc: Vec<syn::Expr>,
|
||||
/// default_byte implementation
|
||||
pub default_byte_impl: proc_macro2::TokenStream,
|
||||
/// Constant name for Metadata (optional)
|
||||
pub metadata_name: Option<syn::Ident>,
|
||||
/// Deprecation_info:
|
||||
pub deprecation_info: proc_macro2::TokenStream,
|
||||
}
|
||||
|
||||
/// Implement the `pezpallet_constants_metadata` function for the pezpallet.
|
||||
pub fn expand_constants(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
let pezframe_support = &def.pezframe_support;
|
||||
let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site());
|
||||
let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site());
|
||||
let pezpallet_ident = &def.pezpallet_struct.pezpallet;
|
||||
let trait_use_gen = &def.trait_use_generics(proc_macro2::Span::call_site());
|
||||
|
||||
let mut where_clauses = vec![&def.config.where_clause];
|
||||
where_clauses.extend(def.extra_constants.iter().map(|d| &d.where_clause));
|
||||
let completed_where_clause = super::merge_where_clauses(&where_clauses);
|
||||
|
||||
let mut config_consts = vec![];
|
||||
for const_ in def.config.consts_metadata.iter() {
|
||||
let ident = &const_.ident;
|
||||
let const_type = &const_.type_;
|
||||
let deprecation_info = match crate::deprecation::get_deprecation(
|
||||
"e::quote! { #pezframe_support },
|
||||
&const_.attrs,
|
||||
) {
|
||||
Ok(deprecation) => deprecation,
|
||||
Err(e) => return e.into_compile_error(),
|
||||
};
|
||||
|
||||
// Extracts #[allow] attributes, necessary so that we don't run into compiler warnings
|
||||
let maybe_allow_attrs = extract_or_return_allow_attrs(&const_.attrs);
|
||||
|
||||
config_consts.push(ConstDef {
|
||||
ident: const_.ident.clone(),
|
||||
type_: const_.type_.clone(),
|
||||
doc: const_.doc.clone(),
|
||||
default_byte_impl: quote::quote!(
|
||||
#(#maybe_allow_attrs)*
|
||||
let value = <<T as Config #trait_use_gen>::#ident as
|
||||
#pezframe_support::traits::Get<#const_type>>::get();
|
||||
#pezframe_support::__private::codec::Encode::encode(&value)
|
||||
),
|
||||
metadata_name: None,
|
||||
deprecation_info,
|
||||
})
|
||||
}
|
||||
|
||||
let mut extra_consts = vec![];
|
||||
for const_ in def.extra_constants.iter().flat_map(|d| &d.extra_constants) {
|
||||
let ident = &const_.ident;
|
||||
let deprecation_info = match crate::deprecation::get_deprecation(
|
||||
"e::quote! { #pezframe_support },
|
||||
&const_.attrs,
|
||||
) {
|
||||
Ok(deprecation) => deprecation,
|
||||
Err(e) => return e.into_compile_error(),
|
||||
};
|
||||
// Extracts #[allow] attributes, necessary so that we don't run into compiler warnings
|
||||
let maybe_allow_attrs = extract_or_return_allow_attrs(&const_.attrs);
|
||||
|
||||
extra_consts.push(ConstDef {
|
||||
ident: const_.ident.clone(),
|
||||
type_: const_.type_.clone(),
|
||||
doc: const_.doc.clone(),
|
||||
default_byte_impl: quote::quote!(
|
||||
#(#maybe_allow_attrs)*
|
||||
let value = <Pezpallet<#type_use_gen>>::#ident();
|
||||
#pezframe_support::__private::codec::Encode::encode(&value)
|
||||
),
|
||||
metadata_name: const_.metadata_name.clone(),
|
||||
deprecation_info,
|
||||
})
|
||||
}
|
||||
|
||||
let consts = config_consts.into_iter().chain(extra_consts.into_iter()).map(|const_| {
|
||||
let const_type = &const_.type_;
|
||||
let ident_str = format!("{}", const_.metadata_name.unwrap_or(const_.ident));
|
||||
|
||||
let no_docs = vec![];
|
||||
let doc = if cfg!(feature = "no-metadata-docs") { &no_docs } else { &const_.doc };
|
||||
|
||||
let default_byte_impl = &const_.default_byte_impl;
|
||||
let deprecation_info = &const_.deprecation_info;
|
||||
quote::quote!({
|
||||
#pezframe_support::__private::metadata_ir::PalletConstantMetadataIR {
|
||||
name: #ident_str,
|
||||
ty: #pezframe_support::__private::scale_info::meta_type::<#const_type>(),
|
||||
value: { #default_byte_impl },
|
||||
docs: #pezframe_support::__private::vec![ #( #doc ),* ],
|
||||
deprecation_info: #deprecation_info
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
quote::quote!(
|
||||
impl<#type_impl_gen> #pezpallet_ident<#type_use_gen> #completed_where_clause{
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn pezpallet_constants_metadata()
|
||||
-> #pezframe_support::__private::Vec<#pezframe_support::__private::metadata_ir::PalletConstantMetadataIR>
|
||||
{
|
||||
#pezframe_support::__private::vec![ #( #consts ),* ]
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use proc_macro2::Span;
|
||||
|
||||
use crate::pezpallet::Def;
|
||||
|
||||
pub fn expand_doc_only(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
let dispatchables = if let Some(call_def) = &def.call {
|
||||
let type_impl_generics = def.type_impl_generics(Span::call_site());
|
||||
call_def
|
||||
.methods
|
||||
.iter()
|
||||
.map(|method| {
|
||||
let name = &method.name;
|
||||
let args = &method
|
||||
.args
|
||||
.iter()
|
||||
.map(|(_, arg_name, arg_type)| quote::quote!( #arg_name: #arg_type, ))
|
||||
.collect::<proc_macro2::TokenStream>();
|
||||
let docs = &method.docs;
|
||||
|
||||
let real = format!(" [`Pezpallet::{}`].", name);
|
||||
quote::quote!(
|
||||
#( #[doc = #docs] )*
|
||||
///
|
||||
/// # Warning: Doc-Only
|
||||
///
|
||||
/// This function is an automatically generated, and is doc-only, uncallable
|
||||
/// stub. See the real version in
|
||||
#[ doc = #real ]
|
||||
pub fn #name<#type_impl_generics>(#args) { unreachable!(); }
|
||||
)
|
||||
})
|
||||
.collect::<proc_macro2::TokenStream>()
|
||||
} else {
|
||||
quote::quote!()
|
||||
};
|
||||
|
||||
let storage_types = def
|
||||
.storages
|
||||
.iter()
|
||||
.map(|storage| {
|
||||
let storage_name = &storage.ident;
|
||||
let storage_type_docs = &storage.docs;
|
||||
let real = format!("[`pezpallet::{}`].", storage_name);
|
||||
quote::quote!(
|
||||
#( #[doc = #storage_type_docs] )*
|
||||
///
|
||||
/// # Warning: Doc-Only
|
||||
///
|
||||
/// This type is automatically generated, and is doc-only. See the real version in
|
||||
#[ doc = #real ]
|
||||
pub struct #storage_name();
|
||||
)
|
||||
})
|
||||
.collect::<proc_macro2::TokenStream>();
|
||||
|
||||
quote::quote!(
|
||||
/// Auto-generated docs-only module listing all (public and private) defined storage types
|
||||
/// for this pezpallet.
|
||||
///
|
||||
/// # Warning: Doc-Only
|
||||
///
|
||||
/// Members of this module cannot be used directly and are only provided for documentation
|
||||
/// purposes.
|
||||
///
|
||||
/// To see the actual storage type, find a struct with the same name at the root of the
|
||||
/// pezpallet, in the list of [*Type Definitions*](../index.html#types).
|
||||
#[cfg(doc)]
|
||||
pub mod storage_types {
|
||||
use super::*;
|
||||
#storage_types
|
||||
}
|
||||
|
||||
/// Auto-generated docs-only module listing all defined dispatchables for this pezpallet.
|
||||
///
|
||||
/// # Warning: Doc-Only
|
||||
///
|
||||
/// Members of this module cannot be used directly and are only provided for documentation
|
||||
/// purposes. To see the real version of each dispatchable, look for them in [`Pezpallet`] or
|
||||
/// [`Call`].
|
||||
#[cfg(doc)]
|
||||
pub mod dispatchables {
|
||||
use super::*;
|
||||
#dispatchables
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::pezpallet::Def;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::ToTokens;
|
||||
use syn::{spanned::Spanned, Attribute, Lit, LitStr};
|
||||
|
||||
const DOC: &'static str = "doc";
|
||||
const PALLET_DOC: &'static str = "pezpallet_doc";
|
||||
|
||||
/// Get the documentation file path from the `pezpallet_doc` attribute.
|
||||
///
|
||||
/// Supported format:
|
||||
/// `#[pezpallet_doc(PATH)]`: The path of the file from which the documentation is loaded
|
||||
fn parse_pallet_doc_value(attr: &Attribute) -> syn::Result<DocMetaValue> {
|
||||
let lit: syn::LitStr = attr.parse_args().map_err(|_| {
|
||||
let msg = "The `pezpallet_doc` received an unsupported argument. Supported format: `pezpallet_doc(\"PATH\")`";
|
||||
syn::Error::new(attr.span(), msg)
|
||||
})?;
|
||||
|
||||
Ok(DocMetaValue::Path(lit))
|
||||
}
|
||||
|
||||
/// Get the value from the `doc` comment attribute:
|
||||
///
|
||||
/// Supported formats:
|
||||
/// - `#[doc = "A doc string"]`: Documentation as a string literal
|
||||
/// - `#[doc = include_str!(PATH)]`: Documentation obtained from a path
|
||||
fn parse_doc_value(attr: &Attribute) -> syn::Result<Option<DocMetaValue>> {
|
||||
if !attr.path().is_ident(DOC) {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let meta = attr.meta.require_name_value()?;
|
||||
|
||||
match &meta.value {
|
||||
syn::Expr::Lit(lit) => Ok(Some(DocMetaValue::Lit(lit.lit.clone()))),
|
||||
syn::Expr::Macro(mac) if mac.mac.path.is_ident("include_str") =>
|
||||
Ok(Some(DocMetaValue::Path(mac.mac.parse_body()?))),
|
||||
_ =>
|
||||
Err(syn::Error::new(attr.span(), "Expected `= \"docs\"` or `= include_str!(\"PATH\")`")),
|
||||
}
|
||||
}
|
||||
|
||||
/// Supported documentation tokens.
|
||||
#[derive(Debug)]
|
||||
enum DocMetaValue {
|
||||
/// Documentation with string literals.
|
||||
///
|
||||
/// `#[doc = "Lit"]`
|
||||
Lit(Lit),
|
||||
/// Documentation with `include_str!` macro.
|
||||
///
|
||||
/// The string literal represents the file `PATH`.
|
||||
///
|
||||
/// `#[doc = include_str!(PATH)]`
|
||||
Path(LitStr),
|
||||
}
|
||||
|
||||
impl ToTokens for DocMetaValue {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
match self {
|
||||
DocMetaValue::Lit(lit) => lit.to_tokens(tokens),
|
||||
DocMetaValue::Path(path_lit) => {
|
||||
let decl = quote::quote!(include_str!(#path_lit));
|
||||
tokens.extend(decl)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extract the documentation from the given pezpallet definition
|
||||
/// to include in the runtime metadata.
|
||||
///
|
||||
/// Implement a `pezpallet_documentation_metadata` function to fetch the
|
||||
/// documentation that is included in the metadata.
|
||||
///
|
||||
/// The documentation is placed on the pezpallet similar to:
|
||||
///
|
||||
/// ```ignore
|
||||
/// #[pezpallet]
|
||||
/// /// Documentation for pezpallet
|
||||
/// #[doc = "Documentation for pezpallet"]
|
||||
/// #[doc = include_str!("../README.md")]
|
||||
/// #[pezpallet_doc("../documentation1.md")]
|
||||
/// #[pezpallet_doc("../documentation2.md")]
|
||||
/// pub mod pezpallet {}
|
||||
/// ```
|
||||
///
|
||||
/// # pezpallet_doc
|
||||
///
|
||||
/// The `pezpallet_doc` attribute can only be provided with one argument,
|
||||
/// which is the file path that holds the documentation to be added to the metadata.
|
||||
///
|
||||
/// Unlike the `doc` attribute, the documentation provided to the `proc_macro` attribute is
|
||||
/// not added to the pezpallet.
|
||||
pub fn expand_documentation(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
let pezframe_support = &def.pezframe_support;
|
||||
let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site());
|
||||
let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site());
|
||||
let pezpallet_ident = &def.pezpallet_struct.pezpallet;
|
||||
let where_clauses = &def.config.where_clause;
|
||||
|
||||
// TODO: Use [drain_filter](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.drain_filter) when it is stable.
|
||||
|
||||
// The `pezpallet_doc` attributes are excluded from the generation of the pezpallet,
|
||||
// but they are included in the runtime metadata.
|
||||
let mut pezpallet_docs = Vec::with_capacity(def.item.attrs.len());
|
||||
let mut index = 0;
|
||||
while index < def.item.attrs.len() {
|
||||
let attr = &def.item.attrs[index];
|
||||
if attr.path().get_ident().map_or(false, |i| *i == PALLET_DOC) {
|
||||
pezpallet_docs.push(def.item.attrs.remove(index));
|
||||
// Do not increment the index, we have just removed the
|
||||
// element from the attributes.
|
||||
continue;
|
||||
}
|
||||
|
||||
index += 1;
|
||||
}
|
||||
|
||||
// Capture the `#[doc = include_str!("../README.md")]` and `#[doc = "Documentation"]`.
|
||||
let docs = match def
|
||||
.item
|
||||
.attrs
|
||||
.iter()
|
||||
.filter_map(|v| parse_doc_value(v).transpose())
|
||||
.collect::<syn::Result<Vec<_>>>()
|
||||
{
|
||||
Ok(r) => r,
|
||||
Err(err) => return err.into_compile_error(),
|
||||
};
|
||||
|
||||
// Capture the `#[pezpallet_doc("../README.md")]`.
|
||||
let pezpallet_docs = match pezpallet_docs
|
||||
.into_iter()
|
||||
.map(|attr| parse_pallet_doc_value(&attr))
|
||||
.collect::<syn::Result<Vec<_>>>()
|
||||
{
|
||||
Ok(docs) => docs,
|
||||
Err(err) => return err.into_compile_error(),
|
||||
};
|
||||
|
||||
let docs = docs.iter().chain(pezpallet_docs.iter());
|
||||
|
||||
quote::quote!(
|
||||
impl<#type_impl_gen> #pezpallet_ident<#type_use_gen> #where_clauses{
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn pezpallet_documentation_metadata()
|
||||
-> #pezframe_support::__private::Vec<&'static str>
|
||||
{
|
||||
#pezframe_support::__private::vec![ #( #docs ),* ]
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::{
|
||||
deprecation::extract_or_return_allow_attrs,
|
||||
pezpallet::{
|
||||
parse::error::{VariantDef, VariantField},
|
||||
Def,
|
||||
},
|
||||
COUNTER,
|
||||
};
|
||||
use pezframe_support_procedural_tools::get_doc_literals;
|
||||
use quote::ToTokens;
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
///
|
||||
/// * impl various trait on Error
|
||||
pub fn expand_error(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
let count = COUNTER.with(|counter| counter.borrow_mut().inc());
|
||||
let error_token_unique_id =
|
||||
syn::Ident::new(&format!("__tt_error_token_{}", count), def.item.span());
|
||||
|
||||
let pezframe_support = &def.pezframe_support;
|
||||
let pezframe_system = &def.pezframe_system;
|
||||
let config_where_clause = &def.config.where_clause;
|
||||
|
||||
let error = if let Some(error) = &def.error {
|
||||
error
|
||||
} else {
|
||||
return quote::quote! {
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! #error_token_unique_id {
|
||||
{
|
||||
$caller:tt
|
||||
your_tt_return = [{ $my_tt_return:path }]
|
||||
} => {
|
||||
$my_tt_return! {
|
||||
$caller
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub use #error_token_unique_id as tt_error_token;
|
||||
};
|
||||
};
|
||||
|
||||
let error_ident = &error.error;
|
||||
let type_impl_gen = &def.type_impl_generics(error.attr_span);
|
||||
let type_use_gen = &def.type_use_generics(error.attr_span);
|
||||
|
||||
let phantom_variant: syn::Variant = syn::parse_quote!(
|
||||
#[doc(hidden)]
|
||||
#[codec(skip)]
|
||||
__Ignore(
|
||||
core::marker::PhantomData<(#type_use_gen)>,
|
||||
#pezframe_support::Never,
|
||||
)
|
||||
);
|
||||
|
||||
let as_str_matches =
|
||||
error
|
||||
.variants
|
||||
.iter()
|
||||
.map(|VariantDef { ident: variant, field: field_ty, cfg_attrs, maybe_allow_attrs }| {
|
||||
let variant_str = variant.to_string();
|
||||
let cfg_attrs = cfg_attrs.iter().map(|attr| attr.to_token_stream());
|
||||
match field_ty {
|
||||
Some(VariantField { is_named: true }) => {
|
||||
quote::quote_spanned!(error.attr_span => #( #cfg_attrs )* #(#maybe_allow_attrs)* Self::#variant { .. } => #variant_str,)
|
||||
},
|
||||
Some(VariantField { is_named: false }) => {
|
||||
quote::quote_spanned!(error.attr_span => #( #cfg_attrs )* #(#maybe_allow_attrs)* Self::#variant(..) => #variant_str,)
|
||||
},
|
||||
None => {
|
||||
quote::quote_spanned!(error.attr_span => #( #cfg_attrs )* #(#maybe_allow_attrs)* Self::#variant => #variant_str,)
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
let error_item = {
|
||||
let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[error.index];
|
||||
if let syn::Item::Enum(item) = item {
|
||||
item
|
||||
} else {
|
||||
unreachable!("Checked by error parser")
|
||||
}
|
||||
};
|
||||
error_item.variants.insert(0, phantom_variant);
|
||||
|
||||
let capture_docs = if cfg!(feature = "no-metadata-docs") { "never" } else { "always" };
|
||||
|
||||
let deprecation = match crate::deprecation::get_deprecation_enum(
|
||||
"e::quote! {#pezframe_support},
|
||||
error_item.variants.iter().enumerate().map(|(index, item)| {
|
||||
let index = crate::deprecation::variant_index_for_deprecation(index as u8, item);
|
||||
|
||||
(index, item.attrs.as_ref())
|
||||
}),
|
||||
) {
|
||||
Ok(deprecation) => deprecation,
|
||||
Err(e) => return e.into_compile_error(),
|
||||
};
|
||||
|
||||
// derive TypeInfo for error metadata
|
||||
error_item.attrs.push(syn::parse_quote! {
|
||||
#[derive(
|
||||
#pezframe_support::__private::codec::Encode,
|
||||
#pezframe_support::__private::codec::Decode,
|
||||
#pezframe_support::__private::codec::DecodeWithMemTracking,
|
||||
#pezframe_support::__private::scale_info::TypeInfo,
|
||||
#pezframe_support::PalletError,
|
||||
)]
|
||||
});
|
||||
error_item.attrs.push(syn::parse_quote!(
|
||||
#[scale_info(skip_type_params(#type_use_gen), capture_docs = #capture_docs)]
|
||||
));
|
||||
|
||||
if get_doc_literals(&error_item.attrs).is_empty() {
|
||||
error_item.attrs.push(syn::parse_quote!(
|
||||
#[doc = "The `Error` enum of this pezpallet."]
|
||||
));
|
||||
}
|
||||
|
||||
// Extracts #[allow] attributes, necessary so that we don't run into compiler warnings
|
||||
let maybe_allow_attrs: Vec<syn::Attribute> =
|
||||
extract_or_return_allow_attrs(&error_item.attrs).collect();
|
||||
|
||||
quote::quote_spanned!(error.attr_span =>
|
||||
#(#maybe_allow_attrs)*
|
||||
impl<#type_impl_gen> core::fmt::Debug for #error_ident<#type_use_gen>
|
||||
#config_where_clause
|
||||
{
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>)
|
||||
-> core::fmt::Result
|
||||
{
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
#(#maybe_allow_attrs)*
|
||||
impl<#type_impl_gen> #error_ident<#type_use_gen> #config_where_clause {
|
||||
#[doc(hidden)]
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match &self {
|
||||
#(#maybe_allow_attrs)* Self::__Ignore(_, _) => unreachable!("`__Ignore` can never be constructed"),
|
||||
#( #as_str_matches )*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#(#maybe_allow_attrs)*
|
||||
impl<#type_impl_gen> From<#error_ident<#type_use_gen>> for &'static str
|
||||
#config_where_clause
|
||||
{
|
||||
fn from(err: #error_ident<#type_use_gen>) -> &'static str {
|
||||
err.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
#(#maybe_allow_attrs)*
|
||||
impl<#type_impl_gen> From<#error_ident<#type_use_gen>>
|
||||
for #pezframe_support::pezsp_runtime::DispatchError
|
||||
#config_where_clause
|
||||
{
|
||||
fn from(err: #error_ident<#type_use_gen>) -> Self {
|
||||
use #pezframe_support::__private::codec::Encode;
|
||||
let index = <
|
||||
<T as #pezframe_system::Config>::PalletInfo
|
||||
as #pezframe_support::traits::PalletInfo
|
||||
>::index::<Pezpallet<#type_use_gen>>()
|
||||
.expect("Every active module has an index in the runtime; qed") as u8;
|
||||
let mut encoded = err.encode();
|
||||
encoded.resize(#pezframe_support::MAX_MODULE_ERROR_ENCODED_SIZE, 0);
|
||||
|
||||
#pezframe_support::pezsp_runtime::DispatchError::Module(#pezframe_support::pezsp_runtime::ModuleError {
|
||||
index,
|
||||
error: TryInto::try_into(encoded).expect("encoded error is resized to be equal to the maximum encoded error size; qed"),
|
||||
message: Some(err.as_str()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! #error_token_unique_id {
|
||||
{
|
||||
$caller:tt
|
||||
your_tt_return = [{ $my_tt_return:path }]
|
||||
} => {
|
||||
$my_tt_return! {
|
||||
$caller
|
||||
error = [{ #error_ident }]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub use #error_token_unique_id as tt_error_token;
|
||||
|
||||
#(#maybe_allow_attrs)*
|
||||
impl<#type_impl_gen> #error_ident<#type_use_gen> #config_where_clause {
|
||||
#[allow(dead_code)]
|
||||
#[doc(hidden)]
|
||||
pub fn error_metadata() -> #pezframe_support::__private::metadata_ir::PalletErrorMetadataIR {
|
||||
#(#maybe_allow_attrs)*
|
||||
#pezframe_support::__private::metadata_ir::PalletErrorMetadataIR {
|
||||
ty: #pezframe_support::__private::scale_info::meta_type::<#error_ident<#type_use_gen>>(),
|
||||
deprecation_info: #deprecation,
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::{
|
||||
deprecation::extract_or_return_allow_attrs,
|
||||
pezpallet::{parse::event::PalletEventDepositAttr, Def},
|
||||
COUNTER,
|
||||
};
|
||||
use pezframe_support_procedural_tools::get_doc_literals;
|
||||
use syn::{spanned::Spanned, Ident};
|
||||
|
||||
///
|
||||
/// * Add __Ignore variant on Event
|
||||
/// * Impl various trait on Event including metadata
|
||||
/// * if deposit_event is defined, implement deposit_event on module.
|
||||
pub fn expand_event(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
let count = COUNTER.with(|counter| counter.borrow_mut().inc());
|
||||
|
||||
let (event, macro_ident) = if let Some(event) = &def.event {
|
||||
let ident = Ident::new(&format!("__is_event_part_defined_{}", count), event.attr_span);
|
||||
(event, ident)
|
||||
} else {
|
||||
let macro_ident =
|
||||
Ident::new(&format!("__is_event_part_defined_{}", count), def.item.span());
|
||||
|
||||
return quote::quote! {
|
||||
#[doc(hidden)]
|
||||
pub mod __bizinikiwi_event_check {
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! #macro_ident {
|
||||
($pezpallet_name:ident) => {
|
||||
compile_error!(concat!(
|
||||
"`",
|
||||
stringify!($pezpallet_name),
|
||||
"` does not have #[pezpallet::event] defined, perhaps you should \
|
||||
remove `Event` from construct_runtime?",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use #macro_ident as is_event_part_defined;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
let event_where_clause = &event.where_clause;
|
||||
|
||||
// NOTE: actually event where clause must be a subset of config where clause because of
|
||||
// `type RuntimeEvent: From<Event<Self>>`. But we merge either way for potential better error
|
||||
// message
|
||||
let completed_where_clause =
|
||||
super::merge_where_clauses(&[&event.where_clause, &def.config.where_clause]);
|
||||
|
||||
let event_ident = &event.event;
|
||||
let pezframe_system = &def.pezframe_system;
|
||||
let pezframe_support = &def.pezframe_support;
|
||||
let event_use_gen = &event.gen_kind.type_use_gen(event.attr_span);
|
||||
let event_impl_gen = &event.gen_kind.type_impl_gen(event.attr_span);
|
||||
let event_item = {
|
||||
let item = &mut def.item.content.as_mut().expect("Checked by def parser").1[event.index];
|
||||
if let syn::Item::Enum(item) = item {
|
||||
item
|
||||
} else {
|
||||
unreachable!("Checked by event parser")
|
||||
}
|
||||
};
|
||||
|
||||
// Phantom data is added for generic event.
|
||||
if event.gen_kind.is_generic() {
|
||||
let variant = syn::parse_quote!(
|
||||
#[doc(hidden)]
|
||||
#[codec(skip)]
|
||||
__Ignore(
|
||||
::core::marker::PhantomData<(#event_use_gen)>,
|
||||
#pezframe_support::Never,
|
||||
)
|
||||
);
|
||||
|
||||
// Push ignore variant at the end.
|
||||
event_item.variants.push(variant);
|
||||
}
|
||||
|
||||
let deprecation = match crate::deprecation::get_deprecation_enum(
|
||||
"e::quote! {#pezframe_support},
|
||||
event_item.variants.iter().enumerate().map(|(index, item)| {
|
||||
let index = crate::deprecation::variant_index_for_deprecation(index as u8, item);
|
||||
|
||||
(index, item.attrs.as_ref())
|
||||
}),
|
||||
) {
|
||||
Ok(deprecation) => deprecation,
|
||||
Err(e) => return e.into_compile_error(),
|
||||
};
|
||||
|
||||
if get_doc_literals(&event_item.attrs).is_empty() {
|
||||
event_item
|
||||
.attrs
|
||||
.push(syn::parse_quote!(#[doc = "The `Event` enum of this pezpallet"]));
|
||||
}
|
||||
|
||||
// derive some traits because system event require Clone, FullCodec, Eq, PartialEq and Debug
|
||||
event_item.attrs.push(syn::parse_quote!(
|
||||
#[derive(
|
||||
#pezframe_support::CloneNoBound,
|
||||
#pezframe_support::EqNoBound,
|
||||
#pezframe_support::PartialEqNoBound,
|
||||
#pezframe_support::DebugNoBound,
|
||||
#pezframe_support::__private::codec::Encode,
|
||||
#pezframe_support::__private::codec::Decode,
|
||||
#pezframe_support::__private::codec::DecodeWithMemTracking,
|
||||
#pezframe_support::__private::scale_info::TypeInfo,
|
||||
)]
|
||||
));
|
||||
|
||||
let capture_docs = if cfg!(feature = "no-metadata-docs") { "never" } else { "always" };
|
||||
|
||||
// skip requirement for type params to implement `TypeInfo`, and set docs capture
|
||||
event_item.attrs.push(syn::parse_quote!(
|
||||
#[scale_info(skip_type_params(#event_use_gen), capture_docs = #capture_docs)]
|
||||
));
|
||||
|
||||
// Extracts #[allow] attributes, necessary so that we don't run into compiler warnings
|
||||
let maybe_allow_attrs: Vec<syn::Attribute> =
|
||||
extract_or_return_allow_attrs(&event_item.attrs).collect();
|
||||
|
||||
let deposit_event = if let Some(deposit_event) = &event.deposit_event {
|
||||
let event_use_gen = &event.gen_kind.type_use_gen(event.attr_span);
|
||||
let type_impl_gen = &def.type_impl_generics(event.attr_span);
|
||||
let type_use_gen = &def.type_use_generics(event.attr_span);
|
||||
let pezpallet_ident = &def.pezpallet_struct.pezpallet;
|
||||
|
||||
let PalletEventDepositAttr { fn_vis, fn_span, .. } = deposit_event;
|
||||
|
||||
quote::quote_spanned!(*fn_span =>
|
||||
impl<#type_impl_gen> #pezpallet_ident<#type_use_gen> #completed_where_clause {
|
||||
#(#maybe_allow_attrs)*
|
||||
#fn_vis fn deposit_event(event: Event<#event_use_gen>) {
|
||||
let event = <
|
||||
<T as #pezframe_system::Config>::RuntimeEvent as
|
||||
From<Event<#event_use_gen>>
|
||||
>::from(event);
|
||||
|
||||
let event = <
|
||||
<T as #pezframe_system::Config>::RuntimeEvent as
|
||||
Into<<T as #pezframe_system::Config>::RuntimeEvent>
|
||||
>::into(event);
|
||||
|
||||
<#pezframe_system::Pezpallet<T>>::deposit_event(event)
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
|
||||
quote::quote_spanned!(event.attr_span =>
|
||||
#[doc(hidden)]
|
||||
pub mod __bizinikiwi_event_check {
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! #macro_ident {
|
||||
($pezpallet_name:ident) => {};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use #macro_ident as is_event_part_defined;
|
||||
}
|
||||
|
||||
#deposit_event
|
||||
|
||||
#(#maybe_allow_attrs)*
|
||||
impl<#event_impl_gen> From<#event_ident<#event_use_gen>> for () #event_where_clause {
|
||||
fn from(_: #event_ident<#event_use_gen>) {}
|
||||
}
|
||||
|
||||
#(#maybe_allow_attrs)*
|
||||
impl<#event_impl_gen> #event_ident<#event_use_gen> #event_where_clause {
|
||||
#[allow(dead_code)]
|
||||
#[doc(hidden)]
|
||||
pub fn event_metadata<W: #pezframe_support::__private::scale_info::TypeInfo + 'static>() -> #pezframe_support::__private::metadata_ir::PalletEventMetadataIR {
|
||||
#pezframe_support::__private::metadata_ir::PalletEventMetadataIR {
|
||||
ty: #pezframe_support::__private::scale_info::meta_type::<W>(),
|
||||
deprecation_info: #deprecation,
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::pezpallet::Def;
|
||||
|
||||
///
|
||||
/// * implement the trait `pezsp_runtime::BuildStorage`
|
||||
pub fn expand_genesis_build(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
let genesis_config = if let Some(genesis_config) = &def.genesis_config {
|
||||
genesis_config
|
||||
} else {
|
||||
return Default::default();
|
||||
};
|
||||
let genesis_build = def.genesis_build.as_ref().expect("Checked by def parser");
|
||||
|
||||
let pezframe_support = &def.pezframe_support;
|
||||
let type_impl_gen = &genesis_config.gen_kind.type_impl_gen(genesis_build.attr_span);
|
||||
let gen_cfg_ident = &genesis_config.genesis_config;
|
||||
let gen_cfg_use_gen = &genesis_config.gen_kind.type_use_gen(genesis_build.attr_span);
|
||||
|
||||
let where_clause = &genesis_build.where_clause;
|
||||
|
||||
quote::quote_spanned!(genesis_build.attr_span =>
|
||||
#pezframe_support::std_enabled! {
|
||||
impl<#type_impl_gen> #pezframe_support::pezsp_runtime::BuildStorage for #gen_cfg_ident<#gen_cfg_use_gen> #where_clause
|
||||
{
|
||||
fn assimilate_storage(&self, storage: &mut #pezframe_support::pezsp_runtime::Storage) -> std::result::Result<(), std::string::String> {
|
||||
#pezframe_support::__private::BasicExternalities::execute_with_storage(storage, || {
|
||||
self.build();
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::{pezpallet::Def, COUNTER};
|
||||
use pezframe_support_procedural_tools::get_doc_literals;
|
||||
use quote::ToTokens;
|
||||
use syn::{spanned::Spanned, Ident};
|
||||
|
||||
///
|
||||
/// * add various derive trait on GenesisConfig struct.
|
||||
pub fn expand_genesis_config(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
let count = COUNTER.with(|counter| counter.borrow_mut().inc());
|
||||
|
||||
let (genesis_config, def_macro_ident, std_macro_ident) =
|
||||
if let Some(genesis_config) = &def.genesis_config {
|
||||
let def_macro_ident = Ident::new(
|
||||
&format!("__is_genesis_config_defined_{}", count),
|
||||
genesis_config.genesis_config.span(),
|
||||
);
|
||||
|
||||
let std_macro_ident = Ident::new(
|
||||
&format!("__is_std_macro_defined_for_genesis_{}", count),
|
||||
genesis_config.genesis_config.span(),
|
||||
);
|
||||
|
||||
(genesis_config, def_macro_ident, std_macro_ident)
|
||||
} else {
|
||||
let def_macro_ident =
|
||||
Ident::new(&format!("__is_genesis_config_defined_{}", count), def.item.span());
|
||||
|
||||
let std_macro_ident =
|
||||
Ident::new(&format!("__is_std_enabled_for_genesis_{}", count), def.item.span());
|
||||
|
||||
return quote::quote! {
|
||||
#[doc(hidden)]
|
||||
pub mod __bizinikiwi_genesis_config_check {
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! #def_macro_ident {
|
||||
($pezpallet_name:ident) => {
|
||||
compile_error!(concat!(
|
||||
"`",
|
||||
stringify!($pezpallet_name),
|
||||
"` does not have #[pezpallet::genesis_config] defined, perhaps you should \
|
||||
remove `Config` from construct_runtime?",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! #std_macro_ident {
|
||||
($pezpallet_name:ident, $pezpallet_path:expr) => {};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use #def_macro_ident as is_genesis_config_defined;
|
||||
#[doc(hidden)]
|
||||
pub use #std_macro_ident as is_std_enabled_for_genesis;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
let pezframe_support = &def.pezframe_support;
|
||||
|
||||
let genesis_config_item =
|
||||
&mut def.item.content.as_mut().expect("Checked by def parser").1[genesis_config.index];
|
||||
|
||||
let serde_crate = format!("{}::__private::serde", pezframe_support.to_token_stream());
|
||||
|
||||
match genesis_config_item {
|
||||
syn::Item::Enum(syn::ItemEnum { attrs, .. }) |
|
||||
syn::Item::Struct(syn::ItemStruct { attrs, .. }) |
|
||||
syn::Item::Type(syn::ItemType { attrs, .. }) => {
|
||||
if get_doc_literals(attrs).is_empty() {
|
||||
attrs.push(syn::parse_quote!(
|
||||
#[doc = r"
|
||||
Can be used to configure the
|
||||
[genesis state](https://docs.pezkuwichain.io/build/genesis-configuration/)
|
||||
of this pezpallet.
|
||||
"]
|
||||
));
|
||||
}
|
||||
attrs.push(syn::parse_quote!(
|
||||
#[derive(#pezframe_support::Serialize, #pezframe_support::Deserialize)]
|
||||
));
|
||||
attrs.push(syn::parse_quote!( #[serde(rename_all = "camelCase")] ));
|
||||
attrs.push(syn::parse_quote!( #[serde(deny_unknown_fields)] ));
|
||||
attrs.push(syn::parse_quote!( #[serde(bound(serialize = ""))] ));
|
||||
attrs.push(syn::parse_quote!( #[serde(bound(deserialize = ""))] ));
|
||||
attrs.push(syn::parse_quote!( #[serde(crate = #serde_crate)] ));
|
||||
},
|
||||
_ => unreachable!("Checked by genesis_config parser"),
|
||||
}
|
||||
|
||||
quote::quote! {
|
||||
#[doc(hidden)]
|
||||
pub mod __bizinikiwi_genesis_config_check {
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! #def_macro_ident {
|
||||
($pezpallet_name:ident) => {};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "std"))]
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! #std_macro_ident {
|
||||
($pezpallet_name:ident, $pezpallet_path:expr) => {
|
||||
compile_error!(concat!(
|
||||
"`",
|
||||
stringify!($pezpallet_name),
|
||||
"` does not have the std feature enabled, this will cause the `",
|
||||
$pezpallet_path,
|
||||
"::GenesisConfig` type to not implement serde traits."
|
||||
));
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! #std_macro_ident {
|
||||
($pezpallet_name:ident, $pezpallet_path:expr) => {};
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use #def_macro_ident as is_genesis_config_defined;
|
||||
#[doc(hidden)]
|
||||
pub use #std_macro_ident as is_std_enabled_for_genesis;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,339 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::pezpallet::Def;
|
||||
|
||||
/// * implement the individual traits using the Hooks trait
|
||||
pub fn expand_hooks(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
let (where_clause, span, has_runtime_upgrade) = match def.hooks.as_ref() {
|
||||
Some(hooks) => {
|
||||
let where_clause = hooks.where_clause.clone();
|
||||
let span = hooks.attr_span;
|
||||
let has_runtime_upgrade = hooks.has_runtime_upgrade;
|
||||
(where_clause, span, has_runtime_upgrade)
|
||||
},
|
||||
None => (def.config.where_clause.clone(), def.pezpallet_struct.attr_span, false),
|
||||
};
|
||||
|
||||
let pezframe_support = &def.pezframe_support;
|
||||
let type_impl_gen = &def.type_impl_generics(span);
|
||||
let type_use_gen = &def.type_use_generics(span);
|
||||
let pezpallet_ident = &def.pezpallet_struct.pezpallet;
|
||||
let pezframe_system = &def.pezframe_system;
|
||||
let pezpallet_name = quote::quote! {
|
||||
<
|
||||
<T as #pezframe_system::Config>::PalletInfo
|
||||
as
|
||||
#pezframe_support::traits::PalletInfo
|
||||
>::name::<Self>().unwrap_or("<unknown pezpallet name>")
|
||||
};
|
||||
|
||||
let initialize_on_chain_storage_version = if let Some(in_code_version) =
|
||||
&def.pezpallet_struct.storage_version
|
||||
{
|
||||
quote::quote! {
|
||||
#pezframe_support::__private::log::info!(
|
||||
target: #pezframe_support::LOG_TARGET,
|
||||
"🐥 New pezpallet {:?} detected in the runtime. Initializing the on-chain storage version to match the storage version defined in the pezpallet: {:?}",
|
||||
#pezpallet_name,
|
||||
#in_code_version
|
||||
);
|
||||
#in_code_version.put::<Self>();
|
||||
}
|
||||
} else {
|
||||
quote::quote! {
|
||||
let default_version = #pezframe_support::traits::StorageVersion::new(0);
|
||||
#pezframe_support::__private::log::info!(
|
||||
target: #pezframe_support::LOG_TARGET,
|
||||
"🐥 New pezpallet {:?} detected in the runtime. The pezpallet has no defined storage version, so the on-chain version is being initialized to {:?}.",
|
||||
#pezpallet_name,
|
||||
default_version
|
||||
);
|
||||
default_version.put::<Self>();
|
||||
}
|
||||
};
|
||||
|
||||
let log_runtime_upgrade = if has_runtime_upgrade {
|
||||
// a migration is defined here.
|
||||
quote::quote! {
|
||||
#pezframe_support::__private::log::info!(
|
||||
target: #pezframe_support::LOG_TARGET,
|
||||
"⚠️ {} declares internal migrations (which *might* execute). \
|
||||
On-chain `{:?}` vs in-code storage version `{:?}`",
|
||||
#pezpallet_name,
|
||||
<Self as #pezframe_support::traits::GetStorageVersion>::on_chain_storage_version(),
|
||||
<Self as #pezframe_support::traits::GetStorageVersion>::in_code_storage_version(),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// default.
|
||||
quote::quote! {
|
||||
#pezframe_support::__private::log::debug!(
|
||||
target: #pezframe_support::LOG_TARGET,
|
||||
"✅ no migration for {}",
|
||||
#pezpallet_name,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let hooks_impl = if def.hooks.is_none() {
|
||||
let pezframe_system = &def.pezframe_system;
|
||||
quote::quote! {
|
||||
impl<#type_impl_gen>
|
||||
#pezframe_support::traits::Hooks<#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>>
|
||||
for #pezpallet_ident<#type_use_gen> #where_clause {}
|
||||
}
|
||||
} else {
|
||||
proc_macro2::TokenStream::new()
|
||||
};
|
||||
|
||||
// If a storage version is set, we should ensure that the storage version on chain matches the
|
||||
// in-code storage version. This assumes that `Executive` is running custom migrations before
|
||||
// the pallets are called.
|
||||
let post_storage_version_check = if def.pezpallet_struct.storage_version.is_some() {
|
||||
quote::quote! {
|
||||
let on_chain_version = <Self as #pezframe_support::traits::GetStorageVersion>::on_chain_storage_version();
|
||||
let in_code_version = <Self as #pezframe_support::traits::GetStorageVersion>::in_code_storage_version();
|
||||
|
||||
if on_chain_version != in_code_version {
|
||||
#pezframe_support::__private::log::error!(
|
||||
target: #pezframe_support::LOG_TARGET,
|
||||
"{}: On chain storage version {:?} doesn't match in-code storage version {:?}.",
|
||||
#pezpallet_name,
|
||||
on_chain_version,
|
||||
in_code_version,
|
||||
);
|
||||
|
||||
return Err("On chain and in-code storage version do not match. Missing runtime upgrade?".into());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote::quote! {
|
||||
let on_chain_version = <Self as #pezframe_support::traits::GetStorageVersion>::on_chain_storage_version();
|
||||
|
||||
if on_chain_version != #pezframe_support::traits::StorageVersion::new(0) {
|
||||
#pezframe_support::__private::log::error!(
|
||||
target: #pezframe_support::LOG_TARGET,
|
||||
"{}: On chain storage version {:?} is set to non zero, \
|
||||
while the pezpallet is missing the `#[pezpallet::storage_version(VERSION)]` attribute.",
|
||||
#pezpallet_name,
|
||||
on_chain_version,
|
||||
);
|
||||
|
||||
return Err("On chain storage version set, while the pezpallet doesn't \
|
||||
have the `#[pezpallet::storage_version(VERSION)]` attribute.".into());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
quote::quote_spanned!(span =>
|
||||
#hooks_impl
|
||||
|
||||
impl<#type_impl_gen>
|
||||
#pezframe_support::traits::OnFinalize<#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>>
|
||||
for #pezpallet_ident<#type_use_gen> #where_clause
|
||||
{
|
||||
fn on_finalize(n: #pezframe_system::pezpallet_prelude::BlockNumberFor::<T>) {
|
||||
#pezframe_support::__private::pezsp_tracing::enter_span!(
|
||||
#pezframe_support::__private::pezsp_tracing::trace_span!("on_finalize")
|
||||
);
|
||||
<
|
||||
Self as #pezframe_support::traits::Hooks<
|
||||
#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>
|
||||
>
|
||||
>::on_finalize(n)
|
||||
}
|
||||
}
|
||||
|
||||
impl<#type_impl_gen>
|
||||
#pezframe_support::traits::OnIdle<#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>>
|
||||
for #pezpallet_ident<#type_use_gen> #where_clause
|
||||
{
|
||||
fn on_idle(
|
||||
n: #pezframe_system::pezpallet_prelude::BlockNumberFor::<T>,
|
||||
remaining_weight: #pezframe_support::weights::Weight
|
||||
) -> #pezframe_support::weights::Weight {
|
||||
<
|
||||
Self as #pezframe_support::traits::Hooks<
|
||||
#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>
|
||||
>
|
||||
>::on_idle(n, remaining_weight)
|
||||
}
|
||||
}
|
||||
|
||||
impl<#type_impl_gen>
|
||||
#pezframe_support::traits::OnPoll<#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>>
|
||||
for #pezpallet_ident<#type_use_gen> #where_clause
|
||||
{
|
||||
fn on_poll(
|
||||
n: #pezframe_system::pezpallet_prelude::BlockNumberFor::<T>,
|
||||
weight: &mut #pezframe_support::weights::WeightMeter
|
||||
) {
|
||||
<
|
||||
Self as #pezframe_support::traits::Hooks<
|
||||
#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>
|
||||
>
|
||||
>::on_poll(n, weight);
|
||||
}
|
||||
}
|
||||
|
||||
impl<#type_impl_gen>
|
||||
#pezframe_support::traits::OnInitialize<#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>>
|
||||
for #pezpallet_ident<#type_use_gen> #where_clause
|
||||
{
|
||||
fn on_initialize(
|
||||
n: #pezframe_system::pezpallet_prelude::BlockNumberFor::<T>
|
||||
) -> #pezframe_support::weights::Weight {
|
||||
#pezframe_support::__private::pezsp_tracing::enter_span!(
|
||||
#pezframe_support::__private::pezsp_tracing::trace_span!("on_initialize")
|
||||
);
|
||||
<
|
||||
Self as #pezframe_support::traits::Hooks<
|
||||
#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>
|
||||
>
|
||||
>::on_initialize(n)
|
||||
}
|
||||
}
|
||||
|
||||
impl<#type_impl_gen>
|
||||
#pezframe_support::traits::BeforeAllRuntimeMigrations
|
||||
for #pezpallet_ident<#type_use_gen> #where_clause
|
||||
{
|
||||
fn before_all_runtime_migrations() -> #pezframe_support::weights::Weight {
|
||||
use #pezframe_support::traits::{Get, PalletInfoAccess};
|
||||
use #pezframe_support::__private::hashing::twox_128;
|
||||
use #pezframe_support::storage::unhashed::contains_prefixed_key;
|
||||
#pezframe_support::__private::pezsp_tracing::enter_span!(
|
||||
#pezframe_support::__private::pezsp_tracing::trace_span!("before_all")
|
||||
);
|
||||
|
||||
// Check if the pezpallet has any keys set, including the storage version. If there are
|
||||
// no keys set, the pezpallet was just added to the runtime and needs to have its
|
||||
// version initialized.
|
||||
let pezpallet_hashed_prefix = <Self as PalletInfoAccess>::name_hash();
|
||||
let exists = contains_prefixed_key(&pezpallet_hashed_prefix);
|
||||
if !exists {
|
||||
#initialize_on_chain_storage_version
|
||||
<T as #pezframe_system::Config>::DbWeight::get().reads_writes(1, 1)
|
||||
} else {
|
||||
<T as #pezframe_system::Config>::DbWeight::get().reads(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<#type_impl_gen>
|
||||
#pezframe_support::traits::OnRuntimeUpgrade
|
||||
for #pezpallet_ident<#type_use_gen> #where_clause
|
||||
{
|
||||
fn on_runtime_upgrade() -> #pezframe_support::weights::Weight {
|
||||
#pezframe_support::__private::pezsp_tracing::enter_span!(
|
||||
#pezframe_support::__private::pezsp_tracing::trace_span!("on_runtime_update")
|
||||
);
|
||||
|
||||
// log info about the upgrade.
|
||||
#log_runtime_upgrade
|
||||
|
||||
<
|
||||
Self as #pezframe_support::traits::Hooks<
|
||||
#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>
|
||||
>
|
||||
>::on_runtime_upgrade()
|
||||
}
|
||||
|
||||
#pezframe_support::try_runtime_enabled! {
|
||||
fn pre_upgrade() -> Result<#pezframe_support::__private::Vec<u8>, #pezframe_support::pezsp_runtime::TryRuntimeError> {
|
||||
<
|
||||
Self
|
||||
as
|
||||
#pezframe_support::traits::Hooks<#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>>
|
||||
>::pre_upgrade()
|
||||
}
|
||||
|
||||
fn post_upgrade(state: #pezframe_support::__private::Vec<u8>) -> Result<(), #pezframe_support::pezsp_runtime::TryRuntimeError> {
|
||||
#post_storage_version_check
|
||||
|
||||
<
|
||||
Self
|
||||
as
|
||||
#pezframe_support::traits::Hooks<#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>>
|
||||
>::post_upgrade(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<#type_impl_gen>
|
||||
#pezframe_support::traits::OffchainWorker<#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>>
|
||||
for #pezpallet_ident<#type_use_gen> #where_clause
|
||||
{
|
||||
fn offchain_worker(n: #pezframe_system::pezpallet_prelude::BlockNumberFor::<T>) {
|
||||
<
|
||||
Self as #pezframe_support::traits::Hooks<
|
||||
#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>
|
||||
>
|
||||
>::offchain_worker(n)
|
||||
}
|
||||
}
|
||||
|
||||
// Integrity tests are only required for when `std` is enabled.
|
||||
#pezframe_support::std_enabled! {
|
||||
impl<#type_impl_gen>
|
||||
#pezframe_support::traits::IntegrityTest
|
||||
for #pezpallet_ident<#type_use_gen> #where_clause
|
||||
{
|
||||
fn integrity_test() {
|
||||
#pezframe_support::__private::pezsp_io::TestExternalities::default().execute_with(|| {
|
||||
<
|
||||
Self as #pezframe_support::traits::Hooks<
|
||||
#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>
|
||||
>
|
||||
>::integrity_test()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pezframe_support::try_runtime_enabled! {
|
||||
impl<#type_impl_gen>
|
||||
#pezframe_support::traits::TryState<#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>>
|
||||
for #pezpallet_ident<#type_use_gen> #where_clause
|
||||
{
|
||||
fn try_state(
|
||||
n: #pezframe_system::pezpallet_prelude::BlockNumberFor::<T>,
|
||||
_s: #pezframe_support::traits::TryStateSelect
|
||||
) -> Result<(), #pezframe_support::pezsp_runtime::TryRuntimeError> {
|
||||
#pezframe_support::__private::log::info!(
|
||||
target: #pezframe_support::LOG_TARGET,
|
||||
"🩺 Running {:?} try-state checks",
|
||||
#pezpallet_name,
|
||||
);
|
||||
<
|
||||
Self as #pezframe_support::traits::Hooks<
|
||||
#pezframe_system::pezpallet_prelude::BlockNumberFor::<T>
|
||||
>
|
||||
>::try_state(n).inspect_err(|err| {
|
||||
#pezframe_support::__private::log::error!(
|
||||
target: #pezframe_support::LOG_TARGET,
|
||||
"❌ {:?} try_state checks failed: {:?}",
|
||||
#pezpallet_name,
|
||||
err
|
||||
);
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::{pezpallet::Def, COUNTER};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{spanned::Spanned, Ident};
|
||||
|
||||
pub fn expand_inherents(def: &mut Def) -> TokenStream {
|
||||
let count = COUNTER.with(|counter| counter.borrow_mut().inc());
|
||||
let macro_ident = Ident::new(&format!("__is_inherent_part_defined_{}", count), def.item.span());
|
||||
|
||||
let maybe_compile_error = if def.inherent.is_none() {
|
||||
quote! {
|
||||
compile_error!(concat!(
|
||||
"`",
|
||||
stringify!($pezpallet_name),
|
||||
"` does not have #[pezpallet::inherent] defined, perhaps you should \
|
||||
remove `Inherent` from construct_runtime?",
|
||||
));
|
||||
}
|
||||
} else {
|
||||
TokenStream::new()
|
||||
};
|
||||
|
||||
quote! {
|
||||
#[doc(hidden)]
|
||||
pub mod __bizinikiwi_inherent_check {
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! #macro_ident {
|
||||
($pezpallet_name:ident) => {
|
||||
#maybe_compile_error
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use #macro_ident as is_inherent_part_defined;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::{pezpallet::Def, NUMBER_OF_INSTANCE};
|
||||
use proc_macro2::Span;
|
||||
|
||||
///
|
||||
/// * Provide inherent instance to be used by construct_runtime
|
||||
/// * Provide Instance1 ..= Instance16 for instantiable pezpallet
|
||||
pub fn expand_instances(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
let pezframe_support = &def.pezframe_support;
|
||||
let inherent_ident = syn::Ident::new(crate::INHERENT_INSTANCE_NAME, Span::call_site());
|
||||
let instances = if def.config.has_instance {
|
||||
(1..=NUMBER_OF_INSTANCE)
|
||||
.map(|i| syn::Ident::new(&format!("Instance{}", i), Span::call_site()))
|
||||
.collect()
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
quote::quote!(
|
||||
/// Hidden instance generated to be internally used when module is used without
|
||||
/// instance.
|
||||
#[doc(hidden)]
|
||||
pub type #inherent_ident = ();
|
||||
|
||||
#( pub use #pezframe_support::instances::#instances; )*
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
mod call;
|
||||
mod composite;
|
||||
mod config;
|
||||
mod constants;
|
||||
mod doc_only;
|
||||
mod documentation;
|
||||
mod error;
|
||||
mod event;
|
||||
mod genesis_build;
|
||||
mod genesis_config;
|
||||
mod hooks;
|
||||
mod inherent;
|
||||
mod instances;
|
||||
mod origin;
|
||||
mod pezpallet_struct;
|
||||
mod storage;
|
||||
mod tasks;
|
||||
mod tt_default_parts;
|
||||
mod type_value;
|
||||
mod validate_unsigned;
|
||||
mod view_functions;
|
||||
mod warnings;
|
||||
|
||||
use crate::pezpallet::Def;
|
||||
use quote::ToTokens;
|
||||
|
||||
/// Merge where clause together, `where` token span is taken from the first not none one.
|
||||
pub fn merge_where_clauses(clauses: &[&Option<syn::WhereClause>]) -> Option<syn::WhereClause> {
|
||||
let mut clauses = clauses.iter().filter_map(|f| f.as_ref());
|
||||
let mut res = clauses.next()?.clone();
|
||||
for other in clauses {
|
||||
res.predicates.extend(other.predicates.iter().cloned())
|
||||
}
|
||||
Some(res)
|
||||
}
|
||||
|
||||
/// Expand definition, in particular:
|
||||
/// * add some bounds and variants to type defined,
|
||||
/// * create some new types,
|
||||
/// * impl stuff on them.
|
||||
pub fn expand(mut def: Def) -> proc_macro2::TokenStream {
|
||||
// Remove the `pezpallet_doc` attribute first.
|
||||
let metadata_docs = documentation::expand_documentation(&mut def);
|
||||
let constants = constants::expand_constants(&mut def);
|
||||
let pezpallet_struct = pezpallet_struct::expand_pallet_struct(&mut def);
|
||||
let config = config::expand_config(&mut def);
|
||||
let associated_types = config::expand_config_metadata(&def);
|
||||
let call = call::expand_call(&mut def);
|
||||
let tasks = tasks::expand_tasks(&mut def);
|
||||
let error = error::expand_error(&mut def);
|
||||
let event = event::expand_event(&mut def);
|
||||
let storages = storage::expand_storages(&mut def);
|
||||
let view_functions = view_functions::expand_view_functions(&def);
|
||||
let inherents = inherent::expand_inherents(&mut def);
|
||||
let instances = instances::expand_instances(&mut def);
|
||||
let hooks = hooks::expand_hooks(&mut def);
|
||||
let genesis_build = genesis_build::expand_genesis_build(&mut def);
|
||||
let genesis_config = genesis_config::expand_genesis_config(&mut def);
|
||||
let type_values = type_value::expand_type_values(&mut def);
|
||||
let origin = origin::expand_origin(&mut def);
|
||||
let validate_unsigned = validate_unsigned::expand_validate_unsigned(&mut def);
|
||||
let tt_default_parts = tt_default_parts::expand_tt_default_parts(&mut def);
|
||||
let doc_only = doc_only::expand_doc_only(&mut def);
|
||||
let composites = composite::expand_composites(&mut def);
|
||||
|
||||
let warnings = def.config.warnings;
|
||||
|
||||
def.item.attrs.insert(
|
||||
0,
|
||||
syn::parse_quote!(
|
||||
#[doc = r"The `pezpallet` module in each FRAME pezpallet hosts the most important items needed
|
||||
to construct this pezpallet.
|
||||
|
||||
The main components of this pezpallet are:
|
||||
- [`Pezpallet`], which implements all of the dispatchable extrinsics of the pezpallet, among
|
||||
other public functions.
|
||||
- The subset of the functions that are dispatchable can be identified either in the
|
||||
[`dispatchables`] module or in the [`Call`] enum.
|
||||
- [`storage_types`], which contains the list of all types that are representing a
|
||||
storage item. Otherwise, all storage items are listed among [*Type Definitions*](#types).
|
||||
- [`Config`], which contains the configuration trait of this pezpallet.
|
||||
- [`Event`] and [`Error`], which are listed among the [*Enums*](#enums).
|
||||
"]
|
||||
),
|
||||
);
|
||||
|
||||
let new_items = quote::quote!(
|
||||
#(
|
||||
#warnings
|
||||
)*
|
||||
#metadata_docs
|
||||
#constants
|
||||
#pezpallet_struct
|
||||
#config
|
||||
#associated_types
|
||||
#call
|
||||
#tasks
|
||||
#error
|
||||
#event
|
||||
#storages
|
||||
#view_functions
|
||||
#inherents
|
||||
#instances
|
||||
#hooks
|
||||
#genesis_build
|
||||
#genesis_config
|
||||
#type_values
|
||||
#origin
|
||||
#validate_unsigned
|
||||
#tt_default_parts
|
||||
#doc_only
|
||||
#composites
|
||||
);
|
||||
|
||||
let item = &mut def.item.content.as_mut().expect("This is checked by parsing").1;
|
||||
item.push(syn::Item::Verbatim(new_items));
|
||||
|
||||
def.item.into_token_stream()
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::{pezpallet::Def, COUNTER};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{spanned::Spanned, Ident};
|
||||
|
||||
/// expand the `is_origin_part_defined` macro.
|
||||
pub fn expand_origin(def: &mut Def) -> TokenStream {
|
||||
let count = COUNTER.with(|counter| counter.borrow_mut().inc());
|
||||
let macro_ident = Ident::new(&format!("__is_origin_part_defined_{}", count), def.item.span());
|
||||
|
||||
let maybe_compile_error = if def.origin.is_none() {
|
||||
quote! {
|
||||
compile_error!(concat!(
|
||||
"`",
|
||||
stringify!($pezpallet_name),
|
||||
"` does not have #[pezpallet::origin] defined, perhaps you should \
|
||||
remove `Origin` from construct_runtime?",
|
||||
));
|
||||
}
|
||||
} else {
|
||||
TokenStream::new()
|
||||
};
|
||||
|
||||
quote! {
|
||||
#[doc(hidden)]
|
||||
pub mod __bizinikiwi_origin_check {
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! #macro_ident {
|
||||
($pezpallet_name:ident) => {
|
||||
#maybe_compile_error
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use #macro_ident as is_origin_part_defined;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,308 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::pezpallet::{expand::merge_where_clauses, Def};
|
||||
use pezframe_support_procedural_tools::get_doc_literals;
|
||||
|
||||
///
|
||||
/// * Add derive trait on Pezpallet
|
||||
/// * Implement GetStorageVersion on Pezpallet
|
||||
/// * Implement OnGenesis on Pezpallet
|
||||
/// * Implement `fn error_metadata` on Pezpallet
|
||||
/// * declare Module type alias for construct_runtime
|
||||
/// * replace the first field type of `struct Pezpallet` with `PhantomData` if it is `_`
|
||||
/// * implementation of `PalletInfoAccess` information
|
||||
/// * implementation of `StorageInfoTrait` on Pezpallet
|
||||
pub fn expand_pallet_struct(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
let pezframe_support = &def.pezframe_support;
|
||||
let pezframe_system = &def.pezframe_system;
|
||||
let type_impl_gen = &def.type_impl_generics(def.pezpallet_struct.attr_span);
|
||||
let type_use_gen = &def.type_use_generics(def.pezpallet_struct.attr_span);
|
||||
let type_decl_gen = &def.type_decl_generics(def.pezpallet_struct.attr_span);
|
||||
let pezpallet_ident = &def.pezpallet_struct.pezpallet;
|
||||
let config_where_clause = &def.config.where_clause;
|
||||
let deprecation_status =
|
||||
match crate::deprecation::get_deprecation("e::quote! {#pezframe_support}, &def.item.attrs)
|
||||
{
|
||||
Ok(deprecation) => deprecation,
|
||||
Err(e) => return e.into_compile_error(),
|
||||
};
|
||||
|
||||
let mut storages_where_clauses = vec![&def.config.where_clause];
|
||||
storages_where_clauses.extend(def.storages.iter().map(|storage| &storage.where_clause));
|
||||
let storages_where_clauses = merge_where_clauses(&storages_where_clauses);
|
||||
|
||||
let pezpallet_item = {
|
||||
let pezpallet_module_items = &mut def.item.content.as_mut().expect("Checked by def").1;
|
||||
let item = &mut pezpallet_module_items[def.pezpallet_struct.index];
|
||||
if let syn::Item::Struct(item) = item {
|
||||
item
|
||||
} else {
|
||||
unreachable!("Checked by pezpallet struct parser")
|
||||
}
|
||||
};
|
||||
|
||||
// If the first field type is `_` then we replace with `PhantomData`
|
||||
if let Some(field) = pezpallet_item.fields.iter_mut().next() {
|
||||
if field.ty == syn::parse_quote!(_) {
|
||||
field.ty = syn::parse_quote!(
|
||||
core::marker::PhantomData<(#type_use_gen)>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if get_doc_literals(&pezpallet_item.attrs).is_empty() {
|
||||
pezpallet_item.attrs.push(syn::parse_quote!(
|
||||
#[doc = r"
|
||||
The `Pezpallet` struct, the main type that implements traits and standalone
|
||||
functions within the pezpallet.
|
||||
"]
|
||||
));
|
||||
}
|
||||
|
||||
pezpallet_item.attrs.push(syn::parse_quote!(
|
||||
#[derive(
|
||||
#pezframe_support::CloneNoBound,
|
||||
#pezframe_support::EqNoBound,
|
||||
#pezframe_support::PartialEqNoBound,
|
||||
#pezframe_support::RuntimeDebugNoBound,
|
||||
)]
|
||||
));
|
||||
|
||||
let pezpallet_error_metadata = if let Some(error_def) = &def.error {
|
||||
let error_ident = &error_def.error;
|
||||
quote::quote_spanned!(def.pezpallet_struct.attr_span =>
|
||||
impl<#type_impl_gen> #pezpallet_ident<#type_use_gen> #config_where_clause {
|
||||
#[doc(hidden)]
|
||||
#[allow(deprecated)]
|
||||
pub fn error_metadata() -> Option<#pezframe_support::__private::metadata_ir::PalletErrorMetadataIR> {
|
||||
Some(<#error_ident<#type_use_gen>>::error_metadata())
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
quote::quote_spanned!(def.pezpallet_struct.attr_span =>
|
||||
impl<#type_impl_gen> #pezpallet_ident<#type_use_gen> #config_where_clause {
|
||||
#[doc(hidden)]
|
||||
pub fn error_metadata() -> Option<#pezframe_support::__private::metadata_ir::PalletErrorMetadataIR> {
|
||||
None
|
||||
}
|
||||
}
|
||||
)
|
||||
};
|
||||
|
||||
let storage_info_span =
|
||||
def.pezpallet_struct.without_storage_info.unwrap_or(def.pezpallet_struct.attr_span);
|
||||
|
||||
let storage_names = &def.storages.iter().map(|storage| &storage.ident).collect::<Vec<_>>();
|
||||
let storage_cfg_attrs =
|
||||
&def.storages.iter().map(|storage| &storage.cfg_attrs).collect::<Vec<_>>();
|
||||
let storage_maybe_allow_attrs = &def
|
||||
.storages
|
||||
.iter()
|
||||
.map(|storage| crate::deprecation::extract_or_return_allow_attrs(&storage.attrs).collect())
|
||||
.collect::<Vec<Vec<_>>>();
|
||||
// Depending on the flag `without_storage_info` and the storage attribute `unbounded`, we use
|
||||
// partial or full storage info from storage.
|
||||
let storage_info_traits = &def
|
||||
.storages
|
||||
.iter()
|
||||
.map(|storage| {
|
||||
if storage.unbounded || def.pezpallet_struct.without_storage_info.is_some() {
|
||||
quote::quote_spanned!(storage_info_span => PartialStorageInfoTrait)
|
||||
} else {
|
||||
quote::quote_spanned!(storage_info_span => StorageInfoTrait)
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let storage_info_methods = &def
|
||||
.storages
|
||||
.iter()
|
||||
.map(|storage| {
|
||||
if storage.unbounded || def.pezpallet_struct.without_storage_info.is_some() {
|
||||
quote::quote_spanned!(storage_info_span => partial_storage_info)
|
||||
} else {
|
||||
quote::quote_spanned!(storage_info_span => storage_info)
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let storage_info = quote::quote_spanned!(storage_info_span =>
|
||||
impl<#type_impl_gen> #pezframe_support::traits::StorageInfoTrait
|
||||
for #pezpallet_ident<#type_use_gen>
|
||||
#storages_where_clauses
|
||||
{
|
||||
fn storage_info()
|
||||
-> #pezframe_support::__private::Vec<#pezframe_support::traits::StorageInfo>
|
||||
{
|
||||
#[allow(unused_mut)]
|
||||
let mut res = #pezframe_support::__private::vec![];
|
||||
|
||||
#(
|
||||
#(#storage_cfg_attrs)*
|
||||
#(#storage_maybe_allow_attrs)*
|
||||
{
|
||||
let mut storage_info = <
|
||||
#storage_names<#type_use_gen>
|
||||
as #pezframe_support::traits::#storage_info_traits
|
||||
>::#storage_info_methods();
|
||||
res.append(&mut storage_info);
|
||||
}
|
||||
)*
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
let (storage_version, in_code_storage_version_ty) =
|
||||
if let Some(v) = def.pezpallet_struct.storage_version.as_ref() {
|
||||
(quote::quote! { #v }, quote::quote! { #pezframe_support::traits::StorageVersion })
|
||||
} else {
|
||||
(
|
||||
quote::quote! { core::default::Default::default() },
|
||||
quote::quote! { #pezframe_support::traits::NoStorageVersionSet },
|
||||
)
|
||||
};
|
||||
|
||||
let whitelisted_storage_idents: Vec<syn::Ident> = def
|
||||
.storages
|
||||
.iter()
|
||||
.filter_map(|s| s.whitelisted.then(|| s.ident.clone()))
|
||||
.collect();
|
||||
|
||||
let whitelisted_storage_keys_impl = quote::quote![
|
||||
use #pezframe_support::traits::{StorageInfoTrait, TrackedStorageKey, WhitelistedStorageKeys};
|
||||
impl<#type_impl_gen> WhitelistedStorageKeys for #pezpallet_ident<#type_use_gen> #storages_where_clauses {
|
||||
fn whitelisted_storage_keys() -> #pezframe_support::__private::Vec<TrackedStorageKey> {
|
||||
use #pezframe_support::__private::vec;
|
||||
vec![#(
|
||||
TrackedStorageKey::new(#whitelisted_storage_idents::<#type_use_gen>::hashed_key().to_vec())
|
||||
),*]
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
quote::quote_spanned!(def.pezpallet_struct.attr_span =>
|
||||
#pezpallet_error_metadata
|
||||
|
||||
/// Type alias to `Pezpallet`, to be used by `construct_runtime`.
|
||||
///
|
||||
/// Generated by `pezpallet` attribute macro.
|
||||
#[deprecated(note = "use `Pezpallet` instead")]
|
||||
#[allow(dead_code)]
|
||||
pub type Module<#type_decl_gen> = #pezpallet_ident<#type_use_gen>;
|
||||
|
||||
// Implement `GetStorageVersion` for `Pezpallet`
|
||||
impl<#type_impl_gen> #pezframe_support::traits::GetStorageVersion
|
||||
for #pezpallet_ident<#type_use_gen>
|
||||
#config_where_clause
|
||||
{
|
||||
type InCodeStorageVersion = #in_code_storage_version_ty;
|
||||
|
||||
fn in_code_storage_version() -> Self::InCodeStorageVersion {
|
||||
#storage_version
|
||||
}
|
||||
|
||||
fn on_chain_storage_version() -> #pezframe_support::traits::StorageVersion {
|
||||
#pezframe_support::traits::StorageVersion::get::<Self>()
|
||||
}
|
||||
}
|
||||
|
||||
// Implement `OnGenesis` for `Pezpallet`
|
||||
impl<#type_impl_gen> #pezframe_support::traits::OnGenesis
|
||||
for #pezpallet_ident<#type_use_gen>
|
||||
#config_where_clause
|
||||
{
|
||||
fn on_genesis() {
|
||||
let storage_version: #pezframe_support::traits::StorageVersion = #storage_version;
|
||||
storage_version.put::<Self>();
|
||||
}
|
||||
}
|
||||
|
||||
// Implement `PalletInfoAccess` for `Pezpallet`
|
||||
impl<#type_impl_gen> #pezframe_support::traits::PalletInfoAccess
|
||||
for #pezpallet_ident<#type_use_gen>
|
||||
#config_where_clause
|
||||
{
|
||||
fn index() -> usize {
|
||||
<
|
||||
<T as #pezframe_system::Config>::PalletInfo as #pezframe_support::traits::PalletInfo
|
||||
>::index::<Self>()
|
||||
.expect("Pezpallet is part of the runtime because pezpallet `Config` trait is \
|
||||
implemented by the runtime")
|
||||
}
|
||||
|
||||
fn name() -> &'static str {
|
||||
<
|
||||
<T as #pezframe_system::Config>::PalletInfo as #pezframe_support::traits::PalletInfo
|
||||
>::name::<Self>()
|
||||
.expect("Pezpallet is part of the runtime because pezpallet `Config` trait is \
|
||||
implemented by the runtime")
|
||||
}
|
||||
|
||||
fn name_hash() -> [u8; 16] {
|
||||
<
|
||||
<T as #pezframe_system::Config>::PalletInfo as #pezframe_support::traits::PalletInfo
|
||||
>::name_hash::<Self>()
|
||||
.expect("Pezpallet is part of the runtime because pezpallet `Config` trait is \
|
||||
implemented by the runtime")
|
||||
}
|
||||
|
||||
fn module_name() -> &'static str {
|
||||
<
|
||||
<T as #pezframe_system::Config>::PalletInfo as #pezframe_support::traits::PalletInfo
|
||||
>::module_name::<Self>()
|
||||
.expect("Pezpallet is part of the runtime because pezpallet `Config` trait is \
|
||||
implemented by the runtime")
|
||||
}
|
||||
|
||||
fn crate_version() -> #pezframe_support::traits::CrateVersion {
|
||||
#pezframe_support::crate_to_crate_version!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<#type_impl_gen> #pezframe_support::traits::PalletsInfoAccess
|
||||
for #pezpallet_ident<#type_use_gen>
|
||||
#config_where_clause
|
||||
{
|
||||
fn count() -> usize { 1 }
|
||||
fn infos() -> #pezframe_support::__private::Vec<#pezframe_support::traits::PalletInfoData> {
|
||||
use #pezframe_support::traits::PalletInfoAccess;
|
||||
let item = #pezframe_support::traits::PalletInfoData {
|
||||
index: Self::index(),
|
||||
name: Self::name(),
|
||||
module_name: Self::module_name(),
|
||||
crate_version: Self::crate_version(),
|
||||
};
|
||||
#pezframe_support::__private::vec![item]
|
||||
}
|
||||
}
|
||||
|
||||
#storage_info
|
||||
#whitelisted_storage_keys_impl
|
||||
|
||||
impl<#type_use_gen> #pezpallet_ident<#type_use_gen> {
|
||||
#[allow(dead_code)]
|
||||
#[doc(hidden)]
|
||||
pub fn deprecation_info() -> #pezframe_support::__private::metadata_ir::ItemDeprecationInfoIR {
|
||||
#deprecation_status
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,946 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::{
|
||||
counter_prefix,
|
||||
deprecation::extract_or_return_allow_attrs,
|
||||
pezpallet::{
|
||||
parse::{
|
||||
helper::two128_str,
|
||||
storage::{Metadata, QueryKind, StorageDef, StorageGenerics},
|
||||
},
|
||||
Def,
|
||||
},
|
||||
};
|
||||
use quote::ToTokens;
|
||||
use std::{collections::HashMap, ops::IndexMut};
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
/// Generate the prefix_ident related to the storage.
|
||||
/// prefix_ident is used for the prefix struct to be given to storage as first generic param.
|
||||
fn prefix_ident(storage: &StorageDef) -> syn::Ident {
|
||||
let storage_ident = &storage.ident;
|
||||
syn::Ident::new(&format!("_GeneratedPrefixForStorage{}", storage_ident), storage_ident.span())
|
||||
}
|
||||
|
||||
/// Generate the counter_prefix_ident related to the storage.
|
||||
/// counter_prefix_ident is used for the prefix struct to be given to counted storage map.
|
||||
fn counter_prefix_ident(storage_ident: &syn::Ident) -> syn::Ident {
|
||||
syn::Ident::new(
|
||||
&format!("_GeneratedCounterPrefixForStorage{}", storage_ident),
|
||||
storage_ident.span(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Check for duplicated storage prefixes. This step is necessary since users can specify an
|
||||
/// alternative storage prefix using the #[pezpallet::storage_prefix] syntax, and we need to ensure
|
||||
/// that the prefix specified by the user is not a duplicate of an existing one.
|
||||
fn check_prefix_duplicates(
|
||||
storage_def: &StorageDef,
|
||||
// A hashmap of all already used prefix and their associated error if duplication
|
||||
used_prefixes: &mut HashMap<String, syn::Error>,
|
||||
) -> syn::Result<()> {
|
||||
let prefix = storage_def.prefix();
|
||||
let dup_err = syn::Error::new(
|
||||
storage_def.prefix_span(),
|
||||
format!("Duplicate storage prefixes found for `{}`", prefix),
|
||||
);
|
||||
|
||||
if let Some(other_dup_err) = used_prefixes.insert(prefix.clone(), dup_err.clone()) {
|
||||
let mut err = dup_err;
|
||||
err.combine(other_dup_err);
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
if let Metadata::CountedMap { .. } = storage_def.metadata {
|
||||
let counter_prefix = counter_prefix(&prefix);
|
||||
let counter_dup_err = syn::Error::new(
|
||||
storage_def.prefix_span(),
|
||||
format!(
|
||||
"Duplicate storage prefixes found for `{}`, used for counter associated to \
|
||||
counted storage map",
|
||||
counter_prefix,
|
||||
),
|
||||
);
|
||||
|
||||
if let Some(other_dup_err) = used_prefixes.insert(counter_prefix, counter_dup_err.clone()) {
|
||||
let mut err = counter_dup_err;
|
||||
err.combine(other_dup_err);
|
||||
return Err(err);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub struct ResultOnEmptyStructMetadata {
|
||||
/// The Rust ident that is going to be used as the name of the OnEmpty struct.
|
||||
pub name: syn::Ident,
|
||||
/// The path to the error type being returned by the ResultQuery.
|
||||
pub error_path: syn::Path,
|
||||
/// The visibility of the OnEmpty struct.
|
||||
pub visibility: syn::Visibility,
|
||||
/// The type of the storage item.
|
||||
pub value_ty: syn::Type,
|
||||
/// The name of the pezpallet error enum variant that is going to be returned.
|
||||
pub variant_name: syn::Ident,
|
||||
/// The span used to report compilation errors about the OnEmpty struct.
|
||||
pub span: proc_macro2::Span,
|
||||
}
|
||||
|
||||
///
|
||||
/// * if generics are unnamed: replace the first generic `_` by the generated prefix structure
|
||||
/// * if generics are named: reorder the generic, remove their name, and add the missing ones.
|
||||
/// * Add `#[allow(type_alias_bounds)]`
|
||||
pub fn process_generics(def: &mut Def) -> syn::Result<Vec<ResultOnEmptyStructMetadata>> {
|
||||
let pezframe_support = &def.pezframe_support;
|
||||
let mut on_empty_struct_metadata = Vec::new();
|
||||
|
||||
for storage_def in def.storages.iter_mut() {
|
||||
let item = &mut def.item.content.as_mut().expect("Checked by def").1[storage_def.index];
|
||||
|
||||
let typ_item = match item {
|
||||
syn::Item::Type(t) => t,
|
||||
_ => unreachable!("Checked by def"),
|
||||
};
|
||||
|
||||
typ_item.attrs.push(syn::parse_quote!(#[allow(type_alias_bounds)]));
|
||||
|
||||
let typ_path = match &mut *typ_item.ty {
|
||||
syn::Type::Path(p) => p,
|
||||
_ => unreachable!("Checked by def"),
|
||||
};
|
||||
|
||||
let args = match &mut typ_path.path.segments[0].arguments {
|
||||
syn::PathArguments::AngleBracketed(args) => args,
|
||||
_ => unreachable!("Checked by def"),
|
||||
};
|
||||
|
||||
let prefix_ident = prefix_ident(storage_def);
|
||||
let type_use_gen = if def.config.has_instance {
|
||||
quote::quote_spanned!(storage_def.attr_span => T, I)
|
||||
} else {
|
||||
quote::quote_spanned!(storage_def.attr_span => T)
|
||||
};
|
||||
|
||||
let default_query_kind: syn::Type =
|
||||
syn::parse_quote!(#pezframe_support::storage::types::OptionQuery);
|
||||
let mut default_on_empty = |value_ty: syn::Type| -> syn::Type {
|
||||
if let Some(QueryKind::ResultQuery(error_path, variant_name)) =
|
||||
storage_def.query_kind.as_ref()
|
||||
{
|
||||
let on_empty_ident =
|
||||
quote::format_ident!("__Frame_Internal_Get{}Result", storage_def.ident);
|
||||
on_empty_struct_metadata.push(ResultOnEmptyStructMetadata {
|
||||
name: on_empty_ident.clone(),
|
||||
visibility: storage_def.vis.clone(),
|
||||
value_ty,
|
||||
error_path: error_path.clone(),
|
||||
variant_name: variant_name.clone(),
|
||||
span: storage_def.attr_span,
|
||||
});
|
||||
return syn::parse_quote!(#on_empty_ident);
|
||||
}
|
||||
syn::parse_quote!(#pezframe_support::traits::GetDefault)
|
||||
};
|
||||
let default_max_values: syn::Type = syn::parse_quote!(#pezframe_support::traits::GetDefault);
|
||||
|
||||
let set_result_query_type_parameter = |query_type: &mut syn::Type| -> syn::Result<()> {
|
||||
if let Some(QueryKind::ResultQuery(error_path, _)) = storage_def.query_kind.as_ref() {
|
||||
if let syn::Type::Path(syn::TypePath { path: syn::Path { segments, .. }, .. }) =
|
||||
query_type
|
||||
{
|
||||
if let Some(seg) = segments.last_mut() {
|
||||
if let syn::PathArguments::AngleBracketed(
|
||||
syn::AngleBracketedGenericArguments { args, .. },
|
||||
) = &mut seg.arguments
|
||||
{
|
||||
args.clear();
|
||||
args.push(syn::GenericArgument::Type(syn::parse_quote!(#error_path)));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let msg = format!(
|
||||
"Invalid pezpallet::storage, unexpected type for query, expected ResultQuery \
|
||||
with 1 type parameter, found `{}`",
|
||||
query_type.to_token_stream().to_string()
|
||||
);
|
||||
return Err(syn::Error::new(query_type.span(), msg));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
};
|
||||
|
||||
if let Some(named_generics) = storage_def.named_generics.clone() {
|
||||
args.args.clear();
|
||||
args.args.push(syn::parse_quote!( #prefix_ident<#type_use_gen> ));
|
||||
match named_generics {
|
||||
StorageGenerics::Value { value, query_kind, on_empty } => {
|
||||
args.args.push(syn::GenericArgument::Type(value.clone()));
|
||||
let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone());
|
||||
set_result_query_type_parameter(&mut query_kind)?;
|
||||
args.args.push(syn::GenericArgument::Type(query_kind));
|
||||
let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value));
|
||||
args.args.push(syn::GenericArgument::Type(on_empty));
|
||||
},
|
||||
StorageGenerics::Map { hasher, key, value, query_kind, on_empty, max_values } |
|
||||
StorageGenerics::CountedMap {
|
||||
hasher,
|
||||
key,
|
||||
value,
|
||||
query_kind,
|
||||
on_empty,
|
||||
max_values,
|
||||
} => {
|
||||
args.args.push(syn::GenericArgument::Type(hasher));
|
||||
args.args.push(syn::GenericArgument::Type(key));
|
||||
args.args.push(syn::GenericArgument::Type(value.clone()));
|
||||
let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone());
|
||||
set_result_query_type_parameter(&mut query_kind)?;
|
||||
args.args.push(syn::GenericArgument::Type(query_kind));
|
||||
let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value));
|
||||
args.args.push(syn::GenericArgument::Type(on_empty));
|
||||
let max_values = max_values.unwrap_or_else(|| default_max_values.clone());
|
||||
args.args.push(syn::GenericArgument::Type(max_values));
|
||||
},
|
||||
StorageGenerics::DoubleMap {
|
||||
hasher1,
|
||||
key1,
|
||||
hasher2,
|
||||
key2,
|
||||
value,
|
||||
query_kind,
|
||||
on_empty,
|
||||
max_values,
|
||||
} => {
|
||||
args.args.push(syn::GenericArgument::Type(hasher1));
|
||||
args.args.push(syn::GenericArgument::Type(key1));
|
||||
args.args.push(syn::GenericArgument::Type(hasher2));
|
||||
args.args.push(syn::GenericArgument::Type(key2));
|
||||
args.args.push(syn::GenericArgument::Type(value.clone()));
|
||||
let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone());
|
||||
set_result_query_type_parameter(&mut query_kind)?;
|
||||
args.args.push(syn::GenericArgument::Type(query_kind));
|
||||
let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value));
|
||||
args.args.push(syn::GenericArgument::Type(on_empty));
|
||||
let max_values = max_values.unwrap_or_else(|| default_max_values.clone());
|
||||
args.args.push(syn::GenericArgument::Type(max_values));
|
||||
},
|
||||
StorageGenerics::NMap { keygen, value, query_kind, on_empty, max_values } |
|
||||
StorageGenerics::CountedNMap {
|
||||
keygen,
|
||||
value,
|
||||
query_kind,
|
||||
on_empty,
|
||||
max_values,
|
||||
} => {
|
||||
args.args.push(syn::GenericArgument::Type(keygen));
|
||||
args.args.push(syn::GenericArgument::Type(value.clone()));
|
||||
let mut query_kind = query_kind.unwrap_or_else(|| default_query_kind.clone());
|
||||
set_result_query_type_parameter(&mut query_kind)?;
|
||||
args.args.push(syn::GenericArgument::Type(query_kind));
|
||||
let on_empty = on_empty.unwrap_or_else(|| default_on_empty(value));
|
||||
args.args.push(syn::GenericArgument::Type(on_empty));
|
||||
let max_values = max_values.unwrap_or_else(|| default_max_values.clone());
|
||||
args.args.push(syn::GenericArgument::Type(max_values));
|
||||
},
|
||||
}
|
||||
} else {
|
||||
args.args[0] = syn::parse_quote!( #prefix_ident<#type_use_gen> );
|
||||
|
||||
let (value_idx, query_idx, on_empty_idx) = match storage_def.metadata {
|
||||
Metadata::Value { .. } => (1, 2, 3),
|
||||
Metadata::NMap { .. } | Metadata::CountedNMap { .. } => (2, 3, 4),
|
||||
Metadata::Map { .. } | Metadata::CountedMap { .. } => (3, 4, 5),
|
||||
Metadata::DoubleMap { .. } => (5, 6, 7),
|
||||
};
|
||||
|
||||
if storage_def.use_default_hasher {
|
||||
let hasher_indices: Vec<usize> = match storage_def.metadata {
|
||||
Metadata::Map { .. } | Metadata::CountedMap { .. } => vec![1],
|
||||
Metadata::DoubleMap { .. } => vec![1, 3],
|
||||
_ => vec![],
|
||||
};
|
||||
for hasher_idx in hasher_indices {
|
||||
args.args[hasher_idx] = syn::GenericArgument::Type(
|
||||
syn::parse_quote!(#pezframe_support::Blake2_128Concat),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if query_idx < args.args.len() {
|
||||
if let syn::GenericArgument::Type(query_kind) = args.args.index_mut(query_idx) {
|
||||
set_result_query_type_parameter(query_kind)?;
|
||||
}
|
||||
} else if let Some(QueryKind::ResultQuery(error_path, _)) =
|
||||
storage_def.query_kind.as_ref()
|
||||
{
|
||||
args.args.push(syn::GenericArgument::Type(syn::parse_quote!(#error_path)))
|
||||
}
|
||||
|
||||
// Here, we only need to check if OnEmpty is *not* specified, and if so, then we have to
|
||||
// generate a default OnEmpty struct for it.
|
||||
if on_empty_idx >= args.args.len() &&
|
||||
matches!(storage_def.query_kind.as_ref(), Some(QueryKind::ResultQuery(_, _)))
|
||||
{
|
||||
let value_ty = match args.args[value_idx].clone() {
|
||||
syn::GenericArgument::Type(ty) => ty,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let on_empty = default_on_empty(value_ty);
|
||||
args.args.push(syn::GenericArgument::Type(on_empty));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(on_empty_struct_metadata)
|
||||
}
|
||||
|
||||
fn augment_final_docs(def: &mut Def) {
|
||||
// expand the docs with a new line showing the storage type (value, map, double map, etc), and
|
||||
// the key/value type(s).
|
||||
let mut push_string_literal = |doc_line: &str, storage: &mut StorageDef| {
|
||||
let item = &mut def.item.content.as_mut().expect("Checked by def").1[storage.index];
|
||||
let typ_item = match item {
|
||||
syn::Item::Type(t) => t,
|
||||
_ => unreachable!("Checked by def"),
|
||||
};
|
||||
typ_item.attrs.push(syn::parse_quote!(#[doc = ""]));
|
||||
typ_item.attrs.push(syn::parse_quote!(#[doc = #doc_line]));
|
||||
};
|
||||
def.storages.iter_mut().for_each(|storage| match &storage.metadata {
|
||||
Metadata::Value { value } => {
|
||||
let doc_line = format!(
|
||||
"Storage type is [`StorageValue`] with value type `{}`.",
|
||||
value.to_token_stream()
|
||||
);
|
||||
push_string_literal(&doc_line, storage);
|
||||
},
|
||||
Metadata::Map { key, value } => {
|
||||
let doc_line = format!(
|
||||
"Storage type is [`StorageMap`] with key type `{}` and value type `{}`.",
|
||||
key.to_token_stream(),
|
||||
value.to_token_stream()
|
||||
);
|
||||
push_string_literal(&doc_line, storage);
|
||||
},
|
||||
Metadata::DoubleMap { key1, key2, value } => {
|
||||
let doc_line = format!(
|
||||
"Storage type is [`StorageDoubleMap`] with key1 type {}, key2 type {} and value type {}.",
|
||||
key1.to_token_stream(),
|
||||
key2.to_token_stream(),
|
||||
value.to_token_stream()
|
||||
);
|
||||
push_string_literal(&doc_line, storage);
|
||||
},
|
||||
Metadata::NMap { keys, value, .. } => {
|
||||
let doc_line = format!(
|
||||
"Storage type is [`StorageNMap`] with keys type ({}) and value type {}.",
|
||||
keys.iter()
|
||||
.map(|k| k.to_token_stream().to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
value.to_token_stream()
|
||||
);
|
||||
push_string_literal(&doc_line, storage);
|
||||
},
|
||||
Metadata::CountedNMap { keys, value, .. } => {
|
||||
let doc_line = format!(
|
||||
"Storage type is [`CountedStorageNMap`] with keys type ({}) and value type {}.",
|
||||
keys.iter()
|
||||
.map(|k| k.to_token_stream().to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
value.to_token_stream()
|
||||
);
|
||||
push_string_literal(&doc_line, storage);
|
||||
},
|
||||
Metadata::CountedMap { key, value } => {
|
||||
let doc_line = format!(
|
||||
"Storage type is [`CountedStorageMap`] with key type {} and value type {}.",
|
||||
key.to_token_stream(),
|
||||
value.to_token_stream()
|
||||
);
|
||||
push_string_literal(&doc_line, storage);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
///
|
||||
/// * generate StoragePrefix structs (e.g. for a storage `MyStorage` a struct with the name
|
||||
/// `_GeneratedPrefixForStorage$NameOfStorage` is generated) and implements StorageInstance trait.
|
||||
/// * if generics are unnamed: replace the first generic `_` by the generated prefix structure
|
||||
/// * if generics are named: reorder the generic, remove their name, and add the missing ones.
|
||||
/// * Add `#[allow(type_alias_bounds)]` on storages type alias
|
||||
/// * generate metadatas
|
||||
pub fn expand_storages(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
let on_empty_struct_metadata = match process_generics(def) {
|
||||
Ok(idents) => idents,
|
||||
Err(e) => return e.into_compile_error(),
|
||||
};
|
||||
|
||||
augment_final_docs(def);
|
||||
|
||||
// Check for duplicate prefixes
|
||||
let mut prefix_set = HashMap::new();
|
||||
let mut errors = def
|
||||
.storages
|
||||
.iter()
|
||||
.filter_map(|storage_def| check_prefix_duplicates(storage_def, &mut prefix_set).err());
|
||||
if let Some(mut final_error) = errors.next() {
|
||||
errors.for_each(|error| final_error.combine(error));
|
||||
return final_error.into_compile_error();
|
||||
}
|
||||
|
||||
let pezframe_support = &def.pezframe_support;
|
||||
let pezframe_system = &def.pezframe_system;
|
||||
let pezpallet_ident = &def.pezpallet_struct.pezpallet;
|
||||
let mut entries_builder = vec![];
|
||||
for storage in def.storages.iter() {
|
||||
let no_docs = vec![];
|
||||
let docs = if cfg!(feature = "no-metadata-docs") { &no_docs } else { &storage.docs };
|
||||
|
||||
let ident = &storage.ident;
|
||||
let gen = &def.type_use_generics(storage.attr_span);
|
||||
let full_ident = quote::quote_spanned!(storage.attr_span => #ident<#gen> );
|
||||
|
||||
let cfg_attrs = &storage.cfg_attrs;
|
||||
let deprecation = match crate::deprecation::get_deprecation(
|
||||
"e::quote! { #pezframe_support },
|
||||
&storage.attrs,
|
||||
) {
|
||||
Ok(deprecation) => deprecation,
|
||||
Err(e) => return e.into_compile_error(),
|
||||
};
|
||||
|
||||
// Extracts #[allow] attributes, necessary so that we don't run into compiler warnings
|
||||
let maybe_allow_attrs: Vec<syn::Attribute> =
|
||||
extract_or_return_allow_attrs(&storage.attrs).collect();
|
||||
|
||||
entries_builder.push(quote::quote_spanned!(storage.attr_span =>
|
||||
#(#cfg_attrs)*
|
||||
#(#maybe_allow_attrs)*
|
||||
(|entries: &mut #pezframe_support::__private::Vec<_>| {
|
||||
{
|
||||
<#full_ident as #pezframe_support::storage::StorageEntryMetadataBuilder>::build_metadata(
|
||||
#deprecation,
|
||||
#pezframe_support::__private::vec![
|
||||
#( #docs, )*
|
||||
],
|
||||
entries,
|
||||
);
|
||||
}
|
||||
})
|
||||
))
|
||||
}
|
||||
|
||||
let getters = def.storages.iter().map(|storage| {
|
||||
if let Some(getter) = &storage.getter {
|
||||
let completed_where_clause =
|
||||
super::merge_where_clauses(&[&storage.where_clause, &def.config.where_clause]);
|
||||
// Extracts #[allow] attributes, necessary so that we don't run into compiler warnings
|
||||
let maybe_allow_attrs: Vec<syn::Attribute> =
|
||||
extract_or_return_allow_attrs(&storage.attrs).collect();
|
||||
let ident = &storage.ident;
|
||||
let gen = &def.type_use_generics(storage.attr_span);
|
||||
let type_impl_gen = &def.type_impl_generics(storage.attr_span);
|
||||
let type_use_gen = &def.type_use_generics(storage.attr_span);
|
||||
let full_ident = quote::quote_spanned!(storage.attr_span => #ident<#gen> );
|
||||
|
||||
let cfg_attrs = &storage.cfg_attrs;
|
||||
|
||||
// If the storage item is public, link it and otherwise just mention it.
|
||||
//
|
||||
// We can not just copy the docs from a non-public type as it may links to internal
|
||||
// types which makes the compiler very unhappy :(
|
||||
let getter_doc_line = if matches!(storage.vis, syn::Visibility::Public(_)) {
|
||||
format!("An auto-generated getter for [`{}`].", storage.ident)
|
||||
} else {
|
||||
format!("An auto-generated getter for `{}`.", storage.ident)
|
||||
};
|
||||
|
||||
match &storage.metadata {
|
||||
Metadata::Value { value } => {
|
||||
let query = match storage.query_kind.as_ref().expect("Checked by def") {
|
||||
QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span =>
|
||||
Option<#value>
|
||||
),
|
||||
QueryKind::ResultQuery(error_path, _) => {
|
||||
quote::quote_spanned!(storage.attr_span =>
|
||||
Result<#value, #error_path>
|
||||
)
|
||||
},
|
||||
QueryKind::ValueQuery => quote::quote!(#value),
|
||||
};
|
||||
quote::quote_spanned!(storage.attr_span =>
|
||||
#(#cfg_attrs)*
|
||||
impl<#type_impl_gen> #pezpallet_ident<#type_use_gen> #completed_where_clause {
|
||||
#[doc = #getter_doc_line]
|
||||
#(#maybe_allow_attrs)*
|
||||
pub fn #getter() -> #query {
|
||||
<
|
||||
#full_ident as #pezframe_support::storage::StorageValue<#value>
|
||||
>::get()
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
Metadata::Map { key, value } => {
|
||||
let query = match storage.query_kind.as_ref().expect("Checked by def") {
|
||||
QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span =>
|
||||
Option<#value>
|
||||
),
|
||||
QueryKind::ResultQuery(error_path, _) => {
|
||||
quote::quote_spanned!(storage.attr_span =>
|
||||
Result<#value, #error_path>
|
||||
)
|
||||
},
|
||||
QueryKind::ValueQuery => quote::quote!(#value),
|
||||
};
|
||||
quote::quote_spanned!(storage.attr_span =>
|
||||
#(#cfg_attrs)*
|
||||
impl<#type_impl_gen> #pezpallet_ident<#type_use_gen> #completed_where_clause {
|
||||
#[doc = #getter_doc_line]
|
||||
#(#maybe_allow_attrs)*
|
||||
pub fn #getter<KArg>(k: KArg) -> #query where
|
||||
KArg: #pezframe_support::__private::codec::EncodeLike<#key>,
|
||||
{
|
||||
<
|
||||
#full_ident as #pezframe_support::storage::StorageMap<#key, #value>
|
||||
>::get(k)
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
Metadata::CountedMap { key, value } => {
|
||||
let query = match storage.query_kind.as_ref().expect("Checked by def") {
|
||||
QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span =>
|
||||
Option<#value>
|
||||
),
|
||||
QueryKind::ResultQuery(error_path, _) => {
|
||||
quote::quote_spanned!(storage.attr_span =>
|
||||
Result<#value, #error_path>
|
||||
)
|
||||
},
|
||||
QueryKind::ValueQuery => quote::quote!(#value),
|
||||
};
|
||||
quote::quote_spanned!(storage.attr_span =>
|
||||
#(#cfg_attrs)*
|
||||
impl<#type_impl_gen> #pezpallet_ident<#type_use_gen> #completed_where_clause {
|
||||
#[doc = #getter_doc_line]
|
||||
#(#maybe_allow_attrs)*
|
||||
pub fn #getter<KArg>(k: KArg) -> #query where
|
||||
KArg: #pezframe_support::__private::codec::EncodeLike<#key>,
|
||||
{
|
||||
// NOTE: we can't use any trait here because CountedStorageMap
|
||||
// doesn't implement any.
|
||||
<#full_ident>::get(k)
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
Metadata::DoubleMap { key1, key2, value } => {
|
||||
let query = match storage.query_kind.as_ref().expect("Checked by def") {
|
||||
QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span =>
|
||||
Option<#value>
|
||||
),
|
||||
QueryKind::ResultQuery(error_path, _) => {
|
||||
quote::quote_spanned!(storage.attr_span =>
|
||||
Result<#value, #error_path>
|
||||
)
|
||||
},
|
||||
QueryKind::ValueQuery => quote::quote!(#value),
|
||||
};
|
||||
quote::quote_spanned!(storage.attr_span =>
|
||||
#(#cfg_attrs)*
|
||||
impl<#type_impl_gen> #pezpallet_ident<#type_use_gen> #completed_where_clause {
|
||||
#[doc = #getter_doc_line]
|
||||
#(#maybe_allow_attrs)*
|
||||
pub fn #getter<KArg1, KArg2>(k1: KArg1, k2: KArg2) -> #query where
|
||||
KArg1: #pezframe_support::__private::codec::EncodeLike<#key1>,
|
||||
KArg2: #pezframe_support::__private::codec::EncodeLike<#key2>,
|
||||
{
|
||||
<
|
||||
#full_ident as
|
||||
#pezframe_support::storage::StorageDoubleMap<#key1, #key2, #value>
|
||||
>::get(k1, k2)
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
Metadata::NMap { keygen, value, .. } => {
|
||||
let query = match storage.query_kind.as_ref().expect("Checked by def") {
|
||||
QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span =>
|
||||
Option<#value>
|
||||
),
|
||||
QueryKind::ResultQuery(error_path, _) => {
|
||||
quote::quote_spanned!(storage.attr_span =>
|
||||
Result<#value, #error_path>
|
||||
)
|
||||
},
|
||||
QueryKind::ValueQuery => quote::quote!(#value),
|
||||
};
|
||||
quote::quote_spanned!(storage.attr_span =>
|
||||
#(#cfg_attrs)*
|
||||
impl<#type_impl_gen> #pezpallet_ident<#type_use_gen> #completed_where_clause {
|
||||
#[doc = #getter_doc_line]
|
||||
#(#maybe_allow_attrs)*
|
||||
pub fn #getter<KArg>(key: KArg) -> #query
|
||||
where
|
||||
KArg: #pezframe_support::storage::types::EncodeLikeTuple<
|
||||
<#keygen as #pezframe_support::storage::types::KeyGenerator>::KArg
|
||||
>
|
||||
+ #pezframe_support::storage::types::TupleToEncodedIter,
|
||||
{
|
||||
<
|
||||
#full_ident as
|
||||
#pezframe_support::storage::StorageNMap<#keygen, #value>
|
||||
>::get(key)
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
Metadata::CountedNMap { keygen, value, .. } => {
|
||||
let query = match storage.query_kind.as_ref().expect("Checked by def") {
|
||||
QueryKind::OptionQuery => quote::quote_spanned!(storage.attr_span =>
|
||||
Option<#value>
|
||||
),
|
||||
QueryKind::ResultQuery(error_path, _) => {
|
||||
quote::quote_spanned!(storage.attr_span =>
|
||||
Result<#value, #error_path>
|
||||
)
|
||||
},
|
||||
QueryKind::ValueQuery => quote::quote!(#value),
|
||||
};
|
||||
quote::quote_spanned!(storage.attr_span =>
|
||||
#(#cfg_attrs)*
|
||||
impl<#type_impl_gen> #pezpallet_ident<#type_use_gen> #completed_where_clause {
|
||||
#[doc = #getter_doc_line]
|
||||
#(#maybe_allow_attrs)*
|
||||
pub fn #getter<KArg>(key: KArg) -> #query
|
||||
where
|
||||
KArg: #pezframe_support::storage::types::EncodeLikeTuple<
|
||||
<#keygen as #pezframe_support::storage::types::KeyGenerator>::KArg
|
||||
>
|
||||
+ #pezframe_support::storage::types::TupleToEncodedIter,
|
||||
{
|
||||
// NOTE: we can't use any trait here because CountedStorageNMap
|
||||
// doesn't implement any.
|
||||
<#full_ident>::get(key)
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
}
|
||||
} else {
|
||||
Default::default()
|
||||
}
|
||||
});
|
||||
|
||||
let prefix_structs = def.storages.iter().map(|storage_def| {
|
||||
let type_impl_gen = &def.type_impl_generics(storage_def.attr_span);
|
||||
let type_use_gen = &def.type_use_generics(storage_def.attr_span);
|
||||
let prefix_struct_ident = prefix_ident(storage_def);
|
||||
let prefix_struct_vis = &storage_def.vis;
|
||||
let prefix_struct_const = storage_def.prefix();
|
||||
let config_where_clause = &def.config.where_clause;
|
||||
|
||||
let cfg_attrs = &storage_def.cfg_attrs;
|
||||
|
||||
let maybe_counter = match storage_def.metadata {
|
||||
Metadata::CountedMap { .. } => {
|
||||
let counter_prefix_struct_ident = counter_prefix_ident(&storage_def.ident);
|
||||
let counter_prefix_struct_const = counter_prefix(&prefix_struct_const);
|
||||
let storage_prefix_hash = two128_str(&counter_prefix_struct_const);
|
||||
quote::quote_spanned!(storage_def.attr_span =>
|
||||
#(#cfg_attrs)*
|
||||
#[doc(hidden)]
|
||||
#prefix_struct_vis struct #counter_prefix_struct_ident<#type_use_gen>(
|
||||
core::marker::PhantomData<(#type_use_gen,)>
|
||||
);
|
||||
#(#cfg_attrs)*
|
||||
impl<#type_impl_gen> #pezframe_support::traits::StorageInstance
|
||||
for #counter_prefix_struct_ident<#type_use_gen>
|
||||
#config_where_clause
|
||||
{
|
||||
fn pezpallet_prefix() -> &'static str {
|
||||
<
|
||||
<T as #pezframe_system::Config>::PalletInfo
|
||||
as #pezframe_support::traits::PalletInfo
|
||||
>::name::<Pezpallet<#type_use_gen>>()
|
||||
.expect("No name found for the pezpallet in the runtime! This usually means that the pezpallet wasn't added to `construct_runtime!`.")
|
||||
}
|
||||
|
||||
fn pezpallet_prefix_hash() -> [u8; 16] {
|
||||
<
|
||||
<T as #pezframe_system::Config>::PalletInfo
|
||||
as #pezframe_support::traits::PalletInfo
|
||||
>::name_hash::<Pezpallet<#type_use_gen>>()
|
||||
.expect("No name_hash found for the pezpallet in the runtime! This usually means that the pezpallet wasn't added to `construct_runtime!`.")
|
||||
}
|
||||
|
||||
const STORAGE_PREFIX: &'static str = #counter_prefix_struct_const;
|
||||
fn storage_prefix_hash() -> [u8; 16] {
|
||||
#storage_prefix_hash
|
||||
}
|
||||
}
|
||||
#(#cfg_attrs)*
|
||||
impl<#type_impl_gen> #pezframe_support::storage::types::CountedStorageMapInstance
|
||||
for #prefix_struct_ident<#type_use_gen>
|
||||
#config_where_clause
|
||||
{
|
||||
type CounterPrefix = #counter_prefix_struct_ident<#type_use_gen>;
|
||||
}
|
||||
)
|
||||
},
|
||||
Metadata::CountedNMap { .. } => {
|
||||
let counter_prefix_struct_ident = counter_prefix_ident(&storage_def.ident);
|
||||
let counter_prefix_struct_const = counter_prefix(&prefix_struct_const);
|
||||
let storage_prefix_hash = two128_str(&counter_prefix_struct_const);
|
||||
quote::quote_spanned!(storage_def.attr_span =>
|
||||
#(#cfg_attrs)*
|
||||
#[doc(hidden)]
|
||||
#prefix_struct_vis struct #counter_prefix_struct_ident<#type_use_gen>(
|
||||
core::marker::PhantomData<(#type_use_gen,)>
|
||||
);
|
||||
#(#cfg_attrs)*
|
||||
impl<#type_impl_gen> #pezframe_support::traits::StorageInstance
|
||||
for #counter_prefix_struct_ident<#type_use_gen>
|
||||
#config_where_clause
|
||||
{
|
||||
fn pezpallet_prefix() -> &'static str {
|
||||
<
|
||||
<T as #pezframe_system::Config>::PalletInfo
|
||||
as #pezframe_support::traits::PalletInfo
|
||||
>::name::<Pezpallet<#type_use_gen>>()
|
||||
.expect("No name found for the pezpallet in the runtime! This usually means that the pezpallet wasn't added to `construct_runtime!`.")
|
||||
}
|
||||
fn pezpallet_prefix_hash() -> [u8; 16] {
|
||||
<
|
||||
<T as #pezframe_system::Config>::PalletInfo
|
||||
as #pezframe_support::traits::PalletInfo
|
||||
>::name_hash::<Pezpallet<#type_use_gen>>()
|
||||
.expect("No name_hash found for the pezpallet in the runtime! This usually means that the pezpallet wasn't added to `construct_runtime!`.")
|
||||
}
|
||||
const STORAGE_PREFIX: &'static str = #counter_prefix_struct_const;
|
||||
fn storage_prefix_hash() -> [u8; 16] {
|
||||
#storage_prefix_hash
|
||||
}
|
||||
}
|
||||
#(#cfg_attrs)*
|
||||
impl<#type_impl_gen> #pezframe_support::storage::types::CountedStorageNMapInstance
|
||||
for #prefix_struct_ident<#type_use_gen>
|
||||
#config_where_clause
|
||||
{
|
||||
type CounterPrefix = #counter_prefix_struct_ident<#type_use_gen>;
|
||||
}
|
||||
)
|
||||
},
|
||||
_ => proc_macro2::TokenStream::default(),
|
||||
};
|
||||
|
||||
let storage_prefix_hash = two128_str(&prefix_struct_const);
|
||||
quote::quote_spanned!(storage_def.attr_span =>
|
||||
#maybe_counter
|
||||
|
||||
#(#cfg_attrs)*
|
||||
#[doc(hidden)]
|
||||
#prefix_struct_vis struct #prefix_struct_ident<#type_use_gen>(
|
||||
core::marker::PhantomData<(#type_use_gen,)>
|
||||
);
|
||||
#(#cfg_attrs)*
|
||||
impl<#type_impl_gen> #pezframe_support::traits::StorageInstance
|
||||
for #prefix_struct_ident<#type_use_gen>
|
||||
#config_where_clause
|
||||
{
|
||||
fn pezpallet_prefix() -> &'static str {
|
||||
<
|
||||
<T as #pezframe_system::Config>::PalletInfo
|
||||
as #pezframe_support::traits::PalletInfo
|
||||
>::name::<Pezpallet<#type_use_gen>>()
|
||||
.expect("No name found for the pezpallet in the runtime! This usually means that the pezpallet wasn't added to `construct_runtime!`.")
|
||||
}
|
||||
|
||||
fn pezpallet_prefix_hash() -> [u8; 16] {
|
||||
<
|
||||
<T as #pezframe_system::Config>::PalletInfo
|
||||
as #pezframe_support::traits::PalletInfo
|
||||
>::name_hash::<Pezpallet<#type_use_gen>>()
|
||||
.expect("No name_hash found for the pezpallet in the runtime! This usually means that the pezpallet wasn't added to `construct_runtime!`.")
|
||||
}
|
||||
|
||||
const STORAGE_PREFIX: &'static str = #prefix_struct_const;
|
||||
fn storage_prefix_hash() -> [u8; 16] {
|
||||
#storage_prefix_hash
|
||||
}
|
||||
}
|
||||
)
|
||||
});
|
||||
|
||||
let on_empty_structs = on_empty_struct_metadata.into_iter().map(|metadata| {
|
||||
use crate::pezpallet::parse::GenericKind;
|
||||
use syn::{GenericArgument, Path, PathArguments, PathSegment, Type, TypePath};
|
||||
|
||||
let ResultOnEmptyStructMetadata {
|
||||
name,
|
||||
visibility,
|
||||
value_ty,
|
||||
error_path,
|
||||
variant_name,
|
||||
span,
|
||||
} = metadata;
|
||||
|
||||
let generic_kind = match error_path.segments.last() {
|
||||
Some(PathSegment { arguments: PathArguments::AngleBracketed(args), .. }) => {
|
||||
let (has_config, has_instance) =
|
||||
args.args.iter().fold((false, false), |(has_config, has_instance), arg| {
|
||||
match arg {
|
||||
GenericArgument::Type(Type::Path(TypePath {
|
||||
path: Path { segments, .. },
|
||||
..
|
||||
})) => {
|
||||
let maybe_config =
|
||||
segments.first().map_or(false, |seg| seg.ident == "T");
|
||||
let maybe_instance =
|
||||
segments.first().map_or(false, |seg| seg.ident == "I");
|
||||
|
||||
(has_config || maybe_config, has_instance || maybe_instance)
|
||||
},
|
||||
_ => (has_config, has_instance),
|
||||
}
|
||||
});
|
||||
GenericKind::from_gens(has_config, has_instance).unwrap_or(GenericKind::None)
|
||||
},
|
||||
_ => GenericKind::None,
|
||||
};
|
||||
let type_impl_gen = generic_kind.type_impl_gen(proc_macro2::Span::call_site());
|
||||
let config_where_clause = &def.config.where_clause;
|
||||
|
||||
quote::quote_spanned!(span =>
|
||||
#[doc(hidden)]
|
||||
#[allow(non_camel_case_types)]
|
||||
#visibility struct #name;
|
||||
|
||||
impl<#type_impl_gen> #pezframe_support::traits::Get<Result<#value_ty, #error_path>>
|
||||
for #name
|
||||
#config_where_clause
|
||||
{
|
||||
#[allow(deprecated)]
|
||||
fn get() -> Result<#value_ty, #error_path> {
|
||||
Err(<#error_path>::#variant_name)
|
||||
}
|
||||
}
|
||||
)
|
||||
});
|
||||
|
||||
// aggregated where clause of all storage types and the whole pezpallet.
|
||||
let mut where_clauses = vec![&def.config.where_clause];
|
||||
where_clauses.extend(def.storages.iter().map(|storage| &storage.where_clause));
|
||||
let completed_where_clause = super::merge_where_clauses(&where_clauses);
|
||||
let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site());
|
||||
let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site());
|
||||
|
||||
let try_decode_entire_state = {
|
||||
let mut storage_names = def
|
||||
.storages
|
||||
.iter()
|
||||
.filter_map(|storage| {
|
||||
// A little hacky; don't generate for cfg gated storages to not get compile errors
|
||||
// when building "frame-feature-testing" gated storages in the "pezframe-support-test"
|
||||
// crate.
|
||||
if storage.try_decode && storage.cfg_attrs.is_empty() {
|
||||
let ident = &storage.ident;
|
||||
let gen = &def.type_use_generics(storage.attr_span);
|
||||
Some(quote::quote_spanned!(storage.attr_span => #ident<#gen> ))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
storage_names.sort_by_cached_key(|ident| ident.to_string());
|
||||
|
||||
quote::quote!(
|
||||
#pezframe_support::try_runtime_enabled! {
|
||||
#[allow(deprecated)]
|
||||
impl<#type_impl_gen> #pezframe_support::traits::TryDecodeEntireStorage
|
||||
for #pezpallet_ident<#type_use_gen> #completed_where_clause
|
||||
{
|
||||
fn try_decode_entire_state() -> Result<usize, #pezframe_support::__private::Vec<#pezframe_support::traits::TryDecodeEntireStorageError>> {
|
||||
let pezpallet_name = <<T as #pezframe_system::Config>::PalletInfo as #pezframe_support::traits::PalletInfo>
|
||||
::name::<#pezpallet_ident<#type_use_gen>>()
|
||||
.expect("Every active pezpallet has a name in the runtime; qed");
|
||||
|
||||
#pezframe_support::__private::log::debug!(target: "runtime::try-decode-state", "trying to decode pezpallet: {pezpallet_name}");
|
||||
|
||||
// NOTE: for now, we have to exclude storage items that are feature gated.
|
||||
let mut errors = #pezframe_support::__private::Vec::new();
|
||||
let mut decoded = 0usize;
|
||||
|
||||
#(
|
||||
#pezframe_support::__private::log::debug!(target: "runtime::try-decode-state", "trying to decode storage: \
|
||||
{pezpallet_name}::{}", stringify!(#storage_names));
|
||||
|
||||
match <#storage_names as #pezframe_support::traits::TryDecodeEntireStorage>::try_decode_entire_state() {
|
||||
Ok(count) => {
|
||||
decoded += count;
|
||||
},
|
||||
Err(err) => {
|
||||
errors.extend(err);
|
||||
},
|
||||
}
|
||||
)*
|
||||
|
||||
if errors.is_empty() {
|
||||
Ok(decoded)
|
||||
} else {
|
||||
Err(errors)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
};
|
||||
|
||||
quote::quote!(
|
||||
impl<#type_impl_gen> #pezpallet_ident<#type_use_gen>
|
||||
#completed_where_clause
|
||||
{
|
||||
#[doc(hidden)]
|
||||
pub fn storage_metadata() -> #pezframe_support::__private::metadata_ir::PalletStorageMetadataIR {
|
||||
#pezframe_support::__private::metadata_ir::PalletStorageMetadataIR {
|
||||
prefix: <
|
||||
<T as #pezframe_system::Config>::PalletInfo as
|
||||
#pezframe_support::traits::PalletInfo
|
||||
>::name::<#pezpallet_ident<#type_use_gen>>()
|
||||
.expect("No name found for the pezpallet in the runtime! This usually means that the pezpallet wasn't added to `construct_runtime!`."),
|
||||
entries: {
|
||||
#[allow(unused_mut)]
|
||||
let mut entries = #pezframe_support::__private::vec![];
|
||||
#( #entries_builder(&mut entries); )*
|
||||
entries
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#( #getters )*
|
||||
#( #prefix_structs )*
|
||||
#( #on_empty_structs )*
|
||||
|
||||
#try_decode_entire_state
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
//! Contains logic for expanding task-related items.
|
||||
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Home of the expansion code for the Tasks API
|
||||
|
||||
use crate::pezpallet::{parse::tasks::*, Def};
|
||||
use inflector::Inflector;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote, ToTokens};
|
||||
use syn::{parse_quote_spanned, spanned::Spanned};
|
||||
|
||||
impl TaskEnumDef {
|
||||
/// Since we optionally allow users to manually specify a `#[pezpallet::task_enum]`, in the
|
||||
/// event they _don't_ specify one (which is actually the most common behavior) we have to
|
||||
/// generate one based on the existing [`TasksDef`]. This method performs that generation.
|
||||
pub fn generate(tasks: &TasksDef, def: &Def) -> Self {
|
||||
// We use the span of the attribute to indicate that the error comes from code generated
|
||||
// for the specific section, otherwise the item impl.
|
||||
let span = tasks
|
||||
.tasks_attr
|
||||
.as_ref()
|
||||
.map_or_else(|| tasks.item_impl.span(), |attr| attr.span());
|
||||
|
||||
let type_decl_bounded_generics = def.type_decl_bounded_generics(span);
|
||||
|
||||
let variants = if tasks.tasks_attr.is_some() {
|
||||
tasks
|
||||
.tasks
|
||||
.iter()
|
||||
.map(|task| {
|
||||
let ident = &task.item.sig.ident;
|
||||
let ident =
|
||||
format_ident!("{}", ident.to_string().to_class_case(), span = ident.span());
|
||||
|
||||
let args = task.item.sig.inputs.iter().collect::<Vec<_>>();
|
||||
|
||||
if args.is_empty() {
|
||||
quote!(#ident)
|
||||
} else {
|
||||
quote!(#ident {
|
||||
#(#args),*
|
||||
})
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
parse_quote_spanned! { span =>
|
||||
/// Auto-generated enum that encapsulates all tasks defined by this pezpallet.
|
||||
///
|
||||
/// Conceptually similar to the [`Call`] enum, but for tasks. This is only
|
||||
/// generated if there are tasks present in this pezpallet.
|
||||
#[pezpallet::task_enum]
|
||||
pub enum Task<#type_decl_bounded_generics> {
|
||||
#(
|
||||
#variants,
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TaskEnumDef {
|
||||
fn expand_to_tokens(&self, def: &Def) -> TokenStream2 {
|
||||
if let Some(attr) = &self.attr {
|
||||
let ident = &self.item_enum.ident;
|
||||
let vis = &self.item_enum.vis;
|
||||
let attrs = &self.item_enum.attrs;
|
||||
let generics = &self.item_enum.generics;
|
||||
let variants = &self.item_enum.variants;
|
||||
let pezframe_support = &def.pezframe_support;
|
||||
let type_use_generics = &def.type_use_generics(attr.span());
|
||||
let type_impl_generics = &def.type_impl_generics(attr.span());
|
||||
|
||||
// `item_enum` is short-hand / generated enum
|
||||
quote! {
|
||||
#(#attrs)*
|
||||
#[derive(
|
||||
#pezframe_support::CloneNoBound,
|
||||
#pezframe_support::EqNoBound,
|
||||
#pezframe_support::PartialEqNoBound,
|
||||
#pezframe_support::pezpallet_prelude::Encode,
|
||||
#pezframe_support::pezpallet_prelude::Decode,
|
||||
#pezframe_support::pezpallet_prelude::DecodeWithMemTracking,
|
||||
#pezframe_support::pezpallet_prelude::TypeInfo,
|
||||
)]
|
||||
#[codec(encode_bound())]
|
||||
#[codec(decode_bound())]
|
||||
#[scale_info(skip_type_params(#type_use_generics))]
|
||||
#vis enum #ident #generics {
|
||||
#variants
|
||||
#[doc(hidden)]
|
||||
#[codec(skip)]
|
||||
__Ignore(core::marker::PhantomData<(#type_use_generics)>, #pezframe_support::Never),
|
||||
}
|
||||
|
||||
impl<#type_impl_generics> core::fmt::Debug for #ident<#type_use_generics> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct(stringify!(#ident)).field("value", self).finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// `item_enum` is a manually specified enum (no attribute)
|
||||
self.item_enum.to_token_stream()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TasksDef {
|
||||
fn expand_to_tokens(&self, def: &Def) -> TokenStream2 {
|
||||
let pezframe_support = &def.pezframe_support;
|
||||
let enum_ident = syn::Ident::new("Task", self.enum_ident.span());
|
||||
let enum_arguments = &self.enum_arguments;
|
||||
let enum_use = quote!(#enum_ident #enum_arguments);
|
||||
|
||||
let task_fn_idents = self
|
||||
.tasks
|
||||
.iter()
|
||||
.map(|task| {
|
||||
format_ident!(
|
||||
"{}",
|
||||
&task.item.sig.ident.to_string().to_class_case(),
|
||||
span = task.item.sig.ident.span()
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let task_indices = self.tasks.iter().map(|task| &task.index_attr.meta.index);
|
||||
let task_conditions = self.tasks.iter().map(|task| &task.condition_attr.meta.expr);
|
||||
let task_weights = self.tasks.iter().map(|task| &task.weight_attr.meta.expr);
|
||||
let task_iters = self.tasks.iter().map(|task| &task.list_attr.meta.expr);
|
||||
|
||||
let task_fn_impls = self.tasks.iter().map(|task| {
|
||||
let mut task_fn_impl = task.item.clone();
|
||||
task_fn_impl.attrs = vec![];
|
||||
task_fn_impl
|
||||
});
|
||||
|
||||
let task_fn_names = self.tasks.iter().map(|task| &task.item.sig.ident);
|
||||
let task_arg_names = self.tasks.iter().map(|task| &task.arg_names).collect::<Vec<_>>();
|
||||
|
||||
let impl_generics = &self.item_impl.generics;
|
||||
quote! {
|
||||
impl #impl_generics #enum_use
|
||||
{
|
||||
#(#task_fn_impls)*
|
||||
}
|
||||
|
||||
impl #impl_generics #pezframe_support::traits::Task for #enum_use
|
||||
{
|
||||
type Enumeration = #pezframe_support::__private::IntoIter<#enum_use>;
|
||||
|
||||
fn iter() -> Self::Enumeration {
|
||||
let mut all_tasks = #pezframe_support::__private::vec![];
|
||||
#(all_tasks
|
||||
.extend(#task_iters.map(|(#(#task_arg_names),*)| #enum_ident::#task_fn_idents { #(#task_arg_names: #task_arg_names.clone()),* })
|
||||
.collect::<#pezframe_support::__private::Vec<_>>());
|
||||
)*
|
||||
all_tasks.into_iter()
|
||||
}
|
||||
|
||||
fn task_index(&self) -> u32 {
|
||||
match self.clone() {
|
||||
#(#enum_ident::#task_fn_idents { .. } => #task_indices,)*
|
||||
Task::__Ignore(_, _) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_valid(&self) -> bool {
|
||||
match self.clone() {
|
||||
#(#enum_ident::#task_fn_idents { #(#task_arg_names),* } => (#task_conditions)(#(#task_arg_names),* ),)*
|
||||
Task::__Ignore(_, _) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn run(&self) -> Result<(), #pezframe_support::pezpallet_prelude::DispatchError> {
|
||||
match self.clone() {
|
||||
#(#enum_ident::#task_fn_idents { #(#task_arg_names),* } => {
|
||||
<#enum_use>::#task_fn_names(#( #task_arg_names, )* )
|
||||
},)*
|
||||
Task::__Ignore(_, _) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn weight(&self) -> #pezframe_support::pezpallet_prelude::Weight {
|
||||
match self.clone() {
|
||||
#(#enum_ident::#task_fn_idents { #(#task_arg_names),* } => #task_weights,)*
|
||||
Task::__Ignore(_, _) => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate code related to tasks.
|
||||
pub fn expand_tasks(def: &Def) -> TokenStream2 {
|
||||
let Some(tasks_def) = &def.tasks else {
|
||||
return quote!();
|
||||
};
|
||||
|
||||
let default_task_enum = TaskEnumDef::generate(&tasks_def, def);
|
||||
|
||||
let task_enum = def.task_enum.as_ref().unwrap_or_else(|| &default_task_enum);
|
||||
|
||||
let tasks_expansion = tasks_def.expand_to_tokens(def);
|
||||
let task_enum_expansion = task_enum.expand_to_tokens(def);
|
||||
|
||||
quote! {
|
||||
#tasks_expansion
|
||||
#task_enum_expansion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::{
|
||||
pezpallet::{CompositeKeyword, Def},
|
||||
COUNTER,
|
||||
};
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
/// Generate the `tt_default_parts` macro.
|
||||
pub fn expand_tt_default_parts(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
let count = COUNTER.with(|counter| counter.borrow_mut().inc());
|
||||
let default_parts_unique_id =
|
||||
syn::Ident::new(&format!("__tt_default_parts_{}", count), def.item.span());
|
||||
let extra_parts_unique_id =
|
||||
syn::Ident::new(&format!("__tt_extra_parts_{}", count), def.item.span());
|
||||
let default_parts_unique_id_v2 =
|
||||
syn::Ident::new(&format!("__tt_default_parts_v2_{}", count), def.item.span());
|
||||
|
||||
let call_part = def.call.as_ref().map(|_| quote::quote!(Call,));
|
||||
|
||||
let task_part = def.tasks.as_ref().map(|_| quote::quote!(Task,));
|
||||
|
||||
let storage_part = (!def.storages.is_empty()).then(|| quote::quote!(Storage,));
|
||||
|
||||
let event_part = def.event.as_ref().map(|event| {
|
||||
let gen = event.gen_kind.is_generic().then(|| quote::quote!( <T> ));
|
||||
quote::quote!( Event #gen , )
|
||||
});
|
||||
|
||||
let error_part = def.error.as_ref().map(|_| quote::quote!(Error<T>,));
|
||||
|
||||
let origin_part = def.origin.as_ref().map(|origin| {
|
||||
let gen = origin.is_generic.then(|| quote::quote!( <T> ));
|
||||
quote::quote!( Origin #gen , )
|
||||
});
|
||||
|
||||
let config_part = def.genesis_config.as_ref().map(|genesis_config| {
|
||||
let gen = genesis_config.gen_kind.is_generic().then(|| quote::quote!( <T> ));
|
||||
quote::quote!( Config #gen , )
|
||||
});
|
||||
|
||||
let inherent_part = def.inherent.as_ref().map(|_| quote::quote!(Inherent,));
|
||||
|
||||
let validate_unsigned_part =
|
||||
def.validate_unsigned.as_ref().map(|_| quote::quote!(ValidateUnsigned,));
|
||||
|
||||
let freeze_reason_part = def
|
||||
.composites
|
||||
.iter()
|
||||
.any(|c| matches!(c.composite_keyword, CompositeKeyword::FreezeReason(_)))
|
||||
.then_some(quote::quote!(FreezeReason,));
|
||||
|
||||
let hold_reason_part = def
|
||||
.composites
|
||||
.iter()
|
||||
.any(|c| matches!(c.composite_keyword, CompositeKeyword::HoldReason(_)))
|
||||
.then_some(quote::quote!(HoldReason,));
|
||||
|
||||
let lock_id_part = def
|
||||
.composites
|
||||
.iter()
|
||||
.any(|c| matches!(c.composite_keyword, CompositeKeyword::LockId(_)))
|
||||
.then_some(quote::quote!(LockId,));
|
||||
|
||||
let slash_reason_part = def
|
||||
.composites
|
||||
.iter()
|
||||
.any(|c| matches!(c.composite_keyword, CompositeKeyword::SlashReason(_)))
|
||||
.then_some(quote::quote!(SlashReason,));
|
||||
|
||||
let call_part_v2 = def.call.as_ref().map(|_| quote::quote!(+ Call));
|
||||
|
||||
let task_part_v2 = def.tasks.as_ref().map(|_| quote::quote!(+ Task));
|
||||
|
||||
let storage_part_v2 = (!def.storages.is_empty()).then(|| quote::quote!(+ Storage));
|
||||
|
||||
let event_part_v2 = def.event.as_ref().map(|event| {
|
||||
let gen = event.gen_kind.is_generic().then(|| quote::quote!(<T>));
|
||||
quote::quote!(+ Event #gen)
|
||||
});
|
||||
|
||||
let error_part_v2 = def.error.as_ref().map(|_| quote::quote!(+ Error<T>));
|
||||
|
||||
let origin_part_v2 = def.origin.as_ref().map(|origin| {
|
||||
let gen = origin.is_generic.then(|| quote::quote!(<T>));
|
||||
quote::quote!(+ Origin #gen)
|
||||
});
|
||||
|
||||
let config_part_v2 = def.genesis_config.as_ref().map(|genesis_config| {
|
||||
let gen = genesis_config.gen_kind.is_generic().then(|| quote::quote!(<T>));
|
||||
quote::quote!(+ Config #gen)
|
||||
});
|
||||
|
||||
let inherent_part_v2 = def.inherent.as_ref().map(|_| quote::quote!(+ Inherent));
|
||||
|
||||
let validate_unsigned_part_v2 =
|
||||
def.validate_unsigned.as_ref().map(|_| quote::quote!(+ ValidateUnsigned));
|
||||
|
||||
let freeze_reason_part_v2 = def
|
||||
.composites
|
||||
.iter()
|
||||
.any(|c| matches!(c.composite_keyword, CompositeKeyword::FreezeReason(_)))
|
||||
.then_some(quote::quote!(+ FreezeReason));
|
||||
|
||||
let hold_reason_part_v2 = def
|
||||
.composites
|
||||
.iter()
|
||||
.any(|c| matches!(c.composite_keyword, CompositeKeyword::HoldReason(_)))
|
||||
.then_some(quote::quote!(+ HoldReason));
|
||||
|
||||
let lock_id_part_v2 = def
|
||||
.composites
|
||||
.iter()
|
||||
.any(|c| matches!(c.composite_keyword, CompositeKeyword::LockId(_)))
|
||||
.then_some(quote::quote!(+ LockId));
|
||||
|
||||
let slash_reason_part_v2 = def
|
||||
.composites
|
||||
.iter()
|
||||
.any(|c| matches!(c.composite_keyword, CompositeKeyword::SlashReason(_)))
|
||||
.then_some(quote::quote!(+ SlashReason));
|
||||
|
||||
quote::quote!(
|
||||
// This macro follows the conventions as laid out by the `tt-call` crate. It does not
|
||||
// accept any arguments and simply returns the pezpallet parts, separated by commas, then
|
||||
// wrapped inside of braces and finally prepended with double colons, to the caller inside
|
||||
// of a key named `tokens`.
|
||||
//
|
||||
// We need to accept a path argument here, because this macro gets expanded on the
|
||||
// crate that called the `construct_runtime!` macro, and the actual path is unknown.
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! #default_parts_unique_id {
|
||||
{
|
||||
$caller:tt
|
||||
your_tt_return = [{ $my_tt_return:path }]
|
||||
} => {
|
||||
$my_tt_return! {
|
||||
$caller
|
||||
tokens = [{
|
||||
expanded::{
|
||||
Pezpallet, #call_part #storage_part #event_part #error_part #origin_part #config_part
|
||||
#inherent_part #validate_unsigned_part #freeze_reason_part #task_part
|
||||
#hold_reason_part #lock_id_part #slash_reason_part
|
||||
}
|
||||
}]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub use #default_parts_unique_id as tt_default_parts;
|
||||
|
||||
|
||||
// This macro is similar to the `tt_default_parts!`. It expands the pallets that are declared
|
||||
// explicitly (`System: pezframe_system::{Pezpallet, Call}`) with extra parts.
|
||||
//
|
||||
// For example, after expansion an explicit pezpallet would look like:
|
||||
// `System: expanded::{Error} ::{Pezpallet, Call}`.
|
||||
//
|
||||
// The `expanded` keyword is a marker of the final state of the `construct_runtime!`.
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! #extra_parts_unique_id {
|
||||
{
|
||||
$caller:tt
|
||||
your_tt_return = [{ $my_tt_return:path }]
|
||||
} => {
|
||||
$my_tt_return! {
|
||||
$caller
|
||||
tokens = [{
|
||||
expanded::{
|
||||
#error_part
|
||||
}
|
||||
}]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub use #extra_parts_unique_id as tt_extra_parts;
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! #default_parts_unique_id_v2 {
|
||||
{
|
||||
$caller:tt
|
||||
your_tt_return = [{ $my_tt_return:path }]
|
||||
} => {
|
||||
$my_tt_return! {
|
||||
$caller
|
||||
tokens = [{
|
||||
+ Pezpallet #call_part_v2 #storage_part_v2 #event_part_v2 #error_part_v2 #origin_part_v2 #config_part_v2
|
||||
#inherent_part_v2 #validate_unsigned_part_v2 #freeze_reason_part_v2 #task_part_v2
|
||||
#hold_reason_part_v2 #lock_id_part_v2 #slash_reason_part_v2
|
||||
}]
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub use #default_parts_unique_id_v2 as tt_default_parts_v2;
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::pezpallet::Def;
|
||||
|
||||
///
|
||||
/// * Generate the struct
|
||||
/// * implement the `Get<..>` on it
|
||||
/// * Rename the name of the function to internal name
|
||||
pub fn expand_type_values(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
let mut expand = quote::quote!();
|
||||
let pezframe_support = &def.pezframe_support;
|
||||
|
||||
for type_value in &def.type_values {
|
||||
let fn_name_str = &type_value.ident.to_string();
|
||||
let fn_name_snakecase = inflector::cases::snakecase::to_snake_case(fn_name_str);
|
||||
let fn_ident_renamed = syn::Ident::new(
|
||||
&format!("__type_value_for_{}", fn_name_snakecase),
|
||||
type_value.ident.span(),
|
||||
);
|
||||
|
||||
let type_value_item = {
|
||||
let item = &mut def.item.content.as_mut().expect("Checked by def").1[type_value.index];
|
||||
if let syn::Item::Fn(item) = item {
|
||||
item
|
||||
} else {
|
||||
unreachable!("Checked by error parser")
|
||||
}
|
||||
};
|
||||
|
||||
// Rename the type_value function name
|
||||
type_value_item.sig.ident = fn_ident_renamed.clone();
|
||||
|
||||
let vis = &type_value.vis;
|
||||
let ident = &type_value.ident;
|
||||
let type_ = &type_value.type_;
|
||||
let where_clause = &type_value.where_clause;
|
||||
|
||||
let (struct_impl_gen, struct_use_gen) = if type_value.is_generic {
|
||||
(
|
||||
def.type_impl_generics(type_value.attr_span),
|
||||
def.type_use_generics(type_value.attr_span),
|
||||
)
|
||||
} else {
|
||||
(Default::default(), Default::default())
|
||||
};
|
||||
|
||||
let docs = &type_value.docs;
|
||||
|
||||
expand.extend(quote::quote_spanned!(type_value.attr_span =>
|
||||
#( #[doc = #docs] )*
|
||||
#vis struct #ident<#struct_use_gen>(core::marker::PhantomData<((), #struct_use_gen)>);
|
||||
impl<#struct_impl_gen> #pezframe_support::traits::Get<#type_> for #ident<#struct_use_gen>
|
||||
#where_clause
|
||||
{
|
||||
fn get() -> #type_ {
|
||||
#fn_ident_renamed::<#struct_use_gen>()
|
||||
}
|
||||
}
|
||||
));
|
||||
}
|
||||
expand
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::{pezpallet::Def, COUNTER};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{spanned::Spanned, Ident};
|
||||
|
||||
pub fn expand_validate_unsigned(def: &mut Def) -> TokenStream {
|
||||
let count = COUNTER.with(|counter| counter.borrow_mut().inc());
|
||||
let macro_ident =
|
||||
Ident::new(&format!("__is_validate_unsigned_part_defined_{}", count), def.item.span());
|
||||
|
||||
let maybe_compile_error = if def.validate_unsigned.is_none() {
|
||||
quote! {
|
||||
compile_error!(concat!(
|
||||
"`",
|
||||
stringify!($pezpallet_name),
|
||||
"` does not have #[pezpallet::validate_unsigned] defined, perhaps you should \
|
||||
remove `ValidateUnsigned` from construct_runtime?",
|
||||
));
|
||||
}
|
||||
} else {
|
||||
TokenStream::new()
|
||||
};
|
||||
|
||||
quote! {
|
||||
#[doc(hidden)]
|
||||
pub mod __bizinikiwi_validate_unsigned_check {
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! #macro_ident {
|
||||
($pezpallet_name:ident) => {
|
||||
#maybe_compile_error
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use #macro_ident as is_validate_unsigned_part_defined;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::pezpallet::{parse::view_functions::ViewFunctionDef, Def};
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
pub fn expand_view_functions(def: &Def) -> TokenStream {
|
||||
let (span, where_clause, view_fns) = match def.view_functions.as_ref() {
|
||||
Some(view_fns) =>
|
||||
(view_fns.attr_span, view_fns.where_clause.clone(), view_fns.view_functions.clone()),
|
||||
None => (def.item.span(), def.config.where_clause.clone(), Vec::new()),
|
||||
};
|
||||
|
||||
let view_function_prefix_impl =
|
||||
expand_view_function_prefix_impl(def, span, where_clause.as_ref());
|
||||
|
||||
let view_fn_impls = view_fns
|
||||
.iter()
|
||||
.map(|view_fn| expand_view_function(def, span, where_clause.as_ref(), view_fn));
|
||||
let impl_dispatch_view_function =
|
||||
impl_dispatch_view_function(def, span, where_clause.as_ref(), &view_fns);
|
||||
let impl_view_function_metadata =
|
||||
impl_view_function_metadata(def, span, where_clause.as_ref(), &view_fns);
|
||||
|
||||
quote::quote! {
|
||||
#view_function_prefix_impl
|
||||
#( #view_fn_impls )*
|
||||
#impl_dispatch_view_function
|
||||
#impl_view_function_metadata
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_view_function_prefix_impl(
|
||||
def: &Def,
|
||||
span: Span,
|
||||
where_clause: Option<&syn::WhereClause>,
|
||||
) -> TokenStream {
|
||||
let pezpallet_ident = &def.pezpallet_struct.pezpallet;
|
||||
let pezframe_support = &def.pezframe_support;
|
||||
let pezframe_system = &def.pezframe_system;
|
||||
let type_impl_gen = &def.type_impl_generics(span);
|
||||
let type_use_gen = &def.type_use_generics(span);
|
||||
|
||||
quote::quote! {
|
||||
impl<#type_impl_gen> #pezframe_support::view_functions::ViewFunctionIdPrefix for #pezpallet_ident<#type_use_gen> #where_clause {
|
||||
fn prefix() -> [::core::primitive::u8; 16usize] {
|
||||
<
|
||||
<T as #pezframe_system::Config>::PalletInfo
|
||||
as #pezframe_support::traits::PalletInfo
|
||||
>::name_hash::<Pezpallet<#type_use_gen>>()
|
||||
.expect("No name_hash found for the pezpallet in the runtime! This usually means that the pezpallet wasn't added to `construct_runtime!`.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_view_function(
|
||||
def: &Def,
|
||||
span: Span,
|
||||
where_clause: Option<&syn::WhereClause>,
|
||||
view_fn: &ViewFunctionDef,
|
||||
) -> TokenStream {
|
||||
let pezframe_support = &def.pezframe_support;
|
||||
let pezpallet_ident = &def.pezpallet_struct.pezpallet;
|
||||
let type_impl_gen = &def.type_impl_generics(span);
|
||||
let type_decl_bounded_gen = &def.type_decl_bounded_generics(span);
|
||||
let type_use_gen = &def.type_use_generics(span);
|
||||
let capture_docs = if cfg!(feature = "no-metadata-docs") { "never" } else { "always" };
|
||||
|
||||
let view_function_struct_ident = view_fn.view_function_struct_ident();
|
||||
let view_fn_name = &view_fn.name;
|
||||
let (arg_names, arg_types) = match view_fn.args_names_types() {
|
||||
Ok((arg_names, arg_types)) => (arg_names, arg_types),
|
||||
Err(e) => return e.into_compile_error(),
|
||||
};
|
||||
let return_type = &view_fn.return_type;
|
||||
let docs = &view_fn.docs;
|
||||
|
||||
let view_function_id_suffix_bytes_raw = match view_fn.view_function_id_suffix_bytes() {
|
||||
Ok(view_function_id_suffix_bytes_raw) => view_function_id_suffix_bytes_raw,
|
||||
Err(e) => return e.into_compile_error(),
|
||||
};
|
||||
let view_function_id_suffix_bytes = view_function_id_suffix_bytes_raw
|
||||
.map(|byte| syn::LitInt::new(&format!("0x{:X}_u8", byte), Span::call_site()));
|
||||
|
||||
quote::quote! {
|
||||
#( #[doc = #docs] )*
|
||||
#[allow(missing_docs)]
|
||||
#[derive(
|
||||
#pezframe_support::RuntimeDebugNoBound,
|
||||
#pezframe_support::CloneNoBound,
|
||||
#pezframe_support::EqNoBound,
|
||||
#pezframe_support::PartialEqNoBound,
|
||||
#pezframe_support::__private::codec::Encode,
|
||||
#pezframe_support::__private::codec::Decode,
|
||||
#pezframe_support::__private::codec::DecodeWithMemTracking,
|
||||
#pezframe_support::__private::scale_info::TypeInfo,
|
||||
)]
|
||||
#[codec(encode_bound())]
|
||||
#[codec(decode_bound())]
|
||||
#[scale_info(skip_type_params(#type_use_gen), capture_docs = #capture_docs)]
|
||||
pub struct #view_function_struct_ident<#type_decl_bounded_gen> #where_clause {
|
||||
#(
|
||||
pub #arg_names: #arg_types,
|
||||
)*
|
||||
_marker: ::core::marker::PhantomData<(#type_use_gen,)>,
|
||||
}
|
||||
|
||||
impl<#type_impl_gen> #view_function_struct_ident<#type_use_gen> #where_clause {
|
||||
/// Create a new [`#view_function_struct_ident`] instance.
|
||||
pub fn new(#( #arg_names: #arg_types, )*) -> Self {
|
||||
Self {
|
||||
#( #arg_names, )*
|
||||
_marker: ::core::default::Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<#type_impl_gen> #pezframe_support::view_functions::ViewFunctionIdSuffix for #view_function_struct_ident<#type_use_gen> #where_clause {
|
||||
const SUFFIX: [::core::primitive::u8; 16usize] = [ #( #view_function_id_suffix_bytes ),* ];
|
||||
}
|
||||
|
||||
impl<#type_impl_gen> #pezframe_support::view_functions::ViewFunction for #view_function_struct_ident<#type_use_gen> #where_clause {
|
||||
fn id() -> #pezframe_support::view_functions::ViewFunctionId {
|
||||
#pezframe_support::view_functions::ViewFunctionId {
|
||||
prefix: <#pezpallet_ident<#type_use_gen> as #pezframe_support::view_functions::ViewFunctionIdPrefix>::prefix(),
|
||||
suffix: <Self as #pezframe_support::view_functions::ViewFunctionIdSuffix>::SUFFIX,
|
||||
}
|
||||
}
|
||||
|
||||
type ReturnType = #return_type;
|
||||
|
||||
fn invoke(self) -> Self::ReturnType {
|
||||
let Self { #( #arg_names, )* _marker } = self;
|
||||
#pezpallet_ident::<#type_use_gen> :: #view_fn_name( #( #arg_names, )* )
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn impl_dispatch_view_function(
|
||||
def: &Def,
|
||||
span: Span,
|
||||
where_clause: Option<&syn::WhereClause>,
|
||||
view_fns: &[ViewFunctionDef],
|
||||
) -> TokenStream {
|
||||
let pezframe_support = &def.pezframe_support;
|
||||
let pezpallet_ident = &def.pezpallet_struct.pezpallet;
|
||||
let type_impl_gen = &def.type_impl_generics(span);
|
||||
let type_use_gen = &def.type_use_generics(span);
|
||||
|
||||
let query_match_arms = view_fns.iter().map(|view_fn| {
|
||||
let view_function_struct_ident = view_fn.view_function_struct_ident();
|
||||
quote::quote! {
|
||||
<#view_function_struct_ident<#type_use_gen> as #pezframe_support::view_functions::ViewFunctionIdSuffix>::SUFFIX => {
|
||||
<#view_function_struct_ident<#type_use_gen> as #pezframe_support::view_functions::ViewFunction>::execute(input, output)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
quote::quote! {
|
||||
impl<#type_impl_gen> #pezframe_support::view_functions::DispatchViewFunction
|
||||
for #pezpallet_ident<#type_use_gen> #where_clause
|
||||
{
|
||||
#[deny(unreachable_patterns)]
|
||||
fn dispatch_view_function<O: #pezframe_support::__private::codec::Output>(
|
||||
id: & #pezframe_support::view_functions::ViewFunctionId,
|
||||
input: &mut &[u8],
|
||||
output: &mut O
|
||||
) -> Result<(), #pezframe_support::view_functions::ViewFunctionDispatchError>
|
||||
{
|
||||
match id.suffix {
|
||||
#( #query_match_arms )*
|
||||
_ => Err(#pezframe_support::view_functions::ViewFunctionDispatchError::NotFound(id.clone())),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn impl_view_function_metadata(
|
||||
def: &Def,
|
||||
span: Span,
|
||||
where_clause: Option<&syn::WhereClause>,
|
||||
view_fns: &[ViewFunctionDef],
|
||||
) -> TokenStream {
|
||||
let pezframe_support = &def.pezframe_support;
|
||||
let pezpallet_ident = &def.pezpallet_struct.pezpallet;
|
||||
let type_impl_gen = &def.type_impl_generics(span);
|
||||
let type_use_gen = &def.type_use_generics(span);
|
||||
|
||||
let view_functions = view_fns.iter().map(|view_fn| {
|
||||
let view_function_struct_ident = view_fn.view_function_struct_ident();
|
||||
let name = &view_fn.name;
|
||||
let inputs = view_fn.args.iter().filter_map(|fn_arg| {
|
||||
match fn_arg {
|
||||
syn::FnArg::Receiver(_) => None,
|
||||
syn::FnArg::Typed(typed) => {
|
||||
let pat = &typed.pat;
|
||||
let ty = &typed.ty;
|
||||
Some(quote::quote! {
|
||||
#pezframe_support::__private::metadata_ir::PalletViewFunctionParamMetadataIR {
|
||||
name: ::core::stringify!(#pat),
|
||||
ty: #pezframe_support::__private::scale_info::meta_type::<#ty>(),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let no_docs = vec![];
|
||||
let doc = if cfg!(feature = "no-metadata-docs") { &no_docs } else { &view_fn.docs };
|
||||
|
||||
let deprecation = match crate::deprecation::get_deprecation(
|
||||
"e::quote! { #pezframe_support },
|
||||
&def.item.attrs,
|
||||
) {
|
||||
Ok(deprecation) => deprecation,
|
||||
Err(e) => return e.into_compile_error(),
|
||||
};
|
||||
|
||||
quote::quote! {
|
||||
#pezframe_support::__private::metadata_ir::PalletViewFunctionMetadataIR {
|
||||
name: ::core::stringify!(#name),
|
||||
id: <#view_function_struct_ident<#type_use_gen> as #pezframe_support::view_functions::ViewFunction>::id().into(),
|
||||
inputs: #pezframe_support::__private::pezsp_std::vec![ #( #inputs ),* ],
|
||||
output: #pezframe_support::__private::scale_info::meta_type::<
|
||||
<#view_function_struct_ident<#type_use_gen> as #pezframe_support::view_functions::ViewFunction>::ReturnType
|
||||
>(),
|
||||
docs: #pezframe_support::__private::pezsp_std::vec![ #( #doc ),* ],
|
||||
deprecation_info: #deprecation,
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
quote::quote! {
|
||||
impl<#type_impl_gen> #pezpallet_ident<#type_use_gen> #where_clause {
|
||||
#[doc(hidden)]
|
||||
pub fn pezpallet_view_functions_metadata()
|
||||
-> #pezframe_support::__private::Vec<#pezframe_support::__private::metadata_ir::PalletViewFunctionMetadataIR> {
|
||||
#pezframe_support::__private::vec![ #( #view_functions ),* ]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Generates warnings for undesirable pezpallet code.
|
||||
|
||||
use crate::pezpallet::parse::call::{CallVariantDef, CallWeightDef};
|
||||
use proc_macro_warning::Warning;
|
||||
use syn::{
|
||||
spanned::Spanned,
|
||||
visit::{self, Visit},
|
||||
};
|
||||
|
||||
/// Warn if any of the call arguments starts with a underscore and is used in a weight formula.
|
||||
pub(crate) fn weight_witness_warning(
|
||||
method: &CallVariantDef,
|
||||
dev_mode: bool,
|
||||
warnings: &mut Vec<Warning>,
|
||||
) {
|
||||
if dev_mode {
|
||||
return;
|
||||
}
|
||||
let CallWeightDef::Immediate(w) = &method.weight else { return };
|
||||
|
||||
let partial_warning = Warning::new_deprecated("UncheckedWeightWitness")
|
||||
.old("not check weight witness data")
|
||||
.new("ensure that all witness data for weight calculation is checked before usage")
|
||||
.help_link("https://github.com/pezkuwichain/kurdistan-sdk/issues/108");
|
||||
|
||||
for (_, arg_ident, _) in method.args.iter() {
|
||||
if !arg_ident.to_string().starts_with('_') || !contains_ident(w.clone(), &arg_ident) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let warning = partial_warning
|
||||
.clone()
|
||||
.index(warnings.len())
|
||||
.span(arg_ident.span())
|
||||
.build_or_panic();
|
||||
|
||||
warnings.push(warning);
|
||||
}
|
||||
}
|
||||
|
||||
/// Warn if the weight is a constant and the pezpallet not in `dev_mode`.
|
||||
pub(crate) fn weight_constant_warning(
|
||||
weight: &syn::Expr,
|
||||
dev_mode: bool,
|
||||
warnings: &mut Vec<Warning>,
|
||||
) {
|
||||
if dev_mode {
|
||||
return;
|
||||
}
|
||||
let syn::Expr::Lit(lit) = weight else { return };
|
||||
|
||||
let warning = Warning::new_deprecated("ConstantWeight")
|
||||
.index(warnings.len())
|
||||
.old("use hard-coded constant as call weight")
|
||||
.new("benchmark all calls or put the pezpallet into `dev` mode")
|
||||
.help_link("https://github.com/pezkuwichain/kurdistan-sdk/issues/48")
|
||||
.span(lit.span())
|
||||
.build_or_panic();
|
||||
|
||||
warnings.push(warning);
|
||||
}
|
||||
|
||||
/// Returns whether `expr` contains `ident`.
|
||||
fn contains_ident(mut expr: syn::Expr, ident: &syn::Ident) -> bool {
|
||||
struct ContainsIdent {
|
||||
ident: syn::Ident,
|
||||
found: bool,
|
||||
}
|
||||
|
||||
impl<'a> Visit<'a> for ContainsIdent {
|
||||
fn visit_ident(&mut self, i: &syn::Ident) {
|
||||
if *i == self.ident {
|
||||
self.found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut visitor = ContainsIdent { ident: ident.clone(), found: false };
|
||||
visit::visit_expr(&mut visitor, &mut expr);
|
||||
visitor.found
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Implementation for pezpallet attribute macro.
|
||||
//!
|
||||
//! General workflow:
|
||||
//! 1 - parse all pezpallet attributes:
|
||||
//! This step remove all attributes `#[pezpallet::*]` from the ItemMod and build the `Def` struct
|
||||
//! which holds the ItemMod without `#[pezpallet::*]` and information given by those attributes
|
||||
//! 2 - expand from the parsed information
|
||||
//! This step will modify the ItemMod by adding some derive attributes or phantom data variants
|
||||
//! to user defined types. And also crate new types and implement block.
|
||||
|
||||
mod expand;
|
||||
pub(crate) mod parse;
|
||||
|
||||
pub use parse::{composite::keyword::CompositeKeyword, Def};
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
mod keyword {
|
||||
syn::custom_keyword!(dev_mode);
|
||||
}
|
||||
|
||||
pub fn pezpallet(
|
||||
attr: proc_macro::TokenStream,
|
||||
item: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
let mut dev_mode = false;
|
||||
if !attr.is_empty() {
|
||||
if let Ok(_) = syn::parse::<keyword::dev_mode>(attr.clone()) {
|
||||
dev_mode = true;
|
||||
} else {
|
||||
let msg = "Invalid pezpallet macro call: unexpected attribute. Macro call must be \
|
||||
bare, such as `#[pezframe_support::pezpallet]` or `#[pezpallet]`, or must specify the \
|
||||
`dev_mode` attribute, such as `#[pezframe_support::pezpallet(dev_mode)]` or \
|
||||
#[pezpallet(dev_mode)].";
|
||||
let span = proc_macro2::TokenStream::from(attr).span();
|
||||
return syn::Error::new(span, msg).to_compile_error().into();
|
||||
}
|
||||
}
|
||||
|
||||
let item = syn::parse_macro_input!(item as syn::ItemMod);
|
||||
match parse::Def::try_from(item, dev_mode) {
|
||||
Ok(def) => expand::expand(def).into(),
|
||||
Err(e) => e.to_compile_error().into(),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,537 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::{helper, InheritedCallWeightAttr};
|
||||
use pezframe_support_procedural_tools::get_doc_literals;
|
||||
use proc_macro2::Span;
|
||||
use quote::ToTokens;
|
||||
use std::collections::HashMap;
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
/// List of additional token to be used for parsing.
|
||||
mod keyword {
|
||||
syn::custom_keyword!(Call);
|
||||
syn::custom_keyword!(OriginFor);
|
||||
syn::custom_keyword!(RuntimeOrigin);
|
||||
syn::custom_keyword!(weight);
|
||||
syn::custom_keyword!(call_index);
|
||||
syn::custom_keyword!(compact);
|
||||
syn::custom_keyword!(T);
|
||||
syn::custom_keyword!(pezpallet);
|
||||
syn::custom_keyword!(feeless_if);
|
||||
syn::custom_keyword!(authorize);
|
||||
syn::custom_keyword!(weight_of_authorize);
|
||||
}
|
||||
|
||||
/// Definition of dispatchables typically `impl<T: Config> Pezpallet<T> { ... }`
|
||||
pub struct CallDef {
|
||||
/// The where_clause used.
|
||||
pub where_clause: Option<syn::WhereClause>,
|
||||
/// A set of usage of instance, must be check for consistency with trait.
|
||||
pub instances: Vec<helper::InstanceUsage>,
|
||||
/// The index of call item in pezpallet module.
|
||||
pub index: usize,
|
||||
/// Information on methods (used for expansion).
|
||||
pub methods: Vec<CallVariantDef>,
|
||||
/// The span of the pezpallet::call attribute.
|
||||
pub attr_span: proc_macro2::Span,
|
||||
/// Docs, specified on the impl Block.
|
||||
pub docs: Vec<syn::Expr>,
|
||||
}
|
||||
|
||||
/// The weight of a call or the weight of authorize.
|
||||
#[derive(Clone)]
|
||||
pub enum CallWeightDef {
|
||||
/// Explicitly set on the call itself with `#[pezpallet::weight(…)]` or
|
||||
/// `#[pezpallet::weight_of_authorize(…)]`. This value is used.
|
||||
Immediate(syn::Expr),
|
||||
|
||||
/// The default value that should be set for dev-mode pallets. Usually zero.
|
||||
DevModeDefault,
|
||||
|
||||
/// Inherits whatever value is configured on the pezpallet level.
|
||||
///
|
||||
/// The concrete value is not known at this point.
|
||||
Inherited(syn::Type),
|
||||
}
|
||||
|
||||
impl CallWeightDef {
|
||||
fn try_from(
|
||||
weight: Option<syn::Expr>,
|
||||
inherited_call_weight: &Option<InheritedCallWeightAttr>,
|
||||
dev_mode: bool,
|
||||
) -> Option<Self> {
|
||||
match (weight, inherited_call_weight) {
|
||||
(Some(weight), _) => Some(CallWeightDef::Immediate(weight)),
|
||||
(None, Some(inherited)) => Some(CallWeightDef::Inherited(inherited.typename.clone())),
|
||||
(None, _) if dev_mode => Some(CallWeightDef::DevModeDefault),
|
||||
(None, _) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Definition of dispatchable typically: `#[weight...] fn foo(origin .., param1: ...) -> ..`
|
||||
#[derive(Clone)]
|
||||
pub struct CallVariantDef {
|
||||
/// Function name.
|
||||
pub name: syn::Ident,
|
||||
/// Information on args: `(is_compact, name, type)`
|
||||
pub args: Vec<(bool, syn::Ident, Box<syn::Type>)>,
|
||||
/// Weight for the call.
|
||||
pub weight: CallWeightDef,
|
||||
/// Call index of the dispatchable.
|
||||
pub call_index: u8,
|
||||
/// Whether an explicit call index was specified.
|
||||
pub explicit_call_index: bool,
|
||||
/// Docs, used for metadata.
|
||||
pub docs: Vec<syn::Expr>,
|
||||
/// Attributes annotated at the top of the dispatchable function.
|
||||
pub attrs: Vec<syn::Attribute>,
|
||||
/// The `cfg` attributes.
|
||||
pub cfg_attrs: Vec<syn::Attribute>,
|
||||
/// The optional `feeless_if` attribute on the `pezpallet::call`.
|
||||
pub feeless_check: Option<syn::ExprClosure>,
|
||||
/// The return type of the call: `DispatchInfo` or `DispatchResultWithPostInfo`.
|
||||
pub return_type: helper::CallReturnType,
|
||||
/// The information related to `authorize` attribute.
|
||||
/// `(authorize expression, weight of authorize)`
|
||||
pub authorize: Option<AuthorizeDef>,
|
||||
}
|
||||
|
||||
/// Definition related to the `authorize` attribute and other related attributes.
|
||||
#[derive(Clone)]
|
||||
pub struct AuthorizeDef {
|
||||
/// The expression of the authorize attribute.
|
||||
pub expr: syn::Expr,
|
||||
/// The weight of the authorize attribute as define by the attribute
|
||||
/// `[pezpallet::weight_of_authorize]`.
|
||||
pub weight: CallWeightDef,
|
||||
}
|
||||
|
||||
/// Attributes for functions in call impl block.
|
||||
pub enum FunctionAttr {
|
||||
/// Parse for `#[pezpallet::call_index(expr)]`
|
||||
CallIndex(u8),
|
||||
/// Parse for `#[pezpallet::weight(expr)]`
|
||||
Weight(syn::Expr),
|
||||
/// Parse for `#[pezpallet::feeless_if(expr)]`
|
||||
FeelessIf(Span, syn::ExprClosure),
|
||||
/// Parse for `#[pezpallet::authorize(expr)]`
|
||||
Authorize(syn::Expr),
|
||||
/// Parse for `#[pezpallet::weight_of_authorize(expr)]`
|
||||
WeightOfAuthorize(syn::Expr),
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for FunctionAttr {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
input.parse::<syn::Token![#]>()?;
|
||||
let content;
|
||||
syn::bracketed!(content in input);
|
||||
content.parse::<keyword::pezpallet>()?;
|
||||
content.parse::<syn::Token![::]>()?;
|
||||
|
||||
let lookahead = content.lookahead1();
|
||||
if lookahead.peek(keyword::weight) {
|
||||
content.parse::<keyword::weight>()?;
|
||||
let weight_content;
|
||||
syn::parenthesized!(weight_content in content);
|
||||
Ok(FunctionAttr::Weight(weight_content.parse::<syn::Expr>()?))
|
||||
} else if lookahead.peek(keyword::call_index) {
|
||||
content.parse::<keyword::call_index>()?;
|
||||
let call_index_content;
|
||||
syn::parenthesized!(call_index_content in content);
|
||||
let index = call_index_content.parse::<syn::LitInt>()?;
|
||||
if !index.suffix().is_empty() {
|
||||
let msg = "Number literal must not have a suffix";
|
||||
return Err(syn::Error::new(index.span(), msg));
|
||||
}
|
||||
Ok(FunctionAttr::CallIndex(index.base10_parse()?))
|
||||
} else if lookahead.peek(keyword::feeless_if) {
|
||||
content.parse::<keyword::feeless_if>()?;
|
||||
let closure_content;
|
||||
syn::parenthesized!(closure_content in content);
|
||||
Ok(FunctionAttr::FeelessIf(
|
||||
closure_content.span(),
|
||||
closure_content.parse::<syn::ExprClosure>().map_err(|e| {
|
||||
let msg = "Invalid feeless_if attribute: expected a closure";
|
||||
let mut err = syn::Error::new(closure_content.span(), msg);
|
||||
err.combine(e);
|
||||
err
|
||||
})?,
|
||||
))
|
||||
} else if lookahead.peek(keyword::authorize) {
|
||||
content.parse::<keyword::authorize>()?;
|
||||
let closure_content;
|
||||
syn::parenthesized!(closure_content in content);
|
||||
Ok(FunctionAttr::Authorize(closure_content.parse::<syn::Expr>()?))
|
||||
} else if lookahead.peek(keyword::weight_of_authorize) {
|
||||
content.parse::<keyword::weight_of_authorize>()?;
|
||||
let closure_content;
|
||||
syn::parenthesized!(closure_content in content);
|
||||
Ok(FunctionAttr::WeightOfAuthorize(closure_content.parse::<syn::Expr>()?))
|
||||
} else {
|
||||
Err(lookahead.error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Attribute for arguments in function in call impl block.
|
||||
/// Parse for `#[pezpallet::compact]|
|
||||
pub struct ArgAttrIsCompact;
|
||||
|
||||
impl syn::parse::Parse for ArgAttrIsCompact {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
input.parse::<syn::Token![#]>()?;
|
||||
let content;
|
||||
syn::bracketed!(content in input);
|
||||
content.parse::<keyword::pezpallet>()?;
|
||||
content.parse::<syn::Token![::]>()?;
|
||||
|
||||
content.parse::<keyword::compact>()?;
|
||||
Ok(ArgAttrIsCompact)
|
||||
}
|
||||
}
|
||||
|
||||
/// Check the syntax is `OriginFor<T>`, `&OriginFor<T>` or `T::RuntimeOrigin`.
|
||||
pub fn check_dispatchable_first_arg_type(ty: &syn::Type, is_ref: bool) -> syn::Result<()> {
|
||||
pub struct CheckOriginFor(bool);
|
||||
impl syn::parse::Parse for CheckOriginFor {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let is_ref = input.parse::<syn::Token![&]>().is_ok();
|
||||
input.parse::<keyword::OriginFor>()?;
|
||||
input.parse::<syn::Token![<]>()?;
|
||||
input.parse::<keyword::T>()?;
|
||||
input.parse::<syn::Token![>]>()?;
|
||||
|
||||
Ok(Self(is_ref))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CheckRuntimeOrigin;
|
||||
impl syn::parse::Parse for CheckRuntimeOrigin {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
input.parse::<keyword::T>()?;
|
||||
input.parse::<syn::Token![::]>()?;
|
||||
input.parse::<keyword::RuntimeOrigin>()?;
|
||||
|
||||
Ok(Self)
|
||||
}
|
||||
}
|
||||
|
||||
let result_origin_for = syn::parse2::<CheckOriginFor>(ty.to_token_stream());
|
||||
let result_runtime_origin = syn::parse2::<CheckRuntimeOrigin>(ty.to_token_stream());
|
||||
return match (result_origin_for, result_runtime_origin) {
|
||||
(Ok(CheckOriginFor(has_ref)), _) if is_ref == has_ref => Ok(()),
|
||||
(_, Ok(_)) => Ok(()),
|
||||
(_, _) => {
|
||||
let msg = if is_ref {
|
||||
"Invalid type: expected `&OriginFor<T>`"
|
||||
} else {
|
||||
"Invalid type: expected `OriginFor<T>` or `T::RuntimeOrigin`"
|
||||
};
|
||||
return Err(syn::Error::new(ty.span(), msg));
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
impl CallDef {
|
||||
pub fn try_from(
|
||||
attr_span: proc_macro2::Span,
|
||||
index: usize,
|
||||
item: &mut syn::Item,
|
||||
dev_mode: bool,
|
||||
inherited_call_weight: Option<InheritedCallWeightAttr>,
|
||||
) -> syn::Result<Self> {
|
||||
let item_impl = if let syn::Item::Impl(item) = item {
|
||||
item
|
||||
} else {
|
||||
return Err(syn::Error::new(item.span(), "Invalid pezpallet::call, expected item impl"));
|
||||
};
|
||||
|
||||
crate::deprecation::prevent_deprecation_attr_on_outer_enum(&item_impl.attrs)?;
|
||||
|
||||
let instances = vec![
|
||||
helper::check_impl_gen(&item_impl.generics, item_impl.impl_token.span())?,
|
||||
helper::check_pallet_struct_usage(&item_impl.self_ty)?,
|
||||
];
|
||||
|
||||
if let Some((_, _, for_)) = item_impl.trait_ {
|
||||
let msg = "Invalid pezpallet::call, expected no trait ident as in \
|
||||
`impl<..> Pezpallet<..> { .. }`";
|
||||
return Err(syn::Error::new(for_.span(), msg));
|
||||
}
|
||||
|
||||
let mut methods = vec![];
|
||||
let mut indices = HashMap::new();
|
||||
let mut last_index: Option<u8> = None;
|
||||
for item in &mut item_impl.items {
|
||||
if let syn::ImplItem::Fn(method) = item {
|
||||
if !matches!(method.vis, syn::Visibility::Public(_)) {
|
||||
let msg = "Invalid pezpallet::call, dispatchable function must be public: \
|
||||
`pub fn`";
|
||||
|
||||
let span = match method.vis {
|
||||
syn::Visibility::Inherited => method.sig.span(),
|
||||
_ => method.vis.span(),
|
||||
};
|
||||
|
||||
return Err(syn::Error::new(span, msg));
|
||||
}
|
||||
|
||||
match method.sig.inputs.first() {
|
||||
None => {
|
||||
let msg = "Invalid pezpallet::call, must have at least origin arg";
|
||||
return Err(syn::Error::new(method.sig.span(), msg));
|
||||
},
|
||||
Some(syn::FnArg::Receiver(_)) => {
|
||||
let msg = "Invalid pezpallet::call, first argument must be a typed argument, \
|
||||
e.g. `origin: OriginFor<T>`";
|
||||
return Err(syn::Error::new(method.sig.span(), msg));
|
||||
},
|
||||
Some(syn::FnArg::Typed(arg)) => {
|
||||
check_dispatchable_first_arg_type(&arg.ty, false)?;
|
||||
},
|
||||
}
|
||||
|
||||
let return_type = helper::check_pallet_call_return_type(&method.sig)?;
|
||||
|
||||
let cfg_attrs: Vec<syn::Attribute> = helper::get_item_cfg_attrs(&method.attrs);
|
||||
let mut call_index = None;
|
||||
let mut weight = None;
|
||||
let mut feeless_check = None;
|
||||
let mut authorize = None;
|
||||
let mut weight_of_authorize = None;
|
||||
|
||||
for attr in helper::take_item_pallet_attrs(&mut method.attrs)?.into_iter() {
|
||||
match attr {
|
||||
FunctionAttr::CallIndex(idx) => {
|
||||
if call_index.is_some() {
|
||||
let msg =
|
||||
"Invalid pezpallet::call, too many call_index attributes given";
|
||||
return Err(syn::Error::new(method.sig.span(), msg));
|
||||
}
|
||||
|
||||
call_index = Some(idx);
|
||||
},
|
||||
FunctionAttr::Weight(w) => {
|
||||
if weight.is_some() {
|
||||
let msg = "Invalid pezpallet::call, too many weight attributes given";
|
||||
return Err(syn::Error::new(method.sig.span(), msg));
|
||||
}
|
||||
weight = Some(w);
|
||||
},
|
||||
FunctionAttr::FeelessIf(span, closure) => {
|
||||
if feeless_check.is_some() {
|
||||
let msg =
|
||||
"Invalid pezpallet::call, there can only be one feeless_if attribute";
|
||||
return Err(syn::Error::new(span, msg));
|
||||
}
|
||||
|
||||
feeless_check = Some(closure);
|
||||
},
|
||||
FunctionAttr::Authorize(expr) => {
|
||||
if authorize.is_some() {
|
||||
let msg =
|
||||
"Invalid pezpallet::call, there can only be one authorize attribute";
|
||||
return Err(syn::Error::new(method.sig.span(), msg));
|
||||
}
|
||||
|
||||
authorize = Some(expr);
|
||||
},
|
||||
FunctionAttr::WeightOfAuthorize(expr) => {
|
||||
if weight_of_authorize.is_some() {
|
||||
let msg = "Invalid pezpallet::call, there can only be one weight_of_authorize attribute";
|
||||
return Err(syn::Error::new(method.sig.span(), msg));
|
||||
}
|
||||
|
||||
weight_of_authorize = Some(expr);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if weight_of_authorize.is_some() && authorize.is_none() {
|
||||
let msg = "Invalid pezpallet::call, weight_of_authorize attribute must be used with authorize attribute";
|
||||
return Err(syn::Error::new(weight_of_authorize.unwrap().span(), msg));
|
||||
}
|
||||
|
||||
let authorize = if let Some(expr) = authorize {
|
||||
let weight_of_authorize = CallWeightDef::try_from(
|
||||
weight_of_authorize,
|
||||
&inherited_call_weight,
|
||||
dev_mode,
|
||||
)
|
||||
.ok_or_else(|| {
|
||||
syn::Error::new(
|
||||
method.sig.span(),
|
||||
"A pezpallet::call using authorize requires either a concrete \
|
||||
`#[pezpallet::weight_of_authorize($expr)]` or an inherited weight from \
|
||||
the `#[pezpallet:call(weight($type))]` attribute, but \
|
||||
none were given.",
|
||||
)
|
||||
})?;
|
||||
Some(AuthorizeDef { expr, weight: weight_of_authorize })
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let weight = CallWeightDef::try_from(weight, &inherited_call_weight, dev_mode)
|
||||
.ok_or_else(|| {
|
||||
syn::Error::new(
|
||||
method.sig.span(),
|
||||
"A pezpallet::call requires either a concrete `#[pezpallet::weight($expr)]` \
|
||||
or an inherited weight from the `#[pezpallet:call(weight($type))]` \
|
||||
attribute, but none were given.",
|
||||
)
|
||||
})?;
|
||||
|
||||
let explicit_call_index = call_index.is_some();
|
||||
|
||||
let final_index = match call_index {
|
||||
Some(i) => i,
|
||||
None =>
|
||||
last_index.map_or(Some(0), |idx| idx.checked_add(1)).ok_or_else(|| {
|
||||
let msg = "Call index doesn't fit into u8, index is 256";
|
||||
syn::Error::new(method.sig.span(), msg)
|
||||
})?,
|
||||
};
|
||||
last_index = Some(final_index);
|
||||
|
||||
if let Some(used_fn) = indices.insert(final_index, method.sig.ident.clone()) {
|
||||
let msg = format!(
|
||||
"Call indices are conflicting: Both functions {} and {} are at index {}",
|
||||
used_fn, method.sig.ident, final_index,
|
||||
);
|
||||
let mut err = syn::Error::new(used_fn.span(), &msg);
|
||||
err.combine(syn::Error::new(method.sig.ident.span(), msg));
|
||||
return Err(err);
|
||||
}
|
||||
|
||||
let mut args = vec![];
|
||||
for arg in method.sig.inputs.iter_mut().skip(1) {
|
||||
let arg = if let syn::FnArg::Typed(arg) = arg {
|
||||
arg
|
||||
} else {
|
||||
unreachable!("Only first argument can be receiver");
|
||||
};
|
||||
|
||||
let arg_attrs: Vec<ArgAttrIsCompact> =
|
||||
helper::take_item_pallet_attrs(&mut arg.attrs)?;
|
||||
|
||||
if arg_attrs.len() > 1 {
|
||||
let msg = "Invalid pezpallet::call, argument has too many attributes";
|
||||
return Err(syn::Error::new(arg.span(), msg));
|
||||
}
|
||||
|
||||
let arg_ident = if let syn::Pat::Ident(pat) = &*arg.pat {
|
||||
pat.ident.clone()
|
||||
} else {
|
||||
let msg = "Invalid pezpallet::call, argument must be ident";
|
||||
return Err(syn::Error::new(arg.pat.span(), msg));
|
||||
};
|
||||
|
||||
args.push((!arg_attrs.is_empty(), arg_ident, arg.ty.clone()));
|
||||
}
|
||||
|
||||
let docs = get_doc_literals(&method.attrs);
|
||||
|
||||
if let Some(ref feeless_check) = feeless_check {
|
||||
if feeless_check.inputs.len() != args.len() + 1 {
|
||||
let msg = "Invalid pezpallet::call, feeless_if closure must have same \
|
||||
number of arguments as the dispatchable function";
|
||||
return Err(syn::Error::new(feeless_check.span(), msg));
|
||||
}
|
||||
|
||||
match feeless_check.inputs.first() {
|
||||
None => {
|
||||
let msg = "Invalid pezpallet::call, feeless_if closure must have at least origin arg";
|
||||
return Err(syn::Error::new(feeless_check.span(), msg));
|
||||
},
|
||||
Some(syn::Pat::Type(arg)) => {
|
||||
check_dispatchable_first_arg_type(&arg.ty, true)?;
|
||||
},
|
||||
_ => {
|
||||
let msg = "Invalid pezpallet::call, feeless_if closure first argument must be a typed argument, \
|
||||
e.g. `origin: OriginFor<T>`";
|
||||
return Err(syn::Error::new(feeless_check.span(), msg));
|
||||
},
|
||||
}
|
||||
|
||||
for (feeless_arg, arg) in feeless_check.inputs.iter().skip(1).zip(args.iter()) {
|
||||
let feeless_arg_type = if let syn::Pat::Type(syn::PatType { ty, .. }) =
|
||||
feeless_arg.clone()
|
||||
{
|
||||
if let syn::Type::Reference(pat) = *ty {
|
||||
pat.elem.clone()
|
||||
} else {
|
||||
let msg = "Invalid pezpallet::call, feeless_if closure argument must be a reference";
|
||||
return Err(syn::Error::new(ty.span(), msg));
|
||||
}
|
||||
} else {
|
||||
let msg = "Invalid pezpallet::call, feeless_if closure argument must be a type ascription pattern";
|
||||
return Err(syn::Error::new(feeless_arg.span(), msg));
|
||||
};
|
||||
|
||||
if feeless_arg_type != arg.2 {
|
||||
let msg =
|
||||
"Invalid pezpallet::call, feeless_if closure argument must have \
|
||||
a reference to the same type as the dispatchable function argument";
|
||||
return Err(syn::Error::new(feeless_arg.span(), msg));
|
||||
}
|
||||
}
|
||||
|
||||
let valid_return = match &feeless_check.output {
|
||||
syn::ReturnType::Type(_, type_) => match *(type_.clone()) {
|
||||
syn::Type::Path(syn::TypePath { path, .. }) => path.is_ident("bool"),
|
||||
_ => false,
|
||||
},
|
||||
_ => false,
|
||||
};
|
||||
if !valid_return {
|
||||
let msg = "Invalid pezpallet::call, feeless_if closure must return `bool`";
|
||||
return Err(syn::Error::new(feeless_check.output.span(), msg));
|
||||
}
|
||||
}
|
||||
|
||||
methods.push(CallVariantDef {
|
||||
name: method.sig.ident.clone(),
|
||||
weight,
|
||||
call_index: final_index,
|
||||
explicit_call_index,
|
||||
args,
|
||||
docs,
|
||||
attrs: method.attrs.clone(),
|
||||
cfg_attrs,
|
||||
feeless_check,
|
||||
return_type,
|
||||
authorize,
|
||||
});
|
||||
} else {
|
||||
let msg = "Invalid pezpallet::call, only method accepted";
|
||||
return Err(syn::Error::new(item.span(), msg));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
index,
|
||||
attr_span,
|
||||
instances,
|
||||
methods,
|
||||
where_clause: item_impl.generics.where_clause.clone(),
|
||||
docs: get_doc_literals(&item_impl.attrs),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::helper;
|
||||
use quote::ToTokens;
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
pub mod keyword {
|
||||
use super::*;
|
||||
|
||||
syn::custom_keyword!(FreezeReason);
|
||||
syn::custom_keyword!(HoldReason);
|
||||
syn::custom_keyword!(LockId);
|
||||
syn::custom_keyword!(SlashReason);
|
||||
syn::custom_keyword!(Task);
|
||||
|
||||
pub enum CompositeKeyword {
|
||||
FreezeReason(FreezeReason),
|
||||
HoldReason(HoldReason),
|
||||
LockId(LockId),
|
||||
SlashReason(SlashReason),
|
||||
Task(Task),
|
||||
}
|
||||
|
||||
impl ToTokens for CompositeKeyword {
|
||||
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||
use CompositeKeyword::*;
|
||||
match self {
|
||||
FreezeReason(inner) => inner.to_tokens(tokens),
|
||||
HoldReason(inner) => inner.to_tokens(tokens),
|
||||
LockId(inner) => inner.to_tokens(tokens),
|
||||
SlashReason(inner) => inner.to_tokens(tokens),
|
||||
Task(inner) => inner.to_tokens(tokens),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for CompositeKeyword {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let lookahead = input.lookahead1();
|
||||
if lookahead.peek(FreezeReason) {
|
||||
Ok(Self::FreezeReason(input.parse()?))
|
||||
} else if lookahead.peek(HoldReason) {
|
||||
Ok(Self::HoldReason(input.parse()?))
|
||||
} else if lookahead.peek(LockId) {
|
||||
Ok(Self::LockId(input.parse()?))
|
||||
} else if lookahead.peek(SlashReason) {
|
||||
Ok(Self::SlashReason(input.parse()?))
|
||||
} else if lookahead.peek(Task) {
|
||||
Ok(Self::Task(input.parse()?))
|
||||
} else {
|
||||
Err(lookahead.error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for CompositeKeyword {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
use CompositeKeyword::*;
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
FreezeReason(_) => "FreezeReason",
|
||||
HoldReason(_) => "HoldReason",
|
||||
Task(_) => "Task",
|
||||
LockId(_) => "LockId",
|
||||
SlashReason(_) => "SlashReason",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CompositeDef {
|
||||
/// The composite keyword used (contains span).
|
||||
pub composite_keyword: keyword::CompositeKeyword,
|
||||
/// Name of the associated type.
|
||||
pub ident: syn::Ident,
|
||||
/// Type parameters and where clause attached to a declaration of the pezpallet::composite_enum.
|
||||
pub generics: syn::Generics,
|
||||
/// The span of the pezpallet::composite_enum attribute.
|
||||
pub attr_span: proc_macro2::Span,
|
||||
/// Variant count of the pezpallet::composite_enum.
|
||||
pub variant_count: u32,
|
||||
}
|
||||
|
||||
impl CompositeDef {
|
||||
pub fn try_from(
|
||||
attr_span: proc_macro2::Span,
|
||||
scrate: &syn::Path,
|
||||
item: &mut syn::Item,
|
||||
) -> syn::Result<Self> {
|
||||
let item = if let syn::Item::Enum(item) = item {
|
||||
// check variants: composite enums support only field-less enum variants. This is
|
||||
// because fields can introduce too many possibilities, making it challenging to compute
|
||||
// a fixed variant count.
|
||||
for variant in &item.variants {
|
||||
match variant.fields {
|
||||
syn::Fields::Named(_) | syn::Fields::Unnamed(_) =>
|
||||
return Err(syn::Error::new(
|
||||
variant.ident.span(),
|
||||
"The composite enum does not support variants with fields!",
|
||||
)),
|
||||
syn::Fields::Unit => (),
|
||||
}
|
||||
}
|
||||
item
|
||||
} else {
|
||||
return Err(syn::Error::new(
|
||||
item.span(),
|
||||
"Invalid pezpallet::composite_enum, expected enum item",
|
||||
));
|
||||
};
|
||||
|
||||
if !matches!(item.vis, syn::Visibility::Public(_)) {
|
||||
let msg = format!("Invalid pezpallet::composite_enum, `{}` must be public", item.ident);
|
||||
return Err(syn::Error::new(item.span(), msg));
|
||||
}
|
||||
|
||||
let has_instance = if item.generics.params.first().is_some() {
|
||||
helper::check_config_def_gen(&item.generics, item.ident.span())?;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let has_derive_attr = item.attrs.iter().any(|attr| {
|
||||
if let syn::Meta::List(syn::MetaList { path, .. }) = &attr.meta {
|
||||
path.get_ident().map(|ident| ident == "derive").unwrap_or(false)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
|
||||
if !has_derive_attr {
|
||||
let derive_attr: syn::Attribute = syn::parse_quote! {
|
||||
#[derive(
|
||||
Copy, Clone, Eq, PartialEq,
|
||||
#scrate::__private::codec::Encode,
|
||||
#scrate::__private::codec::Decode,
|
||||
#scrate::__private::codec::DecodeWithMemTracking,
|
||||
#scrate::__private::codec::MaxEncodedLen,
|
||||
#scrate::__private::scale_info::TypeInfo,
|
||||
#scrate::__private::RuntimeDebug,
|
||||
)]
|
||||
};
|
||||
item.attrs.push(derive_attr);
|
||||
}
|
||||
|
||||
if has_instance {
|
||||
item.attrs.push(syn::parse_quote! {
|
||||
#[scale_info(skip_type_params(I))]
|
||||
});
|
||||
|
||||
item.variants.push(syn::parse_quote! {
|
||||
#[doc(hidden)]
|
||||
#[codec(skip)]
|
||||
__Ignore(
|
||||
::core::marker::PhantomData<I>,
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
let composite_keyword =
|
||||
syn::parse2::<keyword::CompositeKeyword>(item.ident.to_token_stream())?;
|
||||
|
||||
Ok(CompositeDef {
|
||||
composite_keyword,
|
||||
attr_span,
|
||||
generics: item.generics.clone(),
|
||||
variant_count: item.variants.len() as u32,
|
||||
ident: item.ident.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,727 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::helper;
|
||||
use pezframe_support_procedural_tools::{get_cfg_attributes, get_doc_literals, is_using_frame_crate};
|
||||
use proc_macro_warning::Warning;
|
||||
use quote::ToTokens;
|
||||
use syn::{parse_quote, spanned::Spanned, token, Token, TraitItemType};
|
||||
|
||||
/// List of additional token to be used for parsing.
|
||||
mod keyword {
|
||||
syn::custom_keyword!(Config);
|
||||
syn::custom_keyword!(From);
|
||||
syn::custom_keyword!(T);
|
||||
syn::custom_keyword!(I);
|
||||
syn::custom_keyword!(config);
|
||||
syn::custom_keyword!(pezpallet);
|
||||
syn::custom_keyword!(IsType);
|
||||
syn::custom_keyword!(RuntimeEvent);
|
||||
syn::custom_keyword!(Event);
|
||||
syn::custom_keyword!(pezframe_system);
|
||||
syn::custom_keyword!(disable_pezframe_system_supertrait_check);
|
||||
syn::custom_keyword!(no_default);
|
||||
syn::custom_keyword!(no_default_bounds);
|
||||
syn::custom_keyword!(constant);
|
||||
syn::custom_keyword!(include_metadata);
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct DefaultTrait {
|
||||
/// A bool for each sub-trait item indicates whether the item has
|
||||
/// `#[pezpallet::no_default_bounds]` attached to it. If true, the item will not have any bounds
|
||||
/// in the generated default sub-trait.
|
||||
pub items: Vec<(syn::TraitItem, bool)>,
|
||||
pub has_system: bool,
|
||||
}
|
||||
|
||||
/// Input definition for the pezpallet config.
|
||||
pub struct ConfigDef {
|
||||
/// The index of item in pezpallet module.
|
||||
pub index: usize,
|
||||
/// Whether the trait has instance (i.e. define with `Config<I = ()>`)
|
||||
pub has_instance: bool,
|
||||
/// Const associated type.
|
||||
pub consts_metadata: Vec<ConstMetadataDef>,
|
||||
/// Associated types metadata.
|
||||
pub associated_types_metadata: Vec<AssociatedTypeMetadataDef>,
|
||||
/// The where clause on trait definition but modified so `Self` is `T`.
|
||||
pub where_clause: Option<syn::WhereClause>,
|
||||
/// Whether a default sub-trait should be generated.
|
||||
///
|
||||
/// Contains default sub-trait items (instantiated by `#[pezpallet::config(with_default)]`).
|
||||
/// Vec will be empty if `#[pezpallet::config(with_default)]` is not specified or if there are
|
||||
/// no trait items.
|
||||
pub default_sub_trait: Option<DefaultTrait>,
|
||||
/// Compile time warnings. Mainly for deprecated items.
|
||||
pub warnings: Vec<Warning>,
|
||||
}
|
||||
|
||||
/// Input definition for an associated type in pezpallet config.
|
||||
pub struct AssociatedTypeMetadataDef {
|
||||
/// Name of the associated type.
|
||||
pub ident: syn::Ident,
|
||||
/// The doc associated.
|
||||
pub doc: Vec<syn::Expr>,
|
||||
/// The cfg associated.
|
||||
pub cfg: Vec<syn::Attribute>,
|
||||
}
|
||||
|
||||
impl From<&syn::TraitItemType> for AssociatedTypeMetadataDef {
|
||||
fn from(trait_ty: &syn::TraitItemType) -> Self {
|
||||
let ident = trait_ty.ident.clone();
|
||||
let doc = get_doc_literals(&trait_ty.attrs);
|
||||
let cfg = get_cfg_attributes(&trait_ty.attrs);
|
||||
|
||||
Self { ident, doc, cfg }
|
||||
}
|
||||
}
|
||||
|
||||
/// Input definition for a constant in pezpallet config.
|
||||
pub struct ConstMetadataDef {
|
||||
/// Name of the associated type.
|
||||
pub ident: syn::Ident,
|
||||
/// The type in Get, e.g. `u32` in `type Foo: Get<u32>;`, but `Self` is replaced by `T`
|
||||
pub type_: syn::Type,
|
||||
/// The doc associated
|
||||
pub doc: Vec<syn::Expr>,
|
||||
/// attributes
|
||||
pub attrs: Vec<syn::Attribute>,
|
||||
}
|
||||
|
||||
impl TryFrom<&syn::TraitItemType> for ConstMetadataDef {
|
||||
type Error = syn::Error;
|
||||
|
||||
fn try_from(trait_ty: &syn::TraitItemType) -> Result<Self, Self::Error> {
|
||||
let err = |span, msg| {
|
||||
syn::Error::new(span, format!("Invalid usage of `#[pezpallet::constant]`: {}", msg))
|
||||
};
|
||||
let doc = get_doc_literals(&trait_ty.attrs);
|
||||
let ident = trait_ty.ident.clone();
|
||||
let bound = trait_ty
|
||||
.bounds
|
||||
.iter()
|
||||
.find_map(|param_bound| {
|
||||
let syn::TypeParamBound::Trait(trait_bound) = param_bound else { return None };
|
||||
|
||||
trait_bound.path.segments.last().and_then(|s| (s.ident == "Get").then(|| s))
|
||||
})
|
||||
.ok_or_else(|| err(trait_ty.span(), "`Get<T>` trait bound not found"))?;
|
||||
|
||||
let syn::PathArguments::AngleBracketed(ref ab) = bound.arguments else {
|
||||
return Err(err(bound.span(), "Expected trait generic args"));
|
||||
};
|
||||
|
||||
// Only one type argument is expected.
|
||||
if ab.args.len() != 1 {
|
||||
return Err(err(bound.span(), "Expected a single type argument"));
|
||||
}
|
||||
|
||||
let syn::GenericArgument::Type(ref type_arg) = ab.args[0] else {
|
||||
return Err(err(ab.args[0].span(), "Expected a type argument"));
|
||||
};
|
||||
|
||||
let type_ = syn::parse2::<syn::Type>(replace_self_by_t(type_arg.to_token_stream()))
|
||||
.expect("Internal error: replacing `Self` by `T` should result in valid type");
|
||||
|
||||
Ok(Self { ident, type_, doc, attrs: trait_ty.attrs.clone() })
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse for `#[pezpallet::disable_pezframe_system_supertrait_check]`
|
||||
pub struct DisableFrameSystemSupertraitCheck;
|
||||
|
||||
impl syn::parse::Parse for DisableFrameSystemSupertraitCheck {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
input.parse::<syn::Token![#]>()?;
|
||||
let content;
|
||||
syn::bracketed!(content in input);
|
||||
content.parse::<syn::Ident>()?;
|
||||
content.parse::<syn::Token![::]>()?;
|
||||
|
||||
content.parse::<keyword::disable_pezframe_system_supertrait_check>()?;
|
||||
Ok(Self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Parsing for the `typ` portion of `PalletAttr`
|
||||
#[derive(derive_syn_parse::Parse, PartialEq, Eq)]
|
||||
pub enum PalletAttrType {
|
||||
#[peek(keyword::no_default, name = "no_default")]
|
||||
NoDefault(keyword::no_default),
|
||||
#[peek(keyword::no_default_bounds, name = "no_default_bounds")]
|
||||
NoBounds(keyword::no_default_bounds),
|
||||
#[peek(keyword::constant, name = "constant")]
|
||||
Constant(keyword::constant),
|
||||
#[peek(keyword::include_metadata, name = "include_metadata")]
|
||||
IncludeMetadata(keyword::include_metadata),
|
||||
}
|
||||
|
||||
/// Parsing for `#[pezpallet::X]`
|
||||
#[derive(derive_syn_parse::Parse)]
|
||||
pub struct PalletAttr {
|
||||
_pound: Token![#],
|
||||
#[bracket]
|
||||
_bracket: token::Bracket,
|
||||
#[inside(_bracket)]
|
||||
_pallet: keyword::pezpallet,
|
||||
#[prefix(Token![::] in _bracket)]
|
||||
#[inside(_bracket)]
|
||||
typ: PalletAttrType,
|
||||
}
|
||||
|
||||
/// Parse for `IsType<<Self as $path>::RuntimeEvent>` and retrieve `$path`
|
||||
pub struct IsTypeBoundEventParse(syn::Path);
|
||||
|
||||
impl syn::parse::Parse for IsTypeBoundEventParse {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
input.parse::<keyword::IsType>()?;
|
||||
input.parse::<syn::Token![<]>()?;
|
||||
input.parse::<syn::Token![<]>()?;
|
||||
input.parse::<syn::Token![Self]>()?;
|
||||
input.parse::<syn::Token![as]>()?;
|
||||
let config_path = input.parse::<syn::Path>()?;
|
||||
input.parse::<syn::Token![>]>()?;
|
||||
input.parse::<syn::Token![::]>()?;
|
||||
input.parse::<keyword::RuntimeEvent>()?;
|
||||
input.parse::<syn::Token![>]>()?;
|
||||
|
||||
Ok(Self(config_path))
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse for `From<Event>` or `From<Event<Self>>` or `From<Event<Self, I>>`
|
||||
pub struct FromEventParse {
|
||||
is_generic: bool,
|
||||
has_instance: bool,
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for FromEventParse {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let mut is_generic = false;
|
||||
let mut has_instance = false;
|
||||
|
||||
input.parse::<keyword::From>()?;
|
||||
input.parse::<syn::Token![<]>()?;
|
||||
input.parse::<keyword::Event>()?;
|
||||
if input.peek(syn::Token![<]) {
|
||||
is_generic = true;
|
||||
input.parse::<syn::Token![<]>()?;
|
||||
input.parse::<syn::Token![Self]>()?;
|
||||
if input.peek(syn::Token![,]) {
|
||||
input.parse::<syn::Token![,]>()?;
|
||||
input.parse::<keyword::I>()?;
|
||||
has_instance = true;
|
||||
}
|
||||
input.parse::<syn::Token![>]>()?;
|
||||
}
|
||||
input.parse::<syn::Token![>]>()?;
|
||||
|
||||
Ok(Self { is_generic, has_instance })
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if trait_item is `type RuntimeEvent`, if so checks its bounds are those expected.
|
||||
/// (Event type is reserved type)
|
||||
fn check_event_type(
|
||||
pezframe_system: &syn::Path,
|
||||
trait_item: &syn::TraitItem,
|
||||
trait_has_instance: bool,
|
||||
) -> syn::Result<bool> {
|
||||
let syn::TraitItem::Type(type_) = trait_item else { return Ok(false) };
|
||||
|
||||
if type_.ident != "RuntimeEvent" {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
// Check event has no generics
|
||||
if !type_.generics.params.is_empty() || type_.generics.where_clause.is_some() {
|
||||
let msg =
|
||||
"Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must have\
|
||||
no generics nor where_clause";
|
||||
return Err(syn::Error::new(trait_item.span(), msg));
|
||||
}
|
||||
|
||||
// Check bound contains IsType and From
|
||||
let has_is_type_bound = type_.bounds.iter().any(|s| {
|
||||
syn::parse2::<IsTypeBoundEventParse>(s.to_token_stream())
|
||||
.map_or(false, |b| has_expected_system_config(b.0, pezframe_system))
|
||||
});
|
||||
|
||||
if !has_is_type_bound {
|
||||
let msg =
|
||||
"Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must \
|
||||
bound: `IsType<<Self as pezframe_system::Config>::RuntimeEvent>`"
|
||||
.to_string();
|
||||
return Err(syn::Error::new(type_.span(), msg));
|
||||
}
|
||||
|
||||
let from_event_bound = type_
|
||||
.bounds
|
||||
.iter()
|
||||
.find_map(|s| syn::parse2::<FromEventParse>(s.to_token_stream()).ok());
|
||||
|
||||
let Some(from_event_bound) = from_event_bound else {
|
||||
let msg =
|
||||
"Invalid `type RuntimeEvent`, associated type `RuntimeEvent` is reserved and must \
|
||||
bound: `From<Event>` or `From<Event<Self>>` or `From<Event<Self, I>>`";
|
||||
return Err(syn::Error::new(type_.span(), msg));
|
||||
};
|
||||
|
||||
if from_event_bound.is_generic && (from_event_bound.has_instance != trait_has_instance) {
|
||||
let msg =
|
||||
"Invalid `type RuntimeEvent`, associated type `RuntimeEvent` bounds inconsistent \
|
||||
`From<Event..>`. Config and generic Event must be both with instance or \
|
||||
without instance";
|
||||
return Err(syn::Error::new(type_.span(), msg));
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
/// Check that the path to `pezframe_system::Config` is valid, this is that the path is just
|
||||
/// `pezframe_system::Config` or when using the `frame` crate it is
|
||||
/// `pezkuwi_sdk_frame::xyz::pezframe_system::Config`.
|
||||
fn has_expected_system_config(path: syn::Path, pezframe_system: &syn::Path) -> bool {
|
||||
// Check if `pezframe_system` is actually 'pezframe_system'.
|
||||
if path.segments.iter().all(|s| s.ident != "pezframe_system") {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut expected_system_config =
|
||||
match (is_using_frame_crate(&path), is_using_frame_crate(&pezframe_system)) {
|
||||
(true, false) =>
|
||||
// We can't use the path to `pezframe_system` from `frame` if `pezframe_system` is not being
|
||||
// in scope through `frame`.
|
||||
return false,
|
||||
(false, true) =>
|
||||
// We know that the only valid pezframe_system path is one that is `pezframe_system`, as
|
||||
// `frame` re-exports it as such.
|
||||
syn::parse2::<syn::Path>(quote::quote!(pezframe_system)).expect("is a valid path; qed"),
|
||||
(_, _) =>
|
||||
// They are either both `pezframe_system` or both `pezkuwi_sdk_frame::xyz::pezframe_system`.
|
||||
pezframe_system.clone(),
|
||||
};
|
||||
|
||||
expected_system_config
|
||||
.segments
|
||||
.push(syn::PathSegment::from(syn::Ident::new("Config", path.span())));
|
||||
|
||||
// the parse path might be something like `pezframe_system::Config<...>`, so we
|
||||
// only compare the idents along the path.
|
||||
expected_system_config
|
||||
.segments
|
||||
.into_iter()
|
||||
.map(|ps| ps.ident)
|
||||
.collect::<Vec<_>>() ==
|
||||
path.segments.into_iter().map(|ps| ps.ident).collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
/// Replace ident `Self` by `T`
|
||||
pub fn replace_self_by_t(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
|
||||
input
|
||||
.into_iter()
|
||||
.map(|token_tree| match token_tree {
|
||||
proc_macro2::TokenTree::Group(group) =>
|
||||
proc_macro2::Group::new(group.delimiter(), replace_self_by_t(group.stream())).into(),
|
||||
proc_macro2::TokenTree::Ident(ident) if ident == "Self" =>
|
||||
proc_macro2::Ident::new("T", ident.span()).into(),
|
||||
other => other,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Check that the trait item requires the `TypeInfo` bound (or similar).
|
||||
fn contains_type_info_bound(ty: &TraitItemType) -> bool {
|
||||
const KNOWN_TYPE_INFO_BOUNDS: &[&str] = &[
|
||||
// Explicit TypeInfo trait.
|
||||
"TypeInfo",
|
||||
// Implicit known bizinikiwi traits that implement type info.
|
||||
// Note: Aim to keep this list as small as possible.
|
||||
"Parameter",
|
||||
];
|
||||
|
||||
ty.bounds.iter().any(|bound| {
|
||||
let syn::TypeParamBound::Trait(bound) = bound else { return false };
|
||||
|
||||
KNOWN_TYPE_INFO_BOUNDS
|
||||
.iter()
|
||||
.any(|known| bound.path.segments.last().map_or(false, |last| last.ident == *known))
|
||||
})
|
||||
}
|
||||
|
||||
impl ConfigDef {
|
||||
pub fn try_from(
|
||||
pezframe_system: &syn::Path,
|
||||
index: usize,
|
||||
item: &mut syn::Item,
|
||||
enable_default: bool,
|
||||
disable_associated_metadata: bool,
|
||||
is_pezframe_system: bool,
|
||||
) -> syn::Result<Self> {
|
||||
let syn::Item::Trait(item) = item else {
|
||||
let msg = "Invalid pezpallet::config, expected trait definition";
|
||||
return Err(syn::Error::new(item.span(), msg));
|
||||
};
|
||||
|
||||
if !matches!(item.vis, syn::Visibility::Public(_)) {
|
||||
let msg = "Invalid pezpallet::config, trait must be public";
|
||||
return Err(syn::Error::new(item.span(), msg));
|
||||
}
|
||||
|
||||
syn::parse2::<keyword::Config>(item.ident.to_token_stream())?;
|
||||
|
||||
let where_clause = {
|
||||
let stream = replace_self_by_t(item.generics.where_clause.to_token_stream());
|
||||
syn::parse2::<Option<syn::WhereClause>>(stream).expect(
|
||||
"Internal error: replacing `Self` by `T` should result in valid where
|
||||
clause",
|
||||
)
|
||||
};
|
||||
|
||||
if item.generics.params.len() > 1 {
|
||||
let msg = "Invalid pezpallet::config, expected no more than one generic";
|
||||
return Err(syn::Error::new(item.generics.params[2].span(), msg));
|
||||
}
|
||||
|
||||
let has_instance = if item.generics.params.first().is_some() {
|
||||
helper::check_config_def_gen(&item.generics, item.ident.span())?;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let mut consts_metadata = vec![];
|
||||
let mut associated_types_metadata = vec![];
|
||||
let mut warnings = vec![];
|
||||
let mut default_sub_trait = if enable_default {
|
||||
Some(DefaultTrait { items: Default::default(), has_system: !is_pezframe_system })
|
||||
} else {
|
||||
None
|
||||
};
|
||||
for trait_item in &mut item.items {
|
||||
let is_event = check_event_type(pezframe_system, trait_item, has_instance)?;
|
||||
|
||||
let mut already_no_default = false;
|
||||
let mut already_constant = false;
|
||||
let mut already_no_default_bounds = false;
|
||||
let mut already_collected_associated_type = None;
|
||||
|
||||
// add deprecation notice for `RuntimeEvent`, iff pezpallet is not `pezframe_system`
|
||||
if is_event && !is_pezframe_system {
|
||||
if let syn::TraitItem::Type(type_event) = trait_item {
|
||||
let allow_dep: syn::Attribute = parse_quote!(#[allow(deprecated)]);
|
||||
|
||||
// Check if the `#[allow(deprecated)]` attribute is present
|
||||
if !type_event.attrs.iter().any(|attr| attr == &allow_dep) {
|
||||
let warning = Warning::new_deprecated("RuntimeEvent")
|
||||
.old("have `RuntimeEvent` associated type in the pezpallet config")
|
||||
.new("remove it as it is redundant since associated bound gets appended automatically: \n
|
||||
pub trait Config: pezframe_system::Config<RuntimeEvent: From<Event<Self>>> { }")
|
||||
.help_link("https://github.com/pezkuwichain/kurdistan-sdk/issues/125")
|
||||
.span(type_event.ident.span())
|
||||
.build_or_panic();
|
||||
|
||||
warnings.push(warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(pezpallet_attr) =
|
||||
helper::take_first_item_pallet_attr::<PalletAttr>(trait_item)?
|
||||
{
|
||||
match (pezpallet_attr.typ, &trait_item) {
|
||||
(PalletAttrType::Constant(_), syn::TraitItem::Type(ref typ)) => {
|
||||
if already_constant {
|
||||
return Err(syn::Error::new(
|
||||
pezpallet_attr._bracket.span.join(),
|
||||
"Duplicate #[pezpallet::constant] attribute not allowed.",
|
||||
));
|
||||
}
|
||||
already_constant = true;
|
||||
consts_metadata.push(ConstMetadataDef::try_from(typ)?);
|
||||
},
|
||||
(PalletAttrType::Constant(_), _) =>
|
||||
return Err(syn::Error::new(
|
||||
trait_item.span(),
|
||||
"Invalid #[pezpallet::constant] in #[pezpallet::config], expected type item",
|
||||
)),
|
||||
// Pezpallet developer has explicitly requested to include metadata for this associated type.
|
||||
//
|
||||
// They must provide a type item that implements `TypeInfo`.
|
||||
(PalletAttrType::IncludeMetadata(_), syn::TraitItem::Type(ref typ)) => {
|
||||
if already_collected_associated_type.is_some() {
|
||||
return Err(syn::Error::new(
|
||||
pezpallet_attr._bracket.span.join(),
|
||||
"Duplicate #[pezpallet::include_metadata] attribute not allowed.",
|
||||
));
|
||||
}
|
||||
already_collected_associated_type = Some(pezpallet_attr._bracket.span.join());
|
||||
associated_types_metadata.push(AssociatedTypeMetadataDef::from(AssociatedTypeMetadataDef::from(typ)));
|
||||
}
|
||||
(PalletAttrType::IncludeMetadata(_), _) =>
|
||||
return Err(syn::Error::new(
|
||||
pezpallet_attr._bracket.span.join(),
|
||||
"Invalid #[pezpallet::include_metadata] in #[pezpallet::config], expected type item",
|
||||
)),
|
||||
(PalletAttrType::NoDefault(_), _) => {
|
||||
if !enable_default {
|
||||
return Err(syn::Error::new(
|
||||
pezpallet_attr._bracket.span.join(),
|
||||
"`#[pezpallet::no_default]` can only be used if `#[pezpallet::config(with_default)]` \
|
||||
has been specified"
|
||||
));
|
||||
}
|
||||
if already_no_default {
|
||||
return Err(syn::Error::new(
|
||||
pezpallet_attr._bracket.span.join(),
|
||||
"Duplicate #[pezpallet::no_default] attribute not allowed.",
|
||||
));
|
||||
}
|
||||
|
||||
already_no_default = true;
|
||||
},
|
||||
(PalletAttrType::NoBounds(_), _) => {
|
||||
if !enable_default {
|
||||
return Err(syn::Error::new(
|
||||
pezpallet_attr._bracket.span.join(),
|
||||
"`#[pezpallet:no_default_bounds]` can only be used if `#[pezpallet::config(with_default)]` \
|
||||
has been specified"
|
||||
));
|
||||
}
|
||||
if already_no_default_bounds {
|
||||
return Err(syn::Error::new(
|
||||
pezpallet_attr._bracket.span.join(),
|
||||
"Duplicate #[pezpallet::no_default_bounds] attribute not allowed.",
|
||||
));
|
||||
}
|
||||
already_no_default_bounds = true;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(span) = already_collected_associated_type {
|
||||
// Events and constants are already propagated to the metadata
|
||||
if is_event {
|
||||
return Err(syn::Error::new(
|
||||
span,
|
||||
"Invalid #[pezpallet::include_metadata] for `type RuntimeEvent`. \
|
||||
The associated type `RuntimeEvent` is already collected in the metadata.",
|
||||
));
|
||||
}
|
||||
|
||||
if already_constant {
|
||||
return Err(syn::Error::new(
|
||||
span,
|
||||
"Invalid #[pezpallet::include_metadata]: conflict with #[pezpallet::constant]. \
|
||||
Pezpallet constant already collect the metadata for the type.",
|
||||
));
|
||||
}
|
||||
|
||||
if let syn::TraitItem::Type(ref ty) = trait_item {
|
||||
if !contains_type_info_bound(ty) {
|
||||
let msg = format!(
|
||||
"Invalid #[pezpallet::include_metadata] in #[pezpallet::config], collected type `{}` \
|
||||
does not implement `TypeInfo` or `Parameter`",
|
||||
ty.ident,
|
||||
);
|
||||
return Err(syn::Error::new(span, msg));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Metadata of associated types is collected by default, if the associated type
|
||||
// implements `TypeInfo`, or a similar trait that requires the `TypeInfo` bound.
|
||||
if !disable_associated_metadata && !is_event && !already_constant {
|
||||
if let syn::TraitItem::Type(ref ty) = trait_item {
|
||||
// Collect the metadata of the associated type if it implements `TypeInfo`.
|
||||
if contains_type_info_bound(ty) {
|
||||
associated_types_metadata.push(AssociatedTypeMetadataDef::from(ty));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !already_no_default && enable_default {
|
||||
default_sub_trait
|
||||
.as_mut()
|
||||
.expect("is 'Some(_)' if 'enable_default'; qed")
|
||||
.items
|
||||
.push((trait_item.clone(), already_no_default_bounds));
|
||||
}
|
||||
}
|
||||
|
||||
let attr: Option<DisableFrameSystemSupertraitCheck> =
|
||||
helper::take_first_item_pallet_attr(&mut item.attrs)?;
|
||||
let disable_system_supertrait_check = attr.is_some();
|
||||
|
||||
let has_pezframe_system_supertrait = item.supertraits.iter().any(|s| {
|
||||
syn::parse2::<syn::Path>(s.to_token_stream())
|
||||
.map_or(false, |b| has_expected_system_config(b, pezframe_system))
|
||||
});
|
||||
|
||||
if !has_pezframe_system_supertrait && !disable_system_supertrait_check {
|
||||
let found = if item.supertraits.is_empty() {
|
||||
"none".to_string()
|
||||
} else {
|
||||
let mut found = item
|
||||
.supertraits
|
||||
.iter()
|
||||
.fold(String::new(), |acc, s| format!("{}`{}`, ", acc, quote::quote!(#s)));
|
||||
found.pop();
|
||||
found.pop();
|
||||
found
|
||||
};
|
||||
|
||||
let msg = format!(
|
||||
"Invalid pezpallet::trait, expected explicit `{}::Config` as supertrait, \
|
||||
found {}. \
|
||||
(try `pub trait Config: pezframe_system::Config {{ ...` or \
|
||||
`pub trait Config<I: 'static>: pezframe_system::Config {{ ...`). \
|
||||
To disable this check, use `#[pezpallet::disable_pezframe_system_supertrait_check]`",
|
||||
pezframe_system.to_token_stream(),
|
||||
found,
|
||||
);
|
||||
return Err(syn::Error::new(item.span(), msg));
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
index,
|
||||
has_instance,
|
||||
consts_metadata,
|
||||
associated_types_metadata,
|
||||
where_clause,
|
||||
default_sub_trait,
|
||||
warnings,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn has_expected_system_config_works() {
|
||||
let pezframe_system = syn::parse2::<syn::Path>(quote::quote!(pezframe_system)).unwrap();
|
||||
let path = syn::parse2::<syn::Path>(quote::quote!(pezframe_system::Config)).unwrap();
|
||||
assert!(has_expected_system_config(path, &pezframe_system));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_expected_system_config_works_with_assoc_type() {
|
||||
let pezframe_system = syn::parse2::<syn::Path>(quote::quote!(pezframe_system)).unwrap();
|
||||
let path =
|
||||
syn::parse2::<syn::Path>(quote::quote!(pezframe_system::Config<RuntimeCall = Call>))
|
||||
.unwrap();
|
||||
assert!(has_expected_system_config(path, &pezframe_system));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_expected_system_config_works_with_frame() {
|
||||
let path = syn::parse2::<syn::Path>(quote::quote!(pezframe_system::Config)).unwrap();
|
||||
|
||||
let pezframe_system =
|
||||
syn::parse2::<syn::Path>(quote::quote!(pezkuwi_sdk_frame::deps::pezframe_system)).unwrap();
|
||||
assert!(has_expected_system_config(path.clone(), &pezframe_system));
|
||||
|
||||
let pezframe_system =
|
||||
syn::parse2::<syn::Path>(quote::quote!(frame::deps::pezframe_system)).unwrap();
|
||||
assert!(has_expected_system_config(path, &pezframe_system));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_expected_system_config_works_with_frame_full_path() {
|
||||
let pezframe_system =
|
||||
syn::parse2::<syn::Path>(quote::quote!(pezkuwi_sdk_frame::deps::pezframe_system)).unwrap();
|
||||
let path =
|
||||
syn::parse2::<syn::Path>(quote::quote!(pezkuwi_sdk_frame::deps::pezframe_system::Config))
|
||||
.unwrap();
|
||||
assert!(has_expected_system_config(path, &pezframe_system));
|
||||
|
||||
let pezframe_system =
|
||||
syn::parse2::<syn::Path>(quote::quote!(frame::deps::pezframe_system)).unwrap();
|
||||
let path =
|
||||
syn::parse2::<syn::Path>(quote::quote!(frame::deps::pezframe_system::Config)).unwrap();
|
||||
assert!(has_expected_system_config(path, &pezframe_system));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_expected_system_config_works_with_other_frame_full_path() {
|
||||
let pezframe_system =
|
||||
syn::parse2::<syn::Path>(quote::quote!(pezkuwi_sdk_frame::xyz::pezframe_system)).unwrap();
|
||||
let path =
|
||||
syn::parse2::<syn::Path>(quote::quote!(pezkuwi_sdk_frame::xyz::pezframe_system::Config))
|
||||
.unwrap();
|
||||
assert!(has_expected_system_config(path, &pezframe_system));
|
||||
|
||||
let pezframe_system =
|
||||
syn::parse2::<syn::Path>(quote::quote!(frame::xyz::pezframe_system)).unwrap();
|
||||
let path =
|
||||
syn::parse2::<syn::Path>(quote::quote!(frame::xyz::pezframe_system::Config)).unwrap();
|
||||
assert!(has_expected_system_config(path, &pezframe_system));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_expected_system_config_does_not_works_with_mixed_frame_full_path() {
|
||||
let pezframe_system =
|
||||
syn::parse2::<syn::Path>(quote::quote!(pezkuwi_sdk_frame::xyz::pezframe_system)).unwrap();
|
||||
let path =
|
||||
syn::parse2::<syn::Path>(quote::quote!(pezkuwi_sdk_frame::deps::pezframe_system::Config))
|
||||
.unwrap();
|
||||
assert!(!has_expected_system_config(path, &pezframe_system));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_expected_system_config_does_not_works_with_other_mixed_frame_full_path() {
|
||||
let pezframe_system =
|
||||
syn::parse2::<syn::Path>(quote::quote!(pezkuwi_sdk_frame::deps::pezframe_system)).unwrap();
|
||||
let path =
|
||||
syn::parse2::<syn::Path>(quote::quote!(pezkuwi_sdk_frame::xyz::pezframe_system::Config))
|
||||
.unwrap();
|
||||
assert!(!has_expected_system_config(path, &pezframe_system));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_expected_system_config_does_not_work_with_frame_full_path_if_not_frame_crate() {
|
||||
let pezframe_system = syn::parse2::<syn::Path>(quote::quote!(pezframe_system)).unwrap();
|
||||
let path =
|
||||
syn::parse2::<syn::Path>(quote::quote!(pezkuwi_sdk_frame::deps::pezframe_system::Config))
|
||||
.unwrap();
|
||||
assert!(!has_expected_system_config(path, &pezframe_system));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_expected_system_config_unexpected_pezframe_system() {
|
||||
let pezframe_system =
|
||||
syn::parse2::<syn::Path>(quote::quote!(framez::deps::pezframe_system)).unwrap();
|
||||
let path = syn::parse2::<syn::Path>(quote::quote!(pezframe_system::Config)).unwrap();
|
||||
assert!(!has_expected_system_config(path, &pezframe_system));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_expected_system_config_unexpected_path() {
|
||||
let pezframe_system = syn::parse2::<syn::Path>(quote::quote!(pezframe_system)).unwrap();
|
||||
let path = syn::parse2::<syn::Path>(quote::quote!(pezframe_system::ConfigSystem)).unwrap();
|
||||
assert!(!has_expected_system_config(path, &pezframe_system));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn has_expected_system_config_not_pezframe_system() {
|
||||
let pezframe_system = syn::parse2::<syn::Path>(quote::quote!(something)).unwrap();
|
||||
let path = syn::parse2::<syn::Path>(quote::quote!(something::Config)).unwrap();
|
||||
assert!(!has_expected_system_config(path, &pezframe_system));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::helper;
|
||||
use crate::deprecation::extract_or_return_allow_attrs;
|
||||
use quote::ToTokens;
|
||||
use syn::{spanned::Spanned, Fields};
|
||||
|
||||
/// List of additional token to be used for parsing.
|
||||
mod keyword {
|
||||
syn::custom_keyword!(Error);
|
||||
}
|
||||
|
||||
/// Records information about the error enum variant field.
|
||||
pub struct VariantField {
|
||||
/// Whether or not the field is named, i.e. whether it is a tuple variant or struct variant.
|
||||
pub is_named: bool,
|
||||
}
|
||||
|
||||
/// Records information about the error enum variants.
|
||||
pub struct VariantDef {
|
||||
/// The variant ident.
|
||||
pub ident: syn::Ident,
|
||||
/// The variant field, if any.
|
||||
pub field: Option<VariantField>,
|
||||
/// The `cfg` attributes.
|
||||
pub cfg_attrs: Vec<syn::Attribute>,
|
||||
/// The `allow` attributes.
|
||||
pub maybe_allow_attrs: Vec<syn::Attribute>,
|
||||
}
|
||||
|
||||
/// This checks error declaration as a enum declaration with only variants without fields nor
|
||||
/// discriminant.
|
||||
pub struct ErrorDef {
|
||||
/// The index of error item in pezpallet module.
|
||||
pub index: usize,
|
||||
/// Variant definitions.
|
||||
pub variants: Vec<VariantDef>,
|
||||
/// A set of usage of instance, must be check for consistency with trait.
|
||||
pub instances: Vec<helper::InstanceUsage>,
|
||||
/// The keyword error used (contains span).
|
||||
pub error: keyword::Error,
|
||||
/// The span of the pezpallet::error attribute.
|
||||
pub attr_span: proc_macro2::Span,
|
||||
}
|
||||
|
||||
impl ErrorDef {
|
||||
pub fn try_from(
|
||||
attr_span: proc_macro2::Span,
|
||||
index: usize,
|
||||
item: &mut syn::Item,
|
||||
) -> syn::Result<Self> {
|
||||
let item = if let syn::Item::Enum(item) = item {
|
||||
item
|
||||
} else {
|
||||
return Err(syn::Error::new(item.span(), "Invalid pezpallet::error, expected item enum"));
|
||||
};
|
||||
if !matches!(item.vis, syn::Visibility::Public(_)) {
|
||||
let msg = "Invalid pezpallet::error, `Error` must be public";
|
||||
return Err(syn::Error::new(item.span(), msg));
|
||||
}
|
||||
|
||||
crate::deprecation::prevent_deprecation_attr_on_outer_enum(&item.attrs)?;
|
||||
|
||||
let instances =
|
||||
vec![helper::check_type_def_gen_no_bounds(&item.generics, item.ident.span())?];
|
||||
|
||||
if item.generics.where_clause.is_some() {
|
||||
let msg = "Invalid pezpallet::error, where clause is not allowed on pezpallet error item";
|
||||
return Err(syn::Error::new(item.generics.where_clause.as_ref().unwrap().span(), msg));
|
||||
}
|
||||
|
||||
let error = syn::parse2::<keyword::Error>(item.ident.to_token_stream())?;
|
||||
|
||||
let variants = item
|
||||
.variants
|
||||
.iter()
|
||||
.map(|variant| {
|
||||
let field_ty = match &variant.fields {
|
||||
Fields::Unit => None,
|
||||
Fields::Named(_) => Some(VariantField { is_named: true }),
|
||||
Fields::Unnamed(_) => Some(VariantField { is_named: false }),
|
||||
};
|
||||
|
||||
match &variant.discriminant {
|
||||
None |
|
||||
Some((_, syn::Expr::Lit(syn::ExprLit { lit: syn::Lit::Int(_), .. }))) => {},
|
||||
Some((_, expr)) => {
|
||||
let msg = "Invalid pezpallet::error, only integer discriminants are supported";
|
||||
return Err(syn::Error::new(expr.span(), msg));
|
||||
},
|
||||
}
|
||||
let cfg_attrs: Vec<syn::Attribute> = helper::get_item_cfg_attrs(&variant.attrs);
|
||||
let maybe_allow_attrs = extract_or_return_allow_attrs(&variant.attrs).collect();
|
||||
|
||||
Ok(VariantDef {
|
||||
ident: variant.ident.clone(),
|
||||
field: field_ty,
|
||||
cfg_attrs,
|
||||
maybe_allow_attrs,
|
||||
})
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
Ok(ErrorDef { attr_span, index, variants, instances, error })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::helper;
|
||||
use quote::ToTokens;
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
/// List of additional token to be used for parsing.
|
||||
mod keyword {
|
||||
syn::custom_keyword!(Event);
|
||||
syn::custom_keyword!(pezpallet);
|
||||
syn::custom_keyword!(generate_deposit);
|
||||
syn::custom_keyword!(deposit_event);
|
||||
}
|
||||
|
||||
/// Definition for pezpallet event enum.
|
||||
pub struct EventDef {
|
||||
/// The index of event item in pezpallet module.
|
||||
pub index: usize,
|
||||
/// The keyword Event used (contains span).
|
||||
pub event: keyword::Event,
|
||||
/// A set of usage of instance, must be check for consistency with trait.
|
||||
pub instances: Vec<helper::InstanceUsage>,
|
||||
/// The kind of generic the type `Event` has.
|
||||
pub gen_kind: super::GenericKind,
|
||||
/// Whether the function `deposit_event` must be generated.
|
||||
pub deposit_event: Option<PalletEventDepositAttr>,
|
||||
/// Where clause used in event definition.
|
||||
pub where_clause: Option<syn::WhereClause>,
|
||||
/// The span of the pezpallet::event attribute.
|
||||
pub attr_span: proc_macro2::Span,
|
||||
}
|
||||
|
||||
/// Attribute for a pezpallet's Event.
|
||||
///
|
||||
/// Syntax is:
|
||||
/// * `#[pezpallet::generate_deposit($vis fn deposit_event)]`
|
||||
pub struct PalletEventDepositAttr {
|
||||
pub fn_vis: syn::Visibility,
|
||||
// Span for the keyword deposit_event
|
||||
pub fn_span: proc_macro2::Span,
|
||||
// Span of the attribute
|
||||
pub span: proc_macro2::Span,
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for PalletEventDepositAttr {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
input.parse::<syn::Token![#]>()?;
|
||||
let content;
|
||||
syn::bracketed!(content in input);
|
||||
content.parse::<keyword::pezpallet>()?;
|
||||
content.parse::<syn::Token![::]>()?;
|
||||
|
||||
let span = content.parse::<keyword::generate_deposit>()?.span();
|
||||
let generate_content;
|
||||
syn::parenthesized!(generate_content in content);
|
||||
let fn_vis = generate_content.parse::<syn::Visibility>()?;
|
||||
generate_content.parse::<syn::Token![fn]>()?;
|
||||
let fn_span = generate_content.parse::<keyword::deposit_event>()?.span();
|
||||
|
||||
Ok(PalletEventDepositAttr { fn_vis, span, fn_span })
|
||||
}
|
||||
}
|
||||
|
||||
struct PalletEventAttrInfo {
|
||||
deposit_event: Option<PalletEventDepositAttr>,
|
||||
}
|
||||
|
||||
impl PalletEventAttrInfo {
|
||||
fn from_attrs(attrs: Vec<PalletEventDepositAttr>) -> syn::Result<Self> {
|
||||
let mut deposit_event = None;
|
||||
for attr in attrs {
|
||||
if deposit_event.is_none() {
|
||||
deposit_event = Some(attr)
|
||||
} else {
|
||||
return Err(syn::Error::new(attr.span, "Duplicate attribute"));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(PalletEventAttrInfo { deposit_event })
|
||||
}
|
||||
}
|
||||
|
||||
impl EventDef {
|
||||
pub fn try_from(
|
||||
attr_span: proc_macro2::Span,
|
||||
index: usize,
|
||||
item: &mut syn::Item,
|
||||
) -> syn::Result<Self> {
|
||||
let item = if let syn::Item::Enum(item) = item {
|
||||
item
|
||||
} else {
|
||||
return Err(syn::Error::new(item.span(), "Invalid pezpallet::event, expected enum item"));
|
||||
};
|
||||
|
||||
crate::deprecation::prevent_deprecation_attr_on_outer_enum(&item.attrs)?;
|
||||
|
||||
let event_attrs: Vec<PalletEventDepositAttr> =
|
||||
helper::take_item_pallet_attrs(&mut item.attrs)?;
|
||||
let attr_info = PalletEventAttrInfo::from_attrs(event_attrs)?;
|
||||
let deposit_event = attr_info.deposit_event;
|
||||
|
||||
if !matches!(item.vis, syn::Visibility::Public(_)) {
|
||||
let msg = "Invalid pezpallet::event, `Event` must be public";
|
||||
return Err(syn::Error::new(item.span(), msg));
|
||||
}
|
||||
|
||||
let where_clause = item.generics.where_clause.clone();
|
||||
|
||||
let mut instances = vec![];
|
||||
// NOTE: Event is not allowed to be only generic on I because it is not supported
|
||||
// by construct_runtime.
|
||||
if let Some(u) = helper::check_type_def_optional_gen(&item.generics, item.ident.span())? {
|
||||
instances.push(u);
|
||||
} else {
|
||||
// construct_runtime only allow non generic event for non instantiable pezpallet.
|
||||
instances.push(helper::InstanceUsage { has_instance: false, span: item.ident.span() })
|
||||
}
|
||||
|
||||
let has_instance = item.generics.type_params().any(|t| t.ident == "I");
|
||||
let has_config = item.generics.type_params().any(|t| t.ident == "T");
|
||||
let gen_kind = super::GenericKind::from_gens(has_config, has_instance)
|
||||
.expect("Checked by `helper::check_type_def_optional_gen` above");
|
||||
|
||||
let event = syn::parse2::<keyword::Event>(item.ident.to_token_stream())?;
|
||||
|
||||
Ok(EventDef { attr_span, index, instances, deposit_event, event, gen_kind, where_clause })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::helper;
|
||||
use pezframe_support_procedural_tools::get_doc_literals;
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
/// List of additional token to be used for parsing.
|
||||
mod keyword {
|
||||
syn::custom_keyword!(DispatchResultWithPostInfo);
|
||||
syn::custom_keyword!(Call);
|
||||
syn::custom_keyword!(OriginFor);
|
||||
syn::custom_keyword!(weight);
|
||||
syn::custom_keyword!(compact);
|
||||
syn::custom_keyword!(T);
|
||||
syn::custom_keyword!(pezpallet);
|
||||
syn::custom_keyword!(constant_name);
|
||||
}
|
||||
|
||||
/// Definition of extra constants typically `impl<T: Config> Pezpallet<T> { ... }`
|
||||
pub struct ExtraConstantsDef {
|
||||
/// The where_clause used.
|
||||
pub where_clause: Option<syn::WhereClause>,
|
||||
/// A set of usage of instance, must be check for consistency with trait.
|
||||
pub instances: Vec<helper::InstanceUsage>,
|
||||
/// The extra constant defined.
|
||||
pub extra_constants: Vec<ExtraConstantDef>,
|
||||
}
|
||||
|
||||
/// Input definition for an constant in pezpallet.
|
||||
pub struct ExtraConstantDef {
|
||||
/// Name of the function
|
||||
pub ident: syn::Ident,
|
||||
/// The type returned by the function
|
||||
pub type_: syn::Type,
|
||||
/// The doc associated
|
||||
pub doc: Vec<syn::Expr>,
|
||||
/// Optional MetaData Name
|
||||
pub metadata_name: Option<syn::Ident>,
|
||||
/// Attributes
|
||||
pub attrs: Vec<syn::Attribute>,
|
||||
}
|
||||
|
||||
/// Attributes for functions in extra_constants impl block.
|
||||
/// Parse for `#[pezpallet::constant_name(ConstantName)]`
|
||||
pub struct ExtraConstAttr {
|
||||
metadata_name: syn::Ident,
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for ExtraConstAttr {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
input.parse::<syn::Token![#]>()?;
|
||||
let content;
|
||||
syn::bracketed!(content in input);
|
||||
content.parse::<keyword::pezpallet>()?;
|
||||
content.parse::<syn::Token![::]>()?;
|
||||
content.parse::<keyword::constant_name>()?;
|
||||
|
||||
let metadata_name;
|
||||
syn::parenthesized!(metadata_name in content);
|
||||
Ok(ExtraConstAttr { metadata_name: metadata_name.parse::<syn::Ident>()? })
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtraConstantsDef {
|
||||
pub fn try_from(item: &mut syn::Item) -> syn::Result<Self> {
|
||||
let item = if let syn::Item::Impl(item) = item {
|
||||
item
|
||||
} else {
|
||||
return Err(syn::Error::new(
|
||||
item.span(),
|
||||
"Invalid pezpallet::extra_constants, expected item impl",
|
||||
));
|
||||
};
|
||||
|
||||
let instances = vec![
|
||||
helper::check_impl_gen(&item.generics, item.impl_token.span())?,
|
||||
helper::check_pallet_struct_usage(&item.self_ty)?,
|
||||
];
|
||||
|
||||
if let Some((_, _, for_)) = item.trait_ {
|
||||
let msg = "Invalid pezpallet::call, expected no trait ident as in \
|
||||
`impl<..> Pezpallet<..> { .. }`";
|
||||
return Err(syn::Error::new(for_.span(), msg));
|
||||
}
|
||||
|
||||
let mut extra_constants = vec![];
|
||||
for impl_item in &mut item.items {
|
||||
let method = if let syn::ImplItem::Fn(method) = impl_item {
|
||||
method
|
||||
} else {
|
||||
let msg = "Invalid pezpallet::call, only method accepted";
|
||||
return Err(syn::Error::new(impl_item.span(), msg));
|
||||
};
|
||||
|
||||
if !method.sig.inputs.is_empty() {
|
||||
let msg = "Invalid pezpallet::extra_constants, method must have 0 args";
|
||||
return Err(syn::Error::new(method.sig.span(), msg));
|
||||
}
|
||||
|
||||
if !method.sig.generics.params.is_empty() {
|
||||
let msg = "Invalid pezpallet::extra_constants, method must have 0 generics";
|
||||
return Err(syn::Error::new(method.sig.generics.params[0].span(), msg));
|
||||
}
|
||||
|
||||
if method.sig.generics.where_clause.is_some() {
|
||||
let msg = "Invalid pezpallet::extra_constants, method must have no where clause";
|
||||
return Err(syn::Error::new(method.sig.generics.where_clause.span(), msg));
|
||||
}
|
||||
|
||||
let type_ = match &method.sig.output {
|
||||
syn::ReturnType::Default => {
|
||||
let msg = "Invalid pezpallet::extra_constants, method must have a return type";
|
||||
return Err(syn::Error::new(method.span(), msg));
|
||||
},
|
||||
syn::ReturnType::Type(_, type_) => *type_.clone(),
|
||||
};
|
||||
|
||||
// parse metadata_name
|
||||
let mut extra_constant_attrs: Vec<ExtraConstAttr> =
|
||||
helper::take_item_pallet_attrs(method)?;
|
||||
|
||||
if extra_constant_attrs.len() > 1 {
|
||||
let msg =
|
||||
"Invalid attribute in pezpallet::constant_name, only one attribute is expected";
|
||||
return Err(syn::Error::new(extra_constant_attrs[1].metadata_name.span(), msg));
|
||||
}
|
||||
|
||||
let metadata_name = extra_constant_attrs.pop().map(|attr| attr.metadata_name);
|
||||
|
||||
extra_constants.push(ExtraConstantDef {
|
||||
ident: method.sig.ident.clone(),
|
||||
type_,
|
||||
doc: get_doc_literals(&method.attrs),
|
||||
metadata_name,
|
||||
attrs: method.attrs.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
Ok(Self { instances, where_clause: item.generics.where_clause.clone(), extra_constants })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::helper;
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
/// Definition for pezpallet genesis build implementation.
|
||||
pub struct GenesisBuildDef {
|
||||
/// A set of usage of instance, must be check for consistency with trait.
|
||||
pub instances: Option<Vec<helper::InstanceUsage>>,
|
||||
/// The where_clause used.
|
||||
pub where_clause: Option<syn::WhereClause>,
|
||||
/// The span of the pezpallet::genesis_build attribute.
|
||||
pub attr_span: proc_macro2::Span,
|
||||
}
|
||||
|
||||
impl GenesisBuildDef {
|
||||
pub fn try_from(attr_span: proc_macro2::Span, item: &mut syn::Item) -> syn::Result<Self> {
|
||||
let item = if let syn::Item::Impl(item) = item {
|
||||
item
|
||||
} else {
|
||||
let msg = "Invalid pezpallet::genesis_build, expected item impl";
|
||||
return Err(syn::Error::new(item.span(), msg));
|
||||
};
|
||||
|
||||
let item_trait = &item
|
||||
.trait_
|
||||
.as_ref()
|
||||
.ok_or_else(|| {
|
||||
let msg = "Invalid pezpallet::genesis_build, expected impl<..> GenesisBuild<..> \
|
||||
for GenesisConfig<..>";
|
||||
syn::Error::new(item.span(), msg)
|
||||
})?
|
||||
.1;
|
||||
|
||||
let instances =
|
||||
helper::check_genesis_builder_usage(item_trait)?.map(|instances| vec![instances]);
|
||||
|
||||
Ok(Self { attr_span, instances, where_clause: item.generics.where_clause.clone() })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::helper;
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
/// Definition for pezpallet genesis config type.
|
||||
///
|
||||
/// Either:
|
||||
/// * `struct GenesisConfig`
|
||||
/// * `enum GenesisConfig`
|
||||
pub struct GenesisConfigDef {
|
||||
/// The index of item in pezpallet module.
|
||||
pub index: usize,
|
||||
/// The kind of generic the type `GenesisConfig` has.
|
||||
pub gen_kind: super::GenericKind,
|
||||
/// A set of usage of instance, must be check for consistency with trait.
|
||||
pub instances: Vec<helper::InstanceUsage>,
|
||||
/// The ident of genesis_config, can be used for span.
|
||||
pub genesis_config: syn::Ident,
|
||||
}
|
||||
|
||||
impl GenesisConfigDef {
|
||||
pub fn try_from(index: usize, item: &mut syn::Item) -> syn::Result<Self> {
|
||||
let item_span = item.span();
|
||||
let (vis, ident, generics) = match &item {
|
||||
syn::Item::Enum(item) => (&item.vis, &item.ident, &item.generics),
|
||||
syn::Item::Struct(item) => (&item.vis, &item.ident, &item.generics),
|
||||
_ => {
|
||||
let msg = "Invalid pezpallet::genesis_config, expected enum or struct";
|
||||
return Err(syn::Error::new(item.span(), msg));
|
||||
},
|
||||
};
|
||||
|
||||
let mut instances = vec![];
|
||||
// NOTE: GenesisConfig is not allowed to be only generic on I because it is not supported
|
||||
// by construct_runtime.
|
||||
if let Some(u) = helper::check_type_def_optional_gen(generics, ident.span())? {
|
||||
instances.push(u);
|
||||
}
|
||||
|
||||
let has_instance = generics.type_params().any(|t| t.ident == "I");
|
||||
let has_config = generics.type_params().any(|t| t.ident == "T");
|
||||
let gen_kind = super::GenericKind::from_gens(has_config, has_instance)
|
||||
.expect("Checked by `helper::check_type_def_optional_gen` above");
|
||||
|
||||
if !matches!(vis, syn::Visibility::Public(_)) {
|
||||
let msg = "Invalid pezpallet::genesis_config, GenesisConfig must be public";
|
||||
return Err(syn::Error::new(item_span, msg));
|
||||
}
|
||||
|
||||
if ident != "GenesisConfig" {
|
||||
let msg = "Invalid pezpallet::genesis_config, ident must `GenesisConfig`";
|
||||
return Err(syn::Error::new(ident.span(), msg));
|
||||
}
|
||||
|
||||
Ok(GenesisConfigDef { index, genesis_config: ident.clone(), instances, gen_kind })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,645 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
/// List of additional token to be used for parsing.
|
||||
mod keyword {
|
||||
syn::custom_keyword!(I);
|
||||
syn::custom_keyword!(compact);
|
||||
syn::custom_keyword!(GenesisBuild);
|
||||
syn::custom_keyword!(BuildGenesisConfig);
|
||||
syn::custom_keyword!(Config);
|
||||
syn::custom_keyword!(T);
|
||||
syn::custom_keyword!(Pezpallet);
|
||||
syn::custom_keyword!(origin);
|
||||
syn::custom_keyword!(DispatchResult);
|
||||
syn::custom_keyword!(DispatchResultWithPostInfo);
|
||||
}
|
||||
|
||||
/// A usage of instance, either the trait `Config` has been used with instance or without instance.
|
||||
/// Used to check for consistency.
|
||||
#[derive(Clone)]
|
||||
pub struct InstanceUsage {
|
||||
pub has_instance: bool,
|
||||
pub span: proc_macro2::Span,
|
||||
}
|
||||
|
||||
/// Trait implemented for syn items to get mutable references on their attributes.
|
||||
///
|
||||
/// NOTE: verbatim variants are not supported.
|
||||
pub trait MutItemAttrs {
|
||||
fn mut_item_attrs(&mut self) -> Option<&mut Vec<syn::Attribute>>;
|
||||
}
|
||||
|
||||
/// Take the first pezpallet attribute (e.g. attribute like `#[pezpallet..]`) and decode it to `Attr`
|
||||
pub(crate) fn take_first_item_pallet_attr<Attr>(
|
||||
item: &mut impl MutItemAttrs,
|
||||
) -> syn::Result<Option<Attr>>
|
||||
where
|
||||
Attr: syn::parse::Parse,
|
||||
{
|
||||
let Some(attrs) = item.mut_item_attrs() else { return Ok(None) };
|
||||
|
||||
let Some(index) = attrs.iter().position(|attr| {
|
||||
attr.path().segments.first().map_or(false, |segment| segment.ident == "pezpallet")
|
||||
}) else {
|
||||
return Ok(None);
|
||||
};
|
||||
|
||||
let pezpallet_attr = attrs.remove(index);
|
||||
Ok(Some(syn::parse2(pezpallet_attr.into_token_stream())?))
|
||||
}
|
||||
|
||||
/// Take all the pezpallet attributes (e.g. attribute like `#[pezpallet..]`) and decode them to `Attr`
|
||||
pub(crate) fn take_item_pallet_attrs<Attr>(item: &mut impl MutItemAttrs) -> syn::Result<Vec<Attr>>
|
||||
where
|
||||
Attr: syn::parse::Parse,
|
||||
{
|
||||
let mut pezpallet_attrs = Vec::new();
|
||||
|
||||
while let Some(attr) = take_first_item_pallet_attr(item)? {
|
||||
pezpallet_attrs.push(attr)
|
||||
}
|
||||
|
||||
Ok(pezpallet_attrs)
|
||||
}
|
||||
|
||||
/// Get all the cfg attributes (e.g. attribute like `#[cfg..]`) and decode them to `Attr`
|
||||
pub fn get_item_cfg_attrs(attrs: &[syn::Attribute]) -> Vec<syn::Attribute> {
|
||||
attrs
|
||||
.iter()
|
||||
.filter_map(|attr| {
|
||||
if attr.path().segments.first().map_or(false, |segment| segment.ident == "cfg") {
|
||||
Some(attr.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
impl MutItemAttrs for syn::Item {
|
||||
fn mut_item_attrs(&mut self) -> Option<&mut Vec<syn::Attribute>> {
|
||||
match self {
|
||||
Self::Const(item) => Some(item.attrs.as_mut()),
|
||||
Self::Enum(item) => Some(item.attrs.as_mut()),
|
||||
Self::ExternCrate(item) => Some(item.attrs.as_mut()),
|
||||
Self::Fn(item) => Some(item.attrs.as_mut()),
|
||||
Self::ForeignMod(item) => Some(item.attrs.as_mut()),
|
||||
Self::Impl(item) => Some(item.attrs.as_mut()),
|
||||
Self::Macro(item) => Some(item.attrs.as_mut()),
|
||||
Self::Mod(item) => Some(item.attrs.as_mut()),
|
||||
Self::Static(item) => Some(item.attrs.as_mut()),
|
||||
Self::Struct(item) => Some(item.attrs.as_mut()),
|
||||
Self::Trait(item) => Some(item.attrs.as_mut()),
|
||||
Self::TraitAlias(item) => Some(item.attrs.as_mut()),
|
||||
Self::Type(item) => Some(item.attrs.as_mut()),
|
||||
Self::Union(item) => Some(item.attrs.as_mut()),
|
||||
Self::Use(item) => Some(item.attrs.as_mut()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MutItemAttrs for syn::TraitItem {
|
||||
fn mut_item_attrs(&mut self) -> Option<&mut Vec<syn::Attribute>> {
|
||||
match self {
|
||||
Self::Const(item) => Some(item.attrs.as_mut()),
|
||||
Self::Fn(item) => Some(item.attrs.as_mut()),
|
||||
Self::Type(item) => Some(item.attrs.as_mut()),
|
||||
Self::Macro(item) => Some(item.attrs.as_mut()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MutItemAttrs for Vec<syn::Attribute> {
|
||||
fn mut_item_attrs(&mut self) -> Option<&mut Vec<syn::Attribute>> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl MutItemAttrs for syn::ItemMod {
|
||||
fn mut_item_attrs(&mut self) -> Option<&mut Vec<syn::Attribute>> {
|
||||
Some(&mut self.attrs)
|
||||
}
|
||||
}
|
||||
|
||||
impl MutItemAttrs for syn::ImplItemFn {
|
||||
fn mut_item_attrs(&mut self) -> Option<&mut Vec<syn::Attribute>> {
|
||||
Some(&mut self.attrs)
|
||||
}
|
||||
}
|
||||
|
||||
impl MutItemAttrs for syn::ItemType {
|
||||
fn mut_item_attrs(&mut self) -> Option<&mut Vec<syn::Attribute>> {
|
||||
Some(&mut self.attrs)
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse for `()`
|
||||
struct Unit;
|
||||
impl syn::parse::Parse for Unit {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let content;
|
||||
syn::parenthesized!(content in input);
|
||||
if !content.is_empty() {
|
||||
let msg = "unexpected tokens, expected nothing inside parenthesis as `()`";
|
||||
return Err(syn::Error::new(content.span(), msg));
|
||||
}
|
||||
Ok(Self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse for `'static`
|
||||
struct StaticLifetime;
|
||||
impl syn::parse::Parse for StaticLifetime {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let lifetime = input.parse::<syn::Lifetime>()?;
|
||||
if lifetime.ident != "static" {
|
||||
let msg = "unexpected tokens, expected `static`";
|
||||
return Err(syn::Error::new(lifetime.ident.span(), msg));
|
||||
}
|
||||
Ok(Self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Check the syntax: `I: 'static = ()`
|
||||
///
|
||||
/// `span` is used in case generics is empty (empty generics has span == call_site).
|
||||
///
|
||||
/// return the instance if found.
|
||||
pub fn check_config_def_gen(gen: &syn::Generics, span: proc_macro2::Span) -> syn::Result<()> {
|
||||
let expected = "expected `I: 'static = ()`";
|
||||
pub struct CheckTraitDefGenerics;
|
||||
impl syn::parse::Parse for CheckTraitDefGenerics {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
input.parse::<keyword::I>()?;
|
||||
input.parse::<syn::Token![:]>()?;
|
||||
input.parse::<StaticLifetime>()?;
|
||||
input.parse::<syn::Token![=]>()?;
|
||||
input.parse::<Unit>()?;
|
||||
|
||||
Ok(Self)
|
||||
}
|
||||
}
|
||||
|
||||
syn::parse2::<CheckTraitDefGenerics>(gen.params.to_token_stream()).map_err(|e| {
|
||||
let msg = format!("Invalid generics: {}", expected);
|
||||
let mut err = syn::Error::new(span, msg);
|
||||
err.combine(e);
|
||||
err
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check the syntax:
|
||||
/// * either `T`
|
||||
/// * or `T, I = ()`
|
||||
///
|
||||
/// `span` is used in case generics is empty (empty generics has span == call_site).
|
||||
///
|
||||
/// return the instance if found.
|
||||
pub fn check_type_def_gen_no_bounds(
|
||||
gen: &syn::Generics,
|
||||
span: proc_macro2::Span,
|
||||
) -> syn::Result<InstanceUsage> {
|
||||
let expected = "expected `T` or `T, I = ()`";
|
||||
pub struct Checker(InstanceUsage);
|
||||
impl syn::parse::Parse for Checker {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let mut instance_usage = InstanceUsage { has_instance: false, span: input.span() };
|
||||
|
||||
input.parse::<keyword::T>()?;
|
||||
if input.peek(syn::Token![,]) {
|
||||
instance_usage.has_instance = true;
|
||||
input.parse::<syn::Token![,]>()?;
|
||||
input.parse::<keyword::I>()?;
|
||||
input.parse::<syn::Token![=]>()?;
|
||||
input.parse::<Unit>()?;
|
||||
}
|
||||
|
||||
Ok(Self(instance_usage))
|
||||
}
|
||||
}
|
||||
|
||||
let i = syn::parse2::<Checker>(gen.params.to_token_stream())
|
||||
.map_err(|e| {
|
||||
let msg = format!("Invalid type def generics: {}", expected);
|
||||
let mut err = syn::Error::new(span, msg);
|
||||
err.combine(e);
|
||||
err
|
||||
})?
|
||||
.0;
|
||||
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
/// Check the syntax:
|
||||
/// * either `` (no generics
|
||||
/// * or `T`
|
||||
/// * or `T: Config`
|
||||
/// * or `T, I = ()`
|
||||
/// * or `T: Config<I>, I: 'static = ()`
|
||||
///
|
||||
/// `span` is used in case generics is empty (empty generics has span == call_site).
|
||||
///
|
||||
/// return some instance usage if there is some generic, or none otherwise.
|
||||
pub fn check_type_def_optional_gen(
|
||||
gen: &syn::Generics,
|
||||
span: proc_macro2::Span,
|
||||
) -> syn::Result<Option<InstanceUsage>> {
|
||||
let expected = "expected `` or `T` or `T: Config` or `T, I = ()` or \
|
||||
`T: Config<I>, I: 'static = ()`";
|
||||
pub struct Checker(Option<InstanceUsage>);
|
||||
impl syn::parse::Parse for Checker {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
if input.is_empty() {
|
||||
return Ok(Self(None));
|
||||
}
|
||||
|
||||
let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false };
|
||||
|
||||
input.parse::<keyword::T>()?;
|
||||
|
||||
if input.is_empty() {
|
||||
return Ok(Self(Some(instance_usage)));
|
||||
}
|
||||
|
||||
let lookahead = input.lookahead1();
|
||||
if lookahead.peek(syn::Token![,]) {
|
||||
instance_usage.has_instance = true;
|
||||
input.parse::<syn::Token![,]>()?;
|
||||
input.parse::<keyword::I>()?;
|
||||
input.parse::<syn::Token![=]>()?;
|
||||
input.parse::<Unit>()?;
|
||||
|
||||
Ok(Self(Some(instance_usage)))
|
||||
} else if lookahead.peek(syn::Token![:]) {
|
||||
input.parse::<syn::Token![:]>()?;
|
||||
input.parse::<keyword::Config>()?;
|
||||
|
||||
if input.is_empty() {
|
||||
return Ok(Self(Some(instance_usage)));
|
||||
}
|
||||
|
||||
instance_usage.has_instance = true;
|
||||
input.parse::<syn::Token![<]>()?;
|
||||
input.parse::<keyword::I>()?;
|
||||
input.parse::<syn::Token![>]>()?;
|
||||
input.parse::<syn::Token![,]>()?;
|
||||
input.parse::<keyword::I>()?;
|
||||
input.parse::<syn::Token![:]>()?;
|
||||
input.parse::<StaticLifetime>()?;
|
||||
input.parse::<syn::Token![=]>()?;
|
||||
input.parse::<Unit>()?;
|
||||
|
||||
Ok(Self(Some(instance_usage)))
|
||||
} else {
|
||||
Err(lookahead.error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let i = syn::parse2::<Checker>(gen.params.to_token_stream())
|
||||
.map_err(|e| {
|
||||
let msg = format!("Invalid type def generics: {}", expected);
|
||||
let mut err = syn::Error::new(span, msg);
|
||||
err.combine(e);
|
||||
err
|
||||
})?
|
||||
.0
|
||||
// Span can be call_site if generic is empty. Thus we replace it.
|
||||
.map(|mut i| {
|
||||
i.span = span;
|
||||
i
|
||||
});
|
||||
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
/// Check the syntax:
|
||||
/// * either `Pezpallet<T>`
|
||||
/// * or `Pezpallet<T, I>`
|
||||
///
|
||||
/// return the instance if found.
|
||||
pub fn check_pallet_struct_usage(type_: &Box<syn::Type>) -> syn::Result<InstanceUsage> {
|
||||
let expected = "expected `Pezpallet<T>` or `Pezpallet<T, I>`";
|
||||
pub struct Checker(InstanceUsage);
|
||||
impl syn::parse::Parse for Checker {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false };
|
||||
|
||||
input.parse::<keyword::Pezpallet>()?;
|
||||
input.parse::<syn::Token![<]>()?;
|
||||
input.parse::<keyword::T>()?;
|
||||
if input.peek(syn::Token![,]) {
|
||||
instance_usage.has_instance = true;
|
||||
input.parse::<syn::Token![,]>()?;
|
||||
input.parse::<keyword::I>()?;
|
||||
}
|
||||
input.parse::<syn::Token![>]>()?;
|
||||
|
||||
Ok(Self(instance_usage))
|
||||
}
|
||||
}
|
||||
|
||||
let i = syn::parse2::<Checker>(type_.to_token_stream())
|
||||
.map_err(|e| {
|
||||
let msg = format!("Invalid pezpallet struct: {}", expected);
|
||||
let mut err = syn::Error::new(type_.span(), msg);
|
||||
err.combine(e);
|
||||
err
|
||||
})?
|
||||
.0;
|
||||
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
/// Check the generic is:
|
||||
/// * either `T: Config`
|
||||
/// * or `T: Config<I>, I: 'static`
|
||||
///
|
||||
/// `span` is used in case generics is empty (empty generics has span == call_site).
|
||||
///
|
||||
/// return whether it contains instance.
|
||||
pub fn check_impl_gen(gen: &syn::Generics, span: proc_macro2::Span) -> syn::Result<InstanceUsage> {
|
||||
let expected = "expected `impl<T: Config>` or `impl<T: Config<I>, I: 'static>`";
|
||||
pub struct Checker(InstanceUsage);
|
||||
impl syn::parse::Parse for Checker {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false };
|
||||
|
||||
input.parse::<keyword::T>()?;
|
||||
input.parse::<syn::Token![:]>()?;
|
||||
input.parse::<keyword::Config>()?;
|
||||
if input.peek(syn::Token![<]) {
|
||||
instance_usage.has_instance = true;
|
||||
input.parse::<syn::Token![<]>()?;
|
||||
input.parse::<keyword::I>()?;
|
||||
input.parse::<syn::Token![>]>()?;
|
||||
input.parse::<syn::Token![,]>()?;
|
||||
input.parse::<keyword::I>()?;
|
||||
input.parse::<syn::Token![:]>()?;
|
||||
input.parse::<StaticLifetime>()?;
|
||||
}
|
||||
|
||||
Ok(Self(instance_usage))
|
||||
}
|
||||
}
|
||||
|
||||
let i = syn::parse2::<Checker>(gen.params.to_token_stream())
|
||||
.map_err(|e| {
|
||||
let mut err = syn::Error::new(span, format!("Invalid generics: {}", expected));
|
||||
err.combine(e);
|
||||
err
|
||||
})?
|
||||
.0;
|
||||
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
/// Check the syntax:
|
||||
/// * or `T`
|
||||
/// * or `T: Config`
|
||||
/// * or `T, I = ()`
|
||||
/// * or `T: Config<I>, I: 'static = ()`
|
||||
///
|
||||
/// `span` is used in case generics is empty (empty generics has span == call_site).
|
||||
///
|
||||
/// return the instance if found.
|
||||
pub fn check_type_def_gen(
|
||||
gen: &syn::Generics,
|
||||
span: proc_macro2::Span,
|
||||
) -> syn::Result<InstanceUsage> {
|
||||
let expected = "expected `T` or `T: Config` or `T, I = ()` or \
|
||||
`T: Config<I>, I: 'static = ()`";
|
||||
pub struct Checker(InstanceUsage);
|
||||
impl syn::parse::Parse for Checker {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false };
|
||||
|
||||
input.parse::<keyword::T>()?;
|
||||
|
||||
if input.is_empty() {
|
||||
return Ok(Self(instance_usage));
|
||||
}
|
||||
|
||||
let lookahead = input.lookahead1();
|
||||
if lookahead.peek(syn::Token![,]) {
|
||||
instance_usage.has_instance = true;
|
||||
input.parse::<syn::Token![,]>()?;
|
||||
input.parse::<keyword::I>()?;
|
||||
input.parse::<syn::Token![=]>()?;
|
||||
input.parse::<Unit>()?;
|
||||
|
||||
Ok(Self(instance_usage))
|
||||
} else if lookahead.peek(syn::Token![:]) {
|
||||
input.parse::<syn::Token![:]>()?;
|
||||
input.parse::<keyword::Config>()?;
|
||||
|
||||
if input.is_empty() {
|
||||
return Ok(Self(instance_usage));
|
||||
}
|
||||
|
||||
instance_usage.has_instance = true;
|
||||
input.parse::<syn::Token![<]>()?;
|
||||
input.parse::<keyword::I>()?;
|
||||
input.parse::<syn::Token![>]>()?;
|
||||
input.parse::<syn::Token![,]>()?;
|
||||
input.parse::<keyword::I>()?;
|
||||
input.parse::<syn::Token![:]>()?;
|
||||
input.parse::<StaticLifetime>()?;
|
||||
input.parse::<syn::Token![=]>()?;
|
||||
input.parse::<Unit>()?;
|
||||
|
||||
Ok(Self(instance_usage))
|
||||
} else {
|
||||
Err(lookahead.error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut i = syn::parse2::<Checker>(gen.params.to_token_stream())
|
||||
.map_err(|e| {
|
||||
let msg = format!("Invalid type def generics: {}", expected);
|
||||
let mut err = syn::Error::new(span, msg);
|
||||
err.combine(e);
|
||||
err
|
||||
})?
|
||||
.0;
|
||||
|
||||
// Span can be call_site if generic is empty. Thus we replace it.
|
||||
i.span = span;
|
||||
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
/// Check the syntax:
|
||||
/// * either `GenesisBuild<T>`
|
||||
/// * or `GenesisBuild<T, I>`
|
||||
/// * or `BuildGenesisConfig`
|
||||
///
|
||||
/// return the instance if found for `GenesisBuild`
|
||||
/// return None for BuildGenesisConfig
|
||||
pub fn check_genesis_builder_usage(type_: &syn::Path) -> syn::Result<Option<InstanceUsage>> {
|
||||
let expected = "expected `BuildGenesisConfig` (or the deprecated `GenesisBuild<T>` or `GenesisBuild<T, I>`)";
|
||||
pub struct Checker(Option<InstanceUsage>);
|
||||
impl syn::parse::Parse for Checker {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false };
|
||||
|
||||
if input.peek(keyword::GenesisBuild) {
|
||||
input.parse::<keyword::GenesisBuild>()?;
|
||||
input.parse::<syn::Token![<]>()?;
|
||||
input.parse::<keyword::T>()?;
|
||||
if input.peek(syn::Token![,]) {
|
||||
instance_usage.has_instance = true;
|
||||
input.parse::<syn::Token![,]>()?;
|
||||
input.parse::<keyword::I>()?;
|
||||
}
|
||||
input.parse::<syn::Token![>]>()?;
|
||||
return Ok(Self(Some(instance_usage)));
|
||||
} else {
|
||||
input.parse::<keyword::BuildGenesisConfig>()?;
|
||||
return Ok(Self(None));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let i = syn::parse2::<Checker>(type_.to_token_stream())
|
||||
.map_err(|e| {
|
||||
let msg = format!("Invalid genesis builder: {}", expected);
|
||||
let mut err = syn::Error::new(type_.span(), msg);
|
||||
err.combine(e);
|
||||
err
|
||||
})?
|
||||
.0;
|
||||
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
/// Check the syntax:
|
||||
/// * either `` (no generics)
|
||||
/// * or `T: Config`
|
||||
/// * or `T: Config<I>, I: 'static`
|
||||
///
|
||||
/// `span` is used in case generics is empty (empty generics has span == call_site).
|
||||
///
|
||||
/// return the instance if found.
|
||||
pub fn check_type_value_gen(
|
||||
gen: &syn::Generics,
|
||||
span: proc_macro2::Span,
|
||||
) -> syn::Result<Option<InstanceUsage>> {
|
||||
let expected = "expected `` or `T: Config` or `T: Config<I>, I: 'static`";
|
||||
pub struct Checker(Option<InstanceUsage>);
|
||||
impl syn::parse::Parse for Checker {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
if input.is_empty() {
|
||||
return Ok(Self(None));
|
||||
}
|
||||
|
||||
input.parse::<keyword::T>()?;
|
||||
input.parse::<syn::Token![:]>()?;
|
||||
input.parse::<keyword::Config>()?;
|
||||
|
||||
let mut instance_usage = InstanceUsage { span: input.span(), has_instance: false };
|
||||
|
||||
if input.is_empty() {
|
||||
return Ok(Self(Some(instance_usage)));
|
||||
}
|
||||
|
||||
instance_usage.has_instance = true;
|
||||
input.parse::<syn::Token![<]>()?;
|
||||
input.parse::<keyword::I>()?;
|
||||
input.parse::<syn::Token![>]>()?;
|
||||
input.parse::<syn::Token![,]>()?;
|
||||
input.parse::<keyword::I>()?;
|
||||
input.parse::<syn::Token![:]>()?;
|
||||
input.parse::<StaticLifetime>()?;
|
||||
|
||||
Ok(Self(Some(instance_usage)))
|
||||
}
|
||||
}
|
||||
|
||||
let i = syn::parse2::<Checker>(gen.params.to_token_stream())
|
||||
.map_err(|e| {
|
||||
let msg = format!("Invalid type def generics: {}", expected);
|
||||
let mut err = syn::Error::new(span, msg);
|
||||
err.combine(e);
|
||||
err
|
||||
})?
|
||||
.0
|
||||
// Span can be call_site if generic is empty. Thus we replace it.
|
||||
.map(|mut i| {
|
||||
i.span = span;
|
||||
i
|
||||
});
|
||||
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
/// The possible return type of a dispatchable.
|
||||
#[derive(Clone)]
|
||||
pub enum CallReturnType {
|
||||
DispatchResult,
|
||||
DispatchResultWithPostInfo,
|
||||
}
|
||||
|
||||
/// Check the keyword `DispatchResultWithPostInfo` or `DispatchResult`.
|
||||
pub fn check_pallet_call_return_type(sig: &syn::Signature) -> syn::Result<CallReturnType> {
|
||||
let syn::ReturnType::Type(_, type_) = &sig.output else {
|
||||
let msg = "Invalid pezpallet::call, require return type \
|
||||
DispatchResultWithPostInfo";
|
||||
return Err(syn::Error::new(sig.span(), msg));
|
||||
};
|
||||
|
||||
pub struct Checker(CallReturnType);
|
||||
impl syn::parse::Parse for Checker {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let lookahead = input.lookahead1();
|
||||
if lookahead.peek(keyword::DispatchResultWithPostInfo) {
|
||||
input.parse::<keyword::DispatchResultWithPostInfo>()?;
|
||||
Ok(Self(CallReturnType::DispatchResultWithPostInfo))
|
||||
} else if lookahead.peek(keyword::DispatchResult) {
|
||||
input.parse::<keyword::DispatchResult>()?;
|
||||
Ok(Self(CallReturnType::DispatchResult))
|
||||
} else {
|
||||
Err(lookahead.error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
syn::parse2::<Checker>(type_.to_token_stream()).map(|c| c.0)
|
||||
}
|
||||
|
||||
pub(crate) fn two128_str(s: &str) -> TokenStream {
|
||||
bytes_to_array(pezsp_crypto_hashing::twox_128(s.as_bytes()).into_iter())
|
||||
}
|
||||
|
||||
pub(crate) fn bytes_to_array(bytes: impl IntoIterator<Item = u8>) -> TokenStream {
|
||||
let bytes = bytes.into_iter();
|
||||
|
||||
quote!(
|
||||
[ #( #bytes ),* ]
|
||||
)
|
||||
.into()
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::helper;
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
/// Implementation of the pezpallet hooks.
|
||||
pub struct HooksDef {
|
||||
/// A set of usage of instance, must be check for consistency with trait.
|
||||
pub instances: Vec<helper::InstanceUsage>,
|
||||
/// The where_clause used.
|
||||
pub where_clause: Option<syn::WhereClause>,
|
||||
/// The span of the pezpallet::hooks attribute.
|
||||
pub attr_span: proc_macro2::Span,
|
||||
/// Boolean flag, set to true if the `on_runtime_upgrade` method of hooks was implemented.
|
||||
pub has_runtime_upgrade: bool,
|
||||
}
|
||||
|
||||
impl HooksDef {
|
||||
pub fn try_from(attr_span: proc_macro2::Span, item: &mut syn::Item) -> syn::Result<Self> {
|
||||
let item = if let syn::Item::Impl(item) = item {
|
||||
item
|
||||
} else {
|
||||
let msg = "Invalid pezpallet::hooks, expected item impl";
|
||||
return Err(syn::Error::new(item.span(), msg));
|
||||
};
|
||||
|
||||
let instances = vec![
|
||||
helper::check_pallet_struct_usage(&item.self_ty)?,
|
||||
helper::check_impl_gen(&item.generics, item.impl_token.span())?,
|
||||
];
|
||||
|
||||
let item_trait = &item
|
||||
.trait_
|
||||
.as_ref()
|
||||
.ok_or_else(|| {
|
||||
let msg = "Invalid pezpallet::hooks, expected impl<..> Hooks \
|
||||
for Pezpallet<..>";
|
||||
syn::Error::new(item.span(), msg)
|
||||
})?
|
||||
.1;
|
||||
|
||||
if item_trait.segments.len() != 1 || item_trait.segments[0].ident != "Hooks" {
|
||||
let msg = format!(
|
||||
"Invalid pezpallet::hooks, expected trait to be `Hooks` found `{}`\
|
||||
, you can import from `pezframe_support::pezpallet_prelude`",
|
||||
quote::quote!(#item_trait)
|
||||
);
|
||||
|
||||
return Err(syn::Error::new(item_trait.span(), msg));
|
||||
}
|
||||
|
||||
let has_runtime_upgrade = item.items.iter().any(|i| match i {
|
||||
syn::ImplItem::Fn(method) => method.sig.ident == "on_runtime_upgrade",
|
||||
_ => false,
|
||||
});
|
||||
|
||||
Ok(Self {
|
||||
attr_span,
|
||||
instances,
|
||||
has_runtime_upgrade,
|
||||
where_clause: item.generics.where_clause.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::helper;
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
/// The definition of the pezpallet inherent implementation.
|
||||
pub struct InherentDef {
|
||||
/// A set of usage of instance, must be check for consistency with trait.
|
||||
pub instances: Vec<helper::InstanceUsage>,
|
||||
}
|
||||
|
||||
impl InherentDef {
|
||||
pub fn try_from(item: &mut syn::Item) -> syn::Result<Self> {
|
||||
let item = if let syn::Item::Impl(item) = item {
|
||||
item
|
||||
} else {
|
||||
let msg = "Invalid pezpallet::inherent, expected item impl";
|
||||
return Err(syn::Error::new(item.span(), msg));
|
||||
};
|
||||
|
||||
if item.trait_.is_none() {
|
||||
let msg = "Invalid pezpallet::inherent, expected impl<..> ProvideInherent for Pezpallet<..>";
|
||||
return Err(syn::Error::new(item.span(), msg));
|
||||
}
|
||||
|
||||
if let Some(last) = item.trait_.as_ref().unwrap().1.segments.last() {
|
||||
if last.ident != "ProvideInherent" {
|
||||
let msg = "Invalid pezpallet::inherent, expected trait ProvideInherent";
|
||||
return Err(syn::Error::new(last.span(), msg));
|
||||
}
|
||||
} else {
|
||||
let msg = "Invalid pezpallet::inherent, expected impl<..> ProvideInherent for Pezpallet<..>";
|
||||
return Err(syn::Error::new(item.span(), msg));
|
||||
}
|
||||
|
||||
let instances = vec![
|
||||
helper::check_pallet_struct_usage(&item.self_ty)?,
|
||||
helper::check_impl_gen(&item.generics, item.impl_token.span())?,
|
||||
];
|
||||
|
||||
Ok(InherentDef { instances })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,833 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Parse for pezpallet macro.
|
||||
//!
|
||||
//! Parse the module into `Def` struct through `Def::try_from` function.
|
||||
|
||||
pub mod call;
|
||||
pub mod composite;
|
||||
pub mod config;
|
||||
pub mod error;
|
||||
pub mod event;
|
||||
pub mod extra_constants;
|
||||
pub mod genesis_build;
|
||||
pub mod genesis_config;
|
||||
pub mod helper;
|
||||
pub mod hooks;
|
||||
pub mod inherent;
|
||||
pub mod origin;
|
||||
pub mod pezpallet_struct;
|
||||
pub mod storage;
|
||||
pub mod tasks;
|
||||
pub mod type_value;
|
||||
pub mod validate_unsigned;
|
||||
pub mod view_functions;
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests;
|
||||
|
||||
use composite::{keyword::CompositeKeyword, CompositeDef};
|
||||
use pezframe_support_procedural_tools::generate_access_from_frame_or_crate;
|
||||
use quote::ToTokens;
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
/// Parsed definition of a pezpallet.
|
||||
pub struct Def {
|
||||
/// The module items.
|
||||
/// (their order must not be modified because they are registered in individual definitions).
|
||||
pub item: syn::ItemMod,
|
||||
pub config: config::ConfigDef,
|
||||
pub pezpallet_struct: pezpallet_struct::PalletStructDef,
|
||||
pub hooks: Option<hooks::HooksDef>,
|
||||
pub call: Option<call::CallDef>,
|
||||
pub tasks: Option<tasks::TasksDef>,
|
||||
pub task_enum: Option<tasks::TaskEnumDef>,
|
||||
pub storages: Vec<storage::StorageDef>,
|
||||
pub error: Option<error::ErrorDef>,
|
||||
pub event: Option<event::EventDef>,
|
||||
pub origin: Option<origin::OriginDef>,
|
||||
pub inherent: Option<inherent::InherentDef>,
|
||||
pub genesis_config: Option<genesis_config::GenesisConfigDef>,
|
||||
pub genesis_build: Option<genesis_build::GenesisBuildDef>,
|
||||
pub validate_unsigned: Option<validate_unsigned::ValidateUnsignedDef>,
|
||||
pub extra_constants: Option<extra_constants::ExtraConstantsDef>,
|
||||
pub composites: Vec<composite::CompositeDef>,
|
||||
pub type_values: Vec<type_value::TypeValueDef>,
|
||||
pub pezframe_system: syn::Path,
|
||||
pub pezframe_support: syn::Path,
|
||||
pub dev_mode: bool,
|
||||
pub view_functions: Option<view_functions::ViewFunctionsImplDef>,
|
||||
pub is_pezframe_system: bool,
|
||||
}
|
||||
|
||||
impl Def {
|
||||
pub fn try_from(mut item: syn::ItemMod, dev_mode: bool) -> syn::Result<Self> {
|
||||
let pezframe_system = generate_access_from_frame_or_crate("pezframe-system")?;
|
||||
let pezframe_support = generate_access_from_frame_or_crate("pezframe-support")?;
|
||||
let item_span = item.span();
|
||||
let items = &mut item
|
||||
.content
|
||||
.as_mut()
|
||||
.ok_or_else(|| {
|
||||
let msg = "Invalid pezpallet definition, expected mod to be inlined.";
|
||||
syn::Error::new(item_span, msg)
|
||||
})?
|
||||
.1;
|
||||
|
||||
let mut config = None;
|
||||
let mut pezpallet_struct = None;
|
||||
let mut hooks = None;
|
||||
let mut call = None;
|
||||
let mut tasks = None;
|
||||
let mut task_enum = None;
|
||||
let mut error = None;
|
||||
let mut event = None;
|
||||
let mut origin = None;
|
||||
let mut inherent = None;
|
||||
let mut genesis_config = None;
|
||||
let mut genesis_build = None;
|
||||
let mut validate_unsigned = None;
|
||||
let mut extra_constants = None;
|
||||
let mut storages = vec![];
|
||||
let mut type_values = vec![];
|
||||
let mut composites: Vec<CompositeDef> = vec![];
|
||||
let mut view_functions = None;
|
||||
let mut is_pezframe_system = false;
|
||||
|
||||
for (index, item) in items.iter_mut().enumerate() {
|
||||
let pezpallet_attr: Option<PalletAttr> = helper::take_first_item_pallet_attr(item)?;
|
||||
|
||||
match pezpallet_attr {
|
||||
Some(PalletAttr::Config{ with_default, pezframe_system_config: is_pezframe_system_val, without_automatic_metadata, ..}) if config.is_none() => {
|
||||
is_pezframe_system = is_pezframe_system_val;
|
||||
config = Some(config::ConfigDef::try_from(
|
||||
&pezframe_system,
|
||||
index,
|
||||
item,
|
||||
with_default,
|
||||
without_automatic_metadata,
|
||||
is_pezframe_system,
|
||||
)?);
|
||||
},
|
||||
Some(PalletAttr::Pezpallet(span)) if pezpallet_struct.is_none() => {
|
||||
let p = pezpallet_struct::PalletStructDef::try_from(span, index, item)?;
|
||||
pezpallet_struct = Some(p);
|
||||
},
|
||||
Some(PalletAttr::Hooks(span)) if hooks.is_none() => {
|
||||
let m = hooks::HooksDef::try_from(span, item)?;
|
||||
hooks = Some(m);
|
||||
},
|
||||
Some(PalletAttr::RuntimeCall(cw, span)) if call.is_none() =>
|
||||
call = Some(call::CallDef::try_from(span, index, item, dev_mode, cw)?),
|
||||
Some(PalletAttr::Tasks(span)) if tasks.is_none() => {
|
||||
let item_tokens = item.to_token_stream();
|
||||
// `TasksDef::parse` needs to know if attr was provided so we artificially
|
||||
// re-insert it here
|
||||
tasks = Some(syn::parse2::<tasks::TasksDef>(quote::quote_spanned! { span =>
|
||||
#[pezpallet::tasks_experimental]
|
||||
#item_tokens
|
||||
})?);
|
||||
|
||||
// replace item with a no-op because it will be handled by the expansion of tasks
|
||||
*item = syn::Item::Verbatim(quote::quote!());
|
||||
}
|
||||
Some(PalletAttr::TaskCondition(span)) => return Err(syn::Error::new(
|
||||
span,
|
||||
"`#[pezpallet::task_condition]` can only be used on items within an `impl` statement."
|
||||
)),
|
||||
Some(PalletAttr::TaskIndex(span)) => return Err(syn::Error::new(
|
||||
span,
|
||||
"`#[pezpallet::task_index]` can only be used on items within an `impl` statement."
|
||||
)),
|
||||
Some(PalletAttr::TaskList(span)) => return Err(syn::Error::new(
|
||||
span,
|
||||
"`#[pezpallet::task_list]` can only be used on items within an `impl` statement."
|
||||
)),
|
||||
Some(PalletAttr::RuntimeTask(_)) if task_enum.is_none() =>
|
||||
task_enum = Some(syn::parse2::<tasks::TaskEnumDef>(item.to_token_stream())?),
|
||||
Some(PalletAttr::Error(span)) if error.is_none() =>
|
||||
error = Some(error::ErrorDef::try_from(span, index, item)?),
|
||||
Some(PalletAttr::RuntimeEvent(span)) if event.is_none() =>
|
||||
event = Some(event::EventDef::try_from(span, index, item)?),
|
||||
Some(PalletAttr::GenesisConfig(_)) if genesis_config.is_none() => {
|
||||
let g = genesis_config::GenesisConfigDef::try_from(index, item)?;
|
||||
genesis_config = Some(g);
|
||||
},
|
||||
Some(PalletAttr::GenesisBuild(span)) if genesis_build.is_none() => {
|
||||
let g = genesis_build::GenesisBuildDef::try_from(span, item)?;
|
||||
genesis_build = Some(g);
|
||||
},
|
||||
Some(PalletAttr::RuntimeOrigin(_)) if origin.is_none() =>
|
||||
origin = Some(origin::OriginDef::try_from(item)?),
|
||||
Some(PalletAttr::Inherent(_)) if inherent.is_none() =>
|
||||
inherent = Some(inherent::InherentDef::try_from(item)?),
|
||||
Some(PalletAttr::Storage(span)) =>
|
||||
storages.push(storage::StorageDef::try_from(span, index, item, dev_mode)?),
|
||||
Some(PalletAttr::ValidateUnsigned(_)) if validate_unsigned.is_none() => {
|
||||
let v = validate_unsigned::ValidateUnsignedDef::try_from(item)?;
|
||||
validate_unsigned = Some(v);
|
||||
},
|
||||
Some(PalletAttr::TypeValue(span)) =>
|
||||
type_values.push(type_value::TypeValueDef::try_from(span, index, item)?),
|
||||
Some(PalletAttr::ExtraConstants(_)) =>
|
||||
extra_constants =
|
||||
Some(extra_constants::ExtraConstantsDef::try_from(item)?),
|
||||
Some(PalletAttr::Composite(span)) => {
|
||||
let composite =
|
||||
composite::CompositeDef::try_from(span, &pezframe_support, item)?;
|
||||
if composites.iter().any(|def| {
|
||||
match (&def.composite_keyword, &composite.composite_keyword) {
|
||||
(
|
||||
CompositeKeyword::FreezeReason(_),
|
||||
CompositeKeyword::FreezeReason(_),
|
||||
) |
|
||||
(CompositeKeyword::HoldReason(_), CompositeKeyword::HoldReason(_)) |
|
||||
(CompositeKeyword::LockId(_), CompositeKeyword::LockId(_)) |
|
||||
(
|
||||
CompositeKeyword::SlashReason(_),
|
||||
CompositeKeyword::SlashReason(_),
|
||||
) => true,
|
||||
_ => false,
|
||||
}
|
||||
}) {
|
||||
let msg = format!(
|
||||
"Invalid duplicated `{}` definition",
|
||||
composite.composite_keyword
|
||||
);
|
||||
return Err(syn::Error::new(composite.composite_keyword.span(), &msg))
|
||||
}
|
||||
composites.push(composite);
|
||||
},
|
||||
Some(PalletAttr::ViewFunctions(span)) => {
|
||||
view_functions = Some(view_functions::ViewFunctionsImplDef::try_from(span, item)?);
|
||||
}
|
||||
Some(attr) => {
|
||||
let msg = "Invalid duplicated attribute";
|
||||
return Err(syn::Error::new(attr.span(), msg))
|
||||
},
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
|
||||
if genesis_config.is_some() != genesis_build.is_some() {
|
||||
let msg = format!(
|
||||
"`#[pezpallet::genesis_config]` and `#[pezpallet::genesis_build]` attributes must be \
|
||||
either both used or both not used, instead genesis_config is {} and genesis_build \
|
||||
is {}",
|
||||
genesis_config.as_ref().map_or("unused", |_| "used"),
|
||||
genesis_build.as_ref().map_or("unused", |_| "used"),
|
||||
);
|
||||
return Err(syn::Error::new(item_span, msg));
|
||||
}
|
||||
|
||||
Self::resolve_tasks(&item_span, &mut tasks, &mut task_enum, items)?;
|
||||
|
||||
let def = Def {
|
||||
item,
|
||||
config: config
|
||||
.ok_or_else(|| syn::Error::new(item_span, "Missing `#[pezpallet::config]`"))?,
|
||||
pezpallet_struct: pezpallet_struct
|
||||
.ok_or_else(|| syn::Error::new(item_span, "Missing `#[pezpallet::pezpallet]`"))?,
|
||||
hooks,
|
||||
call,
|
||||
tasks,
|
||||
task_enum,
|
||||
extra_constants,
|
||||
genesis_config,
|
||||
genesis_build,
|
||||
validate_unsigned,
|
||||
error,
|
||||
event,
|
||||
origin,
|
||||
inherent,
|
||||
storages,
|
||||
composites,
|
||||
type_values,
|
||||
pezframe_system,
|
||||
pezframe_support,
|
||||
dev_mode,
|
||||
view_functions,
|
||||
is_pezframe_system,
|
||||
};
|
||||
|
||||
def.check_instance_usage()?;
|
||||
|
||||
Ok(def)
|
||||
}
|
||||
|
||||
/// Performs extra logic checks necessary for the `#[pezpallet::tasks_experimental]` feature.
|
||||
fn resolve_tasks(
|
||||
item_span: &proc_macro2::Span,
|
||||
tasks: &mut Option<tasks::TasksDef>,
|
||||
task_enum: &mut Option<tasks::TaskEnumDef>,
|
||||
items: &mut Vec<syn::Item>,
|
||||
) -> syn::Result<()> {
|
||||
// fallback for manual (without macros) definition of tasks impl
|
||||
Self::resolve_manual_tasks_impl(tasks, task_enum, items)?;
|
||||
|
||||
// fallback for manual (without macros) definition of task enum
|
||||
Self::resolve_manual_task_enum(tasks, task_enum, items)?;
|
||||
|
||||
// ensure that if `task_enum` is specified, `tasks` is also specified
|
||||
match (&task_enum, &tasks) {
|
||||
(Some(_), None) =>
|
||||
return Err(syn::Error::new(
|
||||
*item_span,
|
||||
"Missing `#[pezpallet::tasks_experimental]` impl",
|
||||
)),
|
||||
(None, Some(tasks)) =>
|
||||
if tasks.tasks_attr.is_none() {
|
||||
return Err(syn::Error::new(
|
||||
tasks.item_impl.impl_token.span(),
|
||||
"A `#[pezpallet::tasks_experimental]` attribute must be attached to your `Task` impl if the \
|
||||
task enum has been omitted",
|
||||
));
|
||||
} else {
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Tries to locate task enum based on the tasks impl target if attribute is not specified
|
||||
/// but impl is present. If one is found, `task_enum` is set appropriately.
|
||||
fn resolve_manual_task_enum(
|
||||
tasks: &Option<tasks::TasksDef>,
|
||||
task_enum: &mut Option<tasks::TaskEnumDef>,
|
||||
items: &mut Vec<syn::Item>,
|
||||
) -> syn::Result<()> {
|
||||
let (None, Some(tasks)) = (&task_enum, &tasks) else { return Ok(()) };
|
||||
let syn::Type::Path(type_path) = &*tasks.item_impl.self_ty else { return Ok(()) };
|
||||
let type_path = type_path.path.segments.iter().collect::<Vec<_>>();
|
||||
let (Some(seg), None) = (type_path.get(0), type_path.get(1)) else { return Ok(()) };
|
||||
let mut result = None;
|
||||
for item in items {
|
||||
let syn::Item::Enum(item_enum) = item else { continue };
|
||||
if item_enum.ident == seg.ident {
|
||||
result = Some(syn::parse2::<tasks::TaskEnumDef>(item_enum.to_token_stream())?);
|
||||
// replace item with a no-op because it will be handled by the expansion of
|
||||
// `task_enum`. We use a no-op instead of simply removing it from the vec
|
||||
// so that any indices collected by `Def::try_from` remain accurate
|
||||
*item = syn::Item::Verbatim(quote::quote!());
|
||||
break;
|
||||
}
|
||||
}
|
||||
*task_enum = result;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Tries to locate a manual tasks impl (an impl implementing a trait whose last path segment is
|
||||
/// `Task`) in the event that one has not been found already via the attribute macro
|
||||
pub fn resolve_manual_tasks_impl(
|
||||
tasks: &mut Option<tasks::TasksDef>,
|
||||
task_enum: &Option<tasks::TaskEnumDef>,
|
||||
items: &Vec<syn::Item>,
|
||||
) -> syn::Result<()> {
|
||||
let None = tasks else { return Ok(()) };
|
||||
let mut result = None;
|
||||
for item in items {
|
||||
let syn::Item::Impl(item_impl) = item else { continue };
|
||||
let Some((_, path, _)) = &item_impl.trait_ else { continue };
|
||||
let Some(trait_last_seg) = path.segments.last() else { continue };
|
||||
let syn::Type::Path(target_path) = &*item_impl.self_ty else { continue };
|
||||
let target_path = target_path.path.segments.iter().collect::<Vec<_>>();
|
||||
let (Some(target_ident), None) = (target_path.get(0), target_path.get(1)) else {
|
||||
continue;
|
||||
};
|
||||
let matches_task_enum = match task_enum {
|
||||
Some(task_enum) => task_enum.item_enum.ident == target_ident.ident,
|
||||
None => true,
|
||||
};
|
||||
if trait_last_seg.ident == "Task" && matches_task_enum {
|
||||
result = Some(syn::parse2::<tasks::TasksDef>(item_impl.to_token_stream())?);
|
||||
break;
|
||||
}
|
||||
}
|
||||
*tasks = result;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check that usage of trait `Config` is consistent with the definition, i.e. it is used with
|
||||
/// instance iff it is defined with instance.
|
||||
fn check_instance_usage(&self) -> syn::Result<()> {
|
||||
let mut instances = vec![];
|
||||
instances.extend_from_slice(&self.pezpallet_struct.instances[..]);
|
||||
instances.extend(&mut self.storages.iter().flat_map(|s| s.instances.clone()));
|
||||
if let Some(call) = &self.call {
|
||||
instances.extend_from_slice(&call.instances[..]);
|
||||
}
|
||||
if let Some(hooks) = &self.hooks {
|
||||
instances.extend_from_slice(&hooks.instances[..]);
|
||||
}
|
||||
if let Some(event) = &self.event {
|
||||
instances.extend_from_slice(&event.instances[..]);
|
||||
}
|
||||
if let Some(error) = &self.error {
|
||||
instances.extend_from_slice(&error.instances[..]);
|
||||
}
|
||||
if let Some(inherent) = &self.inherent {
|
||||
instances.extend_from_slice(&inherent.instances[..]);
|
||||
}
|
||||
if let Some(origin) = &self.origin {
|
||||
instances.extend_from_slice(&origin.instances[..]);
|
||||
}
|
||||
if let Some(genesis_config) = &self.genesis_config {
|
||||
instances.extend_from_slice(&genesis_config.instances[..]);
|
||||
}
|
||||
if let Some(genesis_build) = &self.genesis_build {
|
||||
genesis_build.instances.as_ref().map(|i| instances.extend_from_slice(&i));
|
||||
}
|
||||
if let Some(extra_constants) = &self.extra_constants {
|
||||
instances.extend_from_slice(&extra_constants.instances[..]);
|
||||
}
|
||||
if let Some(task_enum) = &self.task_enum {
|
||||
instances.push(task_enum.instance_usage.clone());
|
||||
}
|
||||
|
||||
let mut errors = instances.into_iter().filter_map(|instances| {
|
||||
if instances.has_instance == self.config.has_instance {
|
||||
return None;
|
||||
}
|
||||
let msg = if self.config.has_instance {
|
||||
"Invalid generic declaration, trait is defined with instance but generic use none"
|
||||
} else {
|
||||
"Invalid generic declaration, trait is defined without instance but generic use \
|
||||
some"
|
||||
};
|
||||
Some(syn::Error::new(instances.span, msg))
|
||||
});
|
||||
|
||||
if let Some(mut first_error) = errors.next() {
|
||||
for error in errors {
|
||||
first_error.combine(error)
|
||||
}
|
||||
Err(first_error)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Depending on if pezpallet is instantiable:
|
||||
/// * either `T: Config`
|
||||
/// * or `T: Config<I>, I: 'static`
|
||||
pub fn type_impl_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream {
|
||||
if self.config.has_instance {
|
||||
quote::quote_spanned!(span => T: Config<I>, I: 'static)
|
||||
} else {
|
||||
quote::quote_spanned!(span => T: Config)
|
||||
}
|
||||
}
|
||||
|
||||
/// Depending on if pezpallet is instantiable:
|
||||
/// * either `T: Config`
|
||||
/// * or `T: Config<I>, I: 'static = ()`
|
||||
pub fn type_decl_bounded_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream {
|
||||
if self.config.has_instance {
|
||||
quote::quote_spanned!(span => T: Config<I>, I: 'static = ())
|
||||
} else {
|
||||
quote::quote_spanned!(span => T: Config)
|
||||
}
|
||||
}
|
||||
|
||||
/// Depending on if pezpallet is instantiable:
|
||||
/// * either `T`
|
||||
/// * or `T, I = ()`
|
||||
pub fn type_decl_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream {
|
||||
if self.config.has_instance {
|
||||
quote::quote_spanned!(span => T, I = ())
|
||||
} else {
|
||||
quote::quote_spanned!(span => T)
|
||||
}
|
||||
}
|
||||
|
||||
/// Depending on if pezpallet is instantiable:
|
||||
/// * either ``
|
||||
/// * or `<I>`
|
||||
/// to be used when using pezpallet trait `Config`
|
||||
pub fn trait_use_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream {
|
||||
if self.config.has_instance {
|
||||
quote::quote_spanned!(span => <I>)
|
||||
} else {
|
||||
quote::quote_spanned!(span => )
|
||||
}
|
||||
}
|
||||
|
||||
/// Depending on if pezpallet is instantiable:
|
||||
/// * either `T`
|
||||
/// * or `T, I`
|
||||
pub fn type_use_generics(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream {
|
||||
if self.config.has_instance {
|
||||
quote::quote_spanned!(span => T, I)
|
||||
} else {
|
||||
quote::quote_spanned!(span => T)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Some generic kind for type which can be not generic, or generic over config,
|
||||
/// or generic over config and instance, but not generic only over instance.
|
||||
pub enum GenericKind {
|
||||
None,
|
||||
Config,
|
||||
ConfigAndInstance,
|
||||
}
|
||||
|
||||
impl GenericKind {
|
||||
/// Return Err if it is only generics over instance but not over config.
|
||||
pub fn from_gens(has_config: bool, has_instance: bool) -> Result<Self, ()> {
|
||||
match (has_config, has_instance) {
|
||||
(false, false) => Ok(GenericKind::None),
|
||||
(true, false) => Ok(GenericKind::Config),
|
||||
(true, true) => Ok(GenericKind::ConfigAndInstance),
|
||||
(false, true) => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the generic to be used when using the type.
|
||||
///
|
||||
/// Depending on its definition it can be: ``, `T` or `T, I`
|
||||
pub fn type_use_gen(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream {
|
||||
match self {
|
||||
GenericKind::None => quote::quote!(),
|
||||
GenericKind::Config => quote::quote_spanned!(span => T),
|
||||
GenericKind::ConfigAndInstance => quote::quote_spanned!(span => T, I),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the generic to be used in `impl<..>` when implementing on the type.
|
||||
pub fn type_impl_gen(&self, span: proc_macro2::Span) -> proc_macro2::TokenStream {
|
||||
match self {
|
||||
GenericKind::None => quote::quote!(),
|
||||
GenericKind::Config => quote::quote_spanned!(span => T: Config),
|
||||
GenericKind::ConfigAndInstance => {
|
||||
quote::quote_spanned!(span => T: Config<I>, I: 'static)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Return whereas the type has some generic.
|
||||
pub fn is_generic(&self) -> bool {
|
||||
match self {
|
||||
GenericKind::None => false,
|
||||
GenericKind::Config | GenericKind::ConfigAndInstance => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// List of additional token to be used for parsing.
|
||||
mod keyword {
|
||||
syn::custom_keyword!(origin);
|
||||
syn::custom_keyword!(call);
|
||||
syn::custom_keyword!(tasks_experimental);
|
||||
syn::custom_keyword!(task_enum);
|
||||
syn::custom_keyword!(task_list);
|
||||
syn::custom_keyword!(task_condition);
|
||||
syn::custom_keyword!(task_index);
|
||||
syn::custom_keyword!(weight);
|
||||
syn::custom_keyword!(event);
|
||||
syn::custom_keyword!(config);
|
||||
syn::custom_keyword!(with_default);
|
||||
syn::custom_keyword!(without_automatic_metadata);
|
||||
syn::custom_keyword!(pezframe_system_config);
|
||||
syn::custom_keyword!(hooks);
|
||||
syn::custom_keyword!(inherent);
|
||||
syn::custom_keyword!(error);
|
||||
syn::custom_keyword!(storage);
|
||||
syn::custom_keyword!(genesis_build);
|
||||
syn::custom_keyword!(genesis_config);
|
||||
syn::custom_keyword!(validate_unsigned);
|
||||
syn::custom_keyword!(type_value);
|
||||
syn::custom_keyword!(pezpallet);
|
||||
syn::custom_keyword!(extra_constants);
|
||||
syn::custom_keyword!(composite_enum);
|
||||
syn::custom_keyword!(view_functions);
|
||||
}
|
||||
|
||||
/// The possible values for the `#[pezpallet::config]` attribute.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
enum ConfigValue {
|
||||
/// `#[pezpallet::config(with_default)]`
|
||||
WithDefault(keyword::with_default),
|
||||
/// `#[pezpallet::config(without_automatic_metadata)]`
|
||||
WithoutAutomaticMetadata(keyword::without_automatic_metadata),
|
||||
/// `#[pezpallet::config(pezframe_system_config)]`
|
||||
FrameSystemConfig(keyword::pezframe_system_config),
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for ConfigValue {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let lookahead = input.lookahead1();
|
||||
|
||||
if lookahead.peek(keyword::with_default) {
|
||||
input.parse().map(ConfigValue::WithDefault)
|
||||
} else if lookahead.peek(keyword::without_automatic_metadata) {
|
||||
input.parse().map(ConfigValue::WithoutAutomaticMetadata)
|
||||
} else if lookahead.peek(keyword::pezframe_system_config) {
|
||||
input.parse().map(ConfigValue::FrameSystemConfig)
|
||||
} else {
|
||||
Err(lookahead.error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse attributes for item in pezpallet module
|
||||
/// syntax must be `pezpallet::` (e.g. `#[pezpallet::config]`)
|
||||
enum PalletAttr {
|
||||
Config {
|
||||
span: proc_macro2::Span,
|
||||
with_default: bool,
|
||||
without_automatic_metadata: bool,
|
||||
pezframe_system_config: bool,
|
||||
},
|
||||
Pezpallet(proc_macro2::Span),
|
||||
Hooks(proc_macro2::Span),
|
||||
/// A `#[pezpallet::call]` with optional attributes to specialize the behaviour.
|
||||
///
|
||||
/// # Attributes
|
||||
///
|
||||
/// Each attribute `attr` can take the form of `#[pezpallet::call(attr = …)]` or
|
||||
/// `#[pezpallet::call(attr(…))]`. The possible attributes are:
|
||||
///
|
||||
/// ## `weight`
|
||||
///
|
||||
/// Can be used to reduce the repetitive weight annotation in the trivial case. It accepts one
|
||||
/// argument that is expected to be an implementation of the `WeightInfo` or something that
|
||||
/// behaves syntactically equivalent. This allows to annotate a `WeightInfo` for all the calls.
|
||||
/// Now each call does not need to specify its own `#[pezpallet::weight]` but can instead use the
|
||||
/// one from the `#[pezpallet::call]` definition. So instead of having to write it on each call:
|
||||
///
|
||||
/// ```ignore
|
||||
/// #[pezpallet::call]
|
||||
/// impl<T: Config> Pezpallet<T> {
|
||||
/// #[pezpallet::weight(T::WeightInfo::create())]
|
||||
/// pub fn create(
|
||||
/// ```
|
||||
/// you can now omit it on the call itself, if the name of the weigh function matches the call:
|
||||
///
|
||||
/// ```ignore
|
||||
/// #[pezpallet::call(weight = <T as crate::Config>::WeightInfo)]
|
||||
/// impl<T: Config> Pezpallet<T> {
|
||||
/// pub fn create(
|
||||
/// ```
|
||||
///
|
||||
/// It is possible to use this syntax together with instantiated pallets by using `Config<I>`
|
||||
/// instead.
|
||||
///
|
||||
/// ### Dev Mode
|
||||
///
|
||||
/// Normally the `dev_mode` sets all weights of calls without a `#[pezpallet::weight]` annotation
|
||||
/// to zero. Now when there is a `weight` attribute on the `#[pezpallet::call]`, then that is used
|
||||
/// instead of the zero weight. So to say: it works together with `dev_mode`.
|
||||
RuntimeCall(Option<InheritedCallWeightAttr>, proc_macro2::Span),
|
||||
Error(proc_macro2::Span),
|
||||
Tasks(proc_macro2::Span),
|
||||
TaskList(proc_macro2::Span),
|
||||
TaskCondition(proc_macro2::Span),
|
||||
TaskIndex(proc_macro2::Span),
|
||||
RuntimeTask(proc_macro2::Span),
|
||||
RuntimeEvent(proc_macro2::Span),
|
||||
RuntimeOrigin(proc_macro2::Span),
|
||||
Inherent(proc_macro2::Span),
|
||||
Storage(proc_macro2::Span),
|
||||
GenesisConfig(proc_macro2::Span),
|
||||
GenesisBuild(proc_macro2::Span),
|
||||
ValidateUnsigned(proc_macro2::Span),
|
||||
TypeValue(proc_macro2::Span),
|
||||
ExtraConstants(proc_macro2::Span),
|
||||
Composite(proc_macro2::Span),
|
||||
ViewFunctions(proc_macro2::Span),
|
||||
}
|
||||
|
||||
impl PalletAttr {
|
||||
fn span(&self) -> proc_macro2::Span {
|
||||
match self {
|
||||
Self::Config { span, .. } => *span,
|
||||
Self::Pezpallet(span) => *span,
|
||||
Self::Hooks(span) => *span,
|
||||
Self::Tasks(span) => *span,
|
||||
Self::TaskCondition(span) => *span,
|
||||
Self::TaskIndex(span) => *span,
|
||||
Self::TaskList(span) => *span,
|
||||
Self::Error(span) => *span,
|
||||
Self::RuntimeTask(span) => *span,
|
||||
Self::RuntimeCall(_, span) => *span,
|
||||
Self::RuntimeEvent(span) => *span,
|
||||
Self::RuntimeOrigin(span) => *span,
|
||||
Self::Inherent(span) => *span,
|
||||
Self::Storage(span) => *span,
|
||||
Self::GenesisConfig(span) => *span,
|
||||
Self::GenesisBuild(span) => *span,
|
||||
Self::ValidateUnsigned(span) => *span,
|
||||
Self::TypeValue(span) => *span,
|
||||
Self::ExtraConstants(span) => *span,
|
||||
Self::Composite(span) => *span,
|
||||
Self::ViewFunctions(span) => *span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for PalletAttr {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
input.parse::<syn::Token![#]>()?;
|
||||
let content;
|
||||
syn::bracketed!(content in input);
|
||||
content.parse::<keyword::pezpallet>()?;
|
||||
content.parse::<syn::Token![::]>()?;
|
||||
|
||||
let lookahead = content.lookahead1();
|
||||
if lookahead.peek(keyword::config) {
|
||||
let span = content.parse::<keyword::config>()?.span();
|
||||
if content.peek(syn::token::Paren) {
|
||||
let inside_config;
|
||||
|
||||
// Parse (with_default, without_automatic_metadata) attributes.
|
||||
let _paren = syn::parenthesized!(inside_config in content);
|
||||
|
||||
let fields: syn::punctuated::Punctuated<ConfigValue, syn::Token![,]> =
|
||||
inside_config.parse_terminated(ConfigValue::parse, syn::Token![,])?;
|
||||
let config_values = fields.iter().collect::<Vec<_>>();
|
||||
|
||||
let mut with_default = false;
|
||||
let mut without_automatic_metadata = false;
|
||||
let mut pezframe_system_config = false;
|
||||
for config in config_values {
|
||||
match config {
|
||||
ConfigValue::WithDefault(_) => {
|
||||
if with_default {
|
||||
return Err(syn::Error::new(
|
||||
span,
|
||||
"Invalid duplicated attribute for `#[pezpallet::config]`. Please remove duplicates: with_default.",
|
||||
));
|
||||
}
|
||||
with_default = true;
|
||||
},
|
||||
ConfigValue::WithoutAutomaticMetadata(_) => {
|
||||
if without_automatic_metadata {
|
||||
return Err(syn::Error::new(
|
||||
span,
|
||||
"Invalid duplicated attribute for `#[pezpallet::config]`. Please remove duplicates: without_automatic_metadata.",
|
||||
));
|
||||
}
|
||||
without_automatic_metadata = true;
|
||||
},
|
||||
ConfigValue::FrameSystemConfig(_) => {
|
||||
if pezframe_system_config {
|
||||
return Err(syn::Error::new(
|
||||
span,
|
||||
"Invalid duplicated attribute for `#[pezpallet::config]`. Please remove duplicates: pezframe_system_config.",
|
||||
));
|
||||
}
|
||||
pezframe_system_config = true;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Ok(PalletAttr::Config {
|
||||
span,
|
||||
with_default,
|
||||
without_automatic_metadata,
|
||||
pezframe_system_config,
|
||||
})
|
||||
} else {
|
||||
Ok(PalletAttr::Config {
|
||||
span,
|
||||
with_default: false,
|
||||
without_automatic_metadata: false,
|
||||
pezframe_system_config: false,
|
||||
})
|
||||
}
|
||||
} else if lookahead.peek(keyword::pezpallet) {
|
||||
Ok(PalletAttr::Pezpallet(content.parse::<keyword::pezpallet>()?.span()))
|
||||
} else if lookahead.peek(keyword::hooks) {
|
||||
Ok(PalletAttr::Hooks(content.parse::<keyword::hooks>()?.span()))
|
||||
} else if lookahead.peek(keyword::call) {
|
||||
let span = content.parse::<keyword::call>().expect("peeked").span();
|
||||
let attr = match content.is_empty() {
|
||||
true => None,
|
||||
false => Some(InheritedCallWeightAttr::parse(&content)?),
|
||||
};
|
||||
Ok(PalletAttr::RuntimeCall(attr, span))
|
||||
} else if lookahead.peek(keyword::tasks_experimental) {
|
||||
Ok(PalletAttr::Tasks(content.parse::<keyword::tasks_experimental>()?.span()))
|
||||
} else if lookahead.peek(keyword::task_enum) {
|
||||
Ok(PalletAttr::RuntimeTask(content.parse::<keyword::task_enum>()?.span()))
|
||||
} else if lookahead.peek(keyword::task_condition) {
|
||||
Ok(PalletAttr::TaskCondition(content.parse::<keyword::task_condition>()?.span()))
|
||||
} else if lookahead.peek(keyword::task_index) {
|
||||
Ok(PalletAttr::TaskIndex(content.parse::<keyword::task_index>()?.span()))
|
||||
} else if lookahead.peek(keyword::task_list) {
|
||||
Ok(PalletAttr::TaskList(content.parse::<keyword::task_list>()?.span()))
|
||||
} else if lookahead.peek(keyword::error) {
|
||||
Ok(PalletAttr::Error(content.parse::<keyword::error>()?.span()))
|
||||
} else if lookahead.peek(keyword::event) {
|
||||
Ok(PalletAttr::RuntimeEvent(content.parse::<keyword::event>()?.span()))
|
||||
} else if lookahead.peek(keyword::origin) {
|
||||
Ok(PalletAttr::RuntimeOrigin(content.parse::<keyword::origin>()?.span()))
|
||||
} else if lookahead.peek(keyword::inherent) {
|
||||
Ok(PalletAttr::Inherent(content.parse::<keyword::inherent>()?.span()))
|
||||
} else if lookahead.peek(keyword::storage) {
|
||||
Ok(PalletAttr::Storage(content.parse::<keyword::storage>()?.span()))
|
||||
} else if lookahead.peek(keyword::genesis_config) {
|
||||
Ok(PalletAttr::GenesisConfig(content.parse::<keyword::genesis_config>()?.span()))
|
||||
} else if lookahead.peek(keyword::genesis_build) {
|
||||
Ok(PalletAttr::GenesisBuild(content.parse::<keyword::genesis_build>()?.span()))
|
||||
} else if lookahead.peek(keyword::validate_unsigned) {
|
||||
Ok(PalletAttr::ValidateUnsigned(content.parse::<keyword::validate_unsigned>()?.span()))
|
||||
} else if lookahead.peek(keyword::type_value) {
|
||||
Ok(PalletAttr::TypeValue(content.parse::<keyword::type_value>()?.span()))
|
||||
} else if lookahead.peek(keyword::extra_constants) {
|
||||
Ok(PalletAttr::ExtraConstants(content.parse::<keyword::extra_constants>()?.span()))
|
||||
} else if lookahead.peek(keyword::composite_enum) {
|
||||
Ok(PalletAttr::Composite(content.parse::<keyword::composite_enum>()?.span()))
|
||||
} else if lookahead.peek(keyword::view_functions) {
|
||||
Ok(PalletAttr::ViewFunctions(content.parse::<keyword::view_functions>()?.span()))
|
||||
} else {
|
||||
Err(lookahead.error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The optional weight annotation on a `#[pezpallet::call]` like `#[pezpallet::call(weight($type))]`.
|
||||
#[derive(Clone)]
|
||||
pub struct InheritedCallWeightAttr {
|
||||
pub typename: syn::Type,
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for InheritedCallWeightAttr {
|
||||
// Parses `(weight($type))` or `(weight = $type)`.
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
let content;
|
||||
syn::parenthesized!(content in input);
|
||||
content.parse::<keyword::weight>()?;
|
||||
let lookahead = content.lookahead1();
|
||||
|
||||
let buffer = if lookahead.peek(syn::token::Paren) {
|
||||
let inner;
|
||||
syn::parenthesized!(inner in content);
|
||||
inner
|
||||
} else if lookahead.peek(syn::Token![=]) {
|
||||
content.parse::<syn::Token![=]>().expect("peeked");
|
||||
content
|
||||
} else {
|
||||
return Err(lookahead.error());
|
||||
};
|
||||
|
||||
Ok(Self { typename: buffer.parse()? })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::helper;
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
/// Definition of the pezpallet origin type.
|
||||
///
|
||||
/// Either:
|
||||
/// * `type Origin`
|
||||
/// * `struct Origin`
|
||||
/// * `enum Origin`
|
||||
pub struct OriginDef {
|
||||
pub is_generic: bool,
|
||||
/// A set of usage of instance, must be check for consistency with trait.
|
||||
pub instances: Vec<helper::InstanceUsage>,
|
||||
}
|
||||
|
||||
impl OriginDef {
|
||||
pub fn try_from(item: &mut syn::Item) -> syn::Result<Self> {
|
||||
let item_span = item.span();
|
||||
let (vis, ident, generics) = match &item {
|
||||
syn::Item::Enum(item) => (&item.vis, &item.ident, &item.generics),
|
||||
syn::Item::Struct(item) => (&item.vis, &item.ident, &item.generics),
|
||||
syn::Item::Type(item) => (&item.vis, &item.ident, &item.generics),
|
||||
_ => {
|
||||
let msg = "Invalid pezpallet::origin, expected enum or struct or type";
|
||||
return Err(syn::Error::new(item.span(), msg));
|
||||
},
|
||||
};
|
||||
|
||||
let is_generic = !generics.params.is_empty();
|
||||
|
||||
let mut instances = vec![];
|
||||
if let Some(u) = helper::check_type_def_optional_gen(generics, item.span())? {
|
||||
instances.push(u);
|
||||
} else {
|
||||
// construct_runtime only allow generic event for instantiable pezpallet.
|
||||
instances.push(helper::InstanceUsage { has_instance: false, span: ident.span() })
|
||||
}
|
||||
|
||||
if !matches!(vis, syn::Visibility::Public(_)) {
|
||||
let msg = "Invalid pezpallet::origin, Origin must be public";
|
||||
return Err(syn::Error::new(item_span, msg));
|
||||
}
|
||||
|
||||
if ident != "Origin" {
|
||||
let msg = "Invalid pezpallet::origin, ident must `Origin`";
|
||||
return Err(syn::Error::new(ident.span(), msg));
|
||||
}
|
||||
|
||||
Ok(OriginDef { is_generic, instances })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::helper;
|
||||
use quote::ToTokens;
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
/// List of additional token to be used for parsing.
|
||||
mod keyword {
|
||||
syn::custom_keyword!(pezpallet);
|
||||
syn::custom_keyword!(Pezpallet);
|
||||
syn::custom_keyword!(without_storage_info);
|
||||
syn::custom_keyword!(storage_version);
|
||||
}
|
||||
|
||||
/// Definition of the pezpallet pezpallet.
|
||||
pub struct PalletStructDef {
|
||||
/// The index of item in pezpallet pezpallet.
|
||||
pub index: usize,
|
||||
/// A set of usage of instance, must be check for consistency with config trait.
|
||||
pub instances: Vec<helper::InstanceUsage>,
|
||||
/// The keyword Pezpallet used (contains span).
|
||||
pub pezpallet: keyword::Pezpallet,
|
||||
/// The span of the pezpallet::pezpallet attribute.
|
||||
pub attr_span: proc_macro2::Span,
|
||||
/// Whether to specify the storages max encoded len when implementing `StorageInfoTrait`.
|
||||
/// Contains the span of the attribute.
|
||||
pub without_storage_info: Option<proc_macro2::Span>,
|
||||
/// The in-code storage version of the pezpallet.
|
||||
pub storage_version: Option<syn::Path>,
|
||||
}
|
||||
|
||||
/// Parse for one variant of:
|
||||
/// * `#[pezpallet::without_storage_info]`
|
||||
/// * `#[pezpallet::storage_version(STORAGE_VERSION)]`
|
||||
pub enum PalletStructAttr {
|
||||
WithoutStorageInfoTrait(proc_macro2::Span),
|
||||
StorageVersion { storage_version: syn::Path, span: proc_macro2::Span },
|
||||
}
|
||||
|
||||
impl PalletStructAttr {
|
||||
fn span(&self) -> proc_macro2::Span {
|
||||
match self {
|
||||
Self::WithoutStorageInfoTrait(span) | Self::StorageVersion { span, .. } => *span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for PalletStructAttr {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
input.parse::<syn::Token![#]>()?;
|
||||
let content;
|
||||
syn::bracketed!(content in input);
|
||||
content.parse::<keyword::pezpallet>()?;
|
||||
content.parse::<syn::Token![::]>()?;
|
||||
|
||||
let lookahead = content.lookahead1();
|
||||
if lookahead.peek(keyword::without_storage_info) {
|
||||
let span = content.parse::<keyword::without_storage_info>()?.span();
|
||||
Ok(Self::WithoutStorageInfoTrait(span))
|
||||
} else if lookahead.peek(keyword::storage_version) {
|
||||
let span = content.parse::<keyword::storage_version>()?.span();
|
||||
|
||||
let version_content;
|
||||
syn::parenthesized!(version_content in content);
|
||||
let storage_version = version_content.parse::<syn::Path>()?;
|
||||
|
||||
Ok(Self::StorageVersion { storage_version, span })
|
||||
} else {
|
||||
Err(lookahead.error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PalletStructDef {
|
||||
pub fn try_from(
|
||||
attr_span: proc_macro2::Span,
|
||||
index: usize,
|
||||
item: &mut syn::Item,
|
||||
) -> syn::Result<Self> {
|
||||
let item = if let syn::Item::Struct(item) = item {
|
||||
item
|
||||
} else {
|
||||
let msg = "Invalid pezpallet::pezpallet, expected struct definition";
|
||||
return Err(syn::Error::new(item.span(), msg));
|
||||
};
|
||||
|
||||
let mut without_storage_info = None;
|
||||
let mut storage_version_found = None;
|
||||
|
||||
let struct_attrs: Vec<PalletStructAttr> = helper::take_item_pallet_attrs(&mut item.attrs)?;
|
||||
for attr in struct_attrs {
|
||||
match attr {
|
||||
PalletStructAttr::WithoutStorageInfoTrait(span)
|
||||
if without_storage_info.is_none() =>
|
||||
{
|
||||
without_storage_info = Some(span);
|
||||
},
|
||||
PalletStructAttr::StorageVersion { storage_version, .. }
|
||||
if storage_version_found.is_none() =>
|
||||
{
|
||||
storage_version_found = Some(storage_version);
|
||||
},
|
||||
attr => {
|
||||
let msg = "Unexpected duplicated attribute";
|
||||
return Err(syn::Error::new(attr.span(), msg));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let pezpallet = syn::parse2::<keyword::Pezpallet>(item.ident.to_token_stream())?;
|
||||
|
||||
if !matches!(item.vis, syn::Visibility::Public(_)) {
|
||||
let msg = "Invalid pezpallet::pezpallet, Pezpallet must be public";
|
||||
return Err(syn::Error::new(item.span(), msg));
|
||||
}
|
||||
|
||||
if item.generics.where_clause.is_some() {
|
||||
let msg = "Invalid pezpallet::pezpallet, where clause not supported on Pezpallet declaration";
|
||||
return Err(syn::Error::new(item.generics.where_clause.span(), msg));
|
||||
}
|
||||
|
||||
let instances =
|
||||
vec![helper::check_type_def_gen_no_bounds(&item.generics, item.ident.span())?];
|
||||
|
||||
Ok(Self {
|
||||
index,
|
||||
instances,
|
||||
pezpallet,
|
||||
attr_span,
|
||||
without_storage_info,
|
||||
storage_version: storage_version_found,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,950 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::helper;
|
||||
use pezframe_support_procedural_tools::get_doc_literals;
|
||||
use quote::ToTokens;
|
||||
use std::collections::HashMap;
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
/// List of additional token to be used for parsing.
|
||||
mod keyword {
|
||||
syn::custom_keyword!(Error);
|
||||
syn::custom_keyword!(pezpallet);
|
||||
syn::custom_keyword!(getter);
|
||||
syn::custom_keyword!(storage_prefix);
|
||||
syn::custom_keyword!(unbounded);
|
||||
syn::custom_keyword!(whitelist_storage);
|
||||
syn::custom_keyword!(disable_try_decode_storage);
|
||||
syn::custom_keyword!(OptionQuery);
|
||||
syn::custom_keyword!(ResultQuery);
|
||||
syn::custom_keyword!(ValueQuery);
|
||||
}
|
||||
|
||||
/// Parse for one of the following:
|
||||
/// * `#[pezpallet::getter(fn dummy)]`
|
||||
/// * `#[pezpallet::storage_prefix = "CustomName"]`
|
||||
/// * `#[pezpallet::unbounded]`
|
||||
/// * `#[pezpallet::whitelist_storage]
|
||||
/// * `#[pezpallet::disable_try_decode_storage]`
|
||||
pub enum PalletStorageAttr {
|
||||
Getter(syn::Ident, proc_macro2::Span),
|
||||
StorageName(syn::LitStr, proc_macro2::Span),
|
||||
Unbounded(proc_macro2::Span),
|
||||
WhitelistStorage(proc_macro2::Span),
|
||||
DisableTryDecodeStorage(proc_macro2::Span),
|
||||
}
|
||||
|
||||
impl PalletStorageAttr {
|
||||
fn attr_span(&self) -> proc_macro2::Span {
|
||||
match self {
|
||||
Self::Getter(_, span) |
|
||||
Self::StorageName(_, span) |
|
||||
Self::Unbounded(span) |
|
||||
Self::WhitelistStorage(span) => *span,
|
||||
Self::DisableTryDecodeStorage(span) => *span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for PalletStorageAttr {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
input.parse::<syn::Token![#]>()?;
|
||||
let attr_span = input.span();
|
||||
let content;
|
||||
syn::bracketed!(content in input);
|
||||
content.parse::<keyword::pezpallet>()?;
|
||||
content.parse::<syn::Token![::]>()?;
|
||||
|
||||
let lookahead = content.lookahead1();
|
||||
if lookahead.peek(keyword::getter) {
|
||||
content.parse::<keyword::getter>()?;
|
||||
|
||||
let generate_content;
|
||||
syn::parenthesized!(generate_content in content);
|
||||
generate_content.parse::<syn::Token![fn]>()?;
|
||||
Ok(Self::Getter(generate_content.parse::<syn::Ident>()?, attr_span))
|
||||
} else if lookahead.peek(keyword::storage_prefix) {
|
||||
content.parse::<keyword::storage_prefix>()?;
|
||||
content.parse::<syn::Token![=]>()?;
|
||||
|
||||
let renamed_prefix = content.parse::<syn::LitStr>()?;
|
||||
// Ensure the renamed prefix is a proper Rust identifier
|
||||
syn::parse_str::<syn::Ident>(&renamed_prefix.value()).map_err(|_| {
|
||||
let msg = format!("`{}` is not a valid identifier", renamed_prefix.value());
|
||||
syn::Error::new(renamed_prefix.span(), msg)
|
||||
})?;
|
||||
|
||||
Ok(Self::StorageName(renamed_prefix, attr_span))
|
||||
} else if lookahead.peek(keyword::unbounded) {
|
||||
content.parse::<keyword::unbounded>()?;
|
||||
|
||||
Ok(Self::Unbounded(attr_span))
|
||||
} else if lookahead.peek(keyword::whitelist_storage) {
|
||||
content.parse::<keyword::whitelist_storage>()?;
|
||||
Ok(Self::WhitelistStorage(attr_span))
|
||||
} else if lookahead.peek(keyword::disable_try_decode_storage) {
|
||||
content.parse::<keyword::disable_try_decode_storage>()?;
|
||||
Ok(Self::DisableTryDecodeStorage(attr_span))
|
||||
} else {
|
||||
Err(lookahead.error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct PalletStorageAttrInfo {
|
||||
getter: Option<syn::Ident>,
|
||||
rename_as: Option<syn::LitStr>,
|
||||
unbounded: bool,
|
||||
whitelisted: bool,
|
||||
try_decode: bool,
|
||||
}
|
||||
|
||||
impl PalletStorageAttrInfo {
|
||||
fn from_attrs(attrs: Vec<PalletStorageAttr>) -> syn::Result<Self> {
|
||||
let mut getter = None;
|
||||
let mut rename_as = None;
|
||||
let mut unbounded = false;
|
||||
let mut whitelisted = false;
|
||||
let mut disable_try_decode_storage = false;
|
||||
for attr in attrs {
|
||||
match attr {
|
||||
PalletStorageAttr::Getter(ident, ..) if getter.is_none() => getter = Some(ident),
|
||||
PalletStorageAttr::StorageName(name, ..) if rename_as.is_none() =>
|
||||
rename_as = Some(name),
|
||||
PalletStorageAttr::Unbounded(..) if !unbounded => unbounded = true,
|
||||
PalletStorageAttr::WhitelistStorage(..) if !whitelisted => whitelisted = true,
|
||||
PalletStorageAttr::DisableTryDecodeStorage(..) if !disable_try_decode_storage =>
|
||||
disable_try_decode_storage = true,
|
||||
attr =>
|
||||
return Err(syn::Error::new(
|
||||
attr.attr_span(),
|
||||
"Invalid attribute: Duplicate attribute",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(PalletStorageAttrInfo {
|
||||
getter,
|
||||
rename_as,
|
||||
unbounded,
|
||||
whitelisted,
|
||||
try_decode: !disable_try_decode_storage,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// The value and key types used by storages. Needed to expand metadata.
|
||||
pub enum Metadata {
|
||||
Value { value: syn::Type },
|
||||
Map { value: syn::Type, key: syn::Type },
|
||||
CountedMap { value: syn::Type, key: syn::Type },
|
||||
DoubleMap { value: syn::Type, key1: syn::Type, key2: syn::Type },
|
||||
NMap { keys: Vec<syn::Type>, keygen: syn::Type, value: syn::Type },
|
||||
CountedNMap { keys: Vec<syn::Type>, keygen: syn::Type, value: syn::Type },
|
||||
}
|
||||
|
||||
pub enum QueryKind {
|
||||
OptionQuery,
|
||||
ResultQuery(syn::Path, syn::Ident),
|
||||
ValueQuery,
|
||||
}
|
||||
|
||||
/// Definition of a storage, storage is a storage type like
|
||||
/// `type MyStorage = StorageValue<MyStorageP, u32>`
|
||||
/// The keys and values types are parsed in order to get metadata
|
||||
pub struct StorageDef {
|
||||
/// The index of storage item in pezpallet module.
|
||||
pub index: usize,
|
||||
/// Visibility of the storage type.
|
||||
pub vis: syn::Visibility,
|
||||
/// The type ident, to generate the StoragePrefix for.
|
||||
pub ident: syn::Ident,
|
||||
/// The keys and value metadata of the storage.
|
||||
pub metadata: Metadata,
|
||||
/// The doc associated to the storage.
|
||||
pub docs: Vec<syn::Expr>,
|
||||
/// A set of usage of instance, must be check for consistency with config.
|
||||
pub instances: Vec<helper::InstanceUsage>,
|
||||
/// Optional getter to generate. If some then query_kind is ensured to be some as well.
|
||||
pub getter: Option<syn::Ident>,
|
||||
/// Optional expression that evaluates to a type that can be used as StoragePrefix instead of
|
||||
/// ident.
|
||||
pub rename_as: Option<syn::LitStr>,
|
||||
/// Whereas the querytype of the storage is OptionQuery, ResultQuery or ValueQuery.
|
||||
/// Note that this is best effort as it can't be determined when QueryKind is generic, and
|
||||
/// result can be false if user do some unexpected type alias.
|
||||
pub query_kind: Option<QueryKind>,
|
||||
/// Where clause of type definition.
|
||||
pub where_clause: Option<syn::WhereClause>,
|
||||
/// The span of the pezpallet::storage attribute.
|
||||
pub attr_span: proc_macro2::Span,
|
||||
/// The `cfg` attributes.
|
||||
pub cfg_attrs: Vec<syn::Attribute>,
|
||||
/// If generics are named (e.g. `StorageValue<Value = u32, ..>`) then this contains all the
|
||||
/// generics of the storage.
|
||||
/// If generics are not named, this is none.
|
||||
pub named_generics: Option<StorageGenerics>,
|
||||
/// If the value stored in this storage is unbounded.
|
||||
pub unbounded: bool,
|
||||
/// Whether or not reads to this storage key will be ignored by benchmarking
|
||||
pub whitelisted: bool,
|
||||
/// Whether or not to try to decode the storage key when running try-runtime checks.
|
||||
pub try_decode: bool,
|
||||
/// Whether or not a default hasher is allowed to replace `_`
|
||||
pub use_default_hasher: bool,
|
||||
/// Attributes
|
||||
pub attrs: Vec<syn::Attribute>,
|
||||
}
|
||||
|
||||
/// The parsed generic from the
|
||||
#[derive(Clone)]
|
||||
pub enum StorageGenerics {
|
||||
DoubleMap {
|
||||
hasher1: syn::Type,
|
||||
key1: syn::Type,
|
||||
hasher2: syn::Type,
|
||||
key2: syn::Type,
|
||||
value: syn::Type,
|
||||
query_kind: Option<syn::Type>,
|
||||
on_empty: Option<syn::Type>,
|
||||
max_values: Option<syn::Type>,
|
||||
},
|
||||
Map {
|
||||
hasher: syn::Type,
|
||||
key: syn::Type,
|
||||
value: syn::Type,
|
||||
query_kind: Option<syn::Type>,
|
||||
on_empty: Option<syn::Type>,
|
||||
max_values: Option<syn::Type>,
|
||||
},
|
||||
CountedMap {
|
||||
hasher: syn::Type,
|
||||
key: syn::Type,
|
||||
value: syn::Type,
|
||||
query_kind: Option<syn::Type>,
|
||||
on_empty: Option<syn::Type>,
|
||||
max_values: Option<syn::Type>,
|
||||
},
|
||||
Value {
|
||||
value: syn::Type,
|
||||
query_kind: Option<syn::Type>,
|
||||
on_empty: Option<syn::Type>,
|
||||
},
|
||||
NMap {
|
||||
keygen: syn::Type,
|
||||
value: syn::Type,
|
||||
query_kind: Option<syn::Type>,
|
||||
on_empty: Option<syn::Type>,
|
||||
max_values: Option<syn::Type>,
|
||||
},
|
||||
CountedNMap {
|
||||
keygen: syn::Type,
|
||||
value: syn::Type,
|
||||
query_kind: Option<syn::Type>,
|
||||
on_empty: Option<syn::Type>,
|
||||
max_values: Option<syn::Type>,
|
||||
},
|
||||
}
|
||||
|
||||
impl StorageGenerics {
|
||||
/// Return the metadata from the defined generics
|
||||
fn metadata(&self) -> syn::Result<Metadata> {
|
||||
let res = match self.clone() {
|
||||
Self::DoubleMap { value, key1, key2, .. } => Metadata::DoubleMap { value, key1, key2 },
|
||||
Self::Map { value, key, .. } => Metadata::Map { value, key },
|
||||
Self::CountedMap { value, key, .. } => Metadata::CountedMap { value, key },
|
||||
Self::Value { value, .. } => Metadata::Value { value },
|
||||
Self::NMap { keygen, value, .. } =>
|
||||
Metadata::NMap { keys: collect_keys(&keygen)?, keygen, value },
|
||||
Self::CountedNMap { keygen, value, .. } =>
|
||||
Metadata::CountedNMap { keys: collect_keys(&keygen)?, keygen, value },
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Return the query kind from the defined generics
|
||||
fn query_kind(&self) -> Option<syn::Type> {
|
||||
match &self {
|
||||
Self::DoubleMap { query_kind, .. } |
|
||||
Self::Map { query_kind, .. } |
|
||||
Self::CountedMap { query_kind, .. } |
|
||||
Self::Value { query_kind, .. } |
|
||||
Self::NMap { query_kind, .. } |
|
||||
Self::CountedNMap { query_kind, .. } => query_kind.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum StorageKind {
|
||||
Value,
|
||||
Map,
|
||||
CountedMap,
|
||||
DoubleMap,
|
||||
NMap,
|
||||
CountedNMap,
|
||||
}
|
||||
|
||||
/// Check the generics in the `map` contains the generics in `gen` may contains generics in
|
||||
/// `optional_gen`, and doesn't contains any other.
|
||||
fn check_generics(
|
||||
map: &HashMap<String, syn::AssocType>,
|
||||
mandatory_generics: &[&str],
|
||||
optional_generics: &[&str],
|
||||
storage_type_name: &str,
|
||||
args_span: proc_macro2::Span,
|
||||
) -> syn::Result<()> {
|
||||
let mut errors = vec![];
|
||||
|
||||
let expectation = {
|
||||
let mut e = format!(
|
||||
"`{}` expect generics {}and optional generics {}",
|
||||
storage_type_name,
|
||||
mandatory_generics
|
||||
.iter()
|
||||
.map(|name| format!("`{}`, ", name))
|
||||
.collect::<String>(),
|
||||
&optional_generics.iter().map(|name| format!("`{}`, ", name)).collect::<String>(),
|
||||
);
|
||||
e.pop();
|
||||
e.pop();
|
||||
e.push('.');
|
||||
e
|
||||
};
|
||||
|
||||
for (gen_name, gen_binding) in map {
|
||||
if !mandatory_generics.contains(&gen_name.as_str()) &&
|
||||
!optional_generics.contains(&gen_name.as_str())
|
||||
{
|
||||
let msg = format!(
|
||||
"Invalid pezpallet::storage, Unexpected generic `{}` for `{}`. {}",
|
||||
gen_name, storage_type_name, expectation,
|
||||
);
|
||||
errors.push(syn::Error::new(gen_binding.span(), msg));
|
||||
}
|
||||
}
|
||||
|
||||
for mandatory_generic in mandatory_generics {
|
||||
if !map.contains_key(&mandatory_generic.to_string()) {
|
||||
let msg = format!(
|
||||
"Invalid pezpallet::storage, cannot find `{}` generic, required for `{}`.",
|
||||
mandatory_generic, storage_type_name
|
||||
);
|
||||
errors.push(syn::Error::new(args_span, msg));
|
||||
}
|
||||
}
|
||||
|
||||
let mut errors = errors.drain(..);
|
||||
if let Some(mut error) = errors.next() {
|
||||
for other_error in errors {
|
||||
error.combine(other_error);
|
||||
}
|
||||
Err(error)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `(named generics, metadata, query kind, use_default_hasher)`
|
||||
fn process_named_generics(
|
||||
storage: &StorageKind,
|
||||
args_span: proc_macro2::Span,
|
||||
args: &[syn::AssocType],
|
||||
dev_mode: bool,
|
||||
) -> syn::Result<(Option<StorageGenerics>, Metadata, Option<syn::Type>, bool)> {
|
||||
let mut parsed = HashMap::<String, syn::AssocType>::new();
|
||||
|
||||
// Ensure no duplicate.
|
||||
for arg in args {
|
||||
if let Some(other) = parsed.get(&arg.ident.to_string()) {
|
||||
let msg = "Invalid pezpallet::storage, Duplicated named generic";
|
||||
let mut err = syn::Error::new(arg.ident.span(), msg);
|
||||
err.combine(syn::Error::new(other.ident.span(), msg));
|
||||
return Err(err);
|
||||
}
|
||||
parsed.insert(arg.ident.to_string(), arg.clone());
|
||||
}
|
||||
|
||||
let mut map_mandatory_generics = vec!["Key", "Value"];
|
||||
let mut map_optional_generics = vec!["QueryKind", "OnEmpty", "MaxValues"];
|
||||
if dev_mode {
|
||||
map_optional_generics.push("Hasher");
|
||||
} else {
|
||||
map_mandatory_generics.push("Hasher");
|
||||
}
|
||||
|
||||
let generics = match storage {
|
||||
StorageKind::Value => {
|
||||
check_generics(
|
||||
&parsed,
|
||||
&["Value"],
|
||||
&["QueryKind", "OnEmpty"],
|
||||
"StorageValue",
|
||||
args_span,
|
||||
)?;
|
||||
|
||||
StorageGenerics::Value {
|
||||
value: parsed
|
||||
.remove("Value")
|
||||
.map(|binding| binding.ty)
|
||||
.expect("checked above as mandatory generic"),
|
||||
query_kind: parsed.remove("QueryKind").map(|binding| binding.ty),
|
||||
on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty),
|
||||
}
|
||||
},
|
||||
StorageKind::Map => {
|
||||
check_generics(
|
||||
&parsed,
|
||||
&map_mandatory_generics,
|
||||
&map_optional_generics,
|
||||
"StorageMap",
|
||||
args_span,
|
||||
)?;
|
||||
|
||||
StorageGenerics::Map {
|
||||
hasher: parsed
|
||||
.remove("Hasher")
|
||||
.map(|binding| binding.ty)
|
||||
.unwrap_or(syn::parse_quote!(Blake2_128Concat)),
|
||||
key: parsed
|
||||
.remove("Key")
|
||||
.map(|binding| binding.ty)
|
||||
.expect("checked above as mandatory generic"),
|
||||
value: parsed
|
||||
.remove("Value")
|
||||
.map(|binding| binding.ty)
|
||||
.expect("checked above as mandatory generic"),
|
||||
query_kind: parsed.remove("QueryKind").map(|binding| binding.ty),
|
||||
on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty),
|
||||
max_values: parsed.remove("MaxValues").map(|binding| binding.ty),
|
||||
}
|
||||
},
|
||||
StorageKind::CountedMap => {
|
||||
check_generics(
|
||||
&parsed,
|
||||
&map_mandatory_generics,
|
||||
&map_optional_generics,
|
||||
"CountedStorageMap",
|
||||
args_span,
|
||||
)?;
|
||||
|
||||
StorageGenerics::CountedMap {
|
||||
hasher: parsed
|
||||
.remove("Hasher")
|
||||
.map(|binding| binding.ty)
|
||||
.unwrap_or(syn::Type::Verbatim(quote::quote! { Blake2_128Concat })),
|
||||
key: parsed
|
||||
.remove("Key")
|
||||
.map(|binding| binding.ty)
|
||||
.expect("checked above as mandatory generic"),
|
||||
value: parsed
|
||||
.remove("Value")
|
||||
.map(|binding| binding.ty)
|
||||
.expect("checked above as mandatory generic"),
|
||||
query_kind: parsed.remove("QueryKind").map(|binding| binding.ty),
|
||||
on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty),
|
||||
max_values: parsed.remove("MaxValues").map(|binding| binding.ty),
|
||||
}
|
||||
},
|
||||
StorageKind::DoubleMap => {
|
||||
let mut double_map_mandatory_generics = vec!["Key1", "Key2", "Value"];
|
||||
if dev_mode {
|
||||
map_optional_generics.extend(["Hasher1", "Hasher2"]);
|
||||
} else {
|
||||
double_map_mandatory_generics.extend(["Hasher1", "Hasher2"]);
|
||||
}
|
||||
|
||||
check_generics(
|
||||
&parsed,
|
||||
&double_map_mandatory_generics,
|
||||
&map_optional_generics,
|
||||
"StorageDoubleMap",
|
||||
args_span,
|
||||
)?;
|
||||
|
||||
StorageGenerics::DoubleMap {
|
||||
hasher1: parsed
|
||||
.remove("Hasher1")
|
||||
.map(|binding| binding.ty)
|
||||
.unwrap_or(syn::parse_quote!(Blake2_128Concat)),
|
||||
key1: parsed
|
||||
.remove("Key1")
|
||||
.map(|binding| binding.ty)
|
||||
.expect("checked above as mandatory generic"),
|
||||
hasher2: parsed
|
||||
.remove("Hasher2")
|
||||
.map(|binding| binding.ty)
|
||||
.unwrap_or(syn::parse_quote!(Blake2_128Concat)),
|
||||
key2: parsed
|
||||
.remove("Key2")
|
||||
.map(|binding| binding.ty)
|
||||
.expect("checked above as mandatory generic"),
|
||||
value: parsed
|
||||
.remove("Value")
|
||||
.map(|binding| binding.ty)
|
||||
.expect("checked above as mandatory generic"),
|
||||
query_kind: parsed.remove("QueryKind").map(|binding| binding.ty),
|
||||
on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty),
|
||||
max_values: parsed.remove("MaxValues").map(|binding| binding.ty),
|
||||
}
|
||||
},
|
||||
StorageKind::NMap => {
|
||||
check_generics(
|
||||
&parsed,
|
||||
&["Key", "Value"],
|
||||
&["QueryKind", "OnEmpty", "MaxValues"],
|
||||
"StorageNMap",
|
||||
args_span,
|
||||
)?;
|
||||
|
||||
StorageGenerics::NMap {
|
||||
keygen: parsed
|
||||
.remove("Key")
|
||||
.map(|binding| binding.ty)
|
||||
.expect("checked above as mandatory generic"),
|
||||
value: parsed
|
||||
.remove("Value")
|
||||
.map(|binding| binding.ty)
|
||||
.expect("checked above as mandatory generic"),
|
||||
query_kind: parsed.remove("QueryKind").map(|binding| binding.ty),
|
||||
on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty),
|
||||
max_values: parsed.remove("MaxValues").map(|binding| binding.ty),
|
||||
}
|
||||
},
|
||||
StorageKind::CountedNMap => {
|
||||
check_generics(
|
||||
&parsed,
|
||||
&["Key", "Value"],
|
||||
&["QueryKind", "OnEmpty", "MaxValues"],
|
||||
"CountedStorageNMap",
|
||||
args_span,
|
||||
)?;
|
||||
|
||||
StorageGenerics::CountedNMap {
|
||||
keygen: parsed
|
||||
.remove("Key")
|
||||
.map(|binding| binding.ty)
|
||||
.expect("checked above as mandatory generic"),
|
||||
value: parsed
|
||||
.remove("Value")
|
||||
.map(|binding| binding.ty)
|
||||
.expect("checked above as mandatory generic"),
|
||||
query_kind: parsed.remove("QueryKind").map(|binding| binding.ty),
|
||||
on_empty: parsed.remove("OnEmpty").map(|binding| binding.ty),
|
||||
max_values: parsed.remove("MaxValues").map(|binding| binding.ty),
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let metadata = generics.metadata()?;
|
||||
let query_kind = generics.query_kind();
|
||||
|
||||
Ok((Some(generics), metadata, query_kind, false))
|
||||
}
|
||||
|
||||
/// Returns `(named generics, metadata, query kind, use_default_hasher)`
|
||||
fn process_unnamed_generics(
|
||||
storage: &StorageKind,
|
||||
args_span: proc_macro2::Span,
|
||||
args: &[syn::Type],
|
||||
dev_mode: bool,
|
||||
) -> syn::Result<(Option<StorageGenerics>, Metadata, Option<syn::Type>, bool)> {
|
||||
let retrieve_arg = |arg_pos| {
|
||||
args.get(arg_pos).cloned().ok_or_else(|| {
|
||||
let msg = format!(
|
||||
"Invalid pezpallet::storage, unexpected number of generic argument, \
|
||||
expect at least {} args, found {}.",
|
||||
arg_pos + 1,
|
||||
args.len(),
|
||||
);
|
||||
syn::Error::new(args_span, msg)
|
||||
})
|
||||
};
|
||||
|
||||
let prefix_arg = retrieve_arg(0)?;
|
||||
syn::parse2::<syn::Token![_]>(prefix_arg.to_token_stream()).map_err(|e| {
|
||||
let msg = "Invalid pezpallet::storage, for unnamed generic arguments the type \
|
||||
first generic argument must be `_`, the argument is then replaced by macro.";
|
||||
let mut err = syn::Error::new(prefix_arg.span(), msg);
|
||||
err.combine(e);
|
||||
err
|
||||
})?;
|
||||
|
||||
let use_default_hasher = |arg_pos| {
|
||||
let arg = retrieve_arg(arg_pos)?;
|
||||
if syn::parse2::<syn::Token![_]>(arg.to_token_stream()).is_ok() {
|
||||
if dev_mode {
|
||||
Ok(true)
|
||||
} else {
|
||||
let msg = "`_` can only be used in dev_mode. Please specify an appropriate hasher.";
|
||||
Err(syn::Error::new(arg.span(), msg))
|
||||
}
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
};
|
||||
|
||||
let res = match storage {
|
||||
StorageKind::Value =>
|
||||
(None, Metadata::Value { value: retrieve_arg(1)? }, retrieve_arg(2).ok(), false),
|
||||
StorageKind::Map => (
|
||||
None,
|
||||
Metadata::Map { key: retrieve_arg(2)?, value: retrieve_arg(3)? },
|
||||
retrieve_arg(4).ok(),
|
||||
use_default_hasher(1)?,
|
||||
),
|
||||
StorageKind::CountedMap => (
|
||||
None,
|
||||
Metadata::CountedMap { key: retrieve_arg(2)?, value: retrieve_arg(3)? },
|
||||
retrieve_arg(4).ok(),
|
||||
use_default_hasher(1)?,
|
||||
),
|
||||
StorageKind::DoubleMap => (
|
||||
None,
|
||||
Metadata::DoubleMap {
|
||||
key1: retrieve_arg(2)?,
|
||||
key2: retrieve_arg(4)?,
|
||||
value: retrieve_arg(5)?,
|
||||
},
|
||||
retrieve_arg(6).ok(),
|
||||
use_default_hasher(1)? && use_default_hasher(3)?,
|
||||
),
|
||||
StorageKind::NMap => {
|
||||
let keygen = retrieve_arg(1)?;
|
||||
let keys = collect_keys(&keygen)?;
|
||||
(
|
||||
None,
|
||||
Metadata::NMap { keys, keygen, value: retrieve_arg(2)? },
|
||||
retrieve_arg(3).ok(),
|
||||
false,
|
||||
)
|
||||
},
|
||||
StorageKind::CountedNMap => {
|
||||
let keygen = retrieve_arg(1)?;
|
||||
let keys = collect_keys(&keygen)?;
|
||||
(
|
||||
None,
|
||||
Metadata::CountedNMap { keys, keygen, value: retrieve_arg(2)? },
|
||||
retrieve_arg(3).ok(),
|
||||
false,
|
||||
)
|
||||
},
|
||||
};
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Returns `(named generics, metadata, query kind, use_default_hasher)`
|
||||
fn process_generics(
|
||||
segment: &syn::PathSegment,
|
||||
dev_mode: bool,
|
||||
) -> syn::Result<(Option<StorageGenerics>, Metadata, Option<syn::Type>, bool)> {
|
||||
let storage_kind = match &*segment.ident.to_string() {
|
||||
"StorageValue" => StorageKind::Value,
|
||||
"StorageMap" => StorageKind::Map,
|
||||
"CountedStorageMap" => StorageKind::CountedMap,
|
||||
"StorageDoubleMap" => StorageKind::DoubleMap,
|
||||
"StorageNMap" => StorageKind::NMap,
|
||||
"CountedStorageNMap" => StorageKind::CountedNMap,
|
||||
found => {
|
||||
let msg = format!(
|
||||
"Invalid pezpallet::storage, expected ident: `StorageValue` or \
|
||||
`StorageMap` or `CountedStorageMap` or `StorageDoubleMap` or `StorageNMap` or `CountedStorageNMap` \
|
||||
in order to expand metadata, found `{}`.",
|
||||
found,
|
||||
);
|
||||
return Err(syn::Error::new(segment.ident.span(), msg));
|
||||
},
|
||||
};
|
||||
|
||||
let args_span = segment.arguments.span();
|
||||
|
||||
let args = match &segment.arguments {
|
||||
syn::PathArguments::AngleBracketed(args) if !args.args.is_empty() => args,
|
||||
_ => {
|
||||
let msg = "Invalid pezpallet::storage, invalid number of generic generic arguments, \
|
||||
expect more that 0 generic arguments.";
|
||||
return Err(syn::Error::new(segment.span(), msg));
|
||||
},
|
||||
};
|
||||
|
||||
if args.args.iter().all(|gen| matches!(gen, syn::GenericArgument::Type(_))) {
|
||||
let args = args
|
||||
.args
|
||||
.iter()
|
||||
.map(|gen| match gen {
|
||||
syn::GenericArgument::Type(gen) => gen.clone(),
|
||||
_ => unreachable!("It is asserted above that all generics are types"),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
process_unnamed_generics(&storage_kind, args_span, &args, dev_mode)
|
||||
} else if args.args.iter().all(|gen| matches!(gen, syn::GenericArgument::AssocType(_))) {
|
||||
let args = args
|
||||
.args
|
||||
.iter()
|
||||
.map(|gen| match gen {
|
||||
syn::GenericArgument::AssocType(gen) => gen.clone(),
|
||||
_ => unreachable!("It is asserted above that all generics are bindings"),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
process_named_generics(&storage_kind, args_span, &args, dev_mode)
|
||||
} else {
|
||||
let msg = "Invalid pezpallet::storage, invalid generic declaration for storage. Expect only \
|
||||
type generics or binding generics, e.g. `<Name1 = Gen1, Name2 = Gen2, ..>` or \
|
||||
`<Gen1, Gen2, ..>`.";
|
||||
Err(syn::Error::new(segment.span(), msg))
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse the 2nd type argument to `StorageNMap` and return its keys.
|
||||
fn collect_keys(keygen: &syn::Type) -> syn::Result<Vec<syn::Type>> {
|
||||
if let syn::Type::Tuple(tup) = keygen {
|
||||
tup.elems.iter().map(extract_key).collect::<syn::Result<Vec<_>>>()
|
||||
} else {
|
||||
Ok(vec![extract_key(keygen)?])
|
||||
}
|
||||
}
|
||||
|
||||
/// In `Key<H, K>`, extract K and return it.
|
||||
fn extract_key(ty: &syn::Type) -> syn::Result<syn::Type> {
|
||||
let typ = if let syn::Type::Path(typ) = ty {
|
||||
typ
|
||||
} else {
|
||||
let msg = "Invalid pezpallet::storage, expected type path";
|
||||
return Err(syn::Error::new(ty.span(), msg));
|
||||
};
|
||||
|
||||
let key_struct = typ.path.segments.last().ok_or_else(|| {
|
||||
let msg = "Invalid pezpallet::storage, expected type path with at least one segment";
|
||||
syn::Error::new(typ.path.span(), msg)
|
||||
})?;
|
||||
if key_struct.ident != "Key" && key_struct.ident != "NMapKey" {
|
||||
let msg = "Invalid pezpallet::storage, expected Key or NMapKey struct";
|
||||
return Err(syn::Error::new(key_struct.ident.span(), msg));
|
||||
}
|
||||
|
||||
let ty_params = if let syn::PathArguments::AngleBracketed(args) = &key_struct.arguments {
|
||||
args
|
||||
} else {
|
||||
let msg = "Invalid pezpallet::storage, expected angle bracketed arguments";
|
||||
return Err(syn::Error::new(key_struct.arguments.span(), msg));
|
||||
};
|
||||
|
||||
if ty_params.args.len() != 2 {
|
||||
let msg = format!(
|
||||
"Invalid pezpallet::storage, unexpected number of generic arguments \
|
||||
for Key struct, expected 2 args, found {}",
|
||||
ty_params.args.len()
|
||||
);
|
||||
return Err(syn::Error::new(ty_params.span(), msg));
|
||||
}
|
||||
|
||||
let key = match &ty_params.args[1] {
|
||||
syn::GenericArgument::Type(key_ty) => key_ty.clone(),
|
||||
_ => {
|
||||
let msg = "Invalid pezpallet::storage, expected type";
|
||||
return Err(syn::Error::new(ty_params.args[1].span(), msg));
|
||||
},
|
||||
};
|
||||
|
||||
Ok(key)
|
||||
}
|
||||
|
||||
impl StorageDef {
|
||||
/// Return the storage prefix for this storage item
|
||||
pub fn prefix(&self) -> String {
|
||||
self.rename_as
|
||||
.as_ref()
|
||||
.map(syn::LitStr::value)
|
||||
.unwrap_or_else(|| self.ident.to_string())
|
||||
}
|
||||
|
||||
/// Return either the span of the ident or the span of the literal in the
|
||||
/// #[storage_prefix] attribute
|
||||
pub fn prefix_span(&self) -> proc_macro2::Span {
|
||||
self.rename_as
|
||||
.as_ref()
|
||||
.map(syn::LitStr::span)
|
||||
.unwrap_or_else(|| self.ident.span())
|
||||
}
|
||||
|
||||
pub fn try_from(
|
||||
attr_span: proc_macro2::Span,
|
||||
index: usize,
|
||||
item: &mut syn::Item,
|
||||
dev_mode: bool,
|
||||
) -> syn::Result<Self> {
|
||||
let item = if let syn::Item::Type(item) = item {
|
||||
item
|
||||
} else {
|
||||
return Err(syn::Error::new(item.span(), "Invalid pezpallet::storage, expect item type."));
|
||||
};
|
||||
|
||||
let attrs: Vec<PalletStorageAttr> = helper::take_item_pallet_attrs(&mut item.attrs)?;
|
||||
let PalletStorageAttrInfo { getter, rename_as, mut unbounded, whitelisted, try_decode } =
|
||||
PalletStorageAttrInfo::from_attrs(attrs)?;
|
||||
|
||||
// set all storages to be unbounded if dev_mode is enabled
|
||||
unbounded |= dev_mode;
|
||||
let cfg_attrs = helper::get_item_cfg_attrs(&item.attrs);
|
||||
|
||||
let instances = vec![helper::check_type_def_gen(&item.generics, item.ident.span())?];
|
||||
|
||||
let where_clause = item.generics.where_clause.clone();
|
||||
let docs = get_doc_literals(&item.attrs);
|
||||
|
||||
let typ = if let syn::Type::Path(typ) = &*item.ty {
|
||||
typ
|
||||
} else {
|
||||
let msg = "Invalid pezpallet::storage, expected type path";
|
||||
return Err(syn::Error::new(item.ty.span(), msg));
|
||||
};
|
||||
|
||||
if typ.path.segments.len() != 1 {
|
||||
let msg = "Invalid pezpallet::storage, expected type path with one segment";
|
||||
return Err(syn::Error::new(item.ty.span(), msg));
|
||||
}
|
||||
|
||||
let (named_generics, metadata, query_kind, use_default_hasher) =
|
||||
process_generics(&typ.path.segments[0], dev_mode)?;
|
||||
|
||||
let query_kind = query_kind
|
||||
.map(|query_kind| {
|
||||
use syn::{
|
||||
AngleBracketedGenericArguments, GenericArgument, Path, PathArguments, Type,
|
||||
TypePath,
|
||||
};
|
||||
|
||||
let result_query = match query_kind {
|
||||
Type::Path(path)
|
||||
if path
|
||||
.path
|
||||
.segments
|
||||
.last()
|
||||
.map_or(false, |s| s.ident == "OptionQuery") =>
|
||||
return Ok(Some(QueryKind::OptionQuery)),
|
||||
Type::Path(TypePath { path: Path { segments, .. }, .. })
|
||||
if segments.last().map_or(false, |s| s.ident == "ResultQuery") =>
|
||||
segments
|
||||
.last()
|
||||
.expect("segments is checked to have the last value; qed")
|
||||
.clone(),
|
||||
Type::Path(path)
|
||||
if path.path.segments.last().map_or(false, |s| s.ident == "ValueQuery") =>
|
||||
return Ok(Some(QueryKind::ValueQuery)),
|
||||
_ => return Ok(None),
|
||||
};
|
||||
|
||||
let error_type = match result_query.arguments {
|
||||
PathArguments::AngleBracketed(AngleBracketedGenericArguments {
|
||||
args, ..
|
||||
}) => {
|
||||
if args.len() != 1 {
|
||||
let msg = format!(
|
||||
"Invalid pezpallet::storage, unexpected number of generic arguments \
|
||||
for ResultQuery, expected 1 type argument, found {}",
|
||||
args.len(),
|
||||
);
|
||||
return Err(syn::Error::new(args.span(), msg));
|
||||
}
|
||||
|
||||
args[0].clone()
|
||||
},
|
||||
args => {
|
||||
let msg = format!(
|
||||
"Invalid pezpallet::storage, unexpected generic args for ResultQuery, \
|
||||
expected angle-bracketed arguments, found `{}`",
|
||||
args.to_token_stream().to_string()
|
||||
);
|
||||
return Err(syn::Error::new(args.span(), msg));
|
||||
},
|
||||
};
|
||||
|
||||
match error_type {
|
||||
GenericArgument::Type(Type::Path(TypePath {
|
||||
path: Path { segments: err_variant, leading_colon },
|
||||
..
|
||||
})) => {
|
||||
if err_variant.len() < 2 {
|
||||
let msg = format!(
|
||||
"Invalid pezpallet::storage, unexpected number of path segments for \
|
||||
the generics in ResultQuery, expected a path with at least 2 \
|
||||
segments, found {}",
|
||||
err_variant.len(),
|
||||
);
|
||||
return Err(syn::Error::new(err_variant.span(), msg));
|
||||
}
|
||||
let mut error = err_variant.clone();
|
||||
let err_variant = error
|
||||
.pop()
|
||||
.expect("Checked to have at least 2; qed")
|
||||
.into_value()
|
||||
.ident;
|
||||
|
||||
// Necessary here to eliminate the last double colon
|
||||
let last =
|
||||
error.pop().expect("Checked to have at least 2; qed").into_value();
|
||||
error.push_value(last);
|
||||
|
||||
Ok(Some(QueryKind::ResultQuery(
|
||||
syn::Path { leading_colon, segments: error },
|
||||
err_variant,
|
||||
)))
|
||||
},
|
||||
gen_arg => {
|
||||
let msg = format!(
|
||||
"Invalid pezpallet::storage, unexpected generic argument kind, expected a \
|
||||
type path to a `PalletError` enum variant, found `{}`",
|
||||
gen_arg.to_token_stream().to_string(),
|
||||
);
|
||||
Err(syn::Error::new(gen_arg.span(), msg))
|
||||
},
|
||||
}
|
||||
})
|
||||
.transpose()?
|
||||
.unwrap_or(Some(QueryKind::OptionQuery));
|
||||
|
||||
if let (None, Some(getter)) = (query_kind.as_ref(), getter.as_ref()) {
|
||||
let msg = "Invalid pezpallet::storage, cannot generate getter because QueryKind is not \
|
||||
identifiable. QueryKind must be `OptionQuery`, `ResultQuery`, `ValueQuery`, or default \
|
||||
one to be identifiable.";
|
||||
return Err(syn::Error::new(getter.span(), msg));
|
||||
}
|
||||
|
||||
Ok(StorageDef {
|
||||
attr_span,
|
||||
index,
|
||||
vis: item.vis.clone(),
|
||||
ident: item.ident.clone(),
|
||||
instances,
|
||||
metadata,
|
||||
docs,
|
||||
getter,
|
||||
rename_as,
|
||||
query_kind,
|
||||
where_clause,
|
||||
cfg_attrs,
|
||||
named_generics,
|
||||
unbounded,
|
||||
whitelisted,
|
||||
try_decode,
|
||||
use_default_hasher,
|
||||
attrs: item.attrs.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,949 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Home of the parsing code for the Tasks API
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[cfg(test)]
|
||||
use crate::assert_parse_error_matches;
|
||||
|
||||
#[cfg(test)]
|
||||
use crate::pezpallet::parse::tests::simulate_manifest_dir;
|
||||
|
||||
use super::helper;
|
||||
use derive_syn_parse::Parse;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::{
|
||||
parse::ParseStream,
|
||||
parse2,
|
||||
spanned::Spanned,
|
||||
token::{Bracket, Paren, PathSep, Pound},
|
||||
Error, Expr, Ident, ImplItem, ImplItemFn, ItemEnum, ItemImpl, LitInt, PathArguments, Result,
|
||||
TypePath,
|
||||
};
|
||||
|
||||
pub mod keywords {
|
||||
use syn::custom_keyword;
|
||||
|
||||
custom_keyword!(tasks_experimental);
|
||||
custom_keyword!(task_enum);
|
||||
custom_keyword!(task_list);
|
||||
custom_keyword!(task_condition);
|
||||
custom_keyword!(task_index);
|
||||
custom_keyword!(task_weight);
|
||||
custom_keyword!(pezpallet);
|
||||
}
|
||||
|
||||
/// Represents the `#[pezpallet::tasks_experimental]` attribute and its attached item. Also includes
|
||||
/// metadata about the linked [`TaskEnumDef`] if applicable.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TasksDef {
|
||||
pub tasks_attr: Option<PalletTasksAttr>,
|
||||
pub tasks: Vec<TaskDef>,
|
||||
pub item_impl: ItemImpl,
|
||||
pub enum_ident: Ident,
|
||||
pub enum_arguments: PathArguments,
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for TasksDef {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let item_impl: ItemImpl = input.parse()?;
|
||||
let (tasks_attrs, normal_attrs) = partition_tasks_attrs(&item_impl);
|
||||
let tasks_attr = match tasks_attrs.first() {
|
||||
Some(attr) => Some(parse2::<PalletTasksAttr>(attr.to_token_stream())?),
|
||||
None => None,
|
||||
};
|
||||
if let Some(extra_tasks_attr) = tasks_attrs.get(1) {
|
||||
return Err(Error::new(
|
||||
extra_tasks_attr.span(),
|
||||
"unexpected extra `#[pezpallet::tasks_experimental]` attribute",
|
||||
));
|
||||
}
|
||||
let tasks: Vec<TaskDef> = if tasks_attr.is_some() {
|
||||
item_impl
|
||||
.items
|
||||
.clone()
|
||||
.into_iter()
|
||||
.filter(|impl_item| matches!(impl_item, ImplItem::Fn(_)))
|
||||
.map(|item| parse2::<TaskDef>(item.to_token_stream()))
|
||||
.collect::<Result<_>>()?
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
let mut task_indices = HashSet::<LitInt>::new();
|
||||
for task in tasks.iter() {
|
||||
let task_index = &task.index_attr.meta.index;
|
||||
if !task_indices.insert(task_index.clone()) {
|
||||
return Err(Error::new(
|
||||
task_index.span(),
|
||||
format!("duplicate task index `{}`", task_index),
|
||||
));
|
||||
}
|
||||
}
|
||||
let mut item_impl = item_impl;
|
||||
item_impl.attrs = normal_attrs;
|
||||
|
||||
// we require the path on the impl to be a TypePath
|
||||
let enum_path = parse2::<TypePath>(item_impl.self_ty.to_token_stream())?;
|
||||
let segments = enum_path.path.segments.iter().collect::<Vec<_>>();
|
||||
let (Some(last_seg), None) = (segments.get(0), segments.get(1)) else {
|
||||
return Err(Error::new(
|
||||
enum_path.span(),
|
||||
"if specified manually, the task enum must be defined locally in this \
|
||||
pezpallet and cannot be a re-export",
|
||||
));
|
||||
};
|
||||
let enum_ident = last_seg.ident.clone();
|
||||
let enum_arguments = last_seg.arguments.clone();
|
||||
|
||||
Ok(TasksDef { tasks_attr, item_impl, tasks, enum_ident, enum_arguments })
|
||||
}
|
||||
}
|
||||
|
||||
/// Parsing for a `#[pezpallet::tasks_experimental]` attr.
|
||||
pub type PalletTasksAttr = PalletTaskAttr<keywords::tasks_experimental>;
|
||||
|
||||
/// Parsing for any of the attributes that can be used within a `#[pezpallet::tasks_experimental]`
|
||||
/// [`ItemImpl`].
|
||||
pub type TaskAttr = PalletTaskAttr<TaskAttrMeta>;
|
||||
|
||||
/// Parsing for a `#[pezpallet::task_index]` attr.
|
||||
pub type TaskIndexAttr = PalletTaskAttr<TaskIndexAttrMeta>;
|
||||
|
||||
/// Parsing for a `#[pezpallet::task_condition]` attr.
|
||||
pub type TaskConditionAttr = PalletTaskAttr<TaskConditionAttrMeta>;
|
||||
|
||||
/// Parsing for a `#[pezpallet::task_list]` attr.
|
||||
pub type TaskListAttr = PalletTaskAttr<TaskListAttrMeta>;
|
||||
|
||||
/// Parsing for a `#[pezpallet::task_weight]` attr.
|
||||
pub type TaskWeightAttr = PalletTaskAttr<TaskWeightAttrMeta>;
|
||||
|
||||
/// Parsing for a `#[pezpallet:task_enum]` attr.
|
||||
pub type PalletTaskEnumAttr = PalletTaskAttr<keywords::task_enum>;
|
||||
|
||||
/// Parsing for a manually-specified (or auto-generated) task enum, optionally including the
|
||||
/// attached `#[pezpallet::task_enum]` attribute.
|
||||
#[derive(Clone)]
|
||||
pub struct TaskEnumDef {
|
||||
pub attr: Option<PalletTaskEnumAttr>,
|
||||
pub item_enum: ItemEnum,
|
||||
pub instance_usage: helper::InstanceUsage,
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for TaskEnumDef {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let mut item_enum = input.parse::<ItemEnum>()?;
|
||||
let attr = extract_pallet_attr(&mut item_enum)?;
|
||||
let attr = match attr {
|
||||
Some(attr) => Some(parse2(attr)?),
|
||||
None => None,
|
||||
};
|
||||
|
||||
let instance_usage =
|
||||
helper::check_type_def_gen(&item_enum.generics, item_enum.ident.span())?;
|
||||
|
||||
Ok(TaskEnumDef { attr, item_enum, instance_usage })
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents an individual tasks within a [`TasksDef`].
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TaskDef {
|
||||
pub index_attr: TaskIndexAttr,
|
||||
pub condition_attr: TaskConditionAttr,
|
||||
pub list_attr: TaskListAttr,
|
||||
pub weight_attr: TaskWeightAttr,
|
||||
pub item: ImplItemFn,
|
||||
pub arg_names: Vec<Ident>,
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for TaskDef {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let item = input.parse::<ImplItemFn>()?;
|
||||
// we only want to activate TaskAttrType parsing errors for tasks-related attributes,
|
||||
// so we filter them here
|
||||
let task_attrs = partition_task_attrs(&item).0;
|
||||
|
||||
let task_attrs: Vec<TaskAttr> = task_attrs
|
||||
.into_iter()
|
||||
.map(|attr| parse2(attr.to_token_stream()))
|
||||
.collect::<Result<_>>()?;
|
||||
|
||||
let Some(index_attr) = task_attrs
|
||||
.iter()
|
||||
.find(|attr| matches!(attr.meta, TaskAttrMeta::TaskIndex(_)))
|
||||
.cloned()
|
||||
else {
|
||||
return Err(Error::new(
|
||||
item.sig.ident.span(),
|
||||
"missing `#[pezpallet::task_index(..)]` attribute",
|
||||
));
|
||||
};
|
||||
|
||||
let Some(condition_attr) = task_attrs
|
||||
.iter()
|
||||
.find(|attr| matches!(attr.meta, TaskAttrMeta::TaskCondition(_)))
|
||||
.cloned()
|
||||
else {
|
||||
return Err(Error::new(
|
||||
item.sig.ident.span(),
|
||||
"missing `#[pezpallet::task_condition(..)]` attribute",
|
||||
));
|
||||
};
|
||||
|
||||
let Some(list_attr) = task_attrs
|
||||
.iter()
|
||||
.find(|attr| matches!(attr.meta, TaskAttrMeta::TaskList(_)))
|
||||
.cloned()
|
||||
else {
|
||||
return Err(Error::new(
|
||||
item.sig.ident.span(),
|
||||
"missing `#[pezpallet::task_list(..)]` attribute",
|
||||
));
|
||||
};
|
||||
|
||||
let Some(weight_attr) = task_attrs
|
||||
.iter()
|
||||
.find(|attr| matches!(attr.meta, TaskAttrMeta::TaskWeight(_)))
|
||||
.cloned()
|
||||
else {
|
||||
return Err(Error::new(
|
||||
item.sig.ident.span(),
|
||||
"missing `#[pezpallet::task_weight(..)]` attribute",
|
||||
));
|
||||
};
|
||||
|
||||
if let Some(duplicate) = task_attrs
|
||||
.iter()
|
||||
.filter(|attr| matches!(attr.meta, TaskAttrMeta::TaskCondition(_)))
|
||||
.collect::<Vec<_>>()
|
||||
.get(1)
|
||||
{
|
||||
return Err(Error::new(
|
||||
duplicate.span(),
|
||||
"unexpected extra `#[pezpallet::task_condition(..)]` attribute",
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(duplicate) = task_attrs
|
||||
.iter()
|
||||
.filter(|attr| matches!(attr.meta, TaskAttrMeta::TaskList(_)))
|
||||
.collect::<Vec<_>>()
|
||||
.get(1)
|
||||
{
|
||||
return Err(Error::new(
|
||||
duplicate.span(),
|
||||
"unexpected extra `#[pezpallet::task_list(..)]` attribute",
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(duplicate) = task_attrs
|
||||
.iter()
|
||||
.filter(|attr| matches!(attr.meta, TaskAttrMeta::TaskIndex(_)))
|
||||
.collect::<Vec<_>>()
|
||||
.get(1)
|
||||
{
|
||||
return Err(Error::new(
|
||||
duplicate.span(),
|
||||
"unexpected extra `#[pezpallet::task_index(..)]` attribute",
|
||||
));
|
||||
}
|
||||
|
||||
let mut arg_names = vec![];
|
||||
for input in item.sig.inputs.iter() {
|
||||
match input {
|
||||
syn::FnArg::Typed(pat_type) => match &*pat_type.pat {
|
||||
syn::Pat::Ident(ident) => arg_names.push(ident.ident.clone()),
|
||||
_ => return Err(Error::new(input.span(), "unexpected pattern type")),
|
||||
},
|
||||
_ => return Err(Error::new(input.span(), "unexpected function argument type")),
|
||||
}
|
||||
}
|
||||
|
||||
let index_attr = index_attr.try_into().expect("we check the type above; QED");
|
||||
let condition_attr = condition_attr.try_into().expect("we check the type above; QED");
|
||||
let list_attr = list_attr.try_into().expect("we check the type above; QED");
|
||||
let weight_attr = weight_attr.try_into().expect("we check the type above; QED");
|
||||
|
||||
Ok(TaskDef { index_attr, condition_attr, list_attr, weight_attr, item, arg_names })
|
||||
}
|
||||
}
|
||||
|
||||
/// The contents of a [`TasksDef`]-related attribute.
|
||||
#[derive(Parse, Debug, Clone)]
|
||||
pub enum TaskAttrMeta {
|
||||
#[peek(keywords::task_list, name = "#[pezpallet::task_list(..)]")]
|
||||
TaskList(TaskListAttrMeta),
|
||||
#[peek(keywords::task_index, name = "#[pezpallet::task_index(..)")]
|
||||
TaskIndex(TaskIndexAttrMeta),
|
||||
#[peek(keywords::task_condition, name = "#[pezpallet::task_condition(..)")]
|
||||
TaskCondition(TaskConditionAttrMeta),
|
||||
#[peek(keywords::task_weight, name = "#[pezpallet::task_weight(..)")]
|
||||
TaskWeight(TaskWeightAttrMeta),
|
||||
}
|
||||
|
||||
/// The contents of a `#[pezpallet::task_list]` attribute.
|
||||
#[derive(Parse, Debug, Clone)]
|
||||
pub struct TaskListAttrMeta {
|
||||
pub task_list: keywords::task_list,
|
||||
#[paren]
|
||||
_paren: Paren,
|
||||
#[inside(_paren)]
|
||||
pub expr: Expr,
|
||||
}
|
||||
|
||||
/// The contents of a `#[pezpallet::task_index]` attribute.
|
||||
#[derive(Parse, Debug, Clone)]
|
||||
pub struct TaskIndexAttrMeta {
|
||||
pub task_index: keywords::task_index,
|
||||
#[paren]
|
||||
_paren: Paren,
|
||||
#[inside(_paren)]
|
||||
pub index: LitInt,
|
||||
}
|
||||
|
||||
/// The contents of a `#[pezpallet::task_condition]` attribute.
|
||||
#[derive(Parse, Debug, Clone)]
|
||||
pub struct TaskConditionAttrMeta {
|
||||
pub task_condition: keywords::task_condition,
|
||||
#[paren]
|
||||
_paren: Paren,
|
||||
#[inside(_paren)]
|
||||
pub expr: Expr,
|
||||
}
|
||||
|
||||
/// The contents of a `#[pezpallet::task_weight]` attribute.
|
||||
#[derive(Parse, Debug, Clone)]
|
||||
pub struct TaskWeightAttrMeta {
|
||||
pub task_weight: keywords::task_weight,
|
||||
#[paren]
|
||||
_paren: Paren,
|
||||
#[inside(_paren)]
|
||||
pub expr: Expr,
|
||||
}
|
||||
|
||||
/// The contents of a `#[pezpallet::task]` attribute.
|
||||
#[derive(Parse, Debug, Clone)]
|
||||
pub struct PalletTaskAttr<T: syn::parse::Parse + core::fmt::Debug + ToTokens> {
|
||||
pub pound: Pound,
|
||||
#[bracket]
|
||||
_bracket: Bracket,
|
||||
#[inside(_bracket)]
|
||||
pub pezpallet: keywords::pezpallet,
|
||||
#[inside(_bracket)]
|
||||
pub colons: PathSep,
|
||||
#[inside(_bracket)]
|
||||
pub meta: T,
|
||||
}
|
||||
|
||||
impl ToTokens for TaskListAttrMeta {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
let task_list = self.task_list;
|
||||
let expr = &self.expr;
|
||||
tokens.extend(quote!(#task_list(#expr)));
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for TaskConditionAttrMeta {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
let task_condition = self.task_condition;
|
||||
let expr = &self.expr;
|
||||
tokens.extend(quote!(#task_condition(#expr)));
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for TaskWeightAttrMeta {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
let task_weight = self.task_weight;
|
||||
let expr = &self.expr;
|
||||
tokens.extend(quote!(#task_weight(#expr)));
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for TaskIndexAttrMeta {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
let task_index = self.task_index;
|
||||
let index = &self.index;
|
||||
tokens.extend(quote!(#task_index(#index)))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for TaskAttrMeta {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
match self {
|
||||
TaskAttrMeta::TaskList(list) => tokens.extend(list.to_token_stream()),
|
||||
TaskAttrMeta::TaskIndex(index) => tokens.extend(index.to_token_stream()),
|
||||
TaskAttrMeta::TaskCondition(condition) => tokens.extend(condition.to_token_stream()),
|
||||
TaskAttrMeta::TaskWeight(weight) => tokens.extend(weight.to_token_stream()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: syn::parse::Parse + core::fmt::Debug + ToTokens> ToTokens for PalletTaskAttr<T> {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream2) {
|
||||
let pound = self.pound;
|
||||
let pezpallet = self.pezpallet;
|
||||
let colons = self.colons;
|
||||
let meta = &self.meta;
|
||||
tokens.extend(quote!(#pound[#pezpallet #colons #meta]));
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<PalletTaskAttr<TaskAttrMeta>> for TaskIndexAttr {
|
||||
type Error = syn::Error;
|
||||
|
||||
fn try_from(value: PalletTaskAttr<TaskAttrMeta>) -> Result<Self> {
|
||||
let pound = value.pound;
|
||||
let pezpallet = value.pezpallet;
|
||||
let colons = value.colons;
|
||||
match value.meta {
|
||||
TaskAttrMeta::TaskIndex(meta) => parse2(quote!(#pound[#pezpallet #colons #meta])),
|
||||
_ =>
|
||||
return Err(Error::new(
|
||||
value.span(),
|
||||
format!("`{:?}` cannot be converted to a `TaskIndexAttr`", value.meta),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<PalletTaskAttr<TaskAttrMeta>> for TaskConditionAttr {
|
||||
type Error = syn::Error;
|
||||
|
||||
fn try_from(value: PalletTaskAttr<TaskAttrMeta>) -> Result<Self> {
|
||||
let pound = value.pound;
|
||||
let pezpallet = value.pezpallet;
|
||||
let colons = value.colons;
|
||||
match value.meta {
|
||||
TaskAttrMeta::TaskCondition(meta) => parse2(quote!(#pound[#pezpallet #colons #meta])),
|
||||
_ =>
|
||||
return Err(Error::new(
|
||||
value.span(),
|
||||
format!("`{:?}` cannot be converted to a `TaskConditionAttr`", value.meta),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<PalletTaskAttr<TaskAttrMeta>> for TaskWeightAttr {
|
||||
type Error = syn::Error;
|
||||
|
||||
fn try_from(value: PalletTaskAttr<TaskAttrMeta>) -> Result<Self> {
|
||||
let pound = value.pound;
|
||||
let pezpallet = value.pezpallet;
|
||||
let colons = value.colons;
|
||||
match value.meta {
|
||||
TaskAttrMeta::TaskWeight(meta) => parse2(quote!(#pound[#pezpallet #colons #meta])),
|
||||
_ =>
|
||||
return Err(Error::new(
|
||||
value.span(),
|
||||
format!("`{:?}` cannot be converted to a `TaskWeightAttr`", value.meta),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<PalletTaskAttr<TaskAttrMeta>> for TaskListAttr {
|
||||
type Error = syn::Error;
|
||||
|
||||
fn try_from(value: PalletTaskAttr<TaskAttrMeta>) -> Result<Self> {
|
||||
let pound = value.pound;
|
||||
let pezpallet = value.pezpallet;
|
||||
let colons = value.colons;
|
||||
match value.meta {
|
||||
TaskAttrMeta::TaskList(meta) => parse2(quote!(#pound[#pezpallet #colons #meta])),
|
||||
_ =>
|
||||
return Err(Error::new(
|
||||
value.span(),
|
||||
format!("`{:?}` cannot be converted to a `TaskListAttr`", value.meta),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_pallet_attr(item_enum: &mut ItemEnum) -> Result<Option<TokenStream2>> {
|
||||
let mut duplicate = None;
|
||||
let mut attr = None;
|
||||
item_enum.attrs = item_enum
|
||||
.attrs
|
||||
.iter()
|
||||
.filter(|found_attr| {
|
||||
let segs = found_attr
|
||||
.path()
|
||||
.segments
|
||||
.iter()
|
||||
.map(|seg| seg.ident.clone())
|
||||
.collect::<Vec<_>>();
|
||||
let (Some(seg1), Some(_), None) = (segs.get(0), segs.get(1), segs.get(2)) else {
|
||||
return true;
|
||||
};
|
||||
if seg1 != "pezpallet" {
|
||||
return true;
|
||||
}
|
||||
if attr.is_some() {
|
||||
duplicate = Some(found_attr.span());
|
||||
}
|
||||
attr = Some(found_attr.to_token_stream());
|
||||
false
|
||||
})
|
||||
.cloned()
|
||||
.collect();
|
||||
if let Some(span) = duplicate {
|
||||
return Err(Error::new(span, "only one `#[pezpallet::_]` attribute is supported on this item"));
|
||||
}
|
||||
Ok(attr)
|
||||
}
|
||||
|
||||
fn partition_tasks_attrs(item_impl: &ItemImpl) -> (Vec<syn::Attribute>, Vec<syn::Attribute>) {
|
||||
item_impl.attrs.clone().into_iter().partition(|attr| {
|
||||
let mut path_segs = attr.path().segments.iter();
|
||||
let (Some(prefix), Some(suffix), None) =
|
||||
(path_segs.next(), path_segs.next(), path_segs.next())
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
prefix.ident == "pezpallet" && suffix.ident == "tasks_experimental"
|
||||
})
|
||||
}
|
||||
|
||||
fn partition_task_attrs(item: &ImplItemFn) -> (Vec<syn::Attribute>, Vec<syn::Attribute>) {
|
||||
item.attrs.clone().into_iter().partition(|attr| {
|
||||
let mut path_segs = attr.path().segments.iter();
|
||||
let (Some(prefix), Some(suffix)) = (path_segs.next(), path_segs.next()) else {
|
||||
return false;
|
||||
};
|
||||
// N.B: the `PartialEq` impl between `Ident` and `&str` is more efficient than
|
||||
// parsing and makes no stack or heap allocations
|
||||
prefix.ident == "pezpallet" &&
|
||||
(suffix.ident == "tasks_experimental" ||
|
||||
suffix.ident == "task_list" ||
|
||||
suffix.ident == "task_condition" ||
|
||||
suffix.ident == "task_weight" ||
|
||||
suffix.ident == "task_index")
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_task_list_() {
|
||||
parse2::<TaskAttr>(quote!(#[pezpallet::task_list(Something::iter())])).unwrap();
|
||||
parse2::<TaskAttr>(quote!(#[pezpallet::task_list(Numbers::<T, I>::iter_keys())])).unwrap();
|
||||
parse2::<TaskAttr>(quote!(#[pezpallet::task_list(iter())])).unwrap();
|
||||
assert_parse_error_matches!(
|
||||
parse2::<TaskAttr>(quote!(#[pezpallet::task_list()])),
|
||||
"expected an expression"
|
||||
);
|
||||
assert_parse_error_matches!(
|
||||
parse2::<TaskAttr>(quote!(#[pezpallet::task_list])),
|
||||
"expected parentheses"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_task_index() {
|
||||
parse2::<TaskAttr>(quote!(#[pezpallet::task_index(3)])).unwrap();
|
||||
parse2::<TaskAttr>(quote!(#[pezpallet::task_index(0)])).unwrap();
|
||||
parse2::<TaskAttr>(quote!(#[pezpallet::task_index(17)])).unwrap();
|
||||
assert_parse_error_matches!(
|
||||
parse2::<TaskAttr>(quote!(#[pezpallet::task_index])),
|
||||
"expected parentheses"
|
||||
);
|
||||
assert_parse_error_matches!(
|
||||
parse2::<TaskAttr>(quote!(#[pezpallet::task_index("hey")])),
|
||||
"expected integer literal"
|
||||
);
|
||||
assert_parse_error_matches!(
|
||||
parse2::<TaskAttr>(quote!(#[pezpallet::task_index(0.3)])),
|
||||
"expected integer literal"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_task_condition() {
|
||||
parse2::<TaskAttr>(quote!(#[pezpallet::task_condition(|x| x.is_some())])).unwrap();
|
||||
parse2::<TaskAttr>(quote!(#[pezpallet::task_condition(|_x| some_expr())])).unwrap();
|
||||
parse2::<TaskAttr>(quote!(#[pezpallet::task_condition(|| some_expr())])).unwrap();
|
||||
parse2::<TaskAttr>(quote!(#[pezpallet::task_condition(some_expr())])).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_tasks_attr() {
|
||||
parse2::<PalletTasksAttr>(quote!(#[pezpallet::tasks_experimental])).unwrap();
|
||||
assert_parse_error_matches!(
|
||||
parse2::<PalletTasksAttr>(quote!(#[pezpallet::taskss])),
|
||||
"expected `tasks_experimental`"
|
||||
);
|
||||
assert_parse_error_matches!(
|
||||
parse2::<PalletTasksAttr>(quote!(#[pezpallet::tasks_])),
|
||||
"expected `tasks_experimental`"
|
||||
);
|
||||
assert_parse_error_matches!(
|
||||
parse2::<PalletTasksAttr>(quote!(#[pal::tasks])),
|
||||
"expected `pezpallet`"
|
||||
);
|
||||
assert_parse_error_matches!(
|
||||
parse2::<PalletTasksAttr>(quote!(#[pezpallet::tasks_experimental()])),
|
||||
"unexpected token"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_tasks_def_basic() {
|
||||
simulate_manifest_dir("../../examples/basic", || {
|
||||
let parsed = parse2::<TasksDef>(quote! {
|
||||
#[pezpallet::tasks_experimental]
|
||||
impl<T: Config<I>, I: 'static> Pezpallet<T, I> {
|
||||
/// Add a pair of numbers into the totals and remove them.
|
||||
#[pezpallet::task_list(Numbers::<T, I>::iter_keys())]
|
||||
#[pezpallet::task_condition(|i| Numbers::<T, I>::contains_key(i))]
|
||||
#[pezpallet::task_index(0)]
|
||||
#[pezpallet::task_weight(0)]
|
||||
pub fn add_number_into_total(i: u32) -> DispatchResult {
|
||||
let v = Numbers::<T, I>::take(i).ok_or(Error::<T, I>::NotFound)?;
|
||||
Total::<T, I>::mutate(|(total_keys, total_values)| {
|
||||
*total_keys += i;
|
||||
*total_values += v;
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(parsed.tasks.len(), 1);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_tasks_def_basic_increment_decrement() {
|
||||
simulate_manifest_dir("../../examples/basic", || {
|
||||
let parsed = parse2::<TasksDef>(quote! {
|
||||
#[pezpallet::tasks_experimental]
|
||||
impl<T: Config<I>, I: 'static> Pezpallet<T, I> {
|
||||
/// Get the value and check if it can be incremented
|
||||
#[pezpallet::task_index(0)]
|
||||
#[pezpallet::task_condition(|| {
|
||||
let value = Value::<T>::get().unwrap();
|
||||
value < 255
|
||||
})]
|
||||
#[pezpallet::task_list(Vec::<Task<T>>::new())]
|
||||
#[pezpallet::task_weight(0)]
|
||||
fn increment() -> DispatchResult {
|
||||
let value = Value::<T>::get().unwrap_or_default();
|
||||
if value >= 255 {
|
||||
Err(Error::<T>::ValueOverflow.into())
|
||||
} else {
|
||||
let new_val = value.checked_add(1).ok_or(Error::<T>::ValueOverflow)?;
|
||||
Value::<T>::put(new_val);
|
||||
Pezpallet::<T>::deposit_event(Event::Incremented { new_val });
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Get the value and check if it can be decremented
|
||||
#[pezpallet::task_index(1)]
|
||||
#[pezpallet::task_condition(|| {
|
||||
let value = Value::<T>::get().unwrap();
|
||||
value > 0
|
||||
})]
|
||||
#[pezpallet::task_list(Vec::<Task<T>>::new())]
|
||||
#[pezpallet::task_weight(0)]
|
||||
fn decrement() -> DispatchResult {
|
||||
let value = Value::<T>::get().unwrap_or_default();
|
||||
if value == 0 {
|
||||
Err(Error::<T>::ValueUnderflow.into())
|
||||
} else {
|
||||
let new_val = value.checked_sub(1).ok_or(Error::<T>::ValueUnderflow)?;
|
||||
Value::<T>::put(new_val);
|
||||
Pezpallet::<T>::deposit_event(Event::Decremented { new_val });
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
assert_eq!(parsed.tasks.len(), 2);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_tasks_def_duplicate_index() {
|
||||
simulate_manifest_dir("../../examples/basic", || {
|
||||
assert_parse_error_matches!(
|
||||
parse2::<TasksDef>(quote! {
|
||||
#[pezpallet::tasks_experimental]
|
||||
impl<T: Config<I>, I: 'static> Pezpallet<T, I> {
|
||||
#[pezpallet::task_list(Something::iter())]
|
||||
#[pezpallet::task_condition(|i| i % 2 == 0)]
|
||||
#[pezpallet::task_index(0)]
|
||||
#[pezpallet::task_weight(0)]
|
||||
pub fn foo(i: u32) -> DispatchResult {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[pezpallet::task_list(Numbers::<T, I>::iter_keys())]
|
||||
#[pezpallet::task_condition(|i| Numbers::<T, I>::contains_key(i))]
|
||||
#[pezpallet::task_index(0)]
|
||||
#[pezpallet::task_weight(0)]
|
||||
pub fn bar(i: u32) -> DispatchResult {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}),
|
||||
"duplicate task index `0`"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_tasks_def_missing_task_list() {
|
||||
simulate_manifest_dir("../../examples/basic", || {
|
||||
assert_parse_error_matches!(
|
||||
parse2::<TasksDef>(quote! {
|
||||
#[pezpallet::tasks_experimental]
|
||||
impl<T: Config<I>, I: 'static> Pezpallet<T, I> {
|
||||
#[pezpallet::task_condition(|i| i % 2 == 0)]
|
||||
#[pezpallet::task_index(0)]
|
||||
pub fn foo(i: u32) -> DispatchResult {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}),
|
||||
r"missing `#\[pezpallet::task_list\(\.\.\)\]`"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_tasks_def_missing_task_condition() {
|
||||
simulate_manifest_dir("../../examples/basic", || {
|
||||
assert_parse_error_matches!(
|
||||
parse2::<TasksDef>(quote! {
|
||||
#[pezpallet::tasks_experimental]
|
||||
impl<T: Config<I>, I: 'static> Pezpallet<T, I> {
|
||||
#[pezpallet::task_list(Something::iter())]
|
||||
#[pezpallet::task_index(0)]
|
||||
pub fn foo(i: u32) -> DispatchResult {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}),
|
||||
r"missing `#\[pezpallet::task_condition\(\.\.\)\]`"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_tasks_def_missing_task_index() {
|
||||
simulate_manifest_dir("../../examples/basic", || {
|
||||
assert_parse_error_matches!(
|
||||
parse2::<TasksDef>(quote! {
|
||||
#[pezpallet::tasks_experimental]
|
||||
impl<T: Config<I>, I: 'static> Pezpallet<T, I> {
|
||||
#[pezpallet::task_condition(|i| i % 2 == 0)]
|
||||
#[pezpallet::task_list(Something::iter())]
|
||||
pub fn foo(i: u32) -> DispatchResult {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}),
|
||||
r"missing `#\[pezpallet::task_index\(\.\.\)\]`"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_tasks_def_missing_task_weight() {
|
||||
simulate_manifest_dir("../../examples/basic", || {
|
||||
assert_parse_error_matches!(
|
||||
parse2::<TasksDef>(quote! {
|
||||
#[pezpallet::tasks_experimental]
|
||||
impl<T: Config<I>, I: 'static> Pezpallet<T, I> {
|
||||
#[pezpallet::task_condition(|i| i % 2 == 0)]
|
||||
#[pezpallet::task_list(Something::iter())]
|
||||
#[pezpallet::task_index(0)]
|
||||
pub fn foo(i: u32) -> DispatchResult {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}),
|
||||
r"missing `#\[pezpallet::task_weight\(\.\.\)\]`"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_tasks_def_unexpected_extra_task_list_attr() {
|
||||
simulate_manifest_dir("../../examples/basic", || {
|
||||
assert_parse_error_matches!(
|
||||
parse2::<TasksDef>(quote! {
|
||||
#[pezpallet::tasks_experimental]
|
||||
impl<T: Config<I>, I: 'static> Pezpallet<T, I> {
|
||||
#[pezpallet::task_condition(|i| i % 2 == 0)]
|
||||
#[pezpallet::task_index(0)]
|
||||
#[pezpallet::task_weight(0)]
|
||||
#[pezpallet::task_list(Something::iter())]
|
||||
#[pezpallet::task_list(SomethingElse::iter())]
|
||||
pub fn foo(i: u32) -> DispatchResult {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}),
|
||||
r"unexpected extra `#\[pezpallet::task_list\(\.\.\)\]`"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_tasks_def_unexpected_extra_task_condition_attr() {
|
||||
simulate_manifest_dir("../../examples/basic", || {
|
||||
assert_parse_error_matches!(
|
||||
parse2::<TasksDef>(quote! {
|
||||
#[pezpallet::tasks_experimental]
|
||||
impl<T: Config<I>, I: 'static> Pezpallet<T, I> {
|
||||
#[pezpallet::task_condition(|i| i % 2 == 0)]
|
||||
#[pezpallet::task_condition(|i| i % 4 == 0)]
|
||||
#[pezpallet::task_index(0)]
|
||||
#[pezpallet::task_list(Something::iter())]
|
||||
#[pezpallet::task_weight(0)]
|
||||
pub fn foo(i: u32) -> DispatchResult {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}),
|
||||
r"unexpected extra `#\[pezpallet::task_condition\(\.\.\)\]`"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_tasks_def_unexpected_extra_task_index_attr() {
|
||||
simulate_manifest_dir("../../examples/basic", || {
|
||||
assert_parse_error_matches!(
|
||||
parse2::<TasksDef>(quote! {
|
||||
#[pezpallet::tasks_experimental]
|
||||
impl<T: Config<I>, I: 'static> Pezpallet<T, I> {
|
||||
#[pezpallet::task_condition(|i| i % 2 == 0)]
|
||||
#[pezpallet::task_index(0)]
|
||||
#[pezpallet::task_index(0)]
|
||||
#[pezpallet::task_list(Something::iter())]
|
||||
#[pezpallet::task_weight(0)]
|
||||
pub fn foo(i: u32) -> DispatchResult {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}),
|
||||
r"unexpected extra `#\[pezpallet::task_index\(\.\.\)\]`"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_tasks_def_extra_tasks_attribute() {
|
||||
simulate_manifest_dir("../../examples/basic", || {
|
||||
assert_parse_error_matches!(
|
||||
parse2::<TasksDef>(quote! {
|
||||
#[pezpallet::tasks_experimental]
|
||||
#[pezpallet::tasks_experimental]
|
||||
impl<T: Config<I>, I: 'static> Pezpallet<T, I> {}
|
||||
}),
|
||||
r"unexpected extra `#\[pezpallet::tasks_experimental\]` attribute"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_task_enum_def_basic() {
|
||||
simulate_manifest_dir("../../examples/basic", || {
|
||||
parse2::<TaskEnumDef>(quote! {
|
||||
#[pezpallet::task_enum]
|
||||
pub enum Task<T: Config> {
|
||||
Increment,
|
||||
Decrement,
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_task_enum_def_non_task_name() {
|
||||
simulate_manifest_dir("../../examples/basic", || {
|
||||
parse2::<TaskEnumDef>(quote! {
|
||||
#[pezpallet::task_enum]
|
||||
pub enum Something<T> {
|
||||
Foo
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_task_enum_def_missing_attr_allowed() {
|
||||
simulate_manifest_dir("../../examples/basic", || {
|
||||
parse2::<TaskEnumDef>(quote! {
|
||||
pub enum Task<T: Config> {
|
||||
Increment,
|
||||
Decrement,
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_task_enum_def_missing_attr_alternate_name_allowed() {
|
||||
simulate_manifest_dir("../../examples/basic", || {
|
||||
parse2::<TaskEnumDef>(quote! {
|
||||
pub enum Foo<T> {
|
||||
Red,
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_task_enum_def_wrong_attr() {
|
||||
simulate_manifest_dir("../../examples/basic", || {
|
||||
assert_parse_error_matches!(
|
||||
parse2::<TaskEnumDef>(quote! {
|
||||
#[pezpallet::something]
|
||||
pub enum Task<T: Config> {
|
||||
Increment,
|
||||
Decrement,
|
||||
}
|
||||
}),
|
||||
"expected `task_enum`"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_task_enum_def_wrong_item() {
|
||||
simulate_manifest_dir("../../examples/basic", || {
|
||||
assert_parse_error_matches!(
|
||||
parse2::<TaskEnumDef>(quote! {
|
||||
#[pezpallet::task_enum]
|
||||
pub struct Something<T>;
|
||||
}),
|
||||
"expected `enum`"
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use std::{panic, sync::Mutex};
|
||||
use syn::parse_quote;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub mod __private {
|
||||
pub use regex;
|
||||
}
|
||||
|
||||
/// Allows you to assert that the input expression resolves to an error whose string
|
||||
/// representation matches the specified regex literal.
|
||||
///
|
||||
/// ## Example:
|
||||
///
|
||||
/// ```
|
||||
/// use super::tasks::*;
|
||||
///
|
||||
/// assert_parse_error_matches!(
|
||||
/// parse2::<TaskEnumDef>(quote! {
|
||||
/// #[pezpallet::task_enum]
|
||||
/// pub struct Something;
|
||||
/// }),
|
||||
/// "expected `enum`"
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// More complex regular expressions are also possible (anything that could pass as a regex for
|
||||
/// use with the [`regex`] crate.):
|
||||
///
|
||||
/// ```ignore
|
||||
/// assert_parse_error_matches!(
|
||||
/// parse2::<TasksDef>(quote! {
|
||||
/// #[pezpallet::tasks_experimental]
|
||||
/// impl<T: Config<I>, I: 'static> Pezpallet<T, I> {
|
||||
/// #[pezpallet::task_condition(|i| i % 2 == 0)]
|
||||
/// #[pezpallet::task_index(0)]
|
||||
/// pub fn foo(i: u32) -> DispatchResult {
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// }
|
||||
/// }),
|
||||
/// r"missing `#\[pezpallet::task_list\(\.\.\)\]`"
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// Although this is primarily intended to be used with parsing errors, this macro is general
|
||||
/// enough that it will work with any error with a reasonable [`core::fmt::Display`] impl.
|
||||
#[macro_export]
|
||||
macro_rules! assert_parse_error_matches {
|
||||
($expr:expr, $reg:literal) => {
|
||||
match $expr {
|
||||
Ok(_) => panic!("Expected an `Error(..)`, but got Ok(..)"),
|
||||
Err(e) => {
|
||||
let error_message = e.to_string();
|
||||
let re = $crate::pezpallet::parse::tests::__private::regex::Regex::new($reg)
|
||||
.expect("Invalid regex pattern");
|
||||
assert!(
|
||||
re.is_match(&error_message),
|
||||
"Error message \"{}\" does not match the pattern \"{}\"",
|
||||
error_message,
|
||||
$reg
|
||||
);
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Allows you to assert that an entire pezpallet parses successfully. A custom syntax is used for
|
||||
/// specifying arguments so please pay attention to the docs below.
|
||||
///
|
||||
/// The general syntax is:
|
||||
///
|
||||
/// ```ignore
|
||||
/// assert_pallet_parses! {
|
||||
/// #[manifest_dir("../../examples/basic")]
|
||||
/// #[pezframe_support::pezpallet]
|
||||
/// pub mod pezpallet {
|
||||
/// #[pezpallet::config]
|
||||
/// pub trait Config: pezframe_system::Config {}
|
||||
///
|
||||
/// #[pezpallet::pezpallet]
|
||||
/// pub struct Pezpallet<T>(_);
|
||||
/// }
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// The `#[manifest_dir(..)]` attribute _must_ be specified as the _first_ attribute on the
|
||||
/// pezpallet module, and should reference the relative (to your current directory) path of a
|
||||
/// directory containing containing the `Cargo.toml` of a valid pezpallet. Typically you will only
|
||||
/// ever need to use the `examples/basic` pezpallet, but sometimes it might be advantageous to
|
||||
/// specify a different one that has additional dependencies.
|
||||
///
|
||||
/// The reason this must be specified is that our underlying parsing of pallets depends on
|
||||
/// reaching out into the file system to look for particular `Cargo.toml` dependencies via the
|
||||
/// [`generate_access_from_frame_or_crate`] method, so to simulate this properly in a proc
|
||||
/// macro crate, we need to temporarily convince this function that we are running from the
|
||||
/// directory of a valid pezpallet.
|
||||
#[macro_export]
|
||||
macro_rules! assert_pallet_parses {
|
||||
(
|
||||
#[manifest_dir($manifest_dir:literal)]
|
||||
$($tokens:tt)*
|
||||
) => {
|
||||
{
|
||||
let mut pezpallet: Option<$crate::pezpallet::parse::Def> = None;
|
||||
$crate::pezpallet::parse::tests::simulate_manifest_dir($manifest_dir, core::panic::AssertUnwindSafe(|| {
|
||||
pezpallet = Some($crate::pezpallet::parse::Def::try_from(syn::parse_quote! {
|
||||
$($tokens)*
|
||||
}, false).unwrap());
|
||||
}));
|
||||
pezpallet.unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Similar to [`assert_pallet_parses`], except this instead expects the pezpallet not to parse,
|
||||
/// and allows you to specify a regex matching the expected parse error.
|
||||
///
|
||||
/// This is identical syntactically to [`assert_pallet_parses`] in every way except there is a
|
||||
/// second attribute that must be specified immediately after `#[manifest_dir(..)]` which is
|
||||
/// `#[error_regex(..)]` which should contain a string/regex literal designed to match what you
|
||||
/// consider to be the correct parsing error we should see when we try to parse this particular
|
||||
/// pezpallet.
|
||||
///
|
||||
/// ## Example:
|
||||
///
|
||||
/// ```
|
||||
/// assert_pallet_parse_error! {
|
||||
/// #[manifest_dir("../../examples/basic")]
|
||||
/// #[error_regex("Missing `\\#\\[pezpallet::pezpallet\\]`")]
|
||||
/// #[pezframe_support::pezpallet]
|
||||
/// pub mod pezpallet {
|
||||
/// #[pezpallet::config]
|
||||
/// pub trait Config: pezframe_system::Config {}
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! assert_pallet_parse_error {
|
||||
(
|
||||
#[manifest_dir($manifest_dir:literal)]
|
||||
#[error_regex($reg:literal)]
|
||||
$($tokens:tt)*
|
||||
) => {
|
||||
$crate::pezpallet::parse::tests::simulate_manifest_dir($manifest_dir, || {
|
||||
$crate::assert_parse_error_matches!(
|
||||
$crate::pezpallet::parse::Def::try_from(
|
||||
parse_quote! {
|
||||
$($tokens)*
|
||||
},
|
||||
false
|
||||
),
|
||||
$reg
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Safely runs the specified `closure` while simulating an alternative `CARGO_MANIFEST_DIR`,
|
||||
/// restoring `CARGO_MANIFEST_DIR` to its original value upon completion regardless of whether
|
||||
/// the closure panics.
|
||||
///
|
||||
/// This is useful in tests of `Def::try_from` and other pezpallet-related methods that internally
|
||||
/// make use of [`generate_access_from_frame_or_crate`], which is sensitive to entries in the
|
||||
/// "current" `Cargo.toml` files.
|
||||
///
|
||||
/// This function uses a [`Mutex`] to avoid a race condition created when multiple tests try to
|
||||
/// modify and then restore the `CARGO_MANIFEST_DIR` ENV var in an overlapping way.
|
||||
pub fn simulate_manifest_dir<P: AsRef<std::path::Path>, F: FnOnce() + std::panic::UnwindSafe>(
|
||||
path: P,
|
||||
closure: F,
|
||||
) {
|
||||
use std::{env::*, path::*};
|
||||
|
||||
/// Ensures that only one thread can modify/restore the `CARGO_MANIFEST_DIR` ENV var at a time,
|
||||
/// avoiding a race condition because `cargo test` runs tests in parallel.
|
||||
///
|
||||
/// Although this forces all tests that use [`simulate_manifest_dir`] to run sequentially with
|
||||
/// respect to each other, this is still several orders of magnitude faster than using UI
|
||||
/// tests, even if they are run in parallel.
|
||||
static MANIFEST_DIR_LOCK: Mutex<()> = Mutex::new(());
|
||||
|
||||
// avoid race condition when swapping out `CARGO_MANIFEST_DIR`
|
||||
let guard = MANIFEST_DIR_LOCK.lock().unwrap();
|
||||
|
||||
// obtain the current/original `CARGO_MANIFEST_DIR`
|
||||
let orig = PathBuf::from(
|
||||
var("CARGO_MANIFEST_DIR").expect("failed to read ENV var `CARGO_MANIFEST_DIR`"),
|
||||
);
|
||||
|
||||
// set `CARGO_MANIFEST_DIR` to the provided path, relative to current working dir
|
||||
set_var("CARGO_MANIFEST_DIR", orig.join(path.as_ref()));
|
||||
|
||||
// safely run closure catching any panics
|
||||
let result = panic::catch_unwind(closure);
|
||||
|
||||
// restore original `CARGO_MANIFEST_DIR` before unwinding
|
||||
set_var("CARGO_MANIFEST_DIR", &orig);
|
||||
|
||||
// unlock the mutex so we don't poison it if there is a panic
|
||||
drop(guard);
|
||||
|
||||
// unwind any panics originally encountered when running closure
|
||||
result.unwrap();
|
||||
}
|
||||
|
||||
mod tasks;
|
||||
|
||||
#[test]
|
||||
fn test_parse_minimal_pallet() {
|
||||
assert_pallet_parses! {
|
||||
#[manifest_dir("../../examples/basic")]
|
||||
#[pezframe_support::pezpallet]
|
||||
pub mod pezpallet {
|
||||
#[pezpallet::config]
|
||||
pub trait Config: pezframe_system::Config {}
|
||||
|
||||
#[pezpallet::pezpallet]
|
||||
pub struct Pezpallet<T>(_);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_pallet_missing_pallet() {
|
||||
assert_pallet_parse_error! {
|
||||
#[manifest_dir("../../examples/basic")]
|
||||
#[error_regex("Missing `\\#\\[pezpallet::pezpallet\\]`")]
|
||||
#[pezframe_support::pezpallet]
|
||||
pub mod pezpallet {
|
||||
#[pezpallet::config]
|
||||
pub trait Config: pezframe_system::Config {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_pallet_missing_config() {
|
||||
assert_pallet_parse_error! {
|
||||
#[manifest_dir("../../examples/basic")]
|
||||
#[error_regex("Missing `\\#\\[pezpallet::config\\]`")]
|
||||
#[pezframe_support::pezpallet]
|
||||
pub mod pezpallet {
|
||||
#[pezpallet::pezpallet]
|
||||
pub struct Pezpallet<T>(_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_pallet_deprecated_attribute_on_error_enum() {
|
||||
assert_pallet_parse_error! {
|
||||
#[manifest_dir("../../examples/basic")]
|
||||
#[error_regex("The `\\#\\[deprecated\\]` attribute should be applied to individual variants, not the enum as a whole\\.")]
|
||||
#[pezframe_support::pezpallet]
|
||||
pub mod pezpallet {
|
||||
#[pezpallet::error]
|
||||
#[deprecated(note = "This enum is deprecated and should not be used.")]
|
||||
pub enum Error<T> {
|
||||
Foo
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_pallet_deprecated_attribute_on_event_enum() {
|
||||
assert_pallet_parse_error! {
|
||||
#[manifest_dir("../../examples/basic")]
|
||||
#[error_regex("The `\\#\\[deprecated\\]` attribute should be applied to individual variants, not the enum as a whole\\.")]
|
||||
#[pezframe_support::pezpallet]
|
||||
pub mod pezpallet {
|
||||
#[pezpallet::event]
|
||||
#[deprecated(note = "This enum is deprecated and should not be used.")]
|
||||
pub enum Event<T> {
|
||||
Foo
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use syn::parse_quote;
|
||||
|
||||
#[test]
|
||||
fn test_parse_pallet_with_task_enum_missing_impl() {
|
||||
assert_pallet_parse_error! {
|
||||
#[manifest_dir("../../examples/basic")]
|
||||
#[error_regex("Missing `\\#\\[pezpallet::tasks_experimental\\]` impl")]
|
||||
#[pezframe_support::pezpallet]
|
||||
pub mod pezpallet {
|
||||
#[pezpallet::task_enum]
|
||||
pub enum Task<T: Config> {
|
||||
Something,
|
||||
}
|
||||
|
||||
#[pezpallet::config]
|
||||
pub trait Config: pezframe_system::Config {}
|
||||
|
||||
#[pezpallet::pezpallet]
|
||||
pub struct Pezpallet<T>(_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_pallet_with_task_enum_wrong_attribute() {
|
||||
assert_pallet_parse_error! {
|
||||
#[manifest_dir("../../examples/basic")]
|
||||
#[error_regex("expected one of")]
|
||||
#[pezframe_support::pezpallet]
|
||||
pub mod pezpallet {
|
||||
#[pezpallet::wrong_attribute]
|
||||
pub enum Task<T: Config> {
|
||||
Something,
|
||||
}
|
||||
|
||||
#[pezpallet::task_list]
|
||||
impl<T: Config> pezframe_support::traits::Task for Task<T>
|
||||
where
|
||||
T: TypeInfo,
|
||||
{}
|
||||
|
||||
#[pezpallet::config]
|
||||
pub trait Config: pezframe_system::Config {}
|
||||
|
||||
#[pezpallet::pezpallet]
|
||||
pub struct Pezpallet<T>(_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_pallet_missing_task_enum() {
|
||||
assert_pallet_parses! {
|
||||
#[manifest_dir("../../examples/basic")]
|
||||
#[pezframe_support::pezpallet]
|
||||
pub mod pezpallet {
|
||||
#[pezpallet::tasks_experimental]
|
||||
#[cfg(test)] // aha, this means it's being eaten
|
||||
impl<T: Config> pezframe_support::traits::Task for Task<T>
|
||||
where
|
||||
T: TypeInfo,
|
||||
{}
|
||||
|
||||
#[pezpallet::config]
|
||||
pub trait Config: pezframe_system::Config {}
|
||||
|
||||
#[pezpallet::pezpallet]
|
||||
pub struct Pezpallet<T>(_);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_pallet_task_list_in_wrong_place() {
|
||||
assert_pallet_parse_error! {
|
||||
#[manifest_dir("../../examples/basic")]
|
||||
#[error_regex("can only be used on items within an `impl` statement.")]
|
||||
#[pezframe_support::pezpallet]
|
||||
pub mod pezpallet {
|
||||
pub enum MyCustomTaskEnum<T: Config> {
|
||||
Something,
|
||||
}
|
||||
|
||||
#[pezpallet::task_list]
|
||||
pub fn something() {
|
||||
println!("hey");
|
||||
}
|
||||
|
||||
#[pezpallet::config]
|
||||
pub trait Config: pezframe_system::Config {}
|
||||
|
||||
#[pezpallet::pezpallet]
|
||||
pub struct Pezpallet<T>(_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_pallet_manual_tasks_impl_without_manual_tasks_enum() {
|
||||
assert_pallet_parse_error! {
|
||||
#[manifest_dir("../../examples/basic")]
|
||||
#[error_regex(".*attribute must be attached to your.*")]
|
||||
#[pezframe_support::pezpallet]
|
||||
pub mod pezpallet {
|
||||
|
||||
impl<T: Config> pezframe_support::traits::Task for Task<T>
|
||||
where
|
||||
T: TypeInfo,
|
||||
{
|
||||
type Enumeration = alloc::vec::IntoIter<Task<T>>;
|
||||
|
||||
fn iter() -> Self::Enumeration {
|
||||
alloc::vec![Task::increment, Task::decrement].into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
#[pezpallet::config]
|
||||
pub trait Config: pezframe_system::Config {}
|
||||
|
||||
#[pezpallet::pezpallet]
|
||||
pub struct Pezpallet<T>(_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_pallet_manual_task_enum_non_manual_impl() {
|
||||
assert_pallet_parses! {
|
||||
#[manifest_dir("../../examples/basic")]
|
||||
#[pezframe_support::pezpallet]
|
||||
pub mod pezpallet {
|
||||
pub enum MyCustomTaskEnum<T: Config> {
|
||||
Something,
|
||||
}
|
||||
|
||||
#[pezpallet::tasks_experimental]
|
||||
impl<T: Config> pezframe_support::traits::Task for MyCustomTaskEnum<T>
|
||||
where
|
||||
T: TypeInfo,
|
||||
{}
|
||||
|
||||
#[pezpallet::config]
|
||||
pub trait Config: pezframe_system::Config {}
|
||||
|
||||
#[pezpallet::pezpallet]
|
||||
pub struct Pezpallet<T>(_);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_pallet_non_manual_task_enum_manual_impl() {
|
||||
assert_pallet_parses! {
|
||||
#[manifest_dir("../../examples/basic")]
|
||||
#[pezframe_support::pezpallet]
|
||||
pub mod pezpallet {
|
||||
#[pezpallet::task_enum]
|
||||
pub enum MyCustomTaskEnum<T: Config> {
|
||||
Something,
|
||||
}
|
||||
|
||||
impl<T: Config> pezframe_support::traits::Task for MyCustomTaskEnum<T>
|
||||
where
|
||||
T: TypeInfo,
|
||||
{}
|
||||
|
||||
#[pezpallet::config]
|
||||
pub trait Config: pezframe_system::Config {}
|
||||
|
||||
#[pezpallet::pezpallet]
|
||||
pub struct Pezpallet<T>(_);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_pallet_manual_task_enum_manual_impl() {
|
||||
assert_pallet_parses! {
|
||||
#[manifest_dir("../../examples/basic")]
|
||||
#[pezframe_support::pezpallet]
|
||||
pub mod pezpallet {
|
||||
pub enum MyCustomTaskEnum<T: Config> {
|
||||
Something,
|
||||
}
|
||||
|
||||
impl<T: Config> pezframe_support::traits::Task for MyCustomTaskEnum<T>
|
||||
where
|
||||
T: TypeInfo,
|
||||
{}
|
||||
|
||||
#[pezpallet::config]
|
||||
pub trait Config: pezframe_system::Config {}
|
||||
|
||||
#[pezpallet::pezpallet]
|
||||
pub struct Pezpallet<T>(_);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_pallet_manual_task_enum_mismatch_ident() {
|
||||
assert_pallet_parses! {
|
||||
#[manifest_dir("../../examples/basic")]
|
||||
#[pezframe_support::pezpallet]
|
||||
pub mod pezpallet {
|
||||
pub enum WrongIdent<T: Config> {
|
||||
Something,
|
||||
}
|
||||
|
||||
#[pezpallet::tasks_experimental]
|
||||
impl<T: Config> pezframe_support::traits::Task for MyCustomTaskEnum<T>
|
||||
where
|
||||
T: TypeInfo,
|
||||
{}
|
||||
|
||||
#[pezpallet::config]
|
||||
pub trait Config: pezframe_system::Config {}
|
||||
|
||||
#[pezpallet::pezpallet]
|
||||
pub struct Pezpallet<T>(_);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::helper;
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
/// Definition of type value. Just a function which is expanded to a struct implementing `Get`.
|
||||
pub struct TypeValueDef {
|
||||
/// The index of error item in pezpallet module.
|
||||
pub index: usize,
|
||||
/// Visibility of the struct to generate.
|
||||
pub vis: syn::Visibility,
|
||||
/// Ident of the struct to generate.
|
||||
pub ident: syn::Ident,
|
||||
/// The type return by Get.
|
||||
pub type_: Box<syn::Type>,
|
||||
/// If type value is generic over `T` (or `T` and `I` for instantiable pezpallet)
|
||||
pub is_generic: bool,
|
||||
/// The where clause of the function.
|
||||
pub where_clause: Option<syn::WhereClause>,
|
||||
/// The span of the pezpallet::type_value attribute.
|
||||
pub attr_span: proc_macro2::Span,
|
||||
/// Docs on the item.
|
||||
pub docs: Vec<syn::Expr>,
|
||||
}
|
||||
|
||||
impl TypeValueDef {
|
||||
pub fn try_from(
|
||||
attr_span: proc_macro2::Span,
|
||||
index: usize,
|
||||
item: &mut syn::Item,
|
||||
) -> syn::Result<Self> {
|
||||
let item = if let syn::Item::Fn(item) = item {
|
||||
item
|
||||
} else {
|
||||
let msg = "Invalid pezpallet::type_value, expected item fn";
|
||||
return Err(syn::Error::new(item.span(), msg));
|
||||
};
|
||||
|
||||
let mut docs = vec![];
|
||||
for attr in &item.attrs {
|
||||
if let syn::Meta::NameValue(meta) = &attr.meta {
|
||||
if meta.path.get_ident().map_or(false, |ident| ident == "doc") {
|
||||
docs.push(meta.value.clone());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let msg = "Invalid pezpallet::type_value, unexpected attribute, only doc attribute are \
|
||||
allowed";
|
||||
return Err(syn::Error::new(attr.span(), msg));
|
||||
}
|
||||
|
||||
if let Some(span) = item
|
||||
.sig
|
||||
.constness
|
||||
.as_ref()
|
||||
.map(|t| t.span())
|
||||
.or_else(|| item.sig.asyncness.as_ref().map(|t| t.span()))
|
||||
.or_else(|| item.sig.unsafety.as_ref().map(|t| t.span()))
|
||||
.or_else(|| item.sig.abi.as_ref().map(|t| t.span()))
|
||||
.or_else(|| item.sig.variadic.as_ref().map(|t| t.span()))
|
||||
{
|
||||
let msg = "Invalid pezpallet::type_value, unexpected token";
|
||||
return Err(syn::Error::new(span, msg));
|
||||
}
|
||||
|
||||
if !item.sig.inputs.is_empty() {
|
||||
let msg = "Invalid pezpallet::type_value, unexpected argument";
|
||||
return Err(syn::Error::new(item.sig.inputs[0].span(), msg));
|
||||
}
|
||||
|
||||
let vis = item.vis.clone();
|
||||
let ident = item.sig.ident.clone();
|
||||
let type_ = match item.sig.output.clone() {
|
||||
syn::ReturnType::Type(_, type_) => type_,
|
||||
syn::ReturnType::Default => {
|
||||
let msg = "Invalid pezpallet::type_value, expected return type";
|
||||
return Err(syn::Error::new(item.sig.span(), msg));
|
||||
},
|
||||
};
|
||||
|
||||
helper::check_type_value_gen(&item.sig.generics, item.sig.span())?;
|
||||
|
||||
let is_generic = item.sig.generics.type_params().count() > 0;
|
||||
let where_clause = item.sig.generics.where_clause.clone();
|
||||
|
||||
Ok(TypeValueDef { attr_span, index, is_generic, vis, ident, type_, where_clause, docs })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::helper;
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
/// The definition of the pezpallet validate unsigned implementation.
|
||||
pub struct ValidateUnsignedDef {}
|
||||
|
||||
impl ValidateUnsignedDef {
|
||||
pub fn try_from(item: &mut syn::Item) -> syn::Result<Self> {
|
||||
let item = if let syn::Item::Impl(item) = item {
|
||||
item
|
||||
} else {
|
||||
let msg = "Invalid pezpallet::validate_unsigned, expected item impl";
|
||||
return Err(syn::Error::new(item.span(), msg));
|
||||
};
|
||||
|
||||
if item.trait_.is_none() {
|
||||
let msg = "Invalid pezpallet::validate_unsigned, expected impl<..> ValidateUnsigned for \
|
||||
Pezpallet<..>";
|
||||
return Err(syn::Error::new(item.span(), msg));
|
||||
}
|
||||
|
||||
if let Some(last) = item.trait_.as_ref().unwrap().1.segments.last() {
|
||||
if last.ident != "ValidateUnsigned" {
|
||||
let msg = "Invalid pezpallet::validate_unsigned, expected trait ValidateUnsigned";
|
||||
return Err(syn::Error::new(last.span(), msg));
|
||||
}
|
||||
} else {
|
||||
let msg = "Invalid pezpallet::validate_unsigned, expected impl<..> ValidateUnsigned for \
|
||||
Pezpallet<..>";
|
||||
return Err(syn::Error::new(item.span(), msg));
|
||||
}
|
||||
|
||||
helper::check_pallet_struct_usage(&item.self_ty)?;
|
||||
helper::check_impl_gen(&item.generics, item.impl_token.span())?;
|
||||
|
||||
Ok(ValidateUnsignedDef {})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
// This file is part of Bizinikiwi.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governsing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use pezframe_support_procedural_tools::get_doc_literals;
|
||||
use inflector::Inflector;
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
/// Parsed representation of an impl block annotated with `pezpallet::view_functions`.
|
||||
pub struct ViewFunctionsImplDef {
|
||||
/// The where_clause used.
|
||||
pub where_clause: Option<syn::WhereClause>,
|
||||
/// The span of the pezpallet::view_functions attribute.
|
||||
pub attr_span: proc_macro2::Span,
|
||||
/// The view function definitions.
|
||||
pub view_functions: Vec<ViewFunctionDef>,
|
||||
}
|
||||
|
||||
impl ViewFunctionsImplDef {
|
||||
pub fn try_from(attr_span: proc_macro2::Span, item: &mut syn::Item) -> syn::Result<Self> {
|
||||
let syn::Item::Impl(item_impl) = item else {
|
||||
return Err(syn::Error::new(
|
||||
item.span(),
|
||||
"Invalid pezpallet::view_functions, expected item impl",
|
||||
));
|
||||
};
|
||||
let mut view_functions = Vec::new();
|
||||
for item in &mut item_impl.items {
|
||||
if let syn::ImplItem::Fn(method) = item {
|
||||
if !matches!(method.vis, syn::Visibility::Public(_)) {
|
||||
let msg = "Invalid pezpallet::view_functions, view function must be public: \
|
||||
`pub fn`";
|
||||
|
||||
let span = match method.vis {
|
||||
syn::Visibility::Inherited => method.sig.span(),
|
||||
_ => method.vis.span(),
|
||||
};
|
||||
|
||||
return Err(syn::Error::new(span, msg));
|
||||
}
|
||||
|
||||
let view_fn_def = ViewFunctionDef::try_from(method.clone())?;
|
||||
view_functions.push(view_fn_def)
|
||||
} else {
|
||||
return Err(syn::Error::new(
|
||||
item.span(),
|
||||
"Invalid pezpallet::view_functions, expected a function",
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(Self {
|
||||
view_functions,
|
||||
attr_span,
|
||||
where_clause: item_impl.generics.where_clause.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Parsed representation of a view function definition.
|
||||
#[derive(Clone)]
|
||||
pub struct ViewFunctionDef {
|
||||
pub name: syn::Ident,
|
||||
pub docs: Vec<syn::Expr>,
|
||||
pub args: Vec<syn::FnArg>,
|
||||
pub return_type: syn::Type,
|
||||
}
|
||||
|
||||
impl TryFrom<syn::ImplItemFn> for ViewFunctionDef {
|
||||
type Error = syn::Error;
|
||||
fn try_from(method: syn::ImplItemFn) -> Result<Self, Self::Error> {
|
||||
let syn::ReturnType::Type(_, type_) = method.sig.output else {
|
||||
return Err(syn::Error::new(method.sig.span(), "view functions must return a value"));
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
name: method.sig.ident.clone(),
|
||||
docs: get_doc_literals(&method.attrs),
|
||||
args: method.sig.inputs.iter().cloned().collect::<Vec<_>>(),
|
||||
return_type: *type_.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ViewFunctionDef {
|
||||
pub fn view_function_struct_ident(&self) -> syn::Ident {
|
||||
syn::Ident::new(
|
||||
&format!("{}ViewFunction", self.name.to_string().to_pascal_case()),
|
||||
self.name.span(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn view_function_id_suffix_bytes(&self) -> Result<[u8; 16], syn::Error> {
|
||||
let mut output = [0u8; 16];
|
||||
|
||||
// concatenate the signature string
|
||||
let arg_types = self
|
||||
.args_names_types()?
|
||||
.1
|
||||
.iter()
|
||||
.map(|ty| quote::quote!(#ty).to_string().replace(" ", ""))
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
let return_type = &self.return_type;
|
||||
let return_type = quote::quote!(#return_type).to_string().replace(" ", "");
|
||||
let view_fn_signature = format!(
|
||||
"{view_function_name}({arg_types}) -> {return_type}",
|
||||
view_function_name = &self.name,
|
||||
);
|
||||
|
||||
// hash the signature string
|
||||
let hash = pezsp_crypto_hashing::twox_128(view_fn_signature.as_bytes());
|
||||
output.copy_from_slice(&hash[..]);
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
pub fn args_names_types(&self) -> Result<(Vec<syn::Ident>, Vec<syn::Type>), syn::Error> {
|
||||
Ok(self
|
||||
.args
|
||||
.iter()
|
||||
.map(|arg| {
|
||||
let syn::FnArg::Typed(pat_type) = arg else {
|
||||
return Err(syn::Error::new(
|
||||
arg.span(),
|
||||
"Unsupported argument in view function",
|
||||
));
|
||||
};
|
||||
let syn::Pat::Ident(ident) = &*pat_type.pat else {
|
||||
return Err(syn::Error::new(
|
||||
pat_type.pat.span(),
|
||||
"Unsupported pattern in view function argument",
|
||||
));
|
||||
};
|
||||
Ok((ident.ident.clone(), *pat_type.ty.clone()))
|
||||
})
|
||||
.collect::<Result<Vec<(syn::Ident, syn::Type)>, syn::Error>>()?
|
||||
.into_iter()
|
||||
.unzip())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user