mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-21 18:01:07 +00:00
Automatic pallet parts in construct_runtime (#9681)
* implement automatic parts * ui tests * rename * remove unnecessary exclude * better doc * better doc * fix genesis config * fix UI tests * fix UI test * Revert "fix UI test" This reverts commit a910351c0b24cfe42195cfd97d83a416640e3259. * implemented used_parts * Update frame/support/procedural/src/construct_runtime/mod.rs Co-authored-by: Keith Yeung <kungfukeith11@gmail.com> * doc + fmt * Update frame/support/procedural/src/construct_runtime/parse.rs Co-authored-by: Keith Yeung <kungfukeith11@gmail.com> * add doc in the macro * remove yet some more parts * fix ui test * more determnistic error message + fix ui tests * fix ui test * Apply suggestions from code review Co-authored-by: Keith Yeung <kungfukeith11@gmail.com> * do refactor + fix ui tests * fmt * fix test * fix test * fix ui test * Apply suggestions from code review Co-authored-by: Keith Yeung <kungfukeith11@gmail.com> * refactor * remove even more part in node-runtime * fix test * Add flow chart for the construct_runtime! execution flow * Fix typo * Ignore snippets that don't contain code * Refactor some code in expand_after * Rename expand_after to match_and_insert * cargo fmt * Fix rename * Remove frame_support argument to construct_runtime_parts * Make use of tt-call to simplify intermediate expansions * cargo fmt * Update match_and_insert documentation * Reset cursor to 0 when no matching patterns are found * Reorder struct fields on MatchAndInsertDef * Add test for dependency renames and fix frame-support import * Add more doc comments * Update frame/support/test/compile_pass/src/lib.rs Co-authored-by: Guillaume Thiolliere <gui.thiolliere@gmail.com> Co-authored-by: Keith Yeung <kungfukeith11@gmail.com> Co-authored-by: Shawn Tabrizi <shawntabrizi@gmail.com>
This commit is contained in:
committed by
GitHub
parent
0214fde9a6
commit
4292e18e50
@@ -15,114 +15,204 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//! Implementation of `construct_runtime`.
|
||||
//!
|
||||
//! `construct_runtime` implementation is recursive and can generate code which will call itself in
|
||||
//! order to get all the pallet parts for each pallet.
|
||||
//!
|
||||
//! Pallets define their parts (`Call`, `Storage`, ..) either explicitly with the syntax
|
||||
//! `::{Call, ...}` or implicitly.
|
||||
//!
|
||||
//! In case a pallet defines its parts implicitly, then the pallet must provide the
|
||||
//! `tt_default_parts` macro. `construct_rutime` will generate some code which utilizes `tt_call`
|
||||
//! to call the `tt_default_parts` macro of the pallet. `tt_default_parts` will then return the
|
||||
//! default pallet parts as input tokens to the `match_and_replace` macro, which ultimately
|
||||
//! generates a call to `construct_runtime` again, this time with all the pallet parts explicitly
|
||||
//! defined.
|
||||
//!
|
||||
//! E.g.
|
||||
//! ```ignore
|
||||
//! construct_runtime!(
|
||||
//! //...
|
||||
//! {
|
||||
//! System: frame_system = 0, // Implicit definition of parts
|
||||
//! Balances: pallet_balances = 1, // Implicit definition of parts
|
||||
//! }
|
||||
//! );
|
||||
//! ```
|
||||
//! This call has some implicit pallet parts, thus it will expand to:
|
||||
//! ```ignore
|
||||
//! frame_support::tt_call! {
|
||||
//! macro = [{ pallet_balances::tt_default_parts }]
|
||||
//! ~~> frame_support::match_and_insert! {
|
||||
//! target = [{
|
||||
//! frame_support::tt_call! {
|
||||
//! macro = [{ frame_system::tt_default_parts }]
|
||||
//! ~~> frame_support::match_and_insert! {
|
||||
//! target = [{
|
||||
//! construct_runtime!(
|
||||
//! //...
|
||||
//! {
|
||||
//! System: frame_system = 0,
|
||||
//! Balances: pallet_balances = 1,
|
||||
//! }
|
||||
//! );
|
||||
//! }]
|
||||
//! pattern = [{ System: frame_system }]
|
||||
//! }
|
||||
//! }
|
||||
//! }]
|
||||
//! pattern = [{ Balances: pallet_balances }]
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//! `tt_default_parts` 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 = [{
|
||||
//! construct_runtime!(
|
||||
//! //...
|
||||
//! {
|
||||
//! System: frame_system = 0,
|
||||
//! Balances: pallet_balances = 1,
|
||||
//! }
|
||||
//! )
|
||||
//! }]
|
||||
//! 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 = [{
|
||||
//! construct_runtime!(
|
||||
//! //...
|
||||
//! {
|
||||
//! System: frame_system = 0, // Implicit definition of parts
|
||||
//! Balances: pallet_balances::{Pallet, Call} = 1, // Explicit definition of parts
|
||||
//! }
|
||||
//! )
|
||||
//! }]
|
||||
//! pattern = [{ System: frame_system }]
|
||||
//! tokens = [{ ::{Pallet, Call} }]
|
||||
//! }
|
||||
//! ```
|
||||
//! Which will then finally expand to the following:
|
||||
//! ```ignore
|
||||
//! construct_runtime!(
|
||||
//! //...
|
||||
//! {
|
||||
//! System: frame_system::{Pallet, Call},
|
||||
//! 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 `construct_runtime!`, it would look like the following:
|
||||
//!
|
||||
//! ```ignore
|
||||
//! +--------------------+ +---------------------+ +-------------------+
|
||||
//! | | | (defined in pallet) | | |
|
||||
//! | construct_runtime! | --> | tt_default_parts! | --> | match_and_insert! |
|
||||
//! | w/ no pallet parts | | | | |
|
||||
//! +--------------------+ +---------------------+ +-------------------+
|
||||
//!
|
||||
//! +--------------------+
|
||||
//! | |
|
||||
//! --> | construct_runtime! |
|
||||
//! | w/ pallet parts |
|
||||
//! +--------------------+
|
||||
//! ```
|
||||
|
||||
mod expand;
|
||||
mod parse;
|
||||
|
||||
use frame_support_procedural_tools::{
|
||||
generate_crate_access, generate_hidden_includes, syn_ext as ext,
|
||||
generate_crate_access, generate_crate_access_2018, generate_hidden_includes,
|
||||
};
|
||||
use parse::{
|
||||
ExplicitRuntimeDeclaration, ImplicitRuntimeDeclaration, Pallet, RuntimeDeclaration,
|
||||
WhereSection,
|
||||
};
|
||||
use parse::{PalletDeclaration, PalletPart, PalletPath, RuntimeDefinition, WhereSection};
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
use std::collections::HashMap;
|
||||
use syn::{Ident, Result};
|
||||
|
||||
/// The fixed name of the system pallet.
|
||||
const SYSTEM_PALLET_NAME: &str = "System";
|
||||
|
||||
/// The complete definition of a pallet with the resulting fixed index.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Pallet {
|
||||
pub name: Ident,
|
||||
pub index: u8,
|
||||
pub path: PalletPath,
|
||||
pub instance: Option<Ident>,
|
||||
pub pallet_parts: Vec<PalletPart>,
|
||||
}
|
||||
|
||||
impl Pallet {
|
||||
/// Get resolved pallet parts
|
||||
fn pallet_parts(&self) -> &[PalletPart] {
|
||||
&self.pallet_parts
|
||||
}
|
||||
|
||||
/// Find matching parts
|
||||
fn find_part(&self, name: &str) -> Option<&PalletPart> {
|
||||
self.pallet_parts.iter().find(|part| part.name() == name)
|
||||
}
|
||||
|
||||
/// Return whether pallet contains part
|
||||
fn exists_part(&self, name: &str) -> bool {
|
||||
self.find_part(name).is_some()
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert from the parsed pallet to their final information.
|
||||
/// Assign index to each pallet using same rules as rust for fieldless enum.
|
||||
/// I.e. implicit are assigned number incrementedly from last explicit or 0.
|
||||
fn complete_pallets(decl: impl Iterator<Item = PalletDeclaration>) -> syn::Result<Vec<Pallet>> {
|
||||
let mut indices = HashMap::new();
|
||||
let mut last_index: Option<u8> = None;
|
||||
let mut names = HashMap::new();
|
||||
|
||||
decl.map(|pallet| {
|
||||
let final_index = match pallet.index {
|
||||
Some(i) => i,
|
||||
None => last_index.map_or(Some(0), |i| i.checked_add(1)).ok_or_else(|| {
|
||||
let msg = "Pallet index doesn't fit into u8, index is 256";
|
||||
syn::Error::new(pallet.name.span(), msg)
|
||||
})?,
|
||||
};
|
||||
|
||||
last_index = Some(final_index);
|
||||
|
||||
if let Some(used_pallet) = indices.insert(final_index, pallet.name.clone()) {
|
||||
let msg = format!(
|
||||
"Pallet indices are conflicting: Both pallets {} and {} are at index {}",
|
||||
used_pallet, pallet.name, final_index,
|
||||
);
|
||||
let mut err = syn::Error::new(used_pallet.span(), &msg);
|
||||
err.combine(syn::Error::new(pallet.name.span(), msg));
|
||||
return Err(err)
|
||||
}
|
||||
|
||||
if let Some(used_pallet) = names.insert(pallet.name.clone(), pallet.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.name.span(), &msg));
|
||||
return Err(err)
|
||||
}
|
||||
|
||||
Ok(Pallet {
|
||||
name: pallet.name,
|
||||
index: final_index,
|
||||
path: pallet.path,
|
||||
instance: pallet.instance,
|
||||
pallet_parts: pallet.pallet_parts,
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Implementation of `construct_runtime` macro. Either expand to some code which will call
|
||||
/// `construct_runtime` again, or expand to the final runtime definition.
|
||||
pub fn construct_runtime(input: TokenStream) -> TokenStream {
|
||||
let definition = syn::parse_macro_input!(input as RuntimeDefinition);
|
||||
construct_runtime_parsed(definition)
|
||||
.unwrap_or_else(|e| e.to_compile_error())
|
||||
.into()
|
||||
let input_copy = input.clone();
|
||||
let definition = syn::parse_macro_input!(input as RuntimeDeclaration);
|
||||
|
||||
let res = match definition {
|
||||
RuntimeDeclaration::Implicit(implicit_def) =>
|
||||
construct_runtime_intermediary_expansion(input_copy.into(), implicit_def),
|
||||
RuntimeDeclaration::Explicit(explicit_decl) =>
|
||||
construct_runtime_final_expansion(explicit_decl),
|
||||
};
|
||||
|
||||
res.unwrap_or_else(|e| e.to_compile_error()).into()
|
||||
}
|
||||
|
||||
fn construct_runtime_parsed(definition: RuntimeDefinition) -> Result<TokenStream2> {
|
||||
let RuntimeDefinition {
|
||||
name,
|
||||
where_section: WhereSection { block, node_block, unchecked_extrinsic, .. },
|
||||
pallets:
|
||||
ext::Braces { content: ext::Punctuated { inner: pallets, .. }, token: pallets_token },
|
||||
..
|
||||
} = definition;
|
||||
/// When some pallet have implicit parts definition then the macro will expand into a macro call to
|
||||
/// `construct_runtime_args` of each pallets, see root documentation.
|
||||
fn construct_runtime_intermediary_expansion(
|
||||
input: TokenStream2,
|
||||
definition: ImplicitRuntimeDeclaration,
|
||||
) -> Result<TokenStream2> {
|
||||
let frame_support = generate_crate_access_2018("frame-support")?;
|
||||
let mut expansion = quote::quote!(
|
||||
#frame_support::construct_runtime! { #input }
|
||||
);
|
||||
for pallet in definition.pallets.iter().filter(|pallet| pallet.pallet_parts.is_none()) {
|
||||
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::tt_call! {
|
||||
macro = [{ #pallet_path::tt_default_parts }]
|
||||
frame_support = [{ #frame_support }]
|
||||
~~> #frame_support::match_and_insert! {
|
||||
target = [{ #expansion }]
|
||||
pattern = [{ #pallet_name: #pallet_path #pallet_instance }]
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
let pallets = complete_pallets(pallets.into_iter())?;
|
||||
Ok(expansion.into())
|
||||
}
|
||||
|
||||
/// All pallets have explicit definition of parts, this will expand to the runtime declaration.
|
||||
fn construct_runtime_final_expansion(
|
||||
definition: ExplicitRuntimeDeclaration,
|
||||
) -> Result<TokenStream2> {
|
||||
let ExplicitRuntimeDeclaration {
|
||||
name,
|
||||
where_section: WhereSection { block, node_block, unchecked_extrinsic },
|
||||
pallets,
|
||||
pallets_token,
|
||||
} = definition;
|
||||
|
||||
let hidden_crate_name = "construct_runtime";
|
||||
let scrate = generate_crate_access(&hidden_crate_name, "frame-support");
|
||||
|
||||
Reference in New Issue
Block a user