mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-26 15:47:58 +00:00
Construct Runtime v2 (#1378)
Moved from https://github.com/paritytech/substrate/pull/14788 ---- Fixes https://github.com/paritytech/polkadot-sdk/issues/232 This PR introduces outer-macro approach for `construct_runtime` as discussed in the linked issue. It looks like the following: ```rust #[frame_support::runtime] mod runtime { #[runtime::runtime] #[runtime::derive( RuntimeCall, RuntimeEvent, RuntimeError, RuntimeOrigin, RuntimeFreezeReason, RuntimeHoldReason, RuntimeSlashReason, RuntimeLockId, RuntimeTask, )] pub struct Runtime; #[runtime::pallet_index(0)] pub type System = frame_system; #[runtime::pallet_index(1)] pub type Timestamp = pallet_timestamp; #[runtime::pallet_index(2)] pub type Aura = pallet_aura; #[runtime::pallet_index(3)] pub type Grandpa = pallet_grandpa; #[runtime::pallet_index(4)] pub type Balances = pallet_balances; #[runtime::pallet_index(5)] pub type TransactionPayment = pallet_transaction_payment; #[runtime::pallet_index(6)] pub type Sudo = pallet_sudo; // Include the custom logic from the pallet-template in the runtime. #[runtime::pallet_index(7)] pub type TemplateModule = pallet_template; } ``` ## Features - `#[runtime::runtime]` attached to a struct defines the main runtime - `#[runtime::derive]` attached to this struct defines the types generated by runtime - `#[runtime::pallet_index]` must be attached to a pallet to define its index - `#[runtime::disable_call]` can be optionally attached to a pallet to disable its calls - `#[runtime::disable_unsigned]` can be optionally attached to a pallet to disable unsigned calls - A pallet instance can be defined as `TemplateModule: pallet_template<Instance>` - An optional attribute can be defined as `#[frame_support::runtime(legacy_ordering)]` to ensure that the order of hooks is same as the order of pallets (and not based on the pallet_index). This is to support legacy runtimes and should be avoided for new ones. ## Todo - [x] Update the latest syntax in kitchensink and tests - [x] Update UI tests - [x] Docs ## Extension - Abstract away the Executive similar to https://github.com/paritytech/substrate/pull/14742 - Optionally avoid the need to specify all runtime types (TBD) --------- Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com> Co-authored-by: Nikhil Gupta <>
This commit is contained in:
@@ -38,6 +38,7 @@ regex = "1"
|
||||
default = ["std"]
|
||||
std = ["sp-crypto-hashing/std"]
|
||||
no-metadata-docs = []
|
||||
experimental = []
|
||||
# Generate impl-trait for tuples with the given number of tuples. Will be needed as the number of
|
||||
# pallets in a runtime grows. Does increase the compile time!
|
||||
tuples-96 = []
|
||||
|
||||
@@ -208,8 +208,8 @@
|
||||
//! This macro returns the ` :: expanded { Error }` list of additional parts we would like to
|
||||
//! expose.
|
||||
|
||||
mod expand;
|
||||
mod parse;
|
||||
pub(crate) mod expand;
|
||||
pub(crate) mod parse;
|
||||
|
||||
use crate::pallet::parse::helper::two128_str;
|
||||
use cfg_expr::Predicate;
|
||||
@@ -515,7 +515,7 @@ fn construct_runtime_final_expansion(
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn decl_all_pallets<'a>(
|
||||
pub(crate) fn decl_all_pallets<'a>(
|
||||
runtime: &'a Ident,
|
||||
pallet_declarations: impl Iterator<Item = &'a Pallet>,
|
||||
features: &HashSet<&str>,
|
||||
@@ -624,7 +624,8 @@ fn decl_all_pallets<'a>(
|
||||
#( #all_pallets_without_system )*
|
||||
)
|
||||
}
|
||||
fn decl_pallet_runtime_setup(
|
||||
|
||||
pub(crate) fn decl_pallet_runtime_setup(
|
||||
runtime: &Ident,
|
||||
pallet_declarations: &[Pallet],
|
||||
scrate: &TokenStream2,
|
||||
@@ -730,7 +731,7 @@ fn decl_pallet_runtime_setup(
|
||||
)
|
||||
}
|
||||
|
||||
fn decl_integrity_test(scrate: &TokenStream2) -> TokenStream2 {
|
||||
pub(crate) fn decl_integrity_test(scrate: &TokenStream2) -> TokenStream2 {
|
||||
quote!(
|
||||
#[cfg(test)]
|
||||
mod __construct_runtime_integrity_test {
|
||||
@@ -745,7 +746,7 @@ fn decl_integrity_test(scrate: &TokenStream2) -> TokenStream2 {
|
||||
)
|
||||
}
|
||||
|
||||
fn decl_static_assertions(
|
||||
pub(crate) fn decl_static_assertions(
|
||||
runtime: &Ident,
|
||||
pallet_decls: &[Pallet],
|
||||
scrate: &TokenStream2,
|
||||
@@ -776,7 +777,7 @@ fn decl_static_assertions(
|
||||
}
|
||||
}
|
||||
|
||||
fn check_pallet_number(input: TokenStream2, pallet_num: usize) -> Result<()> {
|
||||
pub(crate) fn check_pallet_number(input: TokenStream2, pallet_num: usize) -> Result<()> {
|
||||
let max_pallet_num = {
|
||||
if cfg!(feature = "tuples-96") {
|
||||
96
|
||||
|
||||
@@ -31,6 +31,7 @@ mod match_and_insert;
|
||||
mod no_bound;
|
||||
mod pallet;
|
||||
mod pallet_error;
|
||||
mod runtime;
|
||||
mod storage_alias;
|
||||
mod transactional;
|
||||
mod tt_macro;
|
||||
@@ -1220,6 +1221,73 @@ pub fn import_section(attr: TokenStream, tokens: TokenStream) -> TokenStream {
|
||||
.into()
|
||||
}
|
||||
|
||||
/// Construct a runtime, with the given name and the given pallets.
|
||||
///
|
||||
/// # Example:
|
||||
///
|
||||
/// ```ignore
|
||||
/// #[frame_support::runtime]
|
||||
/// mod runtime {
|
||||
/// // The main runtime
|
||||
/// #[runtime::runtime]
|
||||
/// // Runtime Types to be generated
|
||||
/// #[runtime::derive(
|
||||
/// RuntimeCall,
|
||||
/// RuntimeEvent,
|
||||
/// RuntimeError,
|
||||
/// RuntimeOrigin,
|
||||
/// RuntimeFreezeReason,
|
||||
/// RuntimeHoldReason,
|
||||
/// RuntimeSlashReason,
|
||||
/// RuntimeLockId,
|
||||
/// RuntimeTask,
|
||||
/// )]
|
||||
/// pub struct Runtime;
|
||||
///
|
||||
/// #[runtime::pallet_index(0)]
|
||||
/// pub type System = frame_system;
|
||||
///
|
||||
/// #[runtime::pallet_index(1)]
|
||||
/// pub type Test = path::to::test;
|
||||
///
|
||||
/// // Pallet with instance.
|
||||
/// #[runtime::pallet_index(2)]
|
||||
/// pub type Test2_Instance1 = test2<Instance1>;
|
||||
///
|
||||
/// // Pallet with calls disabled.
|
||||
/// #[runtime::pallet_index(3)]
|
||||
/// #[runtime::disable_call]
|
||||
/// pub type Test3 = test3;
|
||||
///
|
||||
/// // Pallet with unsigned extrinsics disabled.
|
||||
/// #[runtime::pallet_index(4)]
|
||||
/// #[runtime::disable_unsigned]
|
||||
/// pub type Test4 = test4;
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Legacy Ordering
|
||||
///
|
||||
/// An optional attribute can be defined as #[frame_support::runtime(legacy_ordering)] to
|
||||
/// ensure that the order of hooks is same as the order of pallets (and not based on the
|
||||
/// pallet_index). This is to support legacy runtimes and should be avoided for new ones.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// The population of the genesis storage depends on the order of pallets. So, if one of your
|
||||
/// pallets depends on another pallet, the pallet that is depended upon needs to come before
|
||||
/// the pallet depending on it.
|
||||
///
|
||||
/// # Type definitions
|
||||
///
|
||||
/// * The macro generates a type alias for each pallet to their `Pallet`. E.g. `type System =
|
||||
/// frame_system::Pallet<Runtime>`
|
||||
#[cfg(feature = "experimental")]
|
||||
#[proc_macro_attribute]
|
||||
pub fn runtime(attr: TokenStream, item: TokenStream) -> TokenStream {
|
||||
runtime::runtime(attr, item)
|
||||
}
|
||||
|
||||
/// Mark a module that contains dynamic parameters.
|
||||
///
|
||||
/// See the `pallet_parameters` for a full example.
|
||||
|
||||
@@ -28,6 +28,8 @@ pub fn expand_tt_default_parts(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
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,));
|
||||
|
||||
@@ -81,6 +83,58 @@ pub fn expand_tt_default_parts(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
.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.task_enum.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 pallet parts, separated by commas, then
|
||||
@@ -138,5 +192,25 @@ pub fn expand_tt_default_parts(def: &mut Def) -> proc_macro2::TokenStream {
|
||||
}
|
||||
|
||||
pub use #extra_parts_unique_id as tt_extra_parts;
|
||||
|
||||
#[macro_export]
|
||||
#[doc(hidden)]
|
||||
macro_rules! #default_parts_unique_id_v2 {
|
||||
{
|
||||
$caller:tt
|
||||
frame_support = [{ $($frame_support:ident)::* }]
|
||||
} => {
|
||||
$($frame_support)*::__private::tt_return! {
|
||||
$caller
|
||||
tokens = [{
|
||||
+ Pallet #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;
|
||||
)
|
||||
}
|
||||
|
||||
@@ -148,6 +148,12 @@ impl MutItemAttrs for syn::ImplItemFn {
|
||||
}
|
||||
}
|
||||
|
||||
impl MutItemAttrs for syn::ItemType {
|
||||
fn mut_item_attrs(&mut self) -> Option<&mut Vec<syn::Attribute>> {
|
||||
Some(&mut self.attrs)
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse for `()`
|
||||
struct Unit;
|
||||
impl syn::parse::Parse for Unit {
|
||||
|
||||
@@ -0,0 +1,320 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use super::parse::runtime_types::RuntimeType;
|
||||
use crate::{
|
||||
construct_runtime::{
|
||||
check_pallet_number, decl_all_pallets, decl_integrity_test, decl_pallet_runtime_setup,
|
||||
decl_static_assertions, expand,
|
||||
},
|
||||
runtime::{
|
||||
parse::{
|
||||
AllPalletsDeclaration, ExplicitAllPalletsDeclaration, ImplicitAllPalletsDeclaration,
|
||||
},
|
||||
Def,
|
||||
},
|
||||
};
|
||||
use cfg_expr::Predicate;
|
||||
use frame_support_procedural_tools::{
|
||||
generate_access_from_frame_or_crate, generate_crate_access, generate_hidden_includes,
|
||||
};
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
use std::collections::HashSet;
|
||||
use syn::{Ident, Result};
|
||||
|
||||
/// The fixed name of the system pallet.
|
||||
const SYSTEM_PALLET_NAME: &str = "System";
|
||||
|
||||
pub fn expand(def: Def, legacy_ordering: bool) -> TokenStream2 {
|
||||
let input = def.input;
|
||||
|
||||
let (check_pallet_number_res, res) = match def.pallets {
|
||||
AllPalletsDeclaration::Implicit(ref decl) => (
|
||||
check_pallet_number(input.clone(), decl.pallet_count),
|
||||
construct_runtime_implicit_to_explicit(input.into(), decl.clone(), legacy_ordering),
|
||||
),
|
||||
AllPalletsDeclaration::Explicit(ref decl) => (
|
||||
check_pallet_number(input, decl.pallets.len()),
|
||||
construct_runtime_final_expansion(
|
||||
def.runtime_struct.ident.clone(),
|
||||
decl.clone(),
|
||||
def.runtime_types.clone(),
|
||||
legacy_ordering,
|
||||
),
|
||||
),
|
||||
};
|
||||
|
||||
let res = res.unwrap_or_else(|e| e.to_compile_error());
|
||||
|
||||
// We want to provide better error messages to the user and thus, handle the error here
|
||||
// separately. If there is an error, we print the error and still generate all of the code to
|
||||
// get in overall less errors for the user.
|
||||
let res = if let Err(error) = check_pallet_number_res {
|
||||
let error = error.to_compile_error();
|
||||
|
||||
quote! {
|
||||
#error
|
||||
|
||||
#res
|
||||
}
|
||||
} else {
|
||||
res
|
||||
};
|
||||
|
||||
let res = expander::Expander::new("construct_runtime")
|
||||
.dry(std::env::var("FRAME_EXPAND").is_err())
|
||||
.verbose(true)
|
||||
.write_to_out_dir(res)
|
||||
.expect("Does not fail because of IO in OUT_DIR; qed");
|
||||
|
||||
res.into()
|
||||
}
|
||||
|
||||
fn construct_runtime_implicit_to_explicit(
|
||||
input: TokenStream2,
|
||||
definition: ImplicitAllPalletsDeclaration,
|
||||
legacy_ordering: bool,
|
||||
) -> Result<TokenStream2> {
|
||||
let frame_support = generate_access_from_frame_or_crate("frame-support")?;
|
||||
let attr = if legacy_ordering { quote!((legacy_ordering)) } else { quote!() };
|
||||
let mut expansion = quote::quote!(
|
||||
#[frame_support::runtime #attr]
|
||||
#input
|
||||
);
|
||||
for pallet in definition.pallet_decls.iter() {
|
||||
let pallet_path = &pallet.path;
|
||||
let pallet_name = &pallet.name;
|
||||
let pallet_instance = pallet.instance.as_ref().map(|instance| quote::quote!(<#instance>));
|
||||
expansion = quote::quote!(
|
||||
#frame_support::__private::tt_call! {
|
||||
macro = [{ #pallet_path::tt_default_parts_v2 }]
|
||||
frame_support = [{ #frame_support }]
|
||||
~~> #frame_support::match_and_insert! {
|
||||
target = [{ #expansion }]
|
||||
pattern = [{ #pallet_name = #pallet_path #pallet_instance }]
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Ok(expansion)
|
||||
}
|
||||
|
||||
fn construct_runtime_final_expansion(
|
||||
name: Ident,
|
||||
definition: ExplicitAllPalletsDeclaration,
|
||||
runtime_types: Vec<RuntimeType>,
|
||||
legacy_ordering: bool,
|
||||
) -> Result<TokenStream2> {
|
||||
let ExplicitAllPalletsDeclaration { mut pallets, name: pallets_name } = definition;
|
||||
|
||||
if !legacy_ordering {
|
||||
// Ensure that order of hooks is based on the pallet index
|
||||
pallets.sort_by_key(|p| p.index);
|
||||
}
|
||||
|
||||
let system_pallet =
|
||||
pallets.iter().find(|decl| decl.name == SYSTEM_PALLET_NAME).ok_or_else(|| {
|
||||
syn::Error::new(
|
||||
pallets_name.span(),
|
||||
"`System` pallet declaration is missing. \
|
||||
Please add this line: `pub type System = frame_system;`",
|
||||
)
|
||||
})?;
|
||||
if !system_pallet.cfg_pattern.is_empty() {
|
||||
return Err(syn::Error::new(
|
||||
system_pallet.name.span(),
|
||||
"`System` pallet declaration is feature gated, please remove any `#[cfg]` attributes",
|
||||
))
|
||||
}
|
||||
|
||||
let features = pallets
|
||||
.iter()
|
||||
.filter_map(|decl| {
|
||||
(!decl.cfg_pattern.is_empty()).then(|| {
|
||||
decl.cfg_pattern.iter().flat_map(|attr| {
|
||||
attr.predicates().filter_map(|pred| match pred {
|
||||
Predicate::Feature(feat) => Some(feat),
|
||||
Predicate::Test => Some("test"),
|
||||
_ => None,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
.flatten()
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
let hidden_crate_name = "construct_runtime";
|
||||
let scrate = generate_crate_access(hidden_crate_name, "frame-support");
|
||||
let scrate_decl = generate_hidden_includes(hidden_crate_name, "frame-support");
|
||||
|
||||
let frame_system = generate_access_from_frame_or_crate("frame-system")?;
|
||||
let block = quote!(<#name as #frame_system::Config>::Block);
|
||||
let unchecked_extrinsic = quote!(<#block as #scrate::sp_runtime::traits::Block>::Extrinsic);
|
||||
|
||||
let mut dispatch = None;
|
||||
let mut outer_event = None;
|
||||
let mut outer_error = None;
|
||||
let mut outer_origin = None;
|
||||
let mut freeze_reason = None;
|
||||
let mut hold_reason = None;
|
||||
let mut slash_reason = None;
|
||||
let mut lock_id = None;
|
||||
let mut task = None;
|
||||
|
||||
for runtime_type in runtime_types.iter() {
|
||||
match runtime_type {
|
||||
RuntimeType::RuntimeCall(_) => {
|
||||
dispatch =
|
||||
Some(expand::expand_outer_dispatch(&name, system_pallet, &pallets, &scrate));
|
||||
},
|
||||
RuntimeType::RuntimeEvent(_) => {
|
||||
outer_event = Some(expand::expand_outer_enum(
|
||||
&name,
|
||||
&pallets,
|
||||
&scrate,
|
||||
expand::OuterEnumType::Event,
|
||||
)?);
|
||||
},
|
||||
RuntimeType::RuntimeError(_) => {
|
||||
outer_error = Some(expand::expand_outer_enum(
|
||||
&name,
|
||||
&pallets,
|
||||
&scrate,
|
||||
expand::OuterEnumType::Error,
|
||||
)?);
|
||||
},
|
||||
RuntimeType::RuntimeOrigin(_) => {
|
||||
outer_origin =
|
||||
Some(expand::expand_outer_origin(&name, system_pallet, &pallets, &scrate)?);
|
||||
},
|
||||
RuntimeType::RuntimeFreezeReason(_) => {
|
||||
freeze_reason = Some(expand::expand_outer_freeze_reason(&pallets, &scrate));
|
||||
},
|
||||
RuntimeType::RuntimeHoldReason(_) => {
|
||||
hold_reason = Some(expand::expand_outer_hold_reason(&pallets, &scrate));
|
||||
},
|
||||
RuntimeType::RuntimeSlashReason(_) => {
|
||||
slash_reason = Some(expand::expand_outer_slash_reason(&pallets, &scrate));
|
||||
},
|
||||
RuntimeType::RuntimeLockId(_) => {
|
||||
lock_id = Some(expand::expand_outer_lock_id(&pallets, &scrate));
|
||||
},
|
||||
RuntimeType::RuntimeTask(_) => {
|
||||
task = Some(expand::expand_outer_task(&name, &pallets, &scrate));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let all_pallets = decl_all_pallets(&name, pallets.iter(), &features);
|
||||
let pallet_to_index = decl_pallet_runtime_setup(&name, &pallets, &scrate);
|
||||
|
||||
let metadata = expand::expand_runtime_metadata(
|
||||
&name,
|
||||
&pallets,
|
||||
&scrate,
|
||||
&unchecked_extrinsic,
|
||||
&system_pallet.path,
|
||||
);
|
||||
let outer_config = expand::expand_outer_config(&name, &pallets, &scrate);
|
||||
let inherent =
|
||||
expand::expand_outer_inherent(&name, &block, &unchecked_extrinsic, &pallets, &scrate);
|
||||
let validate_unsigned = expand::expand_outer_validate_unsigned(&name, &pallets, &scrate);
|
||||
let integrity_test = decl_integrity_test(&scrate);
|
||||
let static_assertions = decl_static_assertions(&name, &pallets, &scrate);
|
||||
|
||||
let res = quote!(
|
||||
#scrate_decl
|
||||
|
||||
// Prevent UncheckedExtrinsic to print unused warning.
|
||||
const _: () = {
|
||||
#[allow(unused)]
|
||||
type __hidden_use_of_unchecked_extrinsic = #unchecked_extrinsic;
|
||||
};
|
||||
|
||||
#[derive(
|
||||
Clone, Copy, PartialEq, Eq, #scrate::sp_runtime::RuntimeDebug,
|
||||
#scrate::__private::scale_info::TypeInfo
|
||||
)]
|
||||
pub struct #name;
|
||||
impl #scrate::sp_runtime::traits::GetRuntimeBlockType for #name {
|
||||
type RuntimeBlock = #block;
|
||||
}
|
||||
|
||||
// Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata.
|
||||
// The function is implemented by calling `impl_runtime_apis!`.
|
||||
//
|
||||
// However, the `runtime` may be used without calling `impl_runtime_apis!`.
|
||||
// Rely on the `Deref` trait to differentiate between a runtime that implements
|
||||
// APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro runtime).
|
||||
//
|
||||
// Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()` function.
|
||||
// `InternalConstructRuntime` is implemented by the `runtime` for Runtime references (`& Runtime`),
|
||||
// while `InternalImplRuntimeApis` is implemented by the `impl_runtime_apis!` for Runtime (`Runtime`).
|
||||
//
|
||||
// Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!`
|
||||
// when both macros are called; and will resolve an empty `runtime_metadata` when only the `runtime`
|
||||
// is used.
|
||||
|
||||
#[doc(hidden)]
|
||||
trait InternalConstructRuntime {
|
||||
#[inline(always)]
|
||||
fn runtime_metadata(&self) -> #scrate::__private::sp_std::vec::Vec<#scrate::__private::metadata_ir::RuntimeApiMetadataIR> {
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
#[doc(hidden)]
|
||||
impl InternalConstructRuntime for &#name {}
|
||||
|
||||
#outer_event
|
||||
|
||||
#outer_error
|
||||
|
||||
#outer_origin
|
||||
|
||||
#all_pallets
|
||||
|
||||
#pallet_to_index
|
||||
|
||||
#dispatch
|
||||
|
||||
#task
|
||||
|
||||
#metadata
|
||||
|
||||
#outer_config
|
||||
|
||||
#inherent
|
||||
|
||||
#validate_unsigned
|
||||
|
||||
#freeze_reason
|
||||
|
||||
#hold_reason
|
||||
|
||||
#lock_id
|
||||
|
||||
#slash_reason
|
||||
|
||||
#integrity_test
|
||||
|
||||
#static_assertions
|
||||
);
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Implementation of `runtime`.
|
||||
//!
|
||||
//! `runtime` implementation is recursive and can generate code which will call itself
|
||||
//! in order to get all the pallet parts for each pallet.
|
||||
//!
|
||||
//! Pallets can define their parts:
|
||||
//! - Implicitly: `pub type System = frame_system;`
|
||||
//! - Explicitly: `pub type System = frame_system + Pallet + Call;`
|
||||
//!
|
||||
//! The `runtime` transitions from the implicit definition to the explicit one.
|
||||
//! From the explicit state, Substrate expands the pallets with additional information
|
||||
//! that is to be included in the runtime metadata.
|
||||
//!
|
||||
//! Pallets must provide the `tt_default_parts_v2` macro for these transitions.
|
||||
//! These are automatically implemented by the `#[pallet::pallet]` macro.
|
||||
//!
|
||||
//! This macro also generates the following enums for ease of decoding if the respective type
|
||||
//! is defined inside `#[runtime::derive]`:
|
||||
//! - `enum RuntimeCall`: This type contains the information needed to decode extrinsics.
|
||||
//! - `enum RuntimeEvent`: This type contains the information needed to decode events.
|
||||
//! - `enum RuntimeError`: While this cannot be used directly to decode `sp_runtime::DispatchError`
|
||||
//! from the chain, it contains the information needed to decode the
|
||||
//! `sp_runtime::DispatchError::Module`.
|
||||
//!
|
||||
//! # State Transitions
|
||||
//!
|
||||
//! ```ignore
|
||||
//! +----------+
|
||||
//! | Implicit |
|
||||
//! +----------+
|
||||
//! |
|
||||
//! v
|
||||
//! +----------+
|
||||
//! | Explicit |
|
||||
//! +----------+
|
||||
//! ```
|
||||
//!
|
||||
//! The `runtime` macro transforms the implicit declaration of each pallet
|
||||
//! `System: frame_system` to an explicit one `System: frame_system + Pallet + Call` using the
|
||||
//! `tt_default_parts_v2` macro.
|
||||
//!
|
||||
//! The `tt_default_parts_v2` macro exposes a plus separated list of pallet parts. For example, the
|
||||
//! `Event` part is exposed only if the pallet implements an event via `#[pallet::event]` macro.
|
||||
//! The tokens generated by this macro are `+ Pallet + Call` for our example.
|
||||
//!
|
||||
//! The `match_and_insert` macro takes in 3 arguments:
|
||||
//! - target: This is the `TokenStream` that contains the `runtime` macro.
|
||||
//! - pattern: The pattern to match against in the target stream.
|
||||
//! - tokens: The tokens to added after the pattern match.
|
||||
//!
|
||||
//! The `runtime` macro uses the `tt_call` to get the default pallet parts via
|
||||
//! the `tt_default_parts_v2` macro defined by each pallet. The pallet parts are then returned as
|
||||
//! input to the `match_and_replace` macro.
|
||||
//! The `match_and_replace` then will modify the `runtime` to expand the implicit
|
||||
//! definition to the explicit one.
|
||||
//!
|
||||
//! For example,
|
||||
//!
|
||||
//! ```ignore
|
||||
//! #[frame_support::runtime]
|
||||
//! mod runtime {
|
||||
//! //...
|
||||
//!
|
||||
//! #[runtime::pallet_index(0)]
|
||||
//! pub type System = frame_system; // Implicit definition of parts
|
||||
//!
|
||||
//! #[runtime::pallet_index(1)]
|
||||
//! pub type Balances = pallet_balances; // Implicit definition of parts
|
||||
//! }
|
||||
//! ```
|
||||
//! This call has some implicit pallet parts, thus it will expand to:
|
||||
//! ```ignore
|
||||
//! frame_support::__private::tt_call! {
|
||||
//! macro = [{ pallet_balances::tt_default_parts_v2 }]
|
||||
//! ~~> frame_support::match_and_insert! {
|
||||
//! target = [{
|
||||
//! frame_support::__private::tt_call! {
|
||||
//! macro = [{ frame_system::tt_default_parts_v2 }]
|
||||
//! ~~> frame_support::match_and_insert! {
|
||||
//! target = [{
|
||||
//! #[frame_support::runtime]
|
||||
//! mod runtime {
|
||||
//! //...
|
||||
//!
|
||||
//! #[runtime::pallet_index(0)]
|
||||
//! pub type System = frame_system;
|
||||
//!
|
||||
//! #[runtime::pallet_index(1)]
|
||||
//! pub type Balances = pallet_balances;
|
||||
//! }
|
||||
//! }]
|
||||
//! pattern = [{ System = frame_system }]
|
||||
//! }
|
||||
//! }
|
||||
//! }]
|
||||
//! pattern = [{ Balances = pallet_balances }]
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//! `tt_default_parts_v2` must be defined. It returns the pallet parts inside some tokens, and
|
||||
//! then `tt_call` will pipe the returned pallet parts into the input of `match_and_insert`.
|
||||
//! Thus `match_and_insert` will initially receive the following inputs:
|
||||
//! ```ignore
|
||||
//! frame_support::match_and_insert! {
|
||||
//! target = [{
|
||||
//! frame_support::match_and_insert! {
|
||||
//! target = [{
|
||||
//! #[frame_support::runtime]
|
||||
//! mod runtime {
|
||||
//! //...
|
||||
//!
|
||||
//! #[runtime::pallet_index(0)]
|
||||
//! pub type System = frame_system;
|
||||
//!
|
||||
//! #[runtime::pallet_index(1)]
|
||||
//! pub type Balances = pallet_balances;
|
||||
//! }
|
||||
//! }]
|
||||
//! pattern = [{ System = frame_system }]
|
||||
//! tokens = [{ ::{+ Pallet + Call} }]
|
||||
//! }
|
||||
//! }]
|
||||
//! pattern = [{ Balances = pallet_balances }]
|
||||
//! tokens = [{ ::{+ Pallet + Call} }]
|
||||
//! }
|
||||
//! ```
|
||||
//! After dealing with `pallet_balances`, the inner `match_and_insert` will expand to:
|
||||
//! ```ignore
|
||||
//! frame_support::match_and_insert! {
|
||||
//! target = [{
|
||||
//! #[frame_support::runtime]
|
||||
//! mod runtime {
|
||||
//! //...
|
||||
//!
|
||||
//! #[runtime::pallet_index(0)]
|
||||
//! pub type System = frame_system; // Implicit definition of parts
|
||||
//!
|
||||
//! #[runtime::pallet_index(1)]
|
||||
//! pub type Balances = pallet_balances + Pallet + Call; // Explicit definition of parts
|
||||
//! }
|
||||
//! }]
|
||||
//! pattern = [{ System = frame_system }]
|
||||
//! tokens = [{ ::{+ Pallet + Call} }]
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Which will then finally expand to the following:
|
||||
//! ```ignore
|
||||
//! #[frame_support::runtime]
|
||||
//! mod runtime {
|
||||
//! //...
|
||||
//!
|
||||
//! #[runtime::pallet_index(0)]
|
||||
//! pub type System = frame_system + Pallet + Call;
|
||||
//!
|
||||
//! #[runtime::pallet_index(1)]
|
||||
//! pub type Balances = pallet_balances + Pallet + Call;
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! This call has no implicit pallet parts, thus it will expand to the runtime construction:
|
||||
//! ```ignore
|
||||
//! pub struct Runtime { ... }
|
||||
//! pub struct Call { ... }
|
||||
//! impl Call ...
|
||||
//! pub enum Origin { ... }
|
||||
//! ...
|
||||
//! ```
|
||||
//!
|
||||
//! Visualizing the entire flow of `#[frame_support::runtime]`, it would look like the following:
|
||||
//!
|
||||
//! ```ignore
|
||||
//! +----------------------+ +------------------------+ +-------------------+
|
||||
//! | | | (defined in pallet) | | |
|
||||
//! | runtime | --> | tt_default_parts_v2! | --> | match_and_insert! |
|
||||
//! | w/ no pallet parts | | | | |
|
||||
//! +----------------------+ +------------------------+ +-------------------+
|
||||
//!
|
||||
//! +----------------------+
|
||||
//! | |
|
||||
//! --> | runtime |
|
||||
//! | w/ pallet parts |
|
||||
//! +----------------------+
|
||||
//! ```
|
||||
|
||||
#![cfg(feature = "experimental")]
|
||||
|
||||
pub use parse::Def;
|
||||
use proc_macro::TokenStream;
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
mod expand;
|
||||
mod parse;
|
||||
|
||||
mod keyword {
|
||||
syn::custom_keyword!(legacy_ordering);
|
||||
}
|
||||
|
||||
pub fn runtime(attr: TokenStream, tokens: TokenStream) -> TokenStream {
|
||||
let mut legacy_ordering = false;
|
||||
if !attr.is_empty() {
|
||||
if let Ok(_) = syn::parse::<keyword::legacy_ordering>(attr.clone()) {
|
||||
legacy_ordering = true;
|
||||
} else {
|
||||
let msg = "Invalid runtime macro call: unexpected attribute. Macro call must be \
|
||||
bare, such as `#[frame_support::runtime]` or `#[runtime]`, or must specify the \
|
||||
`legacy_ordering` attribute, such as `#[frame_support::runtime(legacy_ordering)]` or \
|
||||
#[runtime(legacy_ordering)].";
|
||||
let span = proc_macro2::TokenStream::from(attr).span();
|
||||
return syn::Error::new(span, msg).to_compile_error().into()
|
||||
}
|
||||
}
|
||||
|
||||
let item = syn::parse_macro_input!(tokens as syn::ItemMod);
|
||||
match parse::Def::try_from(item) {
|
||||
Ok(def) => expand::expand(def, legacy_ordering).into(),
|
||||
Err(e) => e.to_compile_error().into(),
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// 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::pallet::parse::helper::MutItemAttrs;
|
||||
use quote::ToTokens;
|
||||
|
||||
pub(crate) fn take_first_item_runtime_attr<Attr>(
|
||||
item: &mut impl MutItemAttrs,
|
||||
) -> syn::Result<Option<Attr>>
|
||||
where
|
||||
Attr: syn::parse::Parse,
|
||||
{
|
||||
let attrs = if let Some(attrs) = item.mut_item_attrs() { attrs } else { return Ok(None) };
|
||||
|
||||
if let Some(index) = attrs.iter().position(|attr| {
|
||||
attr.path().segments.first().map_or(false, |segment| segment.ident == "runtime")
|
||||
}) {
|
||||
let runtime_attr = attrs.remove(index);
|
||||
Ok(Some(syn::parse2(runtime_attr.into_token_stream())?))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// 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.
|
||||
|
||||
pub mod helper;
|
||||
pub mod pallet;
|
||||
pub mod pallet_decl;
|
||||
pub mod runtime_struct;
|
||||
pub mod runtime_types;
|
||||
|
||||
use crate::construct_runtime::parse::Pallet;
|
||||
use pallet_decl::PalletDeclaration;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::ToTokens;
|
||||
use std::collections::HashMap;
|
||||
use syn::{spanned::Spanned, Ident, Token};
|
||||
|
||||
use frame_support_procedural_tools::syn_ext as ext;
|
||||
use runtime_types::RuntimeType;
|
||||
|
||||
mod keyword {
|
||||
use syn::custom_keyword;
|
||||
|
||||
custom_keyword!(runtime);
|
||||
custom_keyword!(derive);
|
||||
custom_keyword!(pallet_index);
|
||||
custom_keyword!(disable_call);
|
||||
custom_keyword!(disable_unsigned);
|
||||
}
|
||||
|
||||
enum RuntimeAttr {
|
||||
Runtime(proc_macro2::Span),
|
||||
Derive(proc_macro2::Span, Vec<RuntimeType>),
|
||||
PalletIndex(proc_macro2::Span, u8),
|
||||
DisableCall(proc_macro2::Span),
|
||||
DisableUnsigned(proc_macro2::Span),
|
||||
}
|
||||
|
||||
impl RuntimeAttr {
|
||||
fn span(&self) -> proc_macro2::Span {
|
||||
match self {
|
||||
Self::Runtime(span) => *span,
|
||||
Self::Derive(span, _) => *span,
|
||||
Self::PalletIndex(span, _) => *span,
|
||||
Self::DisableCall(span) => *span,
|
||||
Self::DisableUnsigned(span) => *span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl syn::parse::Parse for RuntimeAttr {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
input.parse::<syn::Token![#]>()?;
|
||||
let content;
|
||||
syn::bracketed!(content in input);
|
||||
content.parse::<keyword::runtime>()?;
|
||||
content.parse::<syn::Token![::]>()?;
|
||||
|
||||
let lookahead = content.lookahead1();
|
||||
if lookahead.peek(keyword::runtime) {
|
||||
Ok(RuntimeAttr::Runtime(content.parse::<keyword::runtime>()?.span()))
|
||||
} else if lookahead.peek(keyword::derive) {
|
||||
let _ = content.parse::<keyword::derive>();
|
||||
let derive_content;
|
||||
syn::parenthesized!(derive_content in content);
|
||||
let runtime_types =
|
||||
derive_content.parse::<ext::Punctuated<RuntimeType, Token![,]>>()?;
|
||||
let runtime_types = runtime_types.inner.into_iter().collect();
|
||||
Ok(RuntimeAttr::Derive(derive_content.span(), runtime_types))
|
||||
} else if lookahead.peek(keyword::pallet_index) {
|
||||
let _ = content.parse::<keyword::pallet_index>();
|
||||
let pallet_index_content;
|
||||
syn::parenthesized!(pallet_index_content in content);
|
||||
let pallet_index = pallet_index_content.parse::<syn::LitInt>()?;
|
||||
if !pallet_index.suffix().is_empty() {
|
||||
let msg = "Number literal must not have a suffix";
|
||||
return Err(syn::Error::new(pallet_index.span(), msg))
|
||||
}
|
||||
Ok(RuntimeAttr::PalletIndex(pallet_index.span(), pallet_index.base10_parse()?))
|
||||
} else if lookahead.peek(keyword::disable_call) {
|
||||
Ok(RuntimeAttr::DisableCall(content.parse::<keyword::disable_call>()?.span()))
|
||||
} else if lookahead.peek(keyword::disable_unsigned) {
|
||||
Ok(RuntimeAttr::DisableUnsigned(content.parse::<keyword::disable_unsigned>()?.span()))
|
||||
} else {
|
||||
Err(lookahead.error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum AllPalletsDeclaration {
|
||||
Implicit(ImplicitAllPalletsDeclaration),
|
||||
Explicit(ExplicitAllPalletsDeclaration),
|
||||
}
|
||||
|
||||
/// Declaration of a runtime with some pallet with implicit declaration of parts.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ImplicitAllPalletsDeclaration {
|
||||
pub name: Ident,
|
||||
pub pallet_decls: Vec<PalletDeclaration>,
|
||||
pub pallet_count: usize,
|
||||
}
|
||||
|
||||
/// Declaration of a runtime with all pallet having explicit declaration of parts.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ExplicitAllPalletsDeclaration {
|
||||
pub name: Ident,
|
||||
pub pallets: Vec<Pallet>,
|
||||
}
|
||||
|
||||
pub struct Def {
|
||||
pub input: TokenStream2,
|
||||
pub item: syn::ItemMod,
|
||||
pub runtime_struct: runtime_struct::RuntimeStructDef,
|
||||
pub pallets: AllPalletsDeclaration,
|
||||
pub runtime_types: Vec<RuntimeType>,
|
||||
}
|
||||
|
||||
impl Def {
|
||||
pub fn try_from(mut item: syn::ItemMod) -> syn::Result<Self> {
|
||||
let input: TokenStream2 = item.to_token_stream().into();
|
||||
let item_span = item.span();
|
||||
let items = &mut item
|
||||
.content
|
||||
.as_mut()
|
||||
.ok_or_else(|| {
|
||||
let msg = "Invalid runtime definition, expected mod to be inlined.";
|
||||
syn::Error::new(item_span, msg)
|
||||
})?
|
||||
.1;
|
||||
|
||||
let mut runtime_struct = None;
|
||||
let mut runtime_types = None;
|
||||
|
||||
let mut indices = HashMap::new();
|
||||
let mut names = HashMap::new();
|
||||
|
||||
let mut pallet_decls = vec![];
|
||||
let mut pallets = vec![];
|
||||
|
||||
for item in items.iter_mut() {
|
||||
let mut pallet_item = None;
|
||||
let mut pallet_index = 0;
|
||||
|
||||
let mut disable_call = false;
|
||||
let mut disable_unsigned = false;
|
||||
|
||||
while let Some(runtime_attr) =
|
||||
helper::take_first_item_runtime_attr::<RuntimeAttr>(item)?
|
||||
{
|
||||
match runtime_attr {
|
||||
RuntimeAttr::Runtime(span) if runtime_struct.is_none() => {
|
||||
let p = runtime_struct::RuntimeStructDef::try_from(span, item)?;
|
||||
runtime_struct = Some(p);
|
||||
},
|
||||
RuntimeAttr::Derive(_, types) if runtime_types.is_none() => {
|
||||
runtime_types = Some(types);
|
||||
},
|
||||
RuntimeAttr::PalletIndex(span, index) => {
|
||||
pallet_index = index;
|
||||
pallet_item = if let syn::Item::Type(item) = item {
|
||||
Some(item.clone())
|
||||
} else {
|
||||
let msg = "Invalid runtime::pallet_index, expected type definition";
|
||||
return Err(syn::Error::new(span, msg))
|
||||
};
|
||||
},
|
||||
RuntimeAttr::DisableCall(_) => disable_call = true,
|
||||
RuntimeAttr::DisableUnsigned(_) => disable_unsigned = true,
|
||||
attr => {
|
||||
let msg = "Invalid duplicated attribute";
|
||||
return Err(syn::Error::new(attr.span(), msg))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(pallet_item) = pallet_item {
|
||||
match *pallet_item.ty.clone() {
|
||||
syn::Type::Path(ref path) => {
|
||||
let pallet_decl =
|
||||
PalletDeclaration::try_from(item.span(), &pallet_item, path)?;
|
||||
|
||||
if let Some(used_pallet) =
|
||||
names.insert(pallet_decl.name.clone(), pallet_decl.name.span())
|
||||
{
|
||||
let msg = "Two pallets with the same name!";
|
||||
|
||||
let mut err = syn::Error::new(used_pallet, &msg);
|
||||
err.combine(syn::Error::new(pallet_decl.name.span(), &msg));
|
||||
return Err(err)
|
||||
}
|
||||
|
||||
pallet_decls.push(pallet_decl);
|
||||
},
|
||||
syn::Type::TraitObject(syn::TypeTraitObject { bounds, .. }) => {
|
||||
let pallet = Pallet::try_from(
|
||||
item.span(),
|
||||
&pallet_item,
|
||||
pallet_index,
|
||||
disable_call,
|
||||
disable_unsigned,
|
||||
&bounds,
|
||||
)?;
|
||||
|
||||
if let Some(used_pallet) = indices.insert(pallet.index, pallet.name.clone())
|
||||
{
|
||||
let msg = format!(
|
||||
"Pallet indices are conflicting: Both pallets {} and {} are at index {}",
|
||||
used_pallet, pallet.name, pallet.index,
|
||||
);
|
||||
let mut err = syn::Error::new(used_pallet.span(), &msg);
|
||||
err.combine(syn::Error::new(pallet.name.span(), msg));
|
||||
return Err(err)
|
||||
}
|
||||
|
||||
pallets.push(pallet);
|
||||
},
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let name = item.ident.clone();
|
||||
let decl_count = pallet_decls.len();
|
||||
let pallets = if decl_count > 0 {
|
||||
AllPalletsDeclaration::Implicit(ImplicitAllPalletsDeclaration {
|
||||
name,
|
||||
pallet_decls,
|
||||
pallet_count: decl_count.saturating_add(pallets.len()),
|
||||
})
|
||||
} else {
|
||||
AllPalletsDeclaration::Explicit(ExplicitAllPalletsDeclaration { name, pallets })
|
||||
};
|
||||
|
||||
let def = Def {
|
||||
input,
|
||||
item,
|
||||
runtime_struct: runtime_struct.ok_or_else(|| {
|
||||
syn::Error::new(item_span,
|
||||
"Missing Runtime. Please add a struct inside the module and annotate it with `#[runtime::runtime]`"
|
||||
)
|
||||
})?,
|
||||
pallets,
|
||||
runtime_types: runtime_types.ok_or_else(|| {
|
||||
syn::Error::new(item_span,
|
||||
"Missing Runtime Types. Please annotate the runtime struct with `#[runtime::derive]`"
|
||||
)
|
||||
})?,
|
||||
};
|
||||
|
||||
Ok(def)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// 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::construct_runtime::parse::{Pallet, PalletPart, PalletPartKeyword, PalletPath};
|
||||
use quote::ToTokens;
|
||||
use syn::{punctuated::Punctuated, spanned::Spanned, token, Error, Ident, PathArguments};
|
||||
|
||||
impl Pallet {
|
||||
pub fn try_from(
|
||||
attr_span: proc_macro2::Span,
|
||||
item: &syn::ItemType,
|
||||
pallet_index: u8,
|
||||
disable_call: bool,
|
||||
disable_unsigned: bool,
|
||||
bounds: &Punctuated<syn::TypeParamBound, token::Plus>,
|
||||
) -> syn::Result<Self> {
|
||||
let name = item.ident.clone();
|
||||
|
||||
let mut pallet_path = None;
|
||||
let mut pallet_parts = vec![];
|
||||
|
||||
for (index, bound) in bounds.into_iter().enumerate() {
|
||||
if let syn::TypeParamBound::Trait(syn::TraitBound { path, .. }) = bound {
|
||||
if index == 0 {
|
||||
pallet_path = Some(PalletPath { inner: path.clone() });
|
||||
} else {
|
||||
let pallet_part = syn::parse2::<PalletPart>(bound.into_token_stream())?;
|
||||
pallet_parts.push(pallet_part);
|
||||
}
|
||||
} else {
|
||||
return Err(Error::new(
|
||||
attr_span,
|
||||
"Invalid pallet declaration, expected a path or a trait object",
|
||||
))
|
||||
};
|
||||
}
|
||||
|
||||
let mut path = pallet_path.ok_or(Error::new(
|
||||
attr_span,
|
||||
"Invalid pallet declaration, expected a path or a trait object",
|
||||
))?;
|
||||
|
||||
let mut instance = None;
|
||||
if let Some(segment) = path.inner.segments.iter_mut().find(|seg| !seg.arguments.is_empty())
|
||||
{
|
||||
if let PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
|
||||
args, ..
|
||||
}) = segment.arguments.clone()
|
||||
{
|
||||
if let Some(syn::GenericArgument::Type(syn::Type::Path(arg_path))) = args.first() {
|
||||
instance =
|
||||
Some(Ident::new(&arg_path.to_token_stream().to_string(), arg_path.span()));
|
||||
segment.arguments = PathArguments::None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pallet_parts = pallet_parts
|
||||
.into_iter()
|
||||
.filter(|part| {
|
||||
if let (true, &PalletPartKeyword::Call(_)) = (disable_call, &part.keyword) {
|
||||
false
|
||||
} else if let (true, &PalletPartKeyword::ValidateUnsigned(_)) =
|
||||
(disable_unsigned, &part.keyword)
|
||||
{
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let cfg_pattern = vec![];
|
||||
|
||||
Ok(Pallet {
|
||||
is_expanded: true,
|
||||
name,
|
||||
index: pallet_index,
|
||||
path,
|
||||
instance,
|
||||
cfg_pattern,
|
||||
pallet_parts,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// 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 quote::ToTokens;
|
||||
use syn::{spanned::Spanned, Attribute, Ident, PathArguments};
|
||||
|
||||
/// The declaration of a pallet.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PalletDeclaration {
|
||||
/// The name of the pallet, e.g.`System` in `System: frame_system`.
|
||||
pub name: Ident,
|
||||
/// Optional attributes tagged right above a pallet declaration.
|
||||
pub attrs: Vec<Attribute>,
|
||||
/// The path of the pallet, e.g. `frame_system` in `System: frame_system`.
|
||||
pub path: syn::Path,
|
||||
/// The instance of the pallet, e.g. `Instance1` in `Council: pallet_collective::<Instance1>`.
|
||||
pub instance: Option<Ident>,
|
||||
}
|
||||
|
||||
impl PalletDeclaration {
|
||||
pub fn try_from(
|
||||
_attr_span: proc_macro2::Span,
|
||||
item: &syn::ItemType,
|
||||
path: &syn::TypePath,
|
||||
) -> syn::Result<Self> {
|
||||
let name = item.ident.clone();
|
||||
|
||||
let mut path = path.path.clone();
|
||||
|
||||
let mut instance = None;
|
||||
if let Some(segment) = path.segments.iter_mut().find(|seg| !seg.arguments.is_empty()) {
|
||||
if let PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
|
||||
args, ..
|
||||
}) = segment.arguments.clone()
|
||||
{
|
||||
if let Some(syn::GenericArgument::Type(syn::Type::Path(arg_path))) = args.first() {
|
||||
instance =
|
||||
Some(Ident::new(&arg_path.to_token_stream().to_string(), arg_path.span()));
|
||||
segment.arguments = PathArguments::None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Self { name, path, instance, attrs: item.attrs.clone() })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use syn::spanned::Spanned;
|
||||
pub struct RuntimeStructDef {
|
||||
pub ident: syn::Ident,
|
||||
pub attr_span: proc_macro2::Span,
|
||||
}
|
||||
|
||||
impl RuntimeStructDef {
|
||||
pub fn try_from(attr_span: proc_macro2::Span, item: &mut syn::Item) -> syn::Result<Self> {
|
||||
let item = if let syn::Item::Struct(item) = item {
|
||||
item
|
||||
} else {
|
||||
let msg = "Invalid runtime::runtime, expected struct definition";
|
||||
return Err(syn::Error::new(item.span(), msg))
|
||||
};
|
||||
|
||||
Ok(Self { ident: item.ident.clone(), attr_span })
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use syn::{
|
||||
parse::{Parse, ParseStream},
|
||||
Result,
|
||||
};
|
||||
|
||||
mod keyword {
|
||||
use syn::custom_keyword;
|
||||
|
||||
custom_keyword!(RuntimeCall);
|
||||
custom_keyword!(RuntimeEvent);
|
||||
custom_keyword!(RuntimeError);
|
||||
custom_keyword!(RuntimeOrigin);
|
||||
custom_keyword!(RuntimeFreezeReason);
|
||||
custom_keyword!(RuntimeHoldReason);
|
||||
custom_keyword!(RuntimeSlashReason);
|
||||
custom_keyword!(RuntimeLockId);
|
||||
custom_keyword!(RuntimeTask);
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum RuntimeType {
|
||||
RuntimeCall(keyword::RuntimeCall),
|
||||
RuntimeEvent(keyword::RuntimeEvent),
|
||||
RuntimeError(keyword::RuntimeError),
|
||||
RuntimeOrigin(keyword::RuntimeOrigin),
|
||||
RuntimeFreezeReason(keyword::RuntimeFreezeReason),
|
||||
RuntimeHoldReason(keyword::RuntimeHoldReason),
|
||||
RuntimeSlashReason(keyword::RuntimeSlashReason),
|
||||
RuntimeLockId(keyword::RuntimeLockId),
|
||||
RuntimeTask(keyword::RuntimeTask),
|
||||
}
|
||||
|
||||
impl Parse for RuntimeType {
|
||||
fn parse(input: ParseStream) -> Result<Self> {
|
||||
let lookahead = input.lookahead1();
|
||||
|
||||
if lookahead.peek(keyword::RuntimeCall) {
|
||||
Ok(Self::RuntimeCall(input.parse()?))
|
||||
} else if lookahead.peek(keyword::RuntimeEvent) {
|
||||
Ok(Self::RuntimeEvent(input.parse()?))
|
||||
} else if lookahead.peek(keyword::RuntimeError) {
|
||||
Ok(Self::RuntimeError(input.parse()?))
|
||||
} else if lookahead.peek(keyword::RuntimeOrigin) {
|
||||
Ok(Self::RuntimeOrigin(input.parse()?))
|
||||
} else if lookahead.peek(keyword::RuntimeFreezeReason) {
|
||||
Ok(Self::RuntimeFreezeReason(input.parse()?))
|
||||
} else if lookahead.peek(keyword::RuntimeHoldReason) {
|
||||
Ok(Self::RuntimeHoldReason(input.parse()?))
|
||||
} else if lookahead.peek(keyword::RuntimeSlashReason) {
|
||||
Ok(Self::RuntimeSlashReason(input.parse()?))
|
||||
} else if lookahead.peek(keyword::RuntimeLockId) {
|
||||
Ok(Self::RuntimeLockId(input.parse()?))
|
||||
} else if lookahead.peek(keyword::RuntimeTask) {
|
||||
Ok(Self::RuntimeTask(input.parse()?))
|
||||
} else {
|
||||
Err(lookahead.error())
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user