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};
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());
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("EXPAND_MACROS").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 runtime_param = &pallet.runtime_param;
let pallet_segment_and_instance = match (&pallet.pallet_segment, &pallet.instance) {
(Some(segment), Some(instance)) => quote::quote!(::#segment<#runtime_param, #instance>),
(Some(segment), None) => quote::quote!(::#segment<#runtime_param>),
(None, Some(instance)) => quote::quote!(<#instance>),
(None, None) => quote::quote!(),
};
expansion = quote::quote!(
#frame_support::__private::tt_call! {
macro = [{ #pallet_path::tt_default_parts_v2 }]
your_tt_return = [{ #frame_support::__private::tt_return }]
~~> #frame_support::match_and_insert! {
target = [{ #expansion }]
pattern = [{ #pallet_name = #pallet_path #pallet_segment_and_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 {
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
const _: () = {
#[allow(unused)]
type __HiddenUseOfUncheckedExtrinsic = #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;
}
#[doc(hidden)]
trait InternalConstructRuntime {
#[inline(always)]
fn runtime_metadata(&self) -> #scrate::__private::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)
}