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),
}
#[derive(Debug, Clone)]
pub struct ImplicitAllPalletsDeclaration {
pub pallet_decls: Vec<PalletDeclaration>,
pub pallet_count: usize,
}
#[derive(Debug, Clone)]
pub struct ExplicitAllPalletsDeclaration {
pub name: Ident,
pub pallets: Vec<Pallet>,
}
pub struct Def {
pub input: TokenStream2,
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_index_and_item = None;
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(_) if runtime_struct.is_none() => {
let p = runtime_struct::RuntimeStructDef::try_from(item)?;
runtime_struct = Some(p);
},
RuntimeAttr::Derive(_, types) if runtime_types.is_none() => {
runtime_types = Some(types);
},
RuntimeAttr::PalletIndex(span, index) => {
pallet_index_and_item = if let syn::Item::Type(item) = item {
Some((index, 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_index, pallet_item)) = pallet_index_and_item {
match *pallet_item.ty.clone() {
syn::Type::Path(ref path) => {
let pallet_decl =
PalletDeclaration::try_from(item.span(), &pallet_item, &path.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,
}
} else {
if let syn::Item::Type(item) = item {
let msg = "Missing pallet index for pallet declaration. Please add `#[runtime::pallet_index(...)]`";
return Err(syn::Error::new(item.span(), &msg));
}
}
}
let name = item.ident.clone();
let decl_count = pallet_decls.len();
let pallets = if decl_count > 0 {
AllPalletsDeclaration::Implicit(ImplicitAllPalletsDeclaration {
pallet_decls,
pallet_count: decl_count.saturating_add(pallets.len()),
})
} else {
AllPalletsDeclaration::Explicit(ExplicitAllPalletsDeclaration { name, pallets })
};
let def = Def {
input,
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)
}
}
#[test]
fn runtime_parsing_works() {
let def = Def::try_from(syn::parse_quote! {
#[runtime::runtime]
mod runtime {
#[runtime::derive(RuntimeCall, RuntimeEvent)]
#[runtime::runtime]
pub struct Runtime;
#[runtime::pallet_index(0)]
pub type System = frame_system::Pallet<Runtime>;
#[runtime::pallet_index(1)]
pub type Pallet1 = pallet1<Instance1>;
}
})
.expect("Failed to parse runtime definition");
assert_eq!(def.runtime_struct.ident, "Runtime");
}