From 24317b431122193223d8bcba184a51e840c74d32 Mon Sep 17 00:00:00 2001 From: Andrew Jones Date: Thu, 28 Apr 2022 14:26:34 +0300 Subject: [PATCH] Add custom derives for specific generated types (#520) * WIP implement custom derives per type * WIP wiring up specific type derives * Fmt * Rename GeneratedTypeDerives to Derives * Fmt * Fix errors * Fix test runtime * Make derives appear in alphabetic order * Clippy * Add derive_for_type attribute to example * Add docs to example * Rename GeneratedTypeDerive * Rename ty to type in attribute * Update darling * Update codegen/src/types/derives.rs Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> * Update codegen/src/types/mod.rs Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> * Update codegen/src/types/mod.rs Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> * review: update method name * Add unit tests for combined derives * Remove out of date docs * Add macro usage docs Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> --- cli/src/main.rs | 6 +- codegen/Cargo.toml | 2 +- codegen/src/api/mod.rs | 35 +---- codegen/src/lib.rs | 3 +- codegen/src/types/composite_def.rs | 11 +- codegen/src/types/derives.rs | 97 ++++++++++--- codegen/src/types/mod.rs | 39 +++-- codegen/src/types/tests.rs | 175 ++++++++++++++++++----- codegen/src/types/type_def.rs | 17 +-- examples/examples/custom_type_derives.rs | 10 +- macro/Cargo.toml | 2 +- macro/src/lib.rs | 95 +++++++++++- test-runtime/build.rs | 2 +- 13 files changed, 374 insertions(+), 120 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index bffdfde52d..625a94f7bf 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -43,7 +43,7 @@ use std::{ path::PathBuf, }; use structopt::StructOpt; -use subxt_codegen::GeneratedTypeDerives; +use subxt_codegen::DerivesRegistry; use subxt_metadata::{ get_metadata_hash, get_pallet_hash, @@ -288,8 +288,8 @@ fn codegen( .iter() .map(|raw| syn::parse_str(raw)) .collect::, _>>()?; - let mut derives = GeneratedTypeDerives::default(); - derives.append(p.into_iter()); + let mut derives = DerivesRegistry::default(); + derives.extend_for_all(p.into_iter()); let runtime_api = generator.generate_runtime(item_mod, derives); println!("{}", runtime_api); diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index 393e7c160a..36f1f12ff8 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -13,7 +13,7 @@ description = "Generate an API for interacting with a substrate node from FRAME [dependencies] async-trait = "0.1.49" codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "full", "bit-vec"] } -darling = "0.13.0" +darling = "0.14.0" frame-metadata = "15.0.0" heck = "0.4.0" proc-macro2 = "1.0.24" diff --git a/codegen/src/api/mod.rs b/codegen/src/api/mod.rs index 91e1d944bd..a437f610c8 100644 --- a/codegen/src/api/mod.rs +++ b/codegen/src/api/mod.rs @@ -15,21 +15,6 @@ // along with subxt. If not, see . //! Generate code for submitting extrinsics and query storage of a Substrate runtime. -//! -//! ## Note -//! -//! By default the codegen will search for the `System` pallet's `Account` storage item, which is -//! the conventional location where an account's index (aka nonce) is stored. -//! -//! If this `System::Account` storage item is discovered, then it is assumed that: -//! -//! 1. The type of the storage item is a `struct` (aka a composite type) -//! 2. There exists a field called `nonce` which contains the account index. -//! -//! These assumptions are based on the fact that the `frame_system::AccountInfo` type is the default -//! configured type, and that the vast majority of chain configurations will use this. -//! -//! If either of these conditions are not satisfied, the codegen will fail. mod calls; mod constants; @@ -39,7 +24,7 @@ mod storage; use subxt_metadata::get_metadata_per_pallet_hash; -use super::GeneratedTypeDerives; +use super::DerivesRegistry; use crate::{ ir, types::{ @@ -68,15 +53,12 @@ use std::{ path, string::ToString, }; -use syn::{ - parse_quote, - punctuated::Punctuated, -}; +use syn::parse_quote; pub fn generate_runtime_api

( item_mod: syn::ItemMod, path: P, - generated_type_derives: Option>, + derives: DerivesRegistry, ) -> TokenStream2 where P: AsRef, @@ -92,11 +74,6 @@ where let metadata = frame_metadata::RuntimeMetadataPrefixed::decode(&mut &bytes[..]) .unwrap_or_else(|e| abort_call_site!("Failed to decode metadata: {}", e)); - let mut derives = GeneratedTypeDerives::default(); - if let Some(user_derives) = generated_type_derives { - derives.append(user_derives.iter().cloned()) - } - let generator = RuntimeGenerator::new(metadata); generator.generate_runtime(item_mod, derives) } @@ -116,9 +93,10 @@ impl RuntimeGenerator { pub fn generate_runtime( &self, item_mod: syn::ItemMod, - derives: GeneratedTypeDerives, + derives: DerivesRegistry, ) -> TokenStream2 { let item_mod_ir = ir::ItemMod::from(item_mod); + let default_derives = derives.default_derives(); // some hardcoded default type substitutes, can be overridden by user let mut type_substitutes = [ @@ -265,7 +243,7 @@ impl RuntimeGenerator { }); let outer_event = quote! { - #derives + #default_derives pub enum Event { #( #outer_event_variants )* } @@ -448,6 +426,7 @@ where type_gen, ); let struct_def = CompositeDef::struct_def( + &ty, struct_name.as_ref(), Default::default(), fields, diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 523b0d72ca..d436a50c47 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -26,7 +26,8 @@ pub use self::{ RuntimeGenerator, }, types::{ - GeneratedTypeDerives, + Derives, + DerivesRegistry, Module, TypeGenerator, }, diff --git a/codegen/src/types/composite_def.rs b/codegen/src/types/composite_def.rs index 5f3379898f..34a57c7c76 100644 --- a/codegen/src/types/composite_def.rs +++ b/codegen/src/types/composite_def.rs @@ -15,8 +15,8 @@ // along with subxt. If not, see . use super::{ + Derives, Field, - GeneratedTypeDerives, TypeDefParameters, TypeGenerator, TypeParameter, @@ -29,6 +29,8 @@ use quote::{ quote, }; use scale_info::{ + form::PortableForm, + Type, TypeDef, TypeDefPrimitive, }; @@ -52,6 +54,7 @@ pub struct CompositeDef { impl CompositeDef { /// Construct a definition which will generate code for a standalone `struct`. pub fn struct_def( + ty: &Type, ident: &str, type_params: TypeDefParameters, fields_def: CompositeDefFields, @@ -59,7 +62,7 @@ impl CompositeDef { type_gen: &TypeGenerator, docs: &[String], ) -> Self { - let mut derives = type_gen.derives().clone(); + let mut derives = type_gen.type_derives(ty); let fields: Vec<_> = fields_def.field_types().collect(); if fields.len() == 1 { @@ -82,7 +85,7 @@ impl CompositeDef { | TypeDefPrimitive::U128 ) ) { - derives.push_codec_compact_as() + derives.insert_codec_compact_as() } } } @@ -165,7 +168,7 @@ impl quote::ToTokens for CompositeDef { pub enum CompositeDefKind { /// Composite type comprising a Rust `struct`. Struct { - derives: GeneratedTypeDerives, + derives: Derives, type_params: TypeDefParameters, field_visibility: Option, }, diff --git a/codegen/src/types/derives.rs b/codegen/src/types/derives.rs index db09aade91..085c8b7768 100644 --- a/codegen/src/types/derives.rs +++ b/codegen/src/types/derives.rs @@ -17,48 +17,107 @@ use syn::{ parse_quote, punctuated::Punctuated, + Path, }; -#[derive(Debug, Clone)] -pub struct GeneratedTypeDerives { - derives: Punctuated, +use std::collections::{ + HashMap, + HashSet, +}; + +#[derive(Debug, Default, Clone)] +pub struct DerivesRegistry { + default_derives: Derives, + specific_type_derives: HashMap, } -impl GeneratedTypeDerives { - pub fn new(derives: Punctuated) -> Self { - Self { derives } +impl DerivesRegistry { + /// Insert derives to be applied to all generated types. + pub fn extend_for_all(&mut self, derives: impl IntoIterator) { + self.default_derives.derives.extend(derives) } + /// Insert derives to be applied to a specific generated type. + pub fn extend_for_type( + &mut self, + ty: syn::TypePath, + derives: impl IntoIterator, + ) { + let type_derives = self + .specific_type_derives + .entry(ty) + .or_insert_with(Derives::default); + type_derives.derives.extend(derives) + } + + /// Returns the derives to be applied to all generated types. + pub fn default_derives(&self) -> &Derives { + &self.default_derives + } + + /// Resolve the derives for a generated type. Includes: + /// - The default derives for all types e.g. `scale::Encode, scale::Decode` + /// - Any user-defined derives for all types via `generated_type_derives` + /// - Any user-defined derives for this specific type + pub fn resolve(&self, ty: &syn::TypePath) -> Derives { + let mut defaults = self.default_derives.derives.clone(); + if let Some(specific) = self.specific_type_derives.get(ty) { + defaults.extend(specific.derives.iter().cloned()); + } + Derives { derives: defaults } + } +} + +#[derive(Debug, Clone)] +pub struct Derives { + derives: HashSet, +} + +impl FromIterator for Derives { + fn from_iter>(iter: T) -> Self { + let derives = iter.into_iter().collect(); + Self { derives } + } +} + +impl Derives { /// Add `::subxt::codec::CompactAs` to the derives. - pub fn push_codec_compact_as(&mut self) { - self.derives.push(parse_quote!(::subxt::codec::CompactAs)); + pub fn insert_codec_compact_as(&mut self) { + self.insert(parse_quote!(::subxt::codec::CompactAs)); } pub fn append(&mut self, derives: impl Iterator) { for derive in derives { - self.derives.push(derive) + self.insert(derive) } } - pub fn push(&mut self, derive: syn::Path) { - self.derives.push(derive); + pub fn insert(&mut self, derive: syn::Path) { + self.derives.insert(derive); } } -impl Default for GeneratedTypeDerives { +impl Default for Derives { fn default() -> Self { - let mut derives = Punctuated::new(); - derives.push(syn::parse_quote!(::subxt::codec::Encode)); - derives.push(syn::parse_quote!(::subxt::codec::Decode)); - derives.push(syn::parse_quote!(Debug)); - Self::new(derives) + let mut derives = HashSet::new(); + derives.insert(syn::parse_quote!(::subxt::codec::Encode)); + derives.insert(syn::parse_quote!(::subxt::codec::Decode)); + derives.insert(syn::parse_quote!(Debug)); + Self { derives } } } -impl quote::ToTokens for GeneratedTypeDerives { +impl quote::ToTokens for Derives { fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { if !self.derives.is_empty() { - let derives = &self.derives; + let mut sorted = self.derives.iter().cloned().collect::>(); + sorted.sort_by(|a, b| { + quote::quote!(#a) + .to_string() + .cmp("e::quote!(#b).to_string()) + }); + let derives: Punctuated = + sorted.iter().cloned().collect(); tokens.extend(quote::quote! { #[derive(#derives)] }) diff --git a/codegen/src/types/mod.rs b/codegen/src/types/mod.rs index c29dbd3412..5726d36a96 100644 --- a/codegen/src/types/mod.rs +++ b/codegen/src/types/mod.rs @@ -27,6 +27,7 @@ use proc_macro2::{ Span, TokenStream, }; +use proc_macro_error::abort_call_site; use quote::{ quote, ToTokens, @@ -48,7 +49,10 @@ pub use self::{ CompositeDefFieldType, CompositeDefFields, }, - derives::GeneratedTypeDerives, + derives::{ + Derives, + DerivesRegistry, + }, type_def::TypeDefGen, type_def_params::TypeDefParameters, type_path::{ @@ -71,7 +75,7 @@ pub struct TypeGenerator<'a> { /// User defined overrides for generated types. type_substitutes: HashMap, /// Set of derives with which to annotate generated types. - derives: GeneratedTypeDerives, + derives: DerivesRegistry, } impl<'a> TypeGenerator<'a> { @@ -80,7 +84,7 @@ impl<'a> TypeGenerator<'a> { type_registry: &'a PortableRegistry, root_mod: &'static str, type_substitutes: HashMap, - derives: GeneratedTypeDerives, + derives: DerivesRegistry, ) -> Self { let root_mod_ident = Ident::new(root_mod, Span::call_site()); Self { @@ -92,7 +96,7 @@ impl<'a> TypeGenerator<'a> { } /// Generate a module containing all types defined in the supplied type registry. - pub fn generate_types_mod(&'a self) -> Module<'a> { + pub fn generate_types_mod(&self) -> Module { let mut root_mod = Module::new(self.types_mod_ident.clone(), self.types_mod_ident.clone()); @@ -119,7 +123,7 @@ impl<'a> TypeGenerator<'a> { id: u32, path: Vec, root_mod_ident: &Ident, - module: &mut Module<'a>, + module: &mut Module, ) { let joined_path = path.join("::"); if self.type_substitutes.contains_key(&joined_path) { @@ -215,22 +219,31 @@ impl<'a> TypeGenerator<'a> { } } - /// Returns the derives with which all generated type will be decorated. - pub fn derives(&self) -> &GeneratedTypeDerives { - &self.derives + /// Returns the derives to be applied to all generated types. + pub fn default_derives(&self) -> &Derives { + self.derives.default_derives() + } + + /// Returns the derives to be applied to a generated type. + pub fn type_derives(&self, ty: &Type) -> Derives { + let joined_path = ty.path().segments().join("::"); + let ty_path: syn::TypePath = syn::parse_str(&joined_path).unwrap_or_else(|e| { + abort_call_site!("'{}' is an invalid type path: {:?}", joined_path, e,) + }); + self.derives.resolve(&ty_path) } } /// Represents a Rust `mod`, containing generated types and child `mod`s. #[derive(Debug)] -pub struct Module<'a> { +pub struct Module { name: Ident, root_mod: Ident, - children: BTreeMap>, - types: BTreeMap, TypeDefGen<'a>>, + children: BTreeMap, + types: BTreeMap, TypeDefGen>, } -impl<'a> ToTokens for Module<'a> { +impl ToTokens for Module { fn to_tokens(&self, tokens: &mut TokenStream) { let name = &self.name; let root_mod = &self.root_mod; @@ -248,7 +261,7 @@ impl<'a> ToTokens for Module<'a> { } } -impl<'a> Module<'a> { +impl Module { /// Create a new [`Module`], with a reference to the root `mod` for resolving type paths. pub(crate) fn new(name: Ident, root_mod: Ident) -> Self { Self { diff --git a/codegen/src/types/tests.rs b/codegen/src/types/tests.rs index 507ca92ffa..7f2501677c 100644 --- a/codegen/src/types/tests.rs +++ b/codegen/src/types/tests.rs @@ -21,10 +21,11 @@ use scale_info::{ Registry, TypeInfo, }; +use syn::parse_quote; const MOD_PATH: &[&str] = &["subxt_codegen", "types", "tests"]; -fn get_mod<'a>(module: &'a Module, path_segs: &[&'static str]) -> Option<&'a Module<'a>> { +fn get_mod<'a>(module: &'a Module, path_segs: &[&'static str]) -> Option<&'a Module> { let (mod_name, rest) = path_segs.split_first()?; let mod_ident = Ident::new(mod_name, Span::call_site()); let module = module.children.get(&mod_ident)?; @@ -64,7 +65,7 @@ fn generate_struct_with_primitives() { pub mod tests { use super::root; - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)] + #[derive(::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct S { pub a: ::core::primitive::bool, pub b: ::core::primitive::u32, @@ -110,12 +111,12 @@ fn generate_struct_with_a_struct_field() { pub mod tests { use super::root; - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)] + #[derive(::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct Child { pub a: ::core::primitive::i32, } - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)] + #[derive(::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct Parent { pub a: ::core::primitive::bool, pub b: root::subxt_codegen::types::tests::Child, @@ -155,10 +156,10 @@ fn generate_tuple_struct() { pub mod tests { use super::root; - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)] + #[derive(::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct Child(pub ::core::primitive::i32,); - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)] + #[derive(::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct Parent(pub ::core::primitive::bool, pub root::subxt_codegen::types::tests::Child,); } } @@ -237,34 +238,34 @@ fn derive_compact_as_for_uint_wrapper_structs() { pub mod tests { use super::root; - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug, ::subxt::codec::CompactAs)] + #[derive(::subxt::codec::CompactAs, ::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct Su128 { pub a: ::core::primitive::u128, } - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug, ::subxt::codec::CompactAs)] + #[derive(::subxt::codec::CompactAs, ::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct Su16 { pub a: ::core::primitive::u16, } - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug, ::subxt::codec::CompactAs)] + #[derive(::subxt::codec::CompactAs, ::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct Su32 { pub a: ::core::primitive::u32, } - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug, ::subxt::codec::CompactAs)] + #[derive(::subxt::codec::CompactAs, ::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct Su64 { pub a: ::core::primitive::u64, } - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug, ::subxt::codec::CompactAs)] + #[derive(::subxt::codec::CompactAs, ::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct Su8 { pub a: ::core::primitive::u8, } - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug, ::subxt::codec::CompactAs)] + #[derive(::subxt::codec::CompactAs, ::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct TSu128(pub ::core::primitive::u128,); - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug, ::subxt::codec::CompactAs)] + #[derive(::subxt::codec::CompactAs, ::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct TSu16(pub ::core::primitive::u16,); - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug, ::subxt::codec::CompactAs)] + #[derive(::subxt::codec::CompactAs, ::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct TSu32(pub ::core::primitive::u32,); - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug, ::subxt::codec::CompactAs)] + #[derive(::subxt::codec::CompactAs, ::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct TSu64(pub ::core::primitive::u64,); - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug, ::subxt::codec::CompactAs)] + #[derive(::subxt::codec::CompactAs, ::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct TSu8(pub ::core::primitive::u8,); } } @@ -300,7 +301,7 @@ fn generate_enum() { quote! { pub mod tests { use super::root; - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)] + #[derive(::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub enum E { # [codec (index = 0)] A, @@ -358,7 +359,7 @@ fn compact_fields() { quote! { pub mod tests { use super::root; - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)] + #[derive(::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub enum E { # [codec (index = 0)] A { @@ -369,12 +370,12 @@ fn compact_fields() { B( #[codec(compact)] ::core::primitive::u32,), } - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)] + #[derive(::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct S { #[codec(compact)] pub a: ::core::primitive::u32, } - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)] + #[derive(::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct TupleStruct(#[codec(compact)] pub ::core::primitive::u32,); } } @@ -408,7 +409,7 @@ fn generate_array_field() { quote! { pub mod tests { use super::root; - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)] + #[derive(::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct S { pub a: [::core::primitive::u8; 32usize], } @@ -445,7 +446,7 @@ fn option_fields() { quote! { pub mod tests { use super::root; - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)] + #[derive(::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct S { pub a: ::core::option::Option<::core::primitive::bool>, pub b: ::core::option::Option<::core::primitive::u32>, @@ -485,7 +486,7 @@ fn box_fields_struct() { quote! { pub mod tests { use super::root; - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)] + #[derive(::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct S { pub a: ::std::boxed::Box<::core::primitive::bool>, pub b: ::std::boxed::Box<::core::primitive::u32>, @@ -525,7 +526,7 @@ fn box_fields_enum() { quote! { pub mod tests { use super::root; - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)] + #[derive(::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub enum E { # [codec (index = 0)] A(::std::boxed::Box<::core::primitive::bool>,), @@ -565,7 +566,7 @@ fn range_fields() { quote! { pub mod tests { use super::root; - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)] + #[derive(::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct S { pub a: ::core::ops::Range<::core::primitive::u32>, pub b: ::core::ops::RangeInclusive<::core::primitive::u32>, @@ -609,12 +610,12 @@ fn generics() { quote! { pub mod tests { use super::root; - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)] + #[derive(::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct Bar { pub b: root::subxt_codegen::types::tests::Foo<::core::primitive::u32>, pub c: root::subxt_codegen::types::tests::Foo<::core::primitive::u8>, } - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)] + #[derive(::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct Foo<_0> { pub a: _0, } @@ -657,12 +658,12 @@ fn generics_nested() { quote! { pub mod tests { use super::root; - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)] + #[derive(::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct Bar<_0> { pub b: root::subxt_codegen::types::tests::Foo<_0, ::core::primitive::u32>, } - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)] + #[derive(::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct Foo<_0, _1> { pub a: _0, pub b: ::core::option::Option<(_0, _1,)>, @@ -708,7 +709,7 @@ fn generate_bitvec() { quote! { pub mod tests { use super::root; - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)] + #[derive(::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct S { pub lsb: ::subxt::bitvec::vec::BitVec<::core::primitive::u8, root::bitvec::order::Lsb0>, pub msb: ::subxt::bitvec::vec::BitVec<::core::primitive::u16, root::bitvec::order::Msb0>, @@ -761,12 +762,12 @@ fn generics_with_alias_adds_phantom_data_marker() { quote! { pub mod tests { use super::root; - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug, ::subxt::codec::CompactAs)] + #[derive(::subxt::codec::CompactAs, ::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct NamedFields<_0> { pub b: ::core::primitive::u32, #[codec(skip)] pub __subxt_unused_type_params: ::core::marker::PhantomData<_0> } - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)] + #[derive(::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct UnnamedFields<_0, _1> ( pub (::core::primitive::u32, ::core::primitive::u32,), #[codec(skip)] pub ::core::marker::PhantomData<(_0, _1)> @@ -829,20 +830,20 @@ fn modules() { pub mod b { use super::root; - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)] + #[derive(::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct Bar { pub a: root::subxt_codegen::types::tests::m::a::Foo, } } - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)] + #[derive(::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct Foo; } pub mod c { use super::root; - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)] + #[derive(::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct Foo { pub a: root::subxt_codegen::types::tests::m::a::b::Bar, } @@ -879,10 +880,112 @@ fn dont_force_struct_names_camel_case() { pub mod tests { use super::root; - #[derive(::subxt::codec::Encode, ::subxt::codec::Decode, Debug)] + #[derive(::subxt::codec::Decode, ::subxt::codec::Encode, Debug)] pub struct AB; } } .to_string() ) } + +#[test] +fn apply_user_defined_derives_for_all_types() { + #[allow(unused)] + #[derive(TypeInfo)] + struct A(B); + + #[allow(unused)] + #[derive(TypeInfo)] + struct B; + + let mut registry = Registry::new(); + registry.register_type(&meta_type::()); + let portable_types: PortableRegistry = registry.into(); + + // configure derives + let mut derives = DerivesRegistry::default(); + derives.extend_for_all(vec![parse_quote!(Clone), parse_quote!(Eq)]); + + let type_gen = + TypeGenerator::new(&portable_types, "root", Default::default(), derives); + let types = type_gen.generate_types_mod(); + let tests_mod = get_mod(&types, MOD_PATH).unwrap(); + + assert_eq!( + tests_mod.into_token_stream().to_string(), + quote! { + pub mod tests { + use super::root; + + #[derive(::subxt::codec::Decode, ::subxt::codec::Encode, Clone, Debug, Eq)] + pub struct A(pub root :: subxt_codegen :: types :: tests :: B,); + + #[derive(::subxt::codec::Decode, ::subxt::codec::Encode, Clone, Debug, Eq)] + pub struct B; + } + } + .to_string() + ) +} + +#[test] +fn apply_user_defined_derives_for_specific_types() { + #[allow(unused)] + #[derive(TypeInfo)] + struct A(B); + + #[allow(unused)] + #[derive(TypeInfo)] + struct B(C); + + #[allow(unused)] + #[derive(TypeInfo)] + struct C; + + let mut registry = Registry::new(); + registry.register_type(&meta_type::()); + let portable_types: PortableRegistry = registry.into(); + + // configure derives + let mut derives = DerivesRegistry::default(); + // for all types + derives.extend_for_all(vec![parse_quote!(Eq)]); + // for specific types + derives.extend_for_type( + parse_quote!(subxt_codegen::types::tests::B), + vec![parse_quote!(Hash)], + ); + // duplicates (in this case `Eq`) will be combined (i.e. a set union) + derives.extend_for_type( + parse_quote!(subxt_codegen::types::tests::C), + vec![ + parse_quote!(Eq), + parse_quote!(Ord), + parse_quote!(PartialOrd), + ], + ); + + let type_gen = + TypeGenerator::new(&portable_types, "root", Default::default(), derives); + let types = type_gen.generate_types_mod(); + let tests_mod = get_mod(&types, MOD_PATH).unwrap(); + + assert_eq!( + tests_mod.into_token_stream().to_string(), + quote! { + pub mod tests { + use super::root; + + #[derive(::subxt::codec::Decode, ::subxt::codec::Encode, Debug, Eq)] + pub struct A(pub root :: subxt_codegen :: types :: tests :: B,); + + #[derive(::subxt::codec::Decode, ::subxt::codec::Encode, Debug, Eq, Hash)] + pub struct B(pub root :: subxt_codegen :: types :: tests :: C,); + + #[derive(::subxt::codec::Decode, ::subxt::codec::Encode, Debug, Eq, Ord, PartialOrd)] + pub struct C; + } + } + .to_string() + ) +} diff --git a/codegen/src/types/type_def.rs b/codegen/src/types/type_def.rs index 9b19ddb047..a66a80b519 100644 --- a/codegen/src/types/type_def.rs +++ b/codegen/src/types/type_def.rs @@ -17,7 +17,7 @@ use super::{ CompositeDef, CompositeDefFields, - GeneratedTypeDerives, + Derives, TypeDefParameters, TypeGenerator, TypeParameter, @@ -39,19 +39,19 @@ use syn::parse_quote; /// Field type paths are resolved via the `TypeGenerator`, which contains the registry of all /// generated types in the module. #[derive(Debug)] -pub struct TypeDefGen<'a> { +pub struct TypeDefGen { /// The type parameters of the type to be generated type_params: TypeDefParameters, /// The derives with which to annotate the generated type. - derives: &'a GeneratedTypeDerives, + derives: Derives, /// The kind of type to be generated. ty_kind: TypeDefGenKind, } -impl<'a> TypeDefGen<'a> { +impl TypeDefGen { /// Construct a type definition for codegen from the given [`scale_info::Type`]. - pub fn from_type(ty: Type, type_gen: &'a TypeGenerator) -> Self { - let derives = type_gen.derives(); + pub fn from_type(ty: Type, type_gen: &TypeGenerator) -> Self { + let derives = type_gen.type_derives(&ty); let type_params = ty .type_params() @@ -85,6 +85,7 @@ impl<'a> TypeDefGen<'a> { ); type_params.update_unused(fields.field_types()); let composite_def = CompositeDef::struct_def( + &ty, &type_name, type_params.clone(), fields, @@ -126,7 +127,7 @@ impl<'a> TypeDefGen<'a> { } } -impl<'a> quote::ToTokens for TypeDefGen<'a> { +impl quote::ToTokens for TypeDefGen { fn to_tokens(&self, tokens: &mut TokenStream) { match &self.ty_kind { TypeDefGenKind::Struct(composite) => composite.to_tokens(tokens), @@ -150,7 +151,7 @@ impl<'a> quote::ToTokens for TypeDefGen<'a> { let enum_ident = format_ident!("{}", type_name); let type_params = &self.type_params; - let derives = self.derives; + let derives = &self.derives; let ty_toks = quote! { #derives pub enum #enum_ident #type_params { diff --git a/examples/examples/custom_type_derives.rs b/examples/examples/custom_type_derives.rs index 2812fca268..30373df604 100644 --- a/examples/examples/custom_type_derives.rs +++ b/examples/examples/custom_type_derives.rs @@ -20,11 +20,19 @@ #[subxt::subxt( runtime_metadata_path = "examples/polkadot_metadata.scale", + // We can add (certain) custom derives to the generated types by providing // a comma separated list to the below attribute. Most useful for adding `Clone`. // The derives that we can add ultimately is limited to the traits that the base // types relied upon by the codegen implement. - generated_type_derives = "Clone, PartialEq, Hash" + derive_for_all_types = "Clone, PartialEq", + + // To apply derives to specific generated types, add a `derive_for_type` per type, + // mapping the type path to the derives which should be added for that type only. + // Note that these derives will be in addition to those specified above in + // `derive_for_all_types` + derive_for_type(type = "frame_support::PalletId", derive = "Eq, Ord, PartialOrd"), + derive_for_type(type = "sp_runtime::ModuleError", derive = "Eq, Hash"), )] pub mod polkadot {} diff --git a/macro/Cargo.toml b/macro/Cargo.toml index 480d181296..c2bcf95603 100644 --- a/macro/Cargo.toml +++ b/macro/Cargo.toml @@ -17,7 +17,7 @@ proc-macro = true [dependencies] async-trait = "0.1.49" codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "full"] } -darling = "0.13.0" +darling = "0.14.0" frame-metadata = "15.0.0" heck = "0.4.0" proc-macro2 = "1.0.24" diff --git a/macro/src/lib.rs b/macro/src/lib.rs index ee4c1d76db..7222930bce 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -14,11 +14,85 @@ // You should have received a copy of the GNU General Public License // along with subxt. If not, see . +//! Generate a strongly typed API for interacting with a Substrate runtime from its metadata. +//! +//! Usage: +//! +//! Download metadata from a running Substrate node using `subxt-cli`: +//! +//! ```bash +//! subxt metadata -f bytes > polkadot_metadata.scale +//! ``` +//! +//! Annotate a Rust module with the `subxt` attribute referencing the aforementioned metadata file. +//! +//! ```ignore +//! #[subxt::subxt( +//! runtime_metadata_path = "polkadot_metadata.scale", +//! )] +//! pub mod polkadot {} +//! ``` +//! +//! The `subxt` macro will populate the annotated module with all of the methods and types required +//! for submitting extrinsics and reading from storage for the given runtime. +//! +//! ## Substituting types +//! +//! In order to replace a generated type by a user-defined type, use `substitute_type`: +//! +//! ```ignore +//! #[subxt::subxt( +//! runtime_metadata_path = "polkadot_metadata.scale", +//! )] +//! pub mod polkadot { +//! #[subxt(substitute_type = "sp_arithmetic::per_things::Perbill")] +//! use sp_runtime::Perbill; +//! } +//! ``` +//! +//! This will replace the generated type and any usages with the specified type at the `use` import. +//! It is useful for using custom decoding for specific types, or to provide a type with foreign +//! trait implementations, or other specialized functionality. + +//! ## Custom Derives +//! +//! By default all generated types are annotated with `scale::Encode` and `scale::Decode` derives. +//! However when using the generated types in the client, they may require additional derives to be +//! useful. +//! +//! ### Adding derives for all types +//! +//! Add `derive_for_all_types` with a comma seperated list of the derives to apply to *all* types +//! +//! ```ignore +//! #[subxt::subxt( +//! runtime_metadata_path = "polkadot_metadata.scale", +//! derive_for_all_types = "Eq, PartialEq" +//! )] +//! pub mod polkadot {} +//! ``` +//! +//! ### Adding derives for specific types +//! +//! Add `derive_for_type` for each specific type with a comma seperated list of the derives to +//! apply for that type only. +//! +//! ```ignore +//! #[subxt::subxt( +//! runtime_metadata_path = "polkadot_metadata.scale", +//! derive_for_all_types = "Eq, PartialEq", +//! derive_for_type(type = "frame_support::PalletId", derive = "Ord, PartialOrd"), +//! derive_for_type(type = "sp_runtime::ModuleError", derive = "Hash"), +//! )] +//! pub mod polkadot {} +//! ``` + extern crate proc_macro; use darling::FromMeta; use proc_macro::TokenStream; use proc_macro_error::proc_macro_error; +use subxt_codegen::DerivesRegistry; use syn::{ parse_macro_input, punctuated::Punctuated, @@ -28,11 +102,17 @@ use syn::{ struct RuntimeMetadataArgs { runtime_metadata_path: String, #[darling(default)] - generated_type_derives: Option, + derive_for_all_types: Option>, + #[darling(multiple)] + derive_for_type: Vec, } #[derive(Debug, FromMeta)] -struct GeneratedTypeDerives(Punctuated); +struct DeriveForType { + #[darling(rename = "type")] + ty: syn::TypePath, + derive: Punctuated, +} #[proc_macro_attribute] #[proc_macro_error] @@ -48,7 +128,14 @@ pub fn subxt(args: TokenStream, input: TokenStream) -> TokenStream { let root_path = std::path::Path::new(&root); let path = root_path.join(args.runtime_metadata_path); - let generated_type_derives = args.generated_type_derives.map(|derives| derives.0); + let mut derives_registry = DerivesRegistry::default(); + if let Some(derive_for_all) = args.derive_for_all_types { + derives_registry.extend_for_all(derive_for_all.iter().cloned()); + } + for derives in &args.derive_for_type { + derives_registry + .extend_for_type(derives.ty.clone(), derives.derive.iter().cloned()) + } - subxt_codegen::generate_runtime_api(item_mod, &path, generated_type_derives).into() + subxt_codegen::generate_runtime_api(item_mod, &path, derives_registry).into() } diff --git a/test-runtime/build.rs b/test-runtime/build.rs index 746550b425..acff4a9641 100644 --- a/test-runtime/build.rs +++ b/test-runtime/build.rs @@ -104,7 +104,7 @@ async fn run() { r#" #[subxt::subxt( runtime_metadata_path = "{}", - generated_type_derives = "Eq, PartialEq" + derive_for_all_types = "Eq, PartialEq" )] pub mod node_runtime {{ #[subxt(substitute_type = "sp_arithmetic::per_things::Perbill")]