Generate only runtime types (#845)

* Typos

* Add new macro parameter. Refactor.

* Code generation

* Inline `runtime_types` module

* Documentation

* Can't inline so easily

* Example

* Remove leftover

* Improve clarity of the example
This commit is contained in:
Piotr Mikołajczyk
2023-03-21 15:44:14 +01:00
committed by GitHub
parent 29a4a48dcc
commit c9527abaa8
4 changed files with 165 additions and 9 deletions
+6
View File
@@ -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<String>,
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}"),
+76 -7
View File
@@ -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>(
@@ -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>,
@@ -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<TokenStream2, CodegenError> {
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<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.
@@ -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,
+66
View File
@@ -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(())
}
+17 -2
View File
@@ -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<String>,
#[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)
}