Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate only runtime types #845

Merged
merged 13 commits into from
Mar 21, 2023
6 changes: 6 additions & 0 deletions cli/src/commands/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down Expand Up @@ -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(())
}
Expand All @@ -90,6 +94,7 @@ fn codegen(
derives_for_type: Vec<(String, String)>,
crate_path: Option<String>,
no_docs: bool,
runtime_types_only: bool,
) -> color_eyre::Result<()> {
let item_mod = syn::parse_quote!(
pub mod api {}
Expand Down Expand Up @@ -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}"),
Expand Down
83 changes: 76 additions & 7 deletions codegen/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<P>(
Expand All @@ -130,6 +131,7 @@ pub fn generate_runtime_api_from_path<P>(
type_substitutes: TypeSubstitutes,
crate_path: CratePath,
should_gen_docs: bool,
runtime_types_only: bool,
) -> Result<TokenStream2, CodegenError>
where
P: AsRef<path::Path>,
Expand All @@ -147,6 +149,7 @@ where
type_substitutes,
crate_path,
should_gen_docs,
runtime_types_only,
)
}

Expand All @@ -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(
Expand All @@ -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<TokenStream2, CodegenError> {
let bytes = fetch_metadata_bytes_blocking(url)?;

Expand All @@ -181,6 +186,7 @@ pub fn generate_runtime_api_from_url(
type_substitutes,
crate_path,
should_gen_docs,
runtime_types_only,
)
}

Expand All @@ -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(
Expand All @@ -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<TokenStream2, CodegenError> {
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.
Expand All @@ -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<TokenStream2, CodegenError> {
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,
Expand Down
66 changes: 66 additions & 0 deletions examples/examples/runtime_types_only.rs
Original file line number Diff line number Diff line change
@@ -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<
<PolkadotConfig as subxt::Config>::Address,
RuntimeCall,
<PolkadotConfig as subxt::Config>::Signature,
// Usually we are not interested in `SignedExtra`.
(),
>;

type Header = generic::Header<u32, BlakeTwo256>;
type Block = generic::Block<Header, UncheckedExtrinsic>;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
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::<PolkadotConfig>::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(())
}
19 changes: 17 additions & 2 deletions macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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
Expand Down Expand Up @@ -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)]

Expand Down Expand Up @@ -136,6 +147,8 @@ struct RuntimeMetadataArgs {
crate_path: Option<String>,
#[darling(default)]
generate_docs: darling::util::Flag,
#[darling(default)]
runtime_types_only: bool,
}

#[derive(Debug, FromMeta)]
Expand Down Expand Up @@ -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)
}
Expand All @@ -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)
}
Expand Down