Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

Commit

Permalink
allow pallets to specify an auto_construct_runtime
Browse files Browse the repository at this point in the history
  • Loading branch information
gui1117 committed Jun 24, 2020
1 parent c771821 commit 6dddfb8
Show file tree
Hide file tree
Showing 7 changed files with 164 additions and 6 deletions.
2 changes: 1 addition & 1 deletion bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -819,7 +819,7 @@ construct_runtime!(
TechnicalMembership: pallet_membership::<Instance1>::{Module, Call, Storage, Event<T>, Config<T>},
FinalityTracker: pallet_finality_tracker::{Module, Call, Inherent},
Grandpa: pallet_grandpa::{Module, Call, Storage, Config, Event},
Treasury: pallet_treasury::{Module, Call, Storage, Config, Event<T>},
Treasury: pallet_treasury::{auto},
Contracts: pallet_contracts::{Module, Call, Config, Storage, Event<T>},
Sudo: pallet_sudo::{Module, Call, Config<T>, Storage, Event<T>},
ImOnline: pallet_im_online::{Module, Call, Storage, Event<T>, ValidateUnsigned, Config<T>},
Expand Down
47 changes: 45 additions & 2 deletions frame/support/procedural/src/construct_runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,62 @@ use frame_support_procedural_tools::{generate_crate_access, generate_hidden_incl
use parse::{ModuleDeclaration, RuntimeDefinition, WhereSection};
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::{Ident, Result, TypePath};
use quote::{quote, quote_spanned};
use syn::{Ident, Result, TypePath, spanned::Spanned};

/// The fixed name of the system module.
const SYSTEM_MODULE_NAME: &str = "System";

pub fn construct_runtime(input: TokenStream) -> TokenStream {
let input_clone = input.clone().into();
let definition = syn::parse_macro_input!(input as RuntimeDefinition);

if let Some(preprocess) = construct_runtime_preprocess(&definition, input_clone)
.unwrap_or_else(|e| Some(e.to_compile_error()))
{
return preprocess.into()
}

construct_runtime_parsed(definition)
.unwrap_or_else(|e| e.to_compile_error())
.into()
}

fn construct_runtime_preprocess(
definition: &RuntimeDefinition,
input_clone: TokenStream2,
) -> Result<Option<TokenStream2>> {
let mut auto_modules = vec![];
for module in definition.modules.content.inner.iter() {
if module.module_parts.iter().any(|p| p.keyword.name() == "auto") {
if module.module_parts.len() != 1 {
return Err(syn::Error::new(
module.module_parts[0].keyword.span(),
"Module parts must either provide explicit parts or use `auto` keyword but
cannot combine."
))
}

auto_modules.push(module.module.clone());
}
}

if !auto_modules.is_empty() {
let mut expand = input_clone;

while let Some(module) = auto_modules.pop() {
expand = quote_spanned!(module.span() => #module::auto_construct_runtime!{
construct_runtime! { #expand }
}
)
}

Ok(Some(expand))
} else {
Ok(None)
}
}

fn construct_runtime_parsed(definition: RuntimeDefinition) -> Result<TokenStream2> {
let RuntimeDefinition {
name,
Expand Down
10 changes: 8 additions & 2 deletions frame/support/procedural/src/construct_runtime/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use syn::{
token, Error, Ident, Result, Token,
};

mod keyword {
pub mod keyword {
syn::custom_keyword!(Block);
syn::custom_keyword!(NodeBlock);
syn::custom_keyword!(UncheckedExtrinsic);
Expand All @@ -36,6 +36,7 @@ mod keyword {
syn::custom_keyword!(Origin);
syn::custom_keyword!(Inherent);
syn::custom_keyword!(ValidateUnsigned);
syn::custom_keyword!(auto);
}

#[derive(Debug)]
Expand Down Expand Up @@ -231,6 +232,7 @@ pub enum ModulePartKeyword {
Origin(keyword::Origin),
Inherent(keyword::Inherent),
ValidateUnsigned(keyword::ValidateUnsigned),
Auto(keyword::auto),
}

impl Parse for ModulePartKeyword {
Expand All @@ -253,6 +255,8 @@ impl Parse for ModulePartKeyword {
Ok(Self::Inherent(input.parse()?))
} else if lookahead.peek(keyword::ValidateUnsigned) {
Ok(Self::ValidateUnsigned(input.parse()?))
} else if lookahead.peek(keyword::auto) {
Ok(Self::Auto(input.parse()?))
} else {
Err(lookahead.error())
}
Expand All @@ -261,7 +265,7 @@ impl Parse for ModulePartKeyword {

impl ModulePartKeyword {
/// Returns the name of `Self`.
fn name(&self) -> &'static str {
pub fn name(&self) -> &'static str {
match self {
Self::Module(_) => "Module",
Self::Call(_) => "Call",
Expand All @@ -271,6 +275,7 @@ impl ModulePartKeyword {
Self::Origin(_) => "Origin",
Self::Inherent(_) => "Inherent",
Self::ValidateUnsigned(_) => "ValidateUnsigned",
Self::Auto(_) => "auto",
}
}

Expand Down Expand Up @@ -313,6 +318,7 @@ impl Spanned for ModulePartKeyword {
Self::Origin(inner) => inner.span(),
Self::Inherent(inner) => inner.span(),
Self::ValidateUnsigned(inner) => inner.span(),
Self::Auto(inner) => inner.span(),
}
}
}
Expand Down
27 changes: 27 additions & 0 deletions frame/support/procedural/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

mod storage;
mod construct_runtime;
mod replace_auto_with;

use proc_macro::TokenStream;

Expand Down Expand Up @@ -260,6 +261,8 @@ pub fn decl_storage(input: TokenStream) -> TokenStream {
/// // Module with instances
/// Test3_Instance1: test3::<Instance1>::{Module, Call, Storage, Event<T, I>, Config<T, I>, Origin<T, I>},
/// Test3_DefaultInstance: test3::{Module, Call, Storage, Event<T>, Config<T>, Origin<T>},
///
/// TestAuto: pallet_with_auto_construct_runtime::{auto},
/// }
/// )
/// ```
Expand All @@ -282,6 +285,9 @@ pub fn decl_storage(input: TokenStream) -> TokenStream {
/// inherent.
/// - `ValidateUnsigned` - If the module validates unsigned extrinsics.
///
/// alternatively if the pallet provide the auto_contruct_runtime macro, parts can be automatically
/// filled using `auto` keyword.
///
/// # Note
///
/// The population of the genesis storage depends on the order of modules. So, if one of your
Expand All @@ -291,3 +297,24 @@ pub fn decl_storage(input: TokenStream) -> TokenStream {
pub fn construct_runtime(input: TokenStream) -> TokenStream {
construct_runtime::construct_runtime(input)
}

/// Macro than replace the first found `auto` ident with some specified content.
///
/// # Example:
///
/// ```nocompile
/// replace_auto_with!(
/// { something or else } // content inside braces can be anything.
/// Some content with at some point { an ident named auto } other auto are ignored
/// )
/// ```
///
/// will generate:
///
/// ```nocompile
/// Some content with at some point { an ident named something or else } other auto are ignored
/// ```
#[proc_macro]
pub fn replace_auto_with(input: TokenStream) -> TokenStream {
replace_auto_with::replace_auto_with(input)
}
72 changes: 72 additions & 0 deletions frame/support/procedural/src/replace_auto_with.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use proc_macro2::{TokenStream, TokenTree, Group};
use syn::spanned::Spanned;
use std::iter::once;

pub fn replace_auto_with(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let def = syn::parse_macro_input!(input as ReplaceAutoWithDef);
let replace_in_span = def.replace_in.span();

match replace_in_stream(&mut Some(def.replace_with), def.replace_in) {
Ok(stream) => stream.into(),
Err(_) => {
syn::Error::new(replace_in_span, "cannot find `auto` ident in given token stream")
.to_compile_error().into()
},
}
}
struct ReplaceAutoWithDef {
replace_with: TokenStream,
replace_in: TokenStream,
}

impl syn::parse::Parse for ReplaceAutoWithDef {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let replace_with;
let _replace_with_bracket: syn::token::Brace = syn::braced!(replace_with in input);
let replace_with: TokenStream = replace_with.parse()?;
Ok(Self {
replace_with,
replace_in: input.parse()?,
})
}
}

// Replace the first found `auto` ident by content of `with`. `with` must be some (Option is used
// for internal simplification).
fn replace_in_stream(
with: &mut Option<TokenStream>,
stream: TokenStream
) -> Result<TokenStream, ()> {
assert!(with.is_some(), "`with` must be some, Option is used because `with` is used only once");

let mut stream = stream.into_iter();
let mut builded_replaced = TokenStream::new();

loop {
match stream.next() {
Some(TokenTree::Group(group)) => {
let stream = group.stream();
match replace_in_stream(with, stream) {
Ok(stream) => {
builded_replaced.extend(once(TokenTree::Group(Group::new(group.delimiter(), stream))));
break;
}
Err(_) => {
builded_replaced.extend(once(TokenTree::Group(group)));
}
}
}
Some(TokenTree::Ident(ident)) if ident == "auto" => {
builded_replaced.extend(once(with.take().expect("with is used to replace only once")));
break;
}
Some(other) => builded_replaced.extend(once(other)),
None => return Err(())
}
}

builded_replaced.extend(stream);

Ok(builded_replaced)
}

2 changes: 1 addition & 1 deletion frame/support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ macro_rules! ord_parameter_types {
}

#[doc(inline)]
pub use frame_support_procedural::{decl_storage, construct_runtime};
pub use frame_support_procedural::{decl_storage, construct_runtime, replace_auto_with};

/// Return Err of the expression: `return Err($expression);`.
///
Expand Down
10 changes: 10 additions & 0 deletions frame/treasury/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,16 @@ type BalanceOf<T> = <<T as Trait>::Currency as Currency<<T as frame_system::Trai
type PositiveImbalanceOf<T> = <<T as Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::PositiveImbalance;
type NegativeImbalanceOf<T> = <<T as Trait>::Currency as Currency<<T as frame_system::Trait>::AccountId>>::NegativeImbalance;

#[macro_export]
macro_rules! auto_construct_runtime {
( $( $t:tt )* ) => {
frame_support::replace_auto_with! {
{ Module, Call, Storage, Config, Event<T> }
$( $t )*
}
}
}

pub trait Trait: frame_system::Trait {
/// The treasury's module id, used for deriving its sovereign account ID.
type ModuleId: Get<ModuleId>;
Expand Down

0 comments on commit 6dddfb8

Please sign in to comment.