// Copyright 2019-2020 Parity Technologies (UK) Ltd. // This file is part of substrate-subxt. // // subxt is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // subxt is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with substrate-subxt. If not, see . use crate::utils; use heck::SnakeCase; use proc_macro2::TokenStream; use proc_macro_error::abort; use quote::{ format_ident, quote, }; use syn::parse::{ Parse, ParseStream, }; mod kw { use syn::custom_keyword; custom_keyword!(ignore); } #[derive(Debug)] enum ModuleAttr { Ignore(kw::ignore), } impl Parse for ModuleAttr { fn parse(input: ParseStream) -> syn::Result { Ok(Self::Ignore(input.parse()?)) } } type ModuleAttrs = utils::Attrs; fn ignore(attrs: &[syn::Attribute]) -> bool { for attr in attrs { if let Some(ident) = attr.path.get_ident() { if ident == "module" { let attrs: ModuleAttrs = syn::parse2(attr.tokens.clone()) .map_err(|err| abort!("{}", err)) .unwrap(); if !attrs.attrs.is_empty() { return true } } } } false } fn events_decoder_trait_name(module: &syn::Ident) -> syn::Ident { format_ident!("{}EventsDecoder", module.to_string()) } fn with_module_ident(module: &syn::Ident) -> syn::Ident { format_ident!("with_{}", module.to_string().to_snake_case()) } /// Attribute macro that registers the type sizes used by the module; also sets the `MODULE` constant. pub fn module(_args: TokenStream, tokens: TokenStream) -> TokenStream { let input: Result = syn::parse2(tokens.clone()); let input = if let Ok(input) = input { input } else { // handle #[module(ignore)] by just returning the tokens return tokens }; let subxt = utils::use_crate("substrate-subxt"); let module = &input.ident; let module_name = module.to_string(); let module_events_decoder = events_decoder_trait_name(module); let with_module = with_module_ident(module); let bounds = input.supertraits.iter().filter_map(|bound| { if let syn::TypeParamBound::Trait(syn::TraitBound { path, .. }) = bound { let module = utils::path_to_ident(path); let with_module = with_module_ident(module); Some(quote! { self.#with_module(); }) } else { None } }); let types = input.items.iter().filter_map(|item| { if let syn::TraitItem::Type(ty) = item { if ignore(&ty.attrs) { return None } let ident = &ty.ident; let ident_str = ident.to_string(); Some(quote! { self.register_type_size::(#ident_str); }) } else { None } }); quote! { #input const MODULE: &str = #module_name; /// `EventsDecoder` extension trait. pub trait #module_events_decoder { /// Registers this modules types. fn #with_module(&mut self); } impl #module_events_decoder for #subxt::EventsDecoder { fn #with_module(&mut self) { #(#bounds)* #(#types)* } } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_balance_module() { let attr = quote!(#[module]); let input = quote! { pub trait Balances: System { type Balance: frame_support::Parameter + sp_runtime::traits::Member + sp_runtime::traits::AtLeast32Bit + codec::Codec + Default + Copy + sp_runtime::traits::MaybeSerialize + std::fmt::Debug + From<::BlockNumber>; } }; let expected = quote! { pub trait Balances: System { type Balance: frame_support::Parameter + sp_runtime::traits::Member + sp_runtime::traits::AtLeast32Bit + codec::Codec + Default + Copy + sp_runtime::traits::MaybeSerialize + std::fmt::Debug + From< ::BlockNumber>; } const MODULE: &str = "Balances"; /// `EventsDecoder` extension trait. pub trait BalancesEventsDecoder { /// Registers this modules types. fn with_balances(&mut self); } impl BalancesEventsDecoder for substrate_subxt::EventsDecoder { fn with_balances(&mut self) { self.with_system(); self.register_type_size::("Balance"); } } }; let result = module(attr, input); utils::assert_proc_macro(result, expected); } #[test] fn test_herd() { let attr = quote!(#[module]); let input = quote! { pub trait Herd: Husbandry { type Hoves: u8; type Wool: bool; #[module(ignore)] type Digestion: EnergyProducer + fmt::Debug; } }; let expected = quote! { pub trait Herd: Husbandry { type Hoves: u8; type Wool: bool; #[module(ignore)] type Digestion: EnergyProducer + fmt::Debug; } const MODULE: &str = "Herd"; /// `EventsDecoder` extension trait. pub trait HerdEventsDecoder { /// Registers this modules types. fn with_herd(&mut self); } impl HerdEventsDecoder for substrate_subxt::EventsDecoder { fn with_herd(&mut self) { self.with_husbandry(); self.register_type_size::("Hoves"); self.register_type_size::("Wool"); } } }; let result = module(attr, input); utils::assert_proc_macro(result, expected); } }