diff --git a/cli/src/commands/codegen.rs b/cli/src/commands/codegen.rs index 1e5d0d7b78..ddc3c59f2f 100644 --- a/cli/src/commands/codegen.rs +++ b/cli/src/commands/codegen.rs @@ -25,14 +25,16 @@ pub struct Opts { attributes: Vec, /// Additional derives for a given type. /// - /// Example `--derive-for-type my_module::my_type=serde::Serialize`. + /// Example 1: `--derive-for-type my_module::my_type=serde::Serialize`. + /// Example 2: `--derive-for-type my_module::my_type=serde::Serialize,recursive`. #[clap(long = "derive-for-type", value_parser = derive_for_type_parser)] - derives_for_type: Vec<(String, String)>, + derives_for_type: Vec, /// Additional attributes for a given type. /// - /// Example `--attributes-for-type my_module::my_type=#[allow(clippy::all)]`. + /// Example 1: `--attributes-for-type my_module::my_type=#[allow(clippy::all)]`. + /// Example 2: `--attributes-for-type my_module::my_type=#[allow(clippy::all)],recursive`. #[clap(long = "attributes-for-type", value_parser = attributes_for_type_parser)] - attributes_for_type: Vec<(String, String)>, + attributes_for_type: Vec, /// Substitute a type for another. /// /// Example `--substitute-type sp_runtime::MultiAddress=subxt::utils::Static<::sp_runtime::MultiAddress>` @@ -67,20 +69,63 @@ pub struct Opts { allow_insecure: bool, } -fn derive_for_type_parser(src: &str) -> Result<(String, String), String> { - let (ty, derive) = src - .split_once('=') - .ok_or_else(|| String::from("Invalid pattern for `derive-for-type`. It should be `type=derive`, like `my_type=serde::Serialize`"))?; +#[derive(Debug, Clone)] +struct DeriveForType { + type_path: String, + trait_path: String, + recursive: bool, +} - Ok((ty.to_string(), derive.to_string())) +#[derive(Debug, Clone)] +struct AttributeForType { + type_path: String, + attribute: String, + recursive: bool, } -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)]`"))?; +fn derive_for_type_parser(src: &str) -> Result { + let (type_path, trait_path, recursive) = type_map_parser(src) + .ok_or_else(|| String::from("Invalid pattern for `derive-for-type`. It should be `type=derive` or `type=derive,recursive`, like `my_type=serde::Serialize` or `my_type=serde::Serialize,recursive`"))?; + Ok(DeriveForType { + type_path: type_path.to_string(), + trait_path: trait_path.to_string(), + recursive, + }) +} - Ok((ty.to_string(), attribute.to_string())) +fn attributes_for_type_parser(src: &str) -> Result { + let (type_path, attribute, recursive) = type_map_parser(src) + .ok_or_else(|| String::from("Invalid pattern for `attributes-for-type`. It should be `type=attribute` like `my_type=serde::#[allow(clippy::all)]` or `type=attribute,recursive` like `my_type=serde::#[allow(clippy::all)],recursive`"))?; + Ok(AttributeForType { + type_path: type_path.to_string(), + attribute: attribute.to_string(), + recursive, + }) +} + +/// Parses a `&str` of the form `str1=str2` into `(str1, str2, false)` or `str1=str2,recursive` into `(str1, str2, true)`. +/// +/// A `None` value returned is a parsing error. +fn type_map_parser(src: &str) -> Option<(&str, &str, bool)> { + let (str1, rest) = src.split_once('=')?; + + let mut split_rest = rest.split(','); + let str2 = split_rest + .next() + .expect("split iter always returns at least one element; qed"); + + let mut recursive = false; + for r in split_rest { + match r { + // Note: later we can add other attributes to this match + "recursive" => { + recursive = true; + } + _ => return None, + } + } + + Some((str1, str2, recursive)) } fn substitute_type_parser(src: &str) -> Result<(String, String), String> { @@ -127,8 +172,8 @@ fn codegen( metadata_bytes: &[u8], raw_derives: Vec, raw_attributes: Vec, - derives_for_type: Vec<(String, String)>, - attributes_for_type: Vec<(String, String)>, + derives_for_type: Vec, + attributes_for_type: Vec, substitute_types: Vec<(String, String)>, crate_path: Option, no_docs: bool, @@ -168,13 +213,14 @@ fn codegen( .map_err(|e| eyre!("Cannot parse global derives: {e}"))?; codegen.set_additional_global_derives(global_derives); - for (ty_str, derive) in derives_for_type { - let ty: syn::TypePath = syn::parse_str(&ty_str) + for d in derives_for_type { + let ty_str = &d.type_path; + let ty: syn::TypePath = syn::parse_str(ty_str) .map_err(|e| eyre!("Cannot parse derive for type {ty_str}: {e}"))?; - let derive = syn::parse_str(&derive) + let derive = syn::parse_str(&d.trait_path) .map_err(|e| eyre!("Cannot parse derive for type {ty_str}: {e}"))?; // Note: recursive derives and attributes not supported in the CLI => recursive: false - codegen.add_derives_for_type(ty, std::iter::once(derive), false); + codegen.add_derives_for_type(ty, std::iter::once(derive), d.recursive); } // Configure attribtues: @@ -186,13 +232,14 @@ fn codegen( .map_err(|e| eyre!("Cannot parse global attributes: {e}"))?; codegen.set_additional_global_attributes(universal_attributes); - for (ty_str, attr) in attributes_for_type { - let ty = syn::parse_str(&ty_str) + for a in attributes_for_type { + let ty_str = &a.type_path; + let ty = syn::parse_str(ty_str) .map_err(|e| eyre!("Cannot parse attribute for type {ty_str}: {e}"))?; - let attribute: OuterAttribute = syn::parse_str(&attr) + let attribute: OuterAttribute = syn::parse_str(&a.attribute) .map_err(|e| eyre!("Cannot parse attribute for type {ty_str}: {e}"))?; // Note: recursive derives and attributes not supported in the CLI => recursive: false - codegen.add_attributes_for_type(ty, std::iter::once(attribute.0), false); + codegen.add_attributes_for_type(ty, std::iter::once(attribute.0), a.recursive); } // Insert type substitutions: @@ -213,3 +260,20 @@ fn codegen( writeln!(output, "{code}")?; Ok(()) } + +#[cfg(test)] +mod tests { + use crate::commands::codegen::type_map_parser; + + #[test] + fn parse_types() { + assert_eq!(type_map_parser("Foo"), None); + assert_eq!(type_map_parser("Foo=Bar"), Some(("Foo", "Bar", false))); + assert_eq!( + type_map_parser("Foo=Bar,recursive"), + Some(("Foo", "Bar", true)) + ); + assert_eq!(type_map_parser("Foo=Bar,a"), None); + assert_eq!(type_map_parser("Foo=Bar,a,b,c,recursive"), None); + } +}