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:
2025-12-16 09:57:23 +03:00
parent eea003e14d
commit 3139ffa25e
3022 changed files with 42157 additions and 23579 deletions
@@ -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(
&quote::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(
&quote::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(
&quote::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(
&quote::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(
&quote::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(&quote::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(
&quote::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(
&quote::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
}