diff --git a/cli/src/commands/codegen.rs b/cli/src/commands/codegen.rs index 132e7bf495..e4ce0ce617 100644 --- a/cli/src/commands/codegen.rs +++ b/cli/src/commands/codegen.rs @@ -45,6 +45,9 @@ pub struct Opts { /// Defaults to `false` (documentation is generated). #[clap(long, action)] no_docs: bool, + /// Whether to limit code generation to only runtime types. + #[clap(long)] + runtime_types_only: bool, } fn derive_for_type_parser(src: &str) -> Result<(String, String), String> { @@ -80,6 +83,7 @@ pub async fn run(opts: Opts) -> color_eyre::Result<()> { opts.derives_for_type, opts.crate_path, opts.no_docs, + opts.runtime_types_only, )?; Ok(()) } @@ -90,6 +94,7 @@ fn codegen( derives_for_type: Vec<(String, String)>, crate_path: Option, no_docs: bool, + runtime_types_only: bool, ) -> color_eyre::Result<()> { let item_mod = syn::parse_quote!( pub mod api {} @@ -120,6 +125,7 @@ fn codegen( type_substitutes, crate_path, should_gen_docs, + runtime_types_only, ); match runtime_api { Ok(runtime_api) => println!("{runtime_api}"), diff --git a/codegen/src/api/mod.rs b/codegen/src/api/mod.rs index eb4bbdd1de..ab8adcb866 100644 --- a/codegen/src/api/mod.rs +++ b/codegen/src/api/mod.rs @@ -121,6 +121,7 @@ impl CodegenError { /// * `type_substitutes` - Provide custom type substitutes. /// * `crate_path` - Path to the `subxt` crate. /// * `should_gen_docs` - True if the generated API contains the documentation from the metadata. +/// * `runtime_types_only` - Whether to limit code generation to only runtime types. /// /// **Note:** This is a wrapper over [RuntimeGenerator] for static metadata use-cases. pub fn generate_runtime_api_from_path

( @@ -130,6 +131,7 @@ pub fn generate_runtime_api_from_path

( type_substitutes: TypeSubstitutes, crate_path: CratePath, should_gen_docs: bool, + runtime_types_only: bool, ) -> Result where P: AsRef, @@ -147,6 +149,7 @@ where type_substitutes, crate_path, should_gen_docs, + runtime_types_only, ) } @@ -162,6 +165,7 @@ where /// * `type_substitutes` - Provide custom type substitutes. /// * `crate_path` - Path to the `subxt` crate. /// * `should_gen_docs` - True if the generated API contains the documentation from the metadata. +/// * `runtime_types_only` - Whether to limit code generation to only runtime types. /// /// **Note:** This is a wrapper over [RuntimeGenerator] for static metadata use-cases. pub fn generate_runtime_api_from_url( @@ -171,6 +175,7 @@ pub fn generate_runtime_api_from_url( type_substitutes: TypeSubstitutes, crate_path: CratePath, should_gen_docs: bool, + runtime_types_only: bool, ) -> Result { let bytes = fetch_metadata_bytes_blocking(url)?; @@ -181,6 +186,7 @@ pub fn generate_runtime_api_from_url( type_substitutes, crate_path, should_gen_docs, + runtime_types_only, ) } @@ -194,6 +200,7 @@ pub fn generate_runtime_api_from_url( /// * `type_substitutes` - Provide custom type substitutes. /// * `crate_path` - Path to the `subxt` crate. /// * `should_gen_docs` - True if the generated API contains the documentation from the metadata. +/// * `runtime_types_only` - Whether to limit code generation to only runtime types. /// /// **Note:** This is a wrapper over [RuntimeGenerator] for static metadata use-cases. pub fn generate_runtime_api_from_bytes( @@ -203,17 +210,28 @@ pub fn generate_runtime_api_from_bytes( type_substitutes: TypeSubstitutes, crate_path: CratePath, should_gen_docs: bool, + runtime_types_only: bool, ) -> Result { let metadata = frame_metadata::RuntimeMetadataPrefixed::decode(&mut &bytes[..])?; let generator = RuntimeGenerator::new(metadata); - generator.generate_runtime( - item_mod, - derives, - type_substitutes, - crate_path, - should_gen_docs, - ) + if runtime_types_only { + generator.generate_runtime_types( + item_mod, + derives, + type_substitutes, + crate_path, + should_gen_docs, + ) + } else { + generator.generate_runtime( + item_mod, + derives, + type_substitutes, + crate_path, + should_gen_docs, + ) + } } /// Create the API for interacting with a Substrate runtime. @@ -240,6 +258,57 @@ impl RuntimeGenerator { /// /// * `item_mod` - The module declaration for which the API is implemented. /// * `derives` - Provide custom derives for the generated types. + /// * `type_substitutes` - Provide custom type substitutes. + /// * `crate_path` - Path to the `subxt` crate. + /// * `should_gen_docs` - True if the generated API contains the documentation from the metadata. + pub fn generate_runtime_types( + &self, + item_mod: syn::ItemMod, + derives: DerivesRegistry, + type_substitutes: TypeSubstitutes, + crate_path: CratePath, + should_gen_docs: bool, + ) -> Result { + let item_mod_attrs = item_mod.attrs.clone(); + + let item_mod_ir = ir::ItemMod::try_from(item_mod)?; + let mod_ident = &item_mod_ir.ident; + let rust_items = item_mod_ir.rust_items(); + + let type_gen = TypeGenerator::new( + &self.metadata.types, + "runtime_types", + type_substitutes, + derives, + crate_path, + should_gen_docs, + ); + let types_mod = type_gen.generate_types_mod()?; + + Ok(quote! { + #( #item_mod_attrs )* + #[allow(dead_code, unused_imports, non_camel_case_types)] + #[allow(clippy::all)] + pub mod #mod_ident { + // Preserve any Rust items that were previously defined in the adorned module + #( #rust_items ) * + + // Make it easy to access the root via `root_mod` at different levels: + use super::#mod_ident as root_mod; + #types_mod + } + }) + } + + /// Generate the API for interacting with a Substrate runtime. + /// + /// # Arguments + /// + /// * `item_mod` - The module declaration for which the API is implemented. + /// * `derives` - Provide custom derives for the generated types. + /// * `type_substitutes` - Provide custom type substitutes. + /// * `crate_path` - Path to the `subxt` crate. + /// * `should_gen_docs` - True if the generated API contains the documentation from the metadata. pub fn generate_runtime( &self, item_mod: syn::ItemMod, diff --git a/examples/examples/runtime_types_only.rs b/examples/examples/runtime_types_only.rs new file mode 100644 index 0000000000..2599dd014d --- /dev/null +++ b/examples/examples/runtime_types_only.rs @@ -0,0 +1,66 @@ +// Copyright 2019-2022 Parity Technologies (UK) Ltd. +// This file is dual-licensed as Apache-2.0 or GPL-3.0. +// see LICENSE for license details. + +//! In some cases we are interested only in the `RuntimeCall` enum (or more generally, only in some +//! runtime types). We can ask `subxt` to generate only runtime types by passing a corresponding +//! flag. +//! +//! Here we present how to correctly create `Block` type for the Polkadot chain. + +use sp_core::H256; +use sp_runtime::{ + generic, + traits::{ + BlakeTwo256, + Block as _, + Header as _, + }, + Digest, +}; +use subxt::PolkadotConfig; + +#[subxt::subxt( + runtime_metadata_path = "../artifacts/polkadot_metadata.scale", + derive_for_all_types = "Clone, PartialEq, Eq", + runtime_types_only +)] +pub mod polkadot {} + +type RuntimeCall = polkadot::runtime_types::polkadot_runtime::RuntimeCall; + +type UncheckedExtrinsic = generic::UncheckedExtrinsic< + ::Address, + RuntimeCall, + ::Signature, + // Usually we are not interested in `SignedExtra`. + (), +>; + +type Header = generic::Header; +type Block = generic::Block; + +#[tokio::main] +async fn main() -> Result<(), Box> { + tracing_subscriber::fmt::init(); + + // Although we could build an online client, we do not have access to the full runtime API. For + // that, we would have to specify `runtime_types_only = false` (or just skipping it). + // + // let api = subxt::OnlineClient::::new().await?; + // let address = polkadot::constants().balances().existential_deposit(); <- this won't compile! + + let polkadot_header = Header::new( + 41, + H256::default(), + H256::default(), + H256::default(), + Digest::default(), + ); + + let polkadot_block = Block::new(polkadot_header, vec![]); + + println!("{polkadot_block:?}"); + + Ok(()) +} diff --git a/macro/src/lib.rs b/macro/src/lib.rs index 95dac9597d..ac404ce781 100644 --- a/macro/src/lib.rs +++ b/macro/src/lib.rs @@ -48,7 +48,7 @@ //! //! ### Adding derives for all types //! -//! Add `derive_for_all_types` with a comma seperated list of the derives to apply to *all* types +//! Add `derive_for_all_types` with a comma separated list of the derives to apply to *all* types //! //! ```ignore //! #[subxt::subxt( @@ -60,7 +60,7 @@ //! //! ### Adding derives for specific types //! -//! Add `derive_for_type` for each specific type with a comma seperated list of the derives to +//! Add `derive_for_type` for each specific type with a comma separated list of the derives to //! apply for that type only. //! //! ```ignore @@ -95,6 +95,17 @@ //! ``` //! //! By default the documentation is not generated. +//! +//! ### Runtime types generation +//! +//! In some cases, you may be interested only in the runtime types, like `RuntimeCall` enum. You can +//! limit code generation to just `runtime_types` module with `runtime_types_only` flag: +//! +//! ```ignore +//! #[subxt::subxt(runtime_types_only)] +//! // or equivalently +//! #[subxt::subxt(runtime_types_only = true)] +//! ``` #![deny(unused_crate_dependencies)] @@ -136,6 +147,8 @@ struct RuntimeMetadataArgs { crate_path: Option, #[darling(default)] generate_docs: darling::util::Flag, + #[darling(default)] + runtime_types_only: bool, } #[derive(Debug, FromMeta)] @@ -207,6 +220,7 @@ pub fn subxt(args: TokenStream, input: TokenStream) -> TokenStream { type_substitutes, crate_path, should_gen_docs, + args.runtime_types_only, ) .map_or_else(|err| err.into_compile_error().into(), Into::into) } @@ -221,6 +235,7 @@ pub fn subxt(args: TokenStream, input: TokenStream) -> TokenStream { type_substitutes, crate_path, should_gen_docs, + args.runtime_types_only, ) .map_or_else(|err| err.into_compile_error().into(), Into::into) }