mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-22 19:38:00 +00:00
Use scale-typegen as a backend for the codegen (#1260)
* integrate scale-typegen, remove types mod * reintroduce default substitutes and derives * support runtime_types only again * generating polkadot.rs ok * update scale-typegen to discrete error types * scale-typegen-api-changes * add note about UncheckedExtrinsic in default substitutes * add resursive attributes and derives * adjust example where Clone bound recursive * move scale-typegen dependency to workspace * expose default typegen settings * lightclient: Fix wasm socket closure called after being dropped (#1289) * lightclient: Close wasm socket while dropping from connecting state Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * lightclient: Construct one time only closures Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * testing: Enable console logs for lightclient WASM testing Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * lightclient: Separate wakes and check connectivity on poll_read Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * lightclient: Close the socket depending on internal state Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Revert "lightclient: Separate wakes and check connectivity on poll_read" This reverts commit 866094001d4c0b119a80ed681a74b323f74eae1b. * lightclient: Return pending if socket is opening from poll_read Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * lightclient: Close the socket on `poll_close` Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * lightclient: Reset closures on Drop to avoid recursive invokation Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * lightclient: Close the socket if not already closing Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> --------- Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * workflows: Install rustup component for building substrate (#1295) Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cli: Command to fetch chainSpec and optimise its size (#1278) * cli: Add chainSpec command Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cli/chainSpec: Move to dedicated module Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cli: Compute the state root hash Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cli: Remove code substitutes Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * artifacts: Update polkadot.json Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * scripts: Generate the chain spec Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cli: Remove testing artifacts Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cli: Fix clippy Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cli: Apply rustfmt Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cli: Introduce feature flag for smoldot dependency Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cli: Rename chain-spec to chain-spec-pruning Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * scripts: Update chain-spec command Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> --------- Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * remove comments and unused args * Update substrate- and signer-related dependencies (#1297) * update crypto dependencies, adjust keypair * add scale_info::TypeInfo derive in some places * add multi signature derive * fix lock file * fix lock file again :| * adjust to new interface in scale-typegen * use released scale typegen * reintroduce type aliases * introduce type aliases again using scale-typegen * cargo fmt and clippy * reconcile changes with master branch * update polkadot.rs * bump scale-typgen to fix substitution * implemented Alex suggestions, regenerated polkadot.rs (did not change) * resolve conflicts in Cargo.lock * make expect messages more clear * correct typos --------- Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> Co-authored-by: Alexandru Vasile <60601340+lexnv@users.noreply.github.com>
This commit is contained in:
+31
-45
@@ -3,10 +3,10 @@
|
||||
// see LICENSE for license details.
|
||||
|
||||
use super::CodegenError;
|
||||
use crate::types::{CompositeDefFields, TypeGenerator};
|
||||
use heck::{ToSnakeCase as _, ToUpperCamelCase as _};
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote};
|
||||
use scale_typegen::{typegen::ir::type_ir::CompositeIRKind, TypeGenerator};
|
||||
use subxt_metadata::PalletMetadata;
|
||||
|
||||
/// Generate calls from the provided pallet's metadata. Each call returns a `StaticTxPayload`
|
||||
@@ -14,81 +14,69 @@ use subxt_metadata::PalletMetadata;
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `metadata` - Runtime metadata from which the calls are generated.
|
||||
/// - `type_gen` - The type generator containing all types defined by metadata.
|
||||
/// - `type_gen` - [`scale_typegen::TypeGenerator`] that contains settings and all types from the runtime metadata.
|
||||
/// - `pallet` - Pallet metadata from which the calls are generated.
|
||||
/// - `types_mod_ident` - The ident of the base module that we can use to access the generated types from.
|
||||
/// - `crate_path` - The crate path under which subxt is located, e.g. `::subxt` when using subxt as a dependency.
|
||||
pub fn generate_calls(
|
||||
type_gen: &TypeGenerator,
|
||||
pallet: &PalletMetadata,
|
||||
types_mod_ident: &syn::Ident,
|
||||
crate_path: &syn::Path,
|
||||
should_gen_docs: bool,
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
// Early return if the pallet has no calls.
|
||||
let Some(call_ty) = pallet.call_ty_id() else {
|
||||
return Ok(quote!());
|
||||
};
|
||||
|
||||
let mut struct_defs = super::generate_structs_from_variants(
|
||||
let variant_names_and_struct_defs = super::generate_structs_from_variants(
|
||||
type_gen,
|
||||
types_mod_ident,
|
||||
call_ty,
|
||||
|name| name.to_upper_camel_case().into(),
|
||||
"Call",
|
||||
crate_path,
|
||||
should_gen_docs,
|
||||
)?;
|
||||
|
||||
let result = struct_defs
|
||||
.iter_mut()
|
||||
.map(|(variant_name, struct_def, aliases)| {
|
||||
let fn_name = format_ident!("{}", variant_name.to_snake_case());
|
||||
|
||||
let result: Vec<_> = match struct_def.fields {
|
||||
CompositeDefFields::Named(ref named_fields) => named_fields
|
||||
let (call_structs, call_fns): (Vec<_>, Vec<_>) = variant_names_and_struct_defs
|
||||
.into_iter()
|
||||
.map(|var| {
|
||||
let (call_fn_args, call_args): (Vec<_>, Vec<_>) = match &var.composite.kind {
|
||||
CompositeIRKind::Named(named_fields) => named_fields
|
||||
.iter()
|
||||
.map(|(name, field)| {
|
||||
let call_arg = if field.is_boxed() {
|
||||
// Note: fn_arg_type this is relative the type path of the type alias when prefixed with `types::`, e.g. `set_max_code_size::New`
|
||||
let fn_arg_type = &field.type_path;
|
||||
let call_arg = if field.is_boxed {
|
||||
quote! { #name: ::std::boxed::Box::new(#name) }
|
||||
} else {
|
||||
quote! { #name }
|
||||
};
|
||||
|
||||
let alias_name =
|
||||
format_ident!("{}", name.to_string().to_upper_camel_case());
|
||||
|
||||
(quote!( #name: types::#fn_name::#alias_name ), call_arg)
|
||||
(quote!( #name: types::#fn_arg_type ), call_arg)
|
||||
})
|
||||
.collect(),
|
||||
CompositeDefFields::NoFields => Default::default(),
|
||||
CompositeDefFields::Unnamed(_) => {
|
||||
.unzip(),
|
||||
CompositeIRKind::NoFields => Default::default(),
|
||||
CompositeIRKind::Unnamed(_) => {
|
||||
return Err(CodegenError::InvalidCallVariant(call_ty))
|
||||
}
|
||||
};
|
||||
|
||||
let call_fn_args = result.iter().map(|(call_fn_arg, _)| call_fn_arg);
|
||||
let call_args = result.iter().map(|(_, call_arg)| call_arg);
|
||||
|
||||
let pallet_name = pallet.name();
|
||||
let call_name = &variant_name;
|
||||
let struct_name = &struct_def.name;
|
||||
let call_name = &var.variant_name;
|
||||
let struct_name = &var.composite.name;
|
||||
let Some(call_hash) = pallet.call_hash(call_name) else {
|
||||
return Err(CodegenError::MissingCallMetadata(
|
||||
pallet_name.into(),
|
||||
call_name.to_string(),
|
||||
));
|
||||
};
|
||||
|
||||
let fn_name = format_ident!("{}", var.variant_name.to_snake_case());
|
||||
// Propagate the documentation just to `TransactionApi` methods, while
|
||||
// draining the documentation of inner call structures.
|
||||
let docs = should_gen_docs.then_some(struct_def.docs.take()).flatten();
|
||||
let docs = &var.composite.docs;
|
||||
|
||||
// this converts the composite into a full struct type. No Type Parameters needed here.
|
||||
let struct_def = type_gen.upcast_composite(&var.composite);
|
||||
let alias_mod = var.type_alias_mod;
|
||||
// The call structure's documentation was stripped above.
|
||||
let call_struct = quote! {
|
||||
#struct_def
|
||||
|
||||
#aliases
|
||||
#alias_mod
|
||||
|
||||
impl #crate_path::blocks::StaticExtrinsic for #struct_name {
|
||||
const PALLET: &'static str = #pallet_name;
|
||||
@@ -113,17 +101,15 @@ pub fn generate_calls(
|
||||
|
||||
Ok((call_struct, client_fn))
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
.collect::<Result<Vec<_>, _>>()?
|
||||
.into_iter()
|
||||
.unzip();
|
||||
|
||||
let call_structs = result.iter().map(|(call_struct, _)| call_struct);
|
||||
let call_fns = result.iter().map(|(_, client_fn)| client_fn);
|
||||
let call_type = type_gen.resolve_type_path(call_ty)?;
|
||||
let call_ty = type_gen.resolve_type(call_ty)?;
|
||||
let docs = type_gen.docs_from_scale_info(&call_ty.docs);
|
||||
|
||||
let call_type = type_gen.resolve_type_path(call_ty);
|
||||
let call_ty = type_gen.resolve_type(call_ty);
|
||||
let docs = &call_ty.docs;
|
||||
let docs = should_gen_docs
|
||||
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||
.unwrap_or_default();
|
||||
let types_mod_ident = type_gen.types_mod_ident();
|
||||
|
||||
Ok(quote! {
|
||||
#docs
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use crate::types::TypeGenerator;
|
||||
use heck::ToSnakeCase as _;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote};
|
||||
use scale_typegen::TypeGenerator;
|
||||
use subxt_metadata::PalletMetadata;
|
||||
|
||||
use super::CodegenError;
|
||||
@@ -29,16 +29,13 @@ use super::CodegenError;
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `metadata` - Runtime metadata from which the calls are generated.
|
||||
/// - `type_gen` - The type generator containing all types defined by metadata
|
||||
/// - `pallet` - Pallet metadata from which the calls are generated.
|
||||
/// - `types_mod_ident` - The ident of the base module that we can use to access the generated types from.
|
||||
/// - `type_gen` - [`scale_typegen::TypeGenerator`] that contains settings and all types from the runtime metadata.
|
||||
/// - `pallet` - Pallet metadata from which the constants are generated.
|
||||
/// - `crate_path` - The crate path under which subxt is located, e.g. `::subxt` when using subxt as a dependency.
|
||||
pub fn generate_constants(
|
||||
type_gen: &TypeGenerator,
|
||||
pallet: &PalletMetadata,
|
||||
types_mod_ident: &syn::Ident,
|
||||
crate_path: &syn::Path,
|
||||
should_gen_docs: bool,
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
// Early return if the pallet has no constants.
|
||||
if pallet.constants().len() == 0 {
|
||||
@@ -58,9 +55,11 @@ pub fn generate_constants(
|
||||
));
|
||||
};
|
||||
|
||||
let return_ty = type_gen.resolve_type_path(constant.ty());
|
||||
let return_ty = type_gen.resolve_type_path(constant.ty())?;
|
||||
let docs = constant.docs();
|
||||
let docs = should_gen_docs
|
||||
let docs = type_gen
|
||||
.settings()
|
||||
.should_gen_docs
|
||||
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||
.unwrap_or_default();
|
||||
|
||||
@@ -77,6 +76,8 @@ pub fn generate_constants(
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let types_mod_ident = type_gen.types_mod_ident();
|
||||
|
||||
Ok(quote! {
|
||||
pub mod constants {
|
||||
use super::#types_mod_ident;
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::types::TypeGenerator;
|
||||
use heck::ToSnakeCase as _;
|
||||
use scale_typegen::TypeGenerator;
|
||||
use std::collections::HashSet;
|
||||
use subxt_metadata::{CustomValueMetadata, Metadata};
|
||||
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
@@ -60,6 +59,7 @@ fn generate_custom_value_fn(
|
||||
let (return_ty, decodable) = if type_is_valid {
|
||||
let return_ty = type_gen
|
||||
.resolve_type_path(custom_value.type_id())
|
||||
.expect("type is in metadata; qed")
|
||||
.to_token_stream();
|
||||
let decodable = quote!(#crate_path::custom_values::Yes);
|
||||
(return_ty, decodable)
|
||||
|
||||
@@ -4,26 +4,26 @@
|
||||
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
use scale_typegen::TypeGenerator;
|
||||
use subxt_metadata::PalletMetadata;
|
||||
|
||||
use crate::types::TypeGenerator;
|
||||
|
||||
use super::CodegenError;
|
||||
|
||||
/// Generate error type alias from the provided pallet metadata.
|
||||
pub fn generate_error_type_alias(
|
||||
type_gen: &TypeGenerator,
|
||||
pallet: &PalletMetadata,
|
||||
should_gen_docs: bool,
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
let Some(error_ty) = pallet.error_ty_id() else {
|
||||
return Ok(quote!());
|
||||
};
|
||||
|
||||
let error_type = type_gen.resolve_type_path(error_ty);
|
||||
let error_ty = type_gen.resolve_type(error_ty);
|
||||
let error_type = type_gen.resolve_type_path(error_ty)?;
|
||||
let error_ty = type_gen.resolve_type(error_ty)?;
|
||||
let docs = &error_ty.docs;
|
||||
let docs = should_gen_docs
|
||||
let docs = type_gen
|
||||
.settings()
|
||||
.should_gen_docs
|
||||
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||
.unwrap_or_default();
|
||||
Ok(quote! {
|
||||
|
||||
+26
-33
@@ -2,9 +2,9 @@
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use crate::types::TypeGenerator;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
use scale_typegen::TypeGenerator;
|
||||
use subxt_metadata::PalletMetadata;
|
||||
|
||||
use super::CodegenError;
|
||||
@@ -35,55 +35,48 @@ use super::CodegenError;
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `type_gen` - The type generator containing all types defined by metadata.
|
||||
/// - `type_gen` - [`scale_typegen::TypeGenerator`] that contains settings and all types from the runtime metadata.
|
||||
/// - `pallet` - Pallet metadata from which the events are generated.
|
||||
/// - `types_mod_ident` - The ident of the base module that we can use to access the generated types from.
|
||||
/// - `crate_path` - The crate path under which subxt is located, e.g. `::subxt` when using subxt as a dependency.
|
||||
pub fn generate_events(
|
||||
type_gen: &TypeGenerator,
|
||||
pallet: &PalletMetadata,
|
||||
types_mod_ident: &syn::Ident,
|
||||
crate_path: &syn::Path,
|
||||
should_gen_docs: bool,
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
// Early return if the pallet has no events.
|
||||
let Some(event_ty) = pallet.event_ty_id() else {
|
||||
return Ok(quote!());
|
||||
};
|
||||
|
||||
let struct_defs = super::generate_structs_from_variants(
|
||||
type_gen,
|
||||
types_mod_ident,
|
||||
event_ty,
|
||||
|name| name.into(),
|
||||
"Event",
|
||||
crate_path,
|
||||
should_gen_docs,
|
||||
)?;
|
||||
let variant_names_and_struct_defs =
|
||||
super::generate_structs_from_variants(type_gen, event_ty, |name| name.into(), "Event")?;
|
||||
|
||||
let event_structs = struct_defs
|
||||
.iter()
|
||||
.map(|(variant_name, struct_def, aliases)| {
|
||||
let pallet_name = pallet.name();
|
||||
let event_struct = &struct_def.name;
|
||||
let event_name = variant_name;
|
||||
let event_structs = variant_names_and_struct_defs.into_iter().map(|var| {
|
||||
let pallet_name = pallet.name();
|
||||
let event_struct_name = &var.composite.name;
|
||||
let event_name = var.variant_name;
|
||||
let alias_mod = var.type_alias_mod;
|
||||
let struct_def = type_gen.upcast_composite(&var.composite);
|
||||
quote! {
|
||||
#struct_def
|
||||
#alias_mod
|
||||
|
||||
quote! {
|
||||
#struct_def
|
||||
|
||||
#aliases
|
||||
|
||||
impl #crate_path::events::StaticEvent for #event_struct {
|
||||
const PALLET: &'static str = #pallet_name;
|
||||
const EVENT: &'static str = #event_name;
|
||||
}
|
||||
impl #crate_path::events::StaticEvent for #event_struct_name {
|
||||
const PALLET: &'static str = #pallet_name;
|
||||
const EVENT: &'static str = #event_name;
|
||||
}
|
||||
});
|
||||
let event_type = type_gen.resolve_type_path(event_ty);
|
||||
let event_ty = type_gen.resolve_type(event_ty);
|
||||
}
|
||||
});
|
||||
|
||||
let event_type = type_gen.resolve_type_path(event_ty)?;
|
||||
let event_ty = type_gen.resolve_type(event_ty)?;
|
||||
let docs = &event_ty.docs;
|
||||
let docs = should_gen_docs
|
||||
let docs = type_gen
|
||||
.settings()
|
||||
.should_gen_docs
|
||||
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||
.unwrap_or_default();
|
||||
let types_mod_ident = type_gen.types_mod_ident();
|
||||
|
||||
Ok(quote! {
|
||||
#docs
|
||||
|
||||
+111
-140
@@ -12,20 +12,20 @@ mod events;
|
||||
mod runtime_apis;
|
||||
mod storage;
|
||||
|
||||
use scale_typegen::typegen::ir::type_ir::{CompositeFieldIR, CompositeIR, CompositeIRKind};
|
||||
use scale_typegen::typegen::type_params::TypeParameters;
|
||||
use scale_typegen::typegen::type_path::TypePath;
|
||||
use scale_typegen::TypeGenerator;
|
||||
use subxt_metadata::Metadata;
|
||||
use syn::{parse_quote, Ident};
|
||||
|
||||
use crate::api::custom_values::generate_custom_values;
|
||||
use crate::error::CodegenError;
|
||||
use crate::types::DerivesRegistry;
|
||||
use crate::{
|
||||
ir,
|
||||
types::{CompositeDef, CompositeDefFields, TypeGenerator, TypeSubstitutes},
|
||||
};
|
||||
use crate::subxt_type_gen_settings;
|
||||
use crate::{api::custom_values::generate_custom_values, ir};
|
||||
|
||||
use heck::ToSnakeCase as _;
|
||||
use heck::{ToSnakeCase as _, ToUpperCamelCase};
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::parse_quote;
|
||||
|
||||
/// Create the API for interacting with a Substrate runtime.
|
||||
pub struct RuntimeGenerator {
|
||||
@@ -60,27 +60,22 @@ impl RuntimeGenerator {
|
||||
pub fn generate_runtime_types(
|
||||
&self,
|
||||
item_mod: syn::ItemMod,
|
||||
derives: DerivesRegistry,
|
||||
type_substitutes: TypeSubstitutes,
|
||||
derives: scale_typegen::DerivesRegistry,
|
||||
type_substitutes: scale_typegen::TypeSubstitutes,
|
||||
crate_path: syn::Path,
|
||||
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 settings =
|
||||
subxt_type_gen_settings(derives, type_substitutes, &crate_path, should_gen_docs);
|
||||
|
||||
let type_gen = TypeGenerator::new(self.metadata.types(), &settings);
|
||||
let types_mod = type_gen.generate_types_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)]
|
||||
@@ -114,24 +109,20 @@ impl RuntimeGenerator {
|
||||
pub fn generate_runtime(
|
||||
&self,
|
||||
item_mod: syn::ItemMod,
|
||||
derives: DerivesRegistry,
|
||||
type_substitutes: TypeSubstitutes,
|
||||
derives: scale_typegen::DerivesRegistry,
|
||||
type_substitutes: scale_typegen::TypeSubstitutes,
|
||||
crate_path: syn::Path,
|
||||
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 type_gen = TypeGenerator::new(
|
||||
self.metadata.types(),
|
||||
"runtime_types",
|
||||
type_substitutes,
|
||||
derives,
|
||||
crate_path.clone(),
|
||||
should_gen_docs,
|
||||
);
|
||||
let settings =
|
||||
subxt_type_gen_settings(derives, type_substitutes, &crate_path, should_gen_docs);
|
||||
|
||||
let type_gen = TypeGenerator::new(self.metadata.types(), &settings);
|
||||
let types_mod = type_gen.generate_types_mod()?;
|
||||
let types_mod_ident = types_mod.ident();
|
||||
let types_mod_ident = type_gen.types_mod_ident();
|
||||
let pallets_with_mod_names = self
|
||||
.metadata
|
||||
.pallets()
|
||||
@@ -165,39 +156,15 @@ impl RuntimeGenerator {
|
||||
let modules = pallets_with_mod_names
|
||||
.iter()
|
||||
.map(|(pallet, mod_name)| {
|
||||
let calls = calls::generate_calls(
|
||||
&type_gen,
|
||||
pallet,
|
||||
types_mod_ident,
|
||||
&crate_path,
|
||||
should_gen_docs,
|
||||
)?;
|
||||
let calls = calls::generate_calls(&type_gen, pallet, &crate_path)?;
|
||||
|
||||
let event = events::generate_events(
|
||||
&type_gen,
|
||||
pallet,
|
||||
types_mod_ident,
|
||||
&crate_path,
|
||||
should_gen_docs,
|
||||
)?;
|
||||
let event = events::generate_events(&type_gen, pallet, &crate_path)?;
|
||||
|
||||
let storage_mod = storage::generate_storage(
|
||||
&type_gen,
|
||||
pallet,
|
||||
types_mod_ident,
|
||||
&crate_path,
|
||||
should_gen_docs,
|
||||
)?;
|
||||
let storage_mod = storage::generate_storage(&type_gen, pallet, &crate_path)?;
|
||||
|
||||
let constants_mod = constants::generate_constants(
|
||||
&type_gen,
|
||||
pallet,
|
||||
types_mod_ident,
|
||||
&crate_path,
|
||||
should_gen_docs,
|
||||
)?;
|
||||
let constants_mod = constants::generate_constants(&type_gen, pallet, &crate_path)?;
|
||||
|
||||
let errors = errors::generate_error_type_alias(&type_gen, pallet, should_gen_docs)?;
|
||||
let errors = errors::generate_error_type_alias(&type_gen, pallet)?;
|
||||
|
||||
Ok(quote! {
|
||||
pub mod #mod_name {
|
||||
@@ -242,14 +209,14 @@ impl RuntimeGenerator {
|
||||
&type_gen,
|
||||
types_mod_ident,
|
||||
&crate_path,
|
||||
should_gen_docs,
|
||||
)?;
|
||||
|
||||
// Fetch the paths of the outer enums.
|
||||
// Substrate exposes those under `kitchensink_runtime`, while Polkadot under `polkadot_runtime`.
|
||||
let call_path = type_gen.resolve_type_path(self.metadata.outer_enums().call_enum_ty());
|
||||
let event_path = type_gen.resolve_type_path(self.metadata.outer_enums().event_enum_ty());
|
||||
let error_path = type_gen.resolve_type_path(self.metadata.outer_enums().error_enum_ty());
|
||||
|
||||
let call_path = type_gen.resolve_type_path(self.metadata.outer_enums().call_enum_ty())?;
|
||||
let event_path = type_gen.resolve_type_path(self.metadata.outer_enums().event_enum_ty())?;
|
||||
let error_path = type_gen.resolve_type_path(self.metadata.outer_enums().error_enum_ty())?;
|
||||
|
||||
let custom_values = generate_custom_values(&self.metadata, &type_gen, &crate_path);
|
||||
|
||||
@@ -358,17 +325,14 @@ impl RuntimeGenerator {
|
||||
/// Return a vector of tuples of variant names and corresponding struct definitions.
|
||||
pub fn generate_structs_from_variants<F>(
|
||||
type_gen: &TypeGenerator,
|
||||
types_mod_ident: &syn::Ident,
|
||||
type_id: u32,
|
||||
variant_to_struct_name: F,
|
||||
error_message_type_name: &str,
|
||||
crate_path: &syn::Path,
|
||||
should_gen_docs: bool,
|
||||
) -> Result<Vec<(String, CompositeDef, TypeAliases)>, CodegenError>
|
||||
) -> Result<Vec<StructFromVariant>, CodegenError>
|
||||
where
|
||||
F: Fn(&str) -> std::borrow::Cow<str>,
|
||||
{
|
||||
let ty = type_gen.resolve_type(type_id);
|
||||
let ty = type_gen.resolve_type(type_id)?;
|
||||
|
||||
let scale_info::TypeDef::Variant(variant) = &ty.type_def else {
|
||||
return Err(CodegenError::InvalidType(error_message_type_name.into()));
|
||||
@@ -378,84 +342,91 @@ where
|
||||
.variants
|
||||
.iter()
|
||||
.map(|var| {
|
||||
let mut type_params = TypeParameters::from_scale_info(&[]);
|
||||
let composite_ir_kind =
|
||||
type_gen.create_composite_ir_kind(&var.fields, &mut type_params)?;
|
||||
let struct_name = variant_to_struct_name(&var.name);
|
||||
let mut composite = CompositeIR::new(
|
||||
syn::parse_str(&struct_name).expect("enum variant is a valid ident; qed"),
|
||||
composite_ir_kind,
|
||||
type_gen.docs_from_scale_info(&var.docs),
|
||||
);
|
||||
|
||||
let fields = CompositeDefFields::from_scale_info_fields(
|
||||
struct_name.as_ref(),
|
||||
&var.fields,
|
||||
&[],
|
||||
type_gen,
|
||||
)?;
|
||||
|
||||
let alias_module_name = format_ident!("{}", var.name.to_snake_case());
|
||||
|
||||
let docs = should_gen_docs.then_some(&*var.docs).unwrap_or_default();
|
||||
let struct_def = CompositeDef::struct_def(
|
||||
&ty,
|
||||
struct_name.as_ref(),
|
||||
Default::default(),
|
||||
fields.clone(),
|
||||
Some(parse_quote!(pub)),
|
||||
type_gen,
|
||||
docs,
|
||||
crate_path,
|
||||
Some(alias_module_name.clone()),
|
||||
)?;
|
||||
|
||||
let type_aliases = TypeAliases::new(fields, types_mod_ident.clone(), alias_module_name);
|
||||
|
||||
Ok((var.name.to_string(), struct_def, type_aliases))
|
||||
let type_alias_mod = generate_type_alias_mod(&mut composite, type_gen);
|
||||
Ok(StructFromVariant {
|
||||
variant_name: var.name.to_string(),
|
||||
composite,
|
||||
type_alias_mod,
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Generate the type aliases from a set of enum / struct definitions.
|
||||
pub struct StructFromVariant {
|
||||
variant_name: String,
|
||||
composite: CompositeIR,
|
||||
type_alias_mod: TokenStream2,
|
||||
}
|
||||
|
||||
/// Modifies the composite, by replacing its types with references to the generated type alias module.
|
||||
/// Returns the TokenStream of the type alias module.
|
||||
///
|
||||
/// The type aliases are used to make the generated code more readable.
|
||||
#[derive(Debug)]
|
||||
pub struct TypeAliases {
|
||||
fields: CompositeDefFields,
|
||||
types_mod_ident: syn::Ident,
|
||||
mod_name: syn::Ident,
|
||||
}
|
||||
/// E.g a struct like this:
|
||||
/// ```ignore
|
||||
/// pub struct SetMaxCodeSize {
|
||||
/// pub new: ::core::primitive::u32,
|
||||
/// }
|
||||
/// ```
|
||||
/// will be made into this:
|
||||
/// ```ignore
|
||||
/// pub struct SetMaxCodeSize {
|
||||
/// pub new: set_max_code_size::New,
|
||||
/// }
|
||||
/// ```
|
||||
/// And the type alias module will look like this:
|
||||
/// ```ignore
|
||||
/// pub mod set_max_code_size {
|
||||
/// use super::runtime_types;
|
||||
/// pub type New = ::core::primitive::u32;
|
||||
/// }
|
||||
/// ```
|
||||
pub fn generate_type_alias_mod(
|
||||
composite: &mut CompositeIR,
|
||||
type_gen: &TypeGenerator,
|
||||
) -> TokenStream2 {
|
||||
let mut aliases: Vec<TokenStream2> = vec![];
|
||||
let alias_mod_name: Ident = syn::parse_str(&composite.name.to_string().to_snake_case())
|
||||
.expect("composite name in snake_case should be a valid identifier");
|
||||
|
||||
impl TypeAliases {
|
||||
pub fn new(
|
||||
fields: CompositeDefFields,
|
||||
types_mod_ident: syn::Ident,
|
||||
mod_name: syn::Ident,
|
||||
) -> Self {
|
||||
TypeAliases {
|
||||
fields,
|
||||
types_mod_ident,
|
||||
mod_name,
|
||||
let mut modify_field_to_be_type_alias = |field: &mut CompositeFieldIR, alias_name: Ident| {
|
||||
let type_path = &field.type_path;
|
||||
aliases.push(quote!(pub type #alias_name = #type_path;));
|
||||
|
||||
let type_alias_path: syn::Path = parse_quote!(#alias_mod_name::#alias_name);
|
||||
field.type_path = TypePath::from_syn_path(type_alias_path);
|
||||
};
|
||||
|
||||
match &mut composite.kind {
|
||||
CompositeIRKind::NoFields => {
|
||||
return quote!(); // no types mod generated for unit structs.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl quote::ToTokens for TypeAliases {
|
||||
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||
let has_fields = matches!(&self.fields, CompositeDefFields::Named(fields) if !fields.is_empty())
|
||||
|| matches!(&self.fields, CompositeDefFields::Unnamed(fields) if !fields.is_empty());
|
||||
if !has_fields {
|
||||
return;
|
||||
}
|
||||
|
||||
let visibility: syn::Visibility = parse_quote!(pub);
|
||||
|
||||
let aliases = self
|
||||
.fields
|
||||
.to_type_aliases_tokens(Some(visibility).as_ref());
|
||||
|
||||
let mod_name = &self.mod_name;
|
||||
let types_mod_ident = &self.types_mod_ident;
|
||||
|
||||
tokens.extend(quote! {
|
||||
pub mod #mod_name {
|
||||
use super::#types_mod_ident;
|
||||
|
||||
#aliases
|
||||
CompositeIRKind::Named(named) => {
|
||||
for (name, field) in named.iter_mut() {
|
||||
let alias_name = format_ident!("{}", name.to_string().to_upper_camel_case());
|
||||
modify_field_to_be_type_alias(field, alias_name);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
CompositeIRKind::Unnamed(unnamed) => {
|
||||
for (i, field) in unnamed.iter_mut().enumerate() {
|
||||
let alias_name = format_ident!("Field{}", i);
|
||||
modify_field_to_be_type_alias(field, alias_name);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let types_mod_ident = type_gen.types_mod_ident();
|
||||
quote!(pub mod #alias_mod_name {
|
||||
use super::#types_mod_ident;
|
||||
#( #aliases )*
|
||||
})
|
||||
}
|
||||
|
||||
+107
-95
@@ -4,140 +4,155 @@
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::{types::TypeGenerator, CodegenError};
|
||||
use heck::ToSnakeCase as _;
|
||||
use heck::ToUpperCamelCase as _;
|
||||
|
||||
use scale_typegen::TypeGenerator;
|
||||
use subxt_metadata::{Metadata, RuntimeApiMetadata};
|
||||
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
use crate::CodegenError;
|
||||
|
||||
/// Generates runtime functions for the given API metadata.
|
||||
fn generate_runtime_api(
|
||||
api: RuntimeApiMetadata,
|
||||
type_gen: &TypeGenerator,
|
||||
types_mod_ident: &syn::Ident,
|
||||
crate_path: &syn::Path,
|
||||
should_gen_docs: bool,
|
||||
) -> Result<(TokenStream2, TokenStream2), CodegenError> {
|
||||
// Trait name must remain as is (upper case) to identity the runtime call.
|
||||
let trait_name_str = api.name();
|
||||
// The snake case for the trait name.
|
||||
let trait_name_snake = format_ident!("{}", api.name().to_snake_case());
|
||||
let docs = api.docs();
|
||||
let docs: TokenStream2 = should_gen_docs
|
||||
let docs: TokenStream2 = type_gen
|
||||
.settings()
|
||||
.should_gen_docs
|
||||
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||
.unwrap_or_default();
|
||||
|
||||
let structs_and_methods: Vec<_> = api.methods().map(|method| {
|
||||
let method_name = format_ident!("{}", method.name());
|
||||
let method_name_str = method.name();
|
||||
let structs_and_methods: Vec<_> = api
|
||||
.methods()
|
||||
.map(|method| {
|
||||
let method_name = format_ident!("{}", method.name());
|
||||
let method_name_str = method.name();
|
||||
|
||||
let docs = method.docs();
|
||||
let docs: TokenStream2 = should_gen_docs
|
||||
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||
.unwrap_or_default();
|
||||
let docs = method.docs();
|
||||
let docs: TokenStream2 = type_gen
|
||||
.settings()
|
||||
.should_gen_docs
|
||||
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||
.unwrap_or_default();
|
||||
|
||||
let mut unique_names = HashSet::new();
|
||||
let mut unique_aliases = HashSet::new();
|
||||
let mut unique_names = HashSet::new();
|
||||
let mut unique_aliases = HashSet::new();
|
||||
|
||||
let inputs: Vec<_> = method.inputs().enumerate().map(|(idx, input)| {
|
||||
// These are method names, which can just be '_', but struct field names can't
|
||||
// just be an underscore, so fix any such names we find to work in structs.
|
||||
let inputs: Vec<_> = method
|
||||
.inputs()
|
||||
.enumerate()
|
||||
.map(|(idx, input)| {
|
||||
// These are method names, which can just be '_', but struct field names can't
|
||||
// just be an underscore, so fix any such names we find to work in structs.
|
||||
let mut name = input.name.trim_start_matches('_').to_string();
|
||||
if name.is_empty() {
|
||||
name = format!("_{}", idx);
|
||||
}
|
||||
while !unique_names.insert(name.clone()) {
|
||||
// Name is already used, append the index until it is unique.
|
||||
name = format!("{}_param{}", name, idx);
|
||||
}
|
||||
|
||||
let mut name = input.name.trim_start_matches('_').to_string();
|
||||
if name.is_empty() {
|
||||
name = format!("_{}", idx);
|
||||
}
|
||||
while !unique_names.insert(name.clone()) {
|
||||
// Name is already used, append the index until it is unique.
|
||||
name = format!("{}_param{}", name, idx);
|
||||
}
|
||||
let mut alias = name.to_upper_camel_case();
|
||||
// Note: name is not empty.
|
||||
if alias.as_bytes()[0].is_ascii_digit() {
|
||||
alias = format!("Param{}", alias);
|
||||
}
|
||||
while !unique_aliases.insert(alias.clone()) {
|
||||
alias = format!("{}Param{}", alias, idx);
|
||||
}
|
||||
|
||||
let mut alias = name.to_upper_camel_case();
|
||||
// Note: name is not empty.
|
||||
if alias.as_bytes()[0].is_ascii_digit() {
|
||||
alias = format!("Param{}", alias);
|
||||
}
|
||||
while !unique_aliases.insert(alias.clone()) {
|
||||
alias = format!("{}Param{}", alias, idx);
|
||||
}
|
||||
let (alias_name, name) = (format_ident!("{alias}"), format_ident!("{name}"));
|
||||
|
||||
let (alias_name, name) = (format_ident!("{alias}"), format_ident!("{name}"));
|
||||
// Generate alias for runtime type.
|
||||
let ty = type_gen
|
||||
.resolve_type_path(input.ty)
|
||||
.expect("runtime api input type is in metadata; qed");
|
||||
let aliased_param = quote!( pub type #alias_name = #ty; );
|
||||
|
||||
// Generate alias for runtime type.
|
||||
let ty = type_gen.resolve_type_path(input.ty);
|
||||
let aliased_param = quote!( pub type #alias_name = #ty; );
|
||||
// Structures are placed on the same level as the alias module.
|
||||
let struct_ty_path = quote!( #method_name::#alias_name );
|
||||
let struct_param = quote!(#name: #struct_ty_path);
|
||||
|
||||
// Structures are placed on the same level as the alias module.
|
||||
let struct_ty_path = quote!( #method_name::#alias_name );
|
||||
let struct_param = quote!(#name: #struct_ty_path);
|
||||
// Function parameters must be indented by `types`.
|
||||
let fn_param = quote!(#name: types::#struct_ty_path);
|
||||
(fn_param, struct_param, name, aliased_param)
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Function parameters must be indented by `types`.
|
||||
let fn_param = quote!(#name: types::#struct_ty_path);
|
||||
(fn_param, struct_param, name, aliased_param)
|
||||
}).collect();
|
||||
let fn_params = inputs.iter().map(|(fn_param, _, _, _)| fn_param);
|
||||
let struct_params = inputs.iter().map(|(_, struct_param, _, _)| struct_param);
|
||||
let param_names = inputs.iter().map(|(_, _, name, _)| name);
|
||||
let type_aliases = inputs.iter().map(|(_, _, _, aliased_param)| aliased_param);
|
||||
let types_mod_ident = type_gen.types_mod_ident();
|
||||
|
||||
let fn_params = inputs.iter().map(|(fn_param, _, _, _)| fn_param);
|
||||
let struct_params = inputs.iter().map(|(_, struct_param, _, _)| struct_param);
|
||||
let param_names = inputs.iter().map(|(_, _, name, _,)| name);
|
||||
let type_aliases = inputs.iter().map(|(_, _, _, aliased_param)| aliased_param);
|
||||
|
||||
let output = type_gen.resolve_type_path(method.output_ty());
|
||||
let aliased_module = quote!(
|
||||
pub mod #method_name {
|
||||
use super::#types_mod_ident;
|
||||
|
||||
#( #type_aliases )*
|
||||
|
||||
// Guard the `Output` name against collisions by placing it in a dedicated module.
|
||||
pub mod output {
|
||||
let output = type_gen.resolve_type_path(method.output_ty())?;
|
||||
let aliased_module = quote!(
|
||||
pub mod #method_name {
|
||||
use super::#types_mod_ident;
|
||||
pub type Output = #output;
|
||||
|
||||
#( #type_aliases )*
|
||||
|
||||
// Guard the `Output` name against collisions by placing it in a dedicated module.
|
||||
pub mod output {
|
||||
use super::#types_mod_ident;
|
||||
pub type Output = #output;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
// From the method metadata generate a structure that holds
|
||||
// all parameter types. This structure is used with metadata
|
||||
// to encode parameters to the call via `encode_as_fields_to`.
|
||||
let derives = type_gen.default_derives();
|
||||
let struct_name = format_ident!("{}", method.name().to_upper_camel_case());
|
||||
let struct_input = quote!(
|
||||
#aliased_module
|
||||
// From the method metadata generate a structure that holds
|
||||
// all parameter types. This structure is used with metadata
|
||||
// to encode parameters to the call via `encode_as_fields_to`.
|
||||
let derives = type_gen.settings().derives.default_derives();
|
||||
let struct_name = format_ident!("{}", method.name().to_upper_camel_case());
|
||||
let struct_input = quote!(
|
||||
#aliased_module
|
||||
|
||||
#derives
|
||||
pub struct #struct_name {
|
||||
#( pub #struct_params, )*
|
||||
}
|
||||
);
|
||||
#derives
|
||||
pub struct #struct_name {
|
||||
#( pub #struct_params, )*
|
||||
}
|
||||
);
|
||||
|
||||
let Some(call_hash) = api.method_hash(method.name()) else {
|
||||
return Err(CodegenError::MissingRuntimeApiMetadata(
|
||||
trait_name_str.to_owned(),
|
||||
method_name_str.to_owned(),
|
||||
))
|
||||
};
|
||||
let Some(call_hash) = api.method_hash(method.name()) else {
|
||||
return Err(CodegenError::MissingRuntimeApiMetadata(
|
||||
trait_name_str.to_owned(),
|
||||
method_name_str.to_owned(),
|
||||
))
|
||||
};
|
||||
|
||||
let method = quote!(
|
||||
#docs
|
||||
pub fn #method_name(&self, #( #fn_params, )* ) -> #crate_path::runtime_api::Payload<types::#struct_name, types::#method_name::output::Output> {
|
||||
#crate_path::runtime_api::Payload::new_static(
|
||||
#trait_name_str,
|
||||
#method_name_str,
|
||||
types::#struct_name { #( #param_names, )* },
|
||||
[#(#call_hash,)*],
|
||||
)
|
||||
}
|
||||
);
|
||||
let method = quote!(
|
||||
#docs
|
||||
pub fn #method_name(&self, #( #fn_params, )* ) -> #crate_path::runtime_api::Payload<types::#struct_name, types::#method_name::output::Output> {
|
||||
#crate_path::runtime_api::Payload::new_static(
|
||||
#trait_name_str,
|
||||
#method_name_str,
|
||||
types::#struct_name { #( #param_names, )* },
|
||||
[#(#call_hash,)*],
|
||||
)
|
||||
}
|
||||
);
|
||||
|
||||
Ok((struct_input, method))
|
||||
}).collect::<Result<_, _>>()?;
|
||||
Ok((struct_input, method))
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
let trait_name = format_ident!("{}", trait_name_str);
|
||||
|
||||
let structs = structs_and_methods.iter().map(|(struct_, _)| struct_);
|
||||
let methods = structs_and_methods.iter().map(|(_, method)| method);
|
||||
let types_mod_ident = type_gen.types_mod_ident();
|
||||
|
||||
let runtime_api = quote!(
|
||||
pub mod #trait_name_snake {
|
||||
@@ -175,13 +190,10 @@ pub fn generate_runtime_apis(
|
||||
type_gen: &TypeGenerator,
|
||||
types_mod_ident: &syn::Ident,
|
||||
crate_path: &syn::Path,
|
||||
should_gen_docs: bool,
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
let runtime_fns: Vec<_> = metadata
|
||||
.runtime_api_traits()
|
||||
.map(|api| {
|
||||
generate_runtime_api(api, type_gen, types_mod_ident, crate_path, should_gen_docs)
|
||||
})
|
||||
.map(|api| generate_runtime_api(api, type_gen, crate_path))
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
let runtime_apis_def = runtime_fns.iter().map(|(apis, _)| apis);
|
||||
|
||||
+28
-28
@@ -2,13 +2,11 @@
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use crate::types::TypeGenerator;
|
||||
use crate::types::TypePath;
|
||||
use heck::ToSnakeCase as _;
|
||||
use heck::ToUpperCamelCase as _;
|
||||
use heck::{ToSnakeCase as _, ToUpperCamelCase};
|
||||
use proc_macro2::{Ident, TokenStream as TokenStream2, TokenStream};
|
||||
use quote::{format_ident, quote};
|
||||
use scale_info::TypeDef;
|
||||
use scale_typegen::{typegen::type_path::TypePath, TypeGenerator};
|
||||
use subxt_metadata::{
|
||||
PalletMetadata, StorageEntryMetadata, StorageEntryModifier, StorageEntryType,
|
||||
};
|
||||
@@ -20,37 +18,26 @@ use super::CodegenError;
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `metadata` - Runtime metadata from which the storages are generated.
|
||||
/// - `type_gen` - The type generator containing all types defined by metadata.
|
||||
/// - `pallet` - Pallet metadata from which the storages are generated.
|
||||
/// - `types_mod_ident` - The ident of the base module that we can use to access the generated types from.
|
||||
/// - `type_gen` - [`scale_typegen::TypeGenerator`] that contains settings and all types from the runtime metadata.
|
||||
/// - `pallet` - Pallet metadata from which the storage items are generated.
|
||||
/// - `crate_path` - The crate path under which subxt is located, e.g. `::subxt` when using subxt as a dependency.
|
||||
pub fn generate_storage(
|
||||
type_gen: &TypeGenerator,
|
||||
pallet: &PalletMetadata,
|
||||
types_mod_ident: &syn::Ident,
|
||||
crate_path: &syn::Path,
|
||||
should_gen_docs: bool,
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
let Some(storage) = pallet.storage() else {
|
||||
return Ok(quote!());
|
||||
};
|
||||
|
||||
let (storage_fns, alias_modules): (Vec<_>, Vec<_>) = storage
|
||||
let (storage_fns, alias_modules): (Vec<TokenStream2>, Vec<TokenStream2>) = storage
|
||||
.entries()
|
||||
.iter()
|
||||
.map(|entry| {
|
||||
generate_storage_entry_fns(
|
||||
type_gen,
|
||||
pallet,
|
||||
entry,
|
||||
crate_path,
|
||||
should_gen_docs,
|
||||
types_mod_ident,
|
||||
)
|
||||
})
|
||||
.map(|entry| generate_storage_entry_fns(type_gen, pallet, entry, crate_path))
|
||||
.collect::<Result<Vec<_>, CodegenError>>()?
|
||||
.into_iter()
|
||||
.unzip();
|
||||
let types_mod_ident = type_gen.types_mod_ident();
|
||||
|
||||
Ok(quote! {
|
||||
pub mod storage {
|
||||
@@ -71,17 +58,18 @@ pub fn generate_storage(
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns storage entry functions and alias modules.
|
||||
fn generate_storage_entry_fns(
|
||||
type_gen: &TypeGenerator,
|
||||
pallet: &PalletMetadata,
|
||||
storage_entry: &StorageEntryMetadata,
|
||||
crate_path: &syn::Path,
|
||||
should_gen_docs: bool,
|
||||
types_mod_ident: &syn::Ident,
|
||||
) -> Result<(TokenStream2, TokenStream2), CodegenError> {
|
||||
let snake_case_name = storage_entry.name().to_snake_case();
|
||||
let storage_entry_ty = storage_entry.entry_type().value_ty();
|
||||
let storage_entry_value_ty = type_gen.resolve_type_path(storage_entry_ty);
|
||||
let storage_entry_value_ty = type_gen
|
||||
.resolve_type_path(storage_entry_ty)
|
||||
.expect("storage type is in metadata; qed");
|
||||
|
||||
let alias_name = format_ident!("{}", storage_entry.name().to_upper_camel_case());
|
||||
let alias_module_name = format_ident!("{snake_case_name}");
|
||||
@@ -89,7 +77,9 @@ fn generate_storage_entry_fns(
|
||||
|
||||
let storage_entry_map = |idx, id| {
|
||||
let ident: Ident = format_ident!("_{}", idx);
|
||||
let ty_path = type_gen.resolve_type_path(id);
|
||||
let ty_path = type_gen
|
||||
.resolve_type_path(id)
|
||||
.expect("type is in metadata; qed");
|
||||
|
||||
let alias_name = format_ident!("Param{}", idx);
|
||||
let alias_type = primitive_type_alias(&ty_path);
|
||||
@@ -103,7 +93,11 @@ fn generate_storage_entry_fns(
|
||||
let keys: Vec<(Ident, TokenStream, TokenStream)> = match storage_entry.entry_type() {
|
||||
StorageEntryType::Plain(_) => vec![],
|
||||
StorageEntryType::Map { key_ty, .. } => {
|
||||
match &type_gen.resolve_type(*key_ty).type_def {
|
||||
match &type_gen
|
||||
.resolve_type(*key_ty)
|
||||
.expect("key type should be present")
|
||||
.type_def
|
||||
{
|
||||
// An N-map; return each of the keys separately.
|
||||
TypeDef::Tuple(tuple) => tuple
|
||||
.fields
|
||||
@@ -128,7 +122,9 @@ fn generate_storage_entry_fns(
|
||||
};
|
||||
|
||||
let docs = storage_entry.docs();
|
||||
let docs = should_gen_docs
|
||||
let docs = type_gen
|
||||
.settings()
|
||||
.should_gen_docs
|
||||
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||
.unwrap_or_default();
|
||||
|
||||
@@ -181,6 +177,7 @@ fn generate_storage_entry_fns(
|
||||
|
||||
let alias_types = keys.iter().map(|(_, alias_type, _)| alias_type);
|
||||
|
||||
let types_mod_ident = type_gen.types_mod_ident();
|
||||
// Generate type alias for the return type only, since
|
||||
// the keys of the storage entry are not explicitly named.
|
||||
let alias_module = quote! {
|
||||
@@ -217,9 +214,10 @@ fn primitive_type_alias(type_path: &TypePath) -> TokenStream {
|
||||
mod tests {
|
||||
use crate::RuntimeGenerator;
|
||||
use frame_metadata::v15;
|
||||
use heck::ToUpperCamelCase as _;
|
||||
use heck::ToUpperCamelCase;
|
||||
use quote::{format_ident, quote};
|
||||
use scale_info::{meta_type, MetaType};
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
||||
use subxt_metadata::Metadata;
|
||||
@@ -330,6 +328,8 @@ mod tests {
|
||||
_0: impl ::std::borrow::Borrow<types::#name_ident::Param0>,
|
||||
)
|
||||
);
|
||||
dbg!(&generated_str);
|
||||
dbg!(&expected_storage_constructor.to_string());
|
||||
assert!(generated_str.contains(&expected_storage_constructor.to_string()));
|
||||
|
||||
let alias_name = format_ident!("{}", name.to_upper_camel_case());
|
||||
|
||||
Reference in New Issue
Block a user