// This file is part of Bizinikiwi. // 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 pezpallet; pub mod pezpallet_decl; pub mod runtime_struct; pub mod runtime_types; use crate::construct_runtime::parse::Pezpallet; use pezpallet_decl::PalletDeclaration; use proc_macro2::TokenStream as TokenStream2; use quote::ToTokens; use std::collections::HashMap; use syn::{spanned::Spanned, Ident, Token}; use pezframe_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!(pezpallet_index); custom_keyword!(disable_call); custom_keyword!(disable_unsigned); } enum RuntimeAttr { Runtime(proc_macro2::Span), Derive(proc_macro2::Span, Vec), 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 { input.parse::()?; let content; syn::bracketed!(content in input); content.parse::()?; content.parse::()?; let lookahead = content.lookahead1(); if lookahead.peek(keyword::runtime) { Ok(RuntimeAttr::Runtime(content.parse::()?.span())) } else if lookahead.peek(keyword::derive) { let _ = content.parse::(); let derive_content; syn::parenthesized!(derive_content in content); let runtime_types = derive_content.parse::>()?; let runtime_types = runtime_types.inner.into_iter().collect(); Ok(RuntimeAttr::Derive(derive_content.span(), runtime_types)) } else if lookahead.peek(keyword::pezpallet_index) { let _ = content.parse::(); let pezpallet_index_content; syn::parenthesized!(pezpallet_index_content in content); let pezpallet_index = pezpallet_index_content.parse::()?; if !pezpallet_index.suffix().is_empty() { let msg = "Number literal must not have a suffix"; return Err(syn::Error::new(pezpallet_index.span(), msg)); } Ok(RuntimeAttr::PalletIndex(pezpallet_index.span(), pezpallet_index.base10_parse()?)) } else if lookahead.peek(keyword::disable_call) { Ok(RuntimeAttr::DisableCall(content.parse::()?.span())) } else if lookahead.peek(keyword::disable_unsigned) { Ok(RuntimeAttr::DisableUnsigned(content.parse::()?.span())) } else { Err(lookahead.error()) } } } #[derive(Debug, Clone)] pub enum AllPalletsDeclaration { Implicit(ImplicitAllPalletsDeclaration), Explicit(ExplicitAllPalletsDeclaration), } /// Declaration of a runtime with some pezpallet with implicit declaration of parts. #[derive(Debug, Clone)] pub struct ImplicitAllPalletsDeclaration { pub pezpallet_decls: Vec, pub pezpallet_count: usize, } /// Declaration of a runtime with all pezpallet having explicit declaration of parts. #[derive(Debug, Clone)] pub struct ExplicitAllPalletsDeclaration { pub name: Ident, pub pallets: Vec, } pub struct Def { pub input: TokenStream2, pub runtime_struct: runtime_struct::RuntimeStructDef, pub pallets: AllPalletsDeclaration, pub runtime_types: Vec, } impl Def { pub fn try_from(mut item: syn::ItemMod) -> syn::Result { 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 pezpallet_decls = vec![]; let mut pallets = vec![]; for item in items.iter_mut() { let mut pezpallet_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::(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) => { pezpallet_index_and_item = if let syn::Item::Type(item) = item { Some((index, item.clone())) } else { let msg = "Invalid runtime::pezpallet_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((pezpallet_index, pezpallet_item)) = pezpallet_index_and_item { match *pezpallet_item.ty.clone() { syn::Type::Path(ref path) => { let pezpallet_decl = PalletDeclaration::try_from(item.span(), &pezpallet_item, &path.path)?; if let Some(used_pallet) = names.insert(pezpallet_decl.name.clone(), pezpallet_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(pezpallet_decl.name.span(), &msg)); return Err(err); } pezpallet_decls.push(pezpallet_decl); }, syn::Type::TraitObject(syn::TypeTraitObject { bounds, .. }) => { let pezpallet = Pezpallet::try_from( item.span(), &pezpallet_item, pezpallet_index, disable_call, disable_unsigned, &bounds, )?; if let Some(used_pallet) = indices.insert(pezpallet.index, pezpallet.name.clone()) { let msg = format!( "Pezpallet indices are conflicting: Both pallets {} and {} are at index {}", used_pallet, pezpallet.name, pezpallet.index, ); let mut err = syn::Error::new(used_pallet.span(), &msg); err.combine(syn::Error::new(pezpallet.name.span(), msg)); return Err(err); } pallets.push(pezpallet); }, _ => continue, } } else { if let syn::Item::Type(item) = item { let msg = "Missing pezpallet index for pezpallet declaration. Please add `#[runtime::pezpallet_index(...)]`"; return Err(syn::Error::new(item.span(), &msg)); } } } let name = item.ident.clone(); let decl_count = pezpallet_decls.len(); let pallets = if decl_count > 0 { AllPalletsDeclaration::Implicit(ImplicitAllPalletsDeclaration { pezpallet_decls, pezpallet_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::pezpallet_index(0)] pub type System = pezframe_system::Pezpallet; #[runtime::pezpallet_index(1)] pub type Pallet1 = pallet1; } }) .expect("Failed to parse runtime definition"); assert_eq!(def.runtime_struct.ident, "Runtime"); }