diff --git a/cli/src/commands/codegen.rs b/cli/src/commands/codegen.rs index a835d50bd8..eb2b10f33d 100644 --- a/cli/src/commands/codegen.rs +++ b/cli/src/commands/codegen.rs @@ -19,11 +19,19 @@ pub struct Opts { /// Additional derives #[clap(long = "derive")] derives: Vec, + /// Additional attributes + #[clap(long = "attribute")] + attributes: Vec, /// Additional derives for a given type. /// /// Example `--derive-for-type my_module::my_type=serde::Serialize`. #[clap(long = "derive-for-type", value_parser = derive_for_type_parser)] derives_for_type: Vec<(String, String)>, + /// Additional attributes for a given type. + /// + /// Example `--attributes-for-type my_module::my_type=#[allow(clippy::all)]`. + #[clap(long = "attributes-for-type", value_parser = attributes_for_type_parser)] + attributes_for_type: Vec<(String, String)>, /// Substitute a type for another. /// /// Example `--substitute-type sp_runtime::MultiAddress=subxt::utils::Static<::sp_runtime::MultiAddress>` @@ -39,8 +47,15 @@ pub struct Opts { #[clap(long, action)] no_docs: bool, /// Whether to limit code generation to only runtime types. + /// + /// Defaults to `false` (all types are generated). #[clap(long)] runtime_types_only: bool, + /// Do not provide default trait derivations for the generated types. + /// + /// Defaults to `false` (default trait derivations are provided). + #[clap(long)] + no_default_derives: bool, } fn derive_for_type_parser(src: &str) -> Result<(String, String), String> { @@ -51,6 +66,14 @@ fn derive_for_type_parser(src: &str) -> Result<(String, String), String> { Ok((ty.to_string(), derive.to_string())) } +fn attributes_for_type_parser(src: &str) -> Result<(String, String), String> { + let (ty, attribute) = src + .split_once('=') + .ok_or_else(|| String::from("Invalid pattern for `attribute-type`. It should be `type=attribute`, like `my_type=serde::#[allow(clippy::all)]`"))?; + + Ok((ty.to_string(), attribute.to_string())) +} + fn substitute_type_parser(src: &str) -> Result<(String, String), String> { let (from, to) = src .split_once('=') @@ -65,41 +88,71 @@ pub async fn run(opts: Opts) -> color_eyre::Result<()> { codegen( &bytes, opts.derives, + opts.attributes, opts.derives_for_type, + opts.attributes_for_type, opts.substitute_types, opts.crate_path, opts.no_docs, opts.runtime_types_only, + opts.no_default_derives, )?; Ok(()) } +#[derive(Clone, Debug)] +struct OuterAttribute(syn::Attribute); + +impl syn::parse::Parse for OuterAttribute { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + Ok(Self(input.call(syn::Attribute::parse_outer)?[0].clone())) + } +} + +#[allow(clippy::too_many_arguments)] fn codegen( metadata_bytes: &[u8], raw_derives: Vec, + raw_attributes: Vec, derives_for_type: Vec<(String, String)>, + attributes_for_type: Vec<(String, String)>, substitute_types: Vec<(String, String)>, crate_path: Option, no_docs: bool, runtime_types_only: bool, + no_default_derives: bool, ) -> color_eyre::Result<()> { let item_mod = syn::parse_quote!( pub mod api {} ); - let p = raw_derives + let universal_derives = raw_derives .iter() .map(|raw| syn::parse_str(raw)) .collect::, _>>()?; + let universal_attributes = raw_attributes + .iter() + .map(|raw| syn::parse_str(raw)) + .map(|attr: syn::Result| attr.map(|attr| attr.0)) + .collect::, _>>()?; let crate_path = crate_path.map(Into::into).unwrap_or_default(); - let mut derives = DerivesRegistry::new(&crate_path); - derives.extend_for_all(p.into_iter()); + let mut derives = if no_default_derives { + DerivesRegistry::new() + } else { + DerivesRegistry::with_default_derives(&crate_path) + }; + derives.extend_for_all(universal_derives, universal_attributes); for (ty, derive) in derives_for_type { let ty = syn::parse_str(&ty)?; let derive = syn::parse_str(&derive)?; - derives.extend_for_type(ty, std::iter::once(derive), &crate_path); + derives.extend_for_type(ty, std::iter::once(derive), vec![]); + } + for (ty, attr) in attributes_for_type { + let ty = syn::parse_str(&ty)?; + let attribute: OuterAttribute = syn::parse_str(&attr)?; + derives.extend_for_type(ty, vec![], std::iter::once(attribute.0)); } let mut type_substitutes = TypeSubstitutes::new(&crate_path); diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 0d19a7e06a..d0fb06ff89 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -32,7 +32,7 @@ //! pub mod api {} //! ); //! // Default module derivatives. -//! let mut derives = DerivesRegistry::new(&CratePath::default()); +//! let mut derives = DerivesRegistry::with_default_derives(&CratePath::default()); //! // Default type substitutes. //! let substs = TypeSubstitutes::new(&CratePath::default()); //! // Generate the Runtime API. diff --git a/codegen/src/types/derives.rs b/codegen/src/types/derives.rs index 0775bae982..8f3c3fa7bb 100644 --- a/codegen/src/types/derives.rs +++ b/codegen/src/types/derives.rs @@ -16,21 +16,40 @@ pub struct DerivesRegistry { specific_type_derives: HashMap, } +impl Default for DerivesRegistry { + fn default() -> Self { + Self::new() + } +} + impl DerivesRegistry { - /// Creates a new `DerivesRegistry` with the supplied `crate_path`. + /// Creates a new `DerivesRegistry` with no default derives. + pub fn new() -> Self { + Self { + default_derives: Derives::new(), + specific_type_derives: Default::default(), + } + } + + /// Creates a new `DerivesRegistry` with default derives. /// /// The `crate_path` denotes the `subxt` crate access path in the /// generated code. - pub fn new(crate_path: &CratePath) -> Self { + pub fn with_default_derives(crate_path: &CratePath) -> Self { Self { - default_derives: Derives::new(crate_path), + default_derives: Derives::with_defaults(crate_path), specific_type_derives: Default::default(), } } /// 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) + pub fn extend_for_all( + &mut self, + derives: impl IntoIterator, + attributes: impl IntoIterator, + ) { + self.default_derives.derives.extend(derives); + self.default_derives.attributes.extend(attributes); } /// Insert derives to be applied to a specific generated type. @@ -38,13 +57,14 @@ impl DerivesRegistry { &mut self, ty: syn::TypePath, derives: impl IntoIterator, - crate_path: &CratePath, + attributes: impl IntoIterator, ) { let type_derives = self .specific_type_derives .entry(ty) - .or_insert_with(|| Derives::new(crate_path)); - type_derives.derives.extend(derives) + .or_insert_with(Derives::new); + type_derives.derives.extend(derives); + type_derives.attributes.extend(attributes); } /// Returns the derives to be applied to all generated types. @@ -73,6 +93,12 @@ pub struct Derives { attributes: HashSet, } +impl Default for Derives { + fn default() -> Self { + Self::new() + } +} + impl FromIterator for Derives { fn from_iter>(iter: T) -> Self { let derives = iter.into_iter().collect(); @@ -84,9 +110,17 @@ impl FromIterator for Derives { } impl Derives { + /// Creates an empty instance of `Derives` (with no default derives). + pub fn new() -> Self { + Self { + derives: HashSet::new(), + attributes: HashSet::new(), + } + } + /// Creates a new instance of `Derives` with the `crate_path` prepended /// to the set of default derives that reside in `subxt`. - pub fn new(crate_path: &CratePath) -> Self { + pub fn with_defaults(crate_path: &CratePath) -> Self { let mut derives = HashSet::new(); let mut attributes = HashSet::new(); @@ -99,8 +133,6 @@ impl Derives { attributes.insert(syn::parse_quote!(#[decode_as_type(crate_path = #decode_crate_path)])); derives.insert(syn::parse_quote!(#crate_path::ext::codec::Encode)); - attributes.insert(syn::parse_quote!(#[codec(crate = #crate_path::ext::codec)])); - derives.insert(syn::parse_quote!(#crate_path::ext::codec::Decode)); attributes.insert(syn::parse_quote!(#[codec(crate = #crate_path::ext::codec)])); diff --git a/codegen/src/types/tests.rs b/codegen/src/types/tests.rs index 89b7e442a8..01a238ef7b 100644 --- a/codegen/src/types/tests.rs +++ b/codegen/src/types/tests.rs @@ -39,7 +39,7 @@ fn generate_struct_with_primitives() { &portable_types, "root", TypeSubstitutes::new(&crate_path), - DerivesRegistry::new(&crate_path), + DerivesRegistry::with_default_derives(&crate_path), crate_path, true, ); @@ -91,7 +91,7 @@ fn generate_struct_with_a_struct_field() { &portable_types, "root", TypeSubstitutes::new(&crate_path), - DerivesRegistry::new(&crate_path), + DerivesRegistry::with_default_derives(&crate_path), crate_path, true, ); @@ -145,7 +145,7 @@ fn generate_tuple_struct() { &portable_types, "root", TypeSubstitutes::new(&crate_path), - DerivesRegistry::new(&crate_path), + DerivesRegistry::with_default_derives(&crate_path), crate_path, true, ); @@ -236,7 +236,7 @@ fn derive_compact_as_for_uint_wrapper_structs() { &portable_types, "root", TypeSubstitutes::new(&crate_path), - DerivesRegistry::new(&crate_path), + DerivesRegistry::with_default_derives(&crate_path), crate_path, true, ); @@ -333,7 +333,7 @@ fn generate_enum() { &portable_types, "root", TypeSubstitutes::new(&crate_path), - DerivesRegistry::new(&crate_path), + DerivesRegistry::with_default_derives(&crate_path), crate_path, true, ); @@ -397,7 +397,7 @@ fn compact_fields() { &portable_types, "root", TypeSubstitutes::new(&crate_path), - DerivesRegistry::new(&crate_path), + DerivesRegistry::with_default_derives(&crate_path), crate_path, true, ); @@ -465,7 +465,7 @@ fn compact_generic_parameter() { &portable_types, "root", TypeSubstitutes::new(&crate_path), - DerivesRegistry::new(&crate_path), + DerivesRegistry::with_default_derives(&crate_path), crate_path, true, ); @@ -512,7 +512,7 @@ fn generate_array_field() { &portable_types, "root", TypeSubstitutes::new(&crate_path), - DerivesRegistry::new(&crate_path), + DerivesRegistry::with_default_derives(&crate_path), crate_path, true, ); @@ -555,7 +555,7 @@ fn option_fields() { &portable_types, "root", TypeSubstitutes::new(&crate_path), - DerivesRegistry::new(&crate_path), + DerivesRegistry::with_default_derives(&crate_path), crate_path, true, ); @@ -601,7 +601,7 @@ fn box_fields_struct() { &portable_types, "root", TypeSubstitutes::new(&crate_path), - DerivesRegistry::new(&crate_path), + DerivesRegistry::with_default_derives(&crate_path), crate_path, true, ); @@ -647,7 +647,7 @@ fn box_fields_enum() { &portable_types, "root", TypeSubstitutes::new(&crate_path), - DerivesRegistry::new(&crate_path), + DerivesRegistry::with_default_derives(&crate_path), crate_path, true, ); @@ -693,7 +693,7 @@ fn range_fields() { &portable_types, "root", TypeSubstitutes::new(&crate_path), - DerivesRegistry::new(&crate_path), + DerivesRegistry::with_default_derives(&crate_path), crate_path, true, ); @@ -743,7 +743,7 @@ fn generics() { &portable_types, "root", TypeSubstitutes::new(&crate_path), - DerivesRegistry::new(&crate_path), + DerivesRegistry::with_default_derives(&crate_path), crate_path, true, ); @@ -800,7 +800,7 @@ fn generics_nested() { &portable_types, "root", TypeSubstitutes::new(&crate_path), - DerivesRegistry::new(&crate_path), + DerivesRegistry::with_default_derives(&crate_path), crate_path, true, ); @@ -857,7 +857,7 @@ fn generate_bitvec() { &portable_types, "root", TypeSubstitutes::new(&crate_path), - DerivesRegistry::new(&crate_path), + DerivesRegistry::with_default_derives(&crate_path), crate_path, true, ); @@ -916,7 +916,7 @@ fn generics_with_alias_adds_phantom_data_marker() { &portable_types, "root", TypeSubstitutes::new(&crate_path), - DerivesRegistry::new(&crate_path), + DerivesRegistry::with_default_derives(&crate_path), crate_path, true, ); @@ -987,7 +987,7 @@ fn modules() { &portable_types, "root", TypeSubstitutes::new(&crate_path), - DerivesRegistry::new(&crate_path), + DerivesRegistry::with_default_derives(&crate_path), crate_path, true, ); @@ -1056,7 +1056,7 @@ fn dont_force_struct_names_camel_case() { &portable_types, "root", TypeSubstitutes::new(&crate_path), - DerivesRegistry::new(&crate_path), + DerivesRegistry::with_default_derives(&crate_path), crate_path, true, ); @@ -1096,8 +1096,11 @@ fn apply_user_defined_derives_for_all_types() { let crate_path = "::subxt_path".into(); // configure derives - let mut derives = DerivesRegistry::new(&crate_path); - derives.extend_for_all(vec![parse_quote!(Clone), parse_quote!(Eq)]); + let mut derives = DerivesRegistry::with_default_derives(&crate_path); + derives.extend_for_all( + vec![parse_quote!(Clone), parse_quote!(Eq)], + vec![parse_quote!(#[some_attribute])], + ); let type_gen = TypeGenerator::new( &portable_types, @@ -1120,12 +1123,14 @@ fn apply_user_defined_derives_for_all_types() { #[codec(crate = ::subxt_path::ext::codec)] #[decode_as_type(crate_path = ":: subxt_path :: ext :: scale_decode")] #[encode_as_type(crate_path = ":: subxt_path :: ext :: scale_encode")] + #[some_attribute] pub struct A(pub root :: subxt_codegen :: types :: tests :: B,); #[derive(::subxt_path::ext::codec::Decode, ::subxt_path::ext::codec::Encode, ::subxt_path::ext::scale_decode::DecodeAsType, ::subxt_path::ext::scale_encode::EncodeAsType, Clone, Debug, Eq)] #[codec(crate = ::subxt_path::ext::codec)] #[decode_as_type(crate_path = ":: subxt_path :: ext :: scale_decode")] #[encode_as_type(crate_path = ":: subxt_path :: ext :: scale_encode")] + #[some_attribute] pub struct B; } } @@ -1153,14 +1158,14 @@ fn apply_user_defined_derives_for_specific_types() { let crate_path = "::subxt_path".into(); // configure derives - let mut derives = DerivesRegistry::new(&crate_path); + let mut derives = DerivesRegistry::with_default_derives(&crate_path); // for all types - derives.extend_for_all(vec![parse_quote!(Eq)]); + derives.extend_for_all(vec![parse_quote!(Eq)], vec![]); // for specific types derives.extend_for_type( parse_quote!(subxt_codegen::types::tests::B), vec![parse_quote!(Hash)], - &crate_path, + vec![parse_quote!(#[some_attribute])], ); // duplicates (in this case `Eq`) will be combined (i.e. a set union) derives.extend_for_type( @@ -1170,7 +1175,7 @@ fn apply_user_defined_derives_for_specific_types() { parse_quote!(Ord), parse_quote!(PartialOrd), ], - &crate_path, + vec![], ); let type_gen = TypeGenerator::new( @@ -1200,6 +1205,7 @@ fn apply_user_defined_derives_for_specific_types() { #[codec(crate = ::subxt_path::ext::codec)] #[decode_as_type(crate_path = ":: subxt_path :: ext :: scale_decode")] #[encode_as_type(crate_path = ":: subxt_path :: ext :: scale_encode")] + #[some_attribute] pub struct B(pub root :: subxt_codegen :: types :: tests :: C,); #[derive(::subxt_path::ext::codec::Decode, ::subxt_path::ext::codec::Encode, ::subxt_path::ext::scale_decode::DecodeAsType, ::subxt_path::ext::scale_encode::EncodeAsType, Debug, Eq, Ord, PartialOrd)] @@ -1212,3 +1218,61 @@ fn apply_user_defined_derives_for_specific_types() { .to_string() ) } + +#[test] +fn opt_out_from_default_derives() { + #[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(); + + let crate_path = "::subxt_path".into(); + // configure derives + let mut derives = DerivesRegistry::new(); + derives.extend_for_all( + vec![parse_quote!(Clone), parse_quote!(Eq)], + vec![parse_quote!(#[some_attribute])], + ); + derives.extend_for_type( + parse_quote!(subxt_codegen::types::tests::B), + vec![parse_quote!(Hash)], + vec![parse_quote!(#[some_other_attribute])], + ); + + let type_gen = TypeGenerator::new( + &portable_types, + "root", + TypeSubstitutes::new(&crate_path), + derives, + crate_path, + true, + ); + let types = type_gen.generate_types_mod().expect("Valid type mod; qed"); + 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(Clone, Eq)] + #[some_attribute] + pub struct A(pub root :: subxt_codegen :: types :: tests :: B,); + + #[derive(Clone, Eq, Hash)] + #[some_attribute] + #[some_other_attribute] + pub struct B; + } + } + .to_string() + ) +} diff --git a/macro/src/lib.rs b/macro/src/lib.rs index b82e1589e2..afb4759dbd 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -119,6 +119,15 @@ use proc_macro_error::{abort_call_site, proc_macro_error}; use subxt_codegen::{utils::Uri, CodegenError, DerivesRegistry, TypeSubstitutes}; use syn::{parse_macro_input, punctuated::Punctuated}; +#[derive(Clone, Debug)] +struct OuterAttribute(syn::Attribute); + +impl syn::parse::Parse for OuterAttribute { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + Ok(Self(input.call(syn::Attribute::parse_outer)?[0].clone())) + } +} + #[derive(Debug, FromMeta)] struct RuntimeMetadataArgs { #[darling(default)] @@ -127,9 +136,13 @@ struct RuntimeMetadataArgs { runtime_metadata_url: Option, #[darling(default)] derive_for_all_types: Option>, + #[darling(default)] + attributes_for_all_types: Option>, #[darling(multiple)] derive_for_type: Vec, #[darling(multiple)] + attributes_for_type: Vec, + #[darling(multiple)] substitute_type: Vec, #[darling(default, rename = "crate")] crate_path: Option, @@ -137,6 +150,8 @@ struct RuntimeMetadataArgs { generate_docs: darling::util::Flag, #[darling(default)] runtime_types_only: bool, + #[darling(default)] + no_default_derives: bool, } #[derive(Debug, FromMeta)] @@ -146,6 +161,13 @@ struct DeriveForType { derive: Punctuated, } +#[derive(Debug, FromMeta)] +struct AttributesForType { + #[darling(rename = "type")] + ty: syn::TypePath, + attributes: Punctuated, +} + #[derive(Debug, FromMeta)] struct SubstituteType { #[darling(rename = "type")] @@ -167,16 +189,27 @@ pub fn subxt(args: TokenStream, input: TokenStream) -> TokenStream { Some(crate_path) => crate_path.into(), None => subxt_codegen::CratePath::default(), }; - let mut derives_registry = DerivesRegistry::new(&crate_path); + let mut derives_registry = if args.no_default_derives { + DerivesRegistry::new() + } else { + DerivesRegistry::with_default_derives(&crate_path) + }; + + let universal_derives = args.derive_for_all_types.unwrap_or_default(); + let universal_attributes = args.attributes_for_all_types.unwrap_or_default(); + derives_registry.extend_for_all( + universal_derives, + universal_attributes.iter().map(|a| a.0.clone()), + ); - 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(), vec![]) + } + for attributes in &args.attributes_for_type { derives_registry.extend_for_type( - derives.ty.clone(), - derives.derive.iter().cloned(), - &crate_path, + attributes.ty.clone(), + vec![], + attributes.attributes.iter().map(|a| a.0.clone()), ) } diff --git a/testing/integration-tests/src/codegen/codegen_documentation.rs b/testing/integration-tests/src/codegen/codegen_documentation.rs index b8356d5bd9..0319711db6 100644 --- a/testing/integration-tests/src/codegen/codegen_documentation.rs +++ b/testing/integration-tests/src/codegen/codegen_documentation.rs @@ -52,7 +52,7 @@ fn generate_runtime_interface(crate_path: CratePath, should_gen_docs: bool) -> S let item_mod = syn::parse_quote!( pub mod api {} ); - let derives = DerivesRegistry::new(&crate_path); + let derives = DerivesRegistry::with_default_derives(&crate_path); let type_substitutes = TypeSubstitutes::new(&crate_path); generator .generate_runtime( @@ -142,7 +142,7 @@ fn check_root_attrs_preserved() { // Generate a runtime interface from the provided metadata. let generator = RuntimeGenerator::new(metadata); - let derives = DerivesRegistry::new(&CratePath::default()); + let derives = DerivesRegistry::with_default_derives(&CratePath::default()); let type_substitutes = TypeSubstitutes::new(&CratePath::default()); let generated_code = generator .generate_runtime(