diff --git a/cli/src/commands/codegen.rs b/cli/src/commands/codegen.rs index d7f7a38d21..a89b863174 100644 --- a/cli/src/commands/codegen.rs +++ b/cli/src/commands/codegen.rs @@ -28,14 +28,27 @@ pub struct Opts { /// Additional derives #[clap(long = "derive")] derives: 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)>, /// The `subxt` crate access path in the generated code. /// Defaults to `::subxt`. #[clap(long = "crate")] crate_path: Option, } +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`"))?; + + Ok((ty.to_string(), derive.to_string())) +} + pub async fn run(opts: Opts) -> color_eyre::Result<()> { - if let Some(file) = opts.file.as_ref() { + let bytes = if let Some(file) = opts.file.as_ref() { if opts.url.is_some() { eyre::bail!("specify one of `--url` or `--file` but not both") }; @@ -43,23 +56,24 @@ pub async fn run(opts: Opts) -> color_eyre::Result<()> { let mut file = fs::File::open(file)?; let mut bytes = Vec::new(); file.read_to_end(&mut bytes)?; - codegen(&bytes, opts.derives, opts.crate_path)?; - return Ok(()) - } + bytes + } else { + let url = opts.url.unwrap_or_else(|| { + "http://localhost:9933" + .parse::() + .expect("default url is valid") + }); + subxt_codegen::utils::fetch_metadata_bytes(&url).await? + }; - let url = opts.url.unwrap_or_else(|| { - "http://localhost:9933" - .parse::() - .expect("default url is valid") - }); - let bytes = subxt_codegen::utils::fetch_metadata_bytes(&url).await?; - codegen(&bytes, opts.derives, opts.crate_path)?; + codegen(&bytes, opts.derives, opts.derives_for_type, opts.crate_path)?; Ok(()) } fn codegen( metadata_bytes: &[u8], raw_derives: Vec, + derives_for_type: Vec<(String, String)>, crate_path: Option, ) -> color_eyre::Result<()> { let item_mod = syn::parse_quote!( @@ -75,6 +89,12 @@ fn codegen( let mut derives = DerivesRegistry::new(&crate_path); derives.extend_for_all(p.into_iter()); + for (ty, derive) in derives_for_type.into_iter() { + let ty = syn::parse_str(&ty)?; + let derive = syn::parse_str(&derive)?; + derives.extend_for_type(ty, std::iter::once(derive), &crate_path) + } + let runtime_api = subxt_codegen::generate_runtime_api_from_bytes( item_mod, metadata_bytes, diff --git a/examples/examples/storage_iterating.rs b/examples/examples/storage_iterating.rs index c9bed2c645..bb0873466e 100644 --- a/examples/examples/storage_iterating.rs +++ b/examples/examples/storage_iterating.rs @@ -39,7 +39,7 @@ async fn main() -> Result<(), Box> { println!("\nExample 1. Obtained keys:"); while let Some((key, value)) = iter.next().await? { - println!("Key: 0x{}", hex::encode(&key)); + println!("Key: 0x{}", hex::encode(key)); println!(" Value: {}", value); } } diff --git a/macro/src/lib.rs b/macro/src/lib.rs index cd685d6c38..ae5b623b2e 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -160,7 +160,7 @@ pub fn subxt(args: TokenStream, input: TokenStream) -> TokenStream { let path = root_path.join(rest_of_path); subxt_codegen::generate_runtime_api_from_path( item_mod, - &path, + path, derives_registry, crate_path, ) diff --git a/testing/test-runtime/build.rs b/testing/test-runtime/build.rs index 783d602753..3b910b5d3e 100644 --- a/testing/test-runtime/build.rs +++ b/testing/test-runtime/build.rs @@ -80,7 +80,7 @@ async fn run() { // Save metadata to a file: let out_dir = env::var_os("OUT_DIR").unwrap(); let metadata_path = Path::new(&out_dir).join("metadata.scale"); - fs::write(&metadata_path, &metadata_bytes.0).expect("Couldn't write metadata output"); + fs::write(&metadata_path, metadata_bytes.0).expect("Couldn't write metadata output"); // Write out our expression to generate the runtime API to a file. Ideally, we'd just write this code // in lib.rs, but we must pass a string literal (and not `concat!(..)`) as an arg to `runtime_metadata_path`, @@ -101,7 +101,7 @@ async fn run() { .expect("Path to metadata should be stringifiable") ); let runtime_path = Path::new(&out_dir).join("runtime.rs"); - fs::write(&runtime_path, runtime_api_contents) + fs::write(runtime_path, runtime_api_contents) .expect("Couldn't write runtime rust output"); let substrate_path = diff --git a/testing/ui-tests/src/lib.rs b/testing/ui-tests/src/lib.rs index 8c3e30d30a..d9cee8c3fa 100644 --- a/testing/ui-tests/src/lib.rs +++ b/testing/ui-tests/src/lib.rs @@ -28,21 +28,21 @@ fn ui_tests() { t.pass("src/correct/*.rs"); // Check that storage maps with no keys are handled properly. - t.pass(&m.path_to_ui_test_for_metadata( + t.pass(m.path_to_ui_test_for_metadata( "storage_map_no_keys", storage::metadata_storage_map_no_keys(), )); // Test that the codegen can handle the different types of DispatchError. - t.pass(&m.path_to_ui_test_for_metadata( + t.pass(m.path_to_ui_test_for_metadata( "named_field_dispatch_error", dispatch_errors::metadata_named_field_dispatch_error(), )); - t.pass(&m.path_to_ui_test_for_metadata( + t.pass(m.path_to_ui_test_for_metadata( "legacy_dispatch_error", dispatch_errors::metadata_legacy_dispatch_error(), )); - t.pass(&m.path_to_ui_test_for_metadata( + t.pass(m.path_to_ui_test_for_metadata( "array_dispatch_error", dispatch_errors::metadata_array_dispatch_error(), )); diff --git a/testing/ui-tests/src/utils/metadata_test_runner.rs b/testing/ui-tests/src/utils/metadata_test_runner.rs index 5db07b355b..a041c76062 100644 --- a/testing/ui-tests/src/utils/metadata_test_runner.rs +++ b/testing/ui-tests/src/utils/metadata_test_runner.rs @@ -52,9 +52,9 @@ impl MetadataTestRunner { std::fs::create_dir_all(&tmp_dir).expect("could not create tmp ui test dir"); // Write metadata to tmp folder: - std::fs::write(&tmp_metadata_path, &encoded_metadata).unwrap(); + std::fs::write(&tmp_metadata_path, encoded_metadata).unwrap(); // Write test file to tmp folder (it'll be moved by trybuild): - std::fs::write(&tmp_rust_path, &rust_file).unwrap(); + std::fs::write(&tmp_rust_path, rust_file).unwrap(); tmp_rust_path }