fix: Resolve cargo clippy errors and add CI workflow plan
## Changes
### Clippy Fixes
- Fixed deprecated `cargo_bin` usage in 27 test files (added #![allow(deprecated)])
- Fixed uninlined_format_args in zombienet-sdk-tests
- Fixed subxt API changes in revive/rpc/tests.rs (fetch signature, StorageValue)
- Fixed dead_code warnings in validator-pool and identity-kyc mocks
- Fixed field name `i` -> `_i` in tasks example
### CI Infrastructure
- Added .claude/WORKFLOW_PLAN.md for tracking CI fix progress
- Updated lychee.toml and taplo.toml configs
### Files Modified
- 27 test files with deprecated cargo_bin fix
- bizinikiwi/pezframe/revive/rpc/src/tests.rs (subxt API)
- pezkuwi/pezpallets/validator-pool/src/{mock,tests}.rs
- pezcumulus/teyrchains/pezpallets/identity-kyc/src/mock.rs
- bizinikiwi/pezframe/examples/tasks/src/tests.rs
## Status
- cargo clippy: PASSING
- Next: cargo fmt, zepter, workspace checks
This commit is contained in:
-141
@@ -1,141 +0,0 @@
|
||||
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use super::CodegenError;
|
||||
use heck::{ToSnakeCase as _, ToUpperCamelCase as _};
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote};
|
||||
use scale_typegen::typegen::ir::ToTokensWithSettings;
|
||||
use scale_typegen::{TypeGenerator, typegen::ir::type_ir::CompositeIRKind};
|
||||
use pezkuwi_subxt_metadata::PalletMetadata;
|
||||
|
||||
/// Generate calls from the provided pallet's metadata. Each call returns a `StaticPayload`
|
||||
/// that can be passed to the subxt client to submit/sign/encode.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `type_gen` - [`scale_typegen::TypeGenerator`] that contains settings and all types from the runtime metadata.
|
||||
/// - `pallet` - Pallet metadata from which the calls are generated.
|
||||
/// - `crate_path` - The crate path under which the `subxt-core` crate is located, e.g. `::pezkuwi_subxt::ext::pezkuwi_subxt_core` when using subxt as a dependency.
|
||||
pub fn generate_calls(
|
||||
type_gen: &TypeGenerator,
|
||||
pallet: &PalletMetadata,
|
||||
crate_path: &syn::Path,
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
// Early return if the pallet has no calls.
|
||||
let Some(call_ty) = pallet.call_ty_id() else {
|
||||
return Ok(quote!());
|
||||
};
|
||||
|
||||
let variant_names_and_struct_defs = super::generate_structs_from_variants(
|
||||
type_gen,
|
||||
call_ty,
|
||||
|name| name.to_upper_camel_case().into(),
|
||||
"Call",
|
||||
)?;
|
||||
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)| {
|
||||
// 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.to_token_stream(type_gen.settings());
|
||||
let call_arg = if field.is_boxed {
|
||||
quote! { #name: #crate_path::alloc::boxed::Box::new(#name) }
|
||||
} else {
|
||||
quote! { #name }
|
||||
};
|
||||
(quote!( #name: types::#fn_arg_type ), call_arg)
|
||||
})
|
||||
.unzip(),
|
||||
CompositeIRKind::NoFields => Default::default(),
|
||||
CompositeIRKind::Unnamed(_) => {
|
||||
return Err(CodegenError::InvalidCallVariant(call_ty));
|
||||
}
|
||||
};
|
||||
|
||||
let pallet_name = pallet.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 = &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)
|
||||
.to_token_stream(type_gen.settings());
|
||||
let alias_mod = var.type_alias_mod;
|
||||
// The call structure's documentation was stripped above.
|
||||
let call_struct = quote! {
|
||||
#struct_def
|
||||
#alias_mod
|
||||
|
||||
impl #crate_path::blocks::StaticExtrinsic for #struct_name {
|
||||
const PALLET: &'static str = #pallet_name;
|
||||
const CALL: &'static str = #call_name;
|
||||
}
|
||||
};
|
||||
|
||||
let client_fn = quote! {
|
||||
#docs
|
||||
pub fn #fn_name(
|
||||
&self,
|
||||
#( #call_fn_args, )*
|
||||
) -> #crate_path::tx::payload::StaticPayload<types::#struct_name> {
|
||||
#crate_path::tx::payload::StaticPayload::new_static(
|
||||
#pallet_name,
|
||||
#call_name,
|
||||
types::#struct_name { #( #call_args, )* },
|
||||
[#(#call_hash,)*]
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
Ok((call_struct, client_fn))
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?
|
||||
.into_iter()
|
||||
.unzip();
|
||||
|
||||
let call_type = type_gen
|
||||
.resolve_type_path(call_ty)?
|
||||
.to_token_stream(type_gen.settings());
|
||||
let call_ty = type_gen.resolve_type(call_ty)?;
|
||||
let docs = type_gen.docs_from_scale_info(&call_ty.docs);
|
||||
|
||||
let types_mod_ident = type_gen.types_mod_ident();
|
||||
|
||||
Ok(quote! {
|
||||
#docs
|
||||
pub type Call = #call_type;
|
||||
pub mod calls {
|
||||
use super::root_mod;
|
||||
use super::#types_mod_ident;
|
||||
|
||||
type DispatchError = ::pezsp_runtime::DispatchError;
|
||||
|
||||
pub mod types {
|
||||
use super::#types_mod_ident;
|
||||
|
||||
#( #call_structs )*
|
||||
}
|
||||
|
||||
pub struct TransactionApi;
|
||||
|
||||
impl TransactionApi {
|
||||
#( #call_fns )*
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use heck::ToSnakeCase as _;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote};
|
||||
use scale_typegen::TypeGenerator;
|
||||
use scale_typegen::typegen::ir::ToTokensWithSettings;
|
||||
use pezkuwi_subxt_metadata::PalletMetadata;
|
||||
|
||||
use super::CodegenError;
|
||||
|
||||
/// Generate constants from the provided pallet's metadata.
|
||||
///
|
||||
/// The function creates a new module named `constants` under the pallet's module.
|
||||
/// ```rust,ignore
|
||||
/// pub mod PalletName {
|
||||
/// pub mod constants {
|
||||
/// ...
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The constants are exposed via the `ConstantsApi` wrapper.
|
||||
///
|
||||
/// Although the constants are defined in the provided static metadata, the API
|
||||
/// ensures that the constants are returned from the runtime metadata of the node.
|
||||
/// This ensures that if the node's constants change value, we'll always see the latest values.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `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 the `subxt-core` crate is located, e.g. `::pezkuwi_subxt::ext::pezkuwi_subxt_core` when using subxt as a dependency.
|
||||
pub fn generate_constants(
|
||||
type_gen: &TypeGenerator,
|
||||
pallet: &PalletMetadata,
|
||||
crate_path: &syn::Path,
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
// Early return if the pallet has no constants.
|
||||
if pallet.constants().len() == 0 {
|
||||
return Ok(quote!());
|
||||
}
|
||||
|
||||
let constant_fns = pallet
|
||||
.constants()
|
||||
.map(|constant| {
|
||||
let fn_name = format_ident!("{}", constant.name().to_snake_case());
|
||||
let pallet_name = pallet.name();
|
||||
let constant_name = constant.name();
|
||||
let Some(constant_hash) = pallet.constant_hash(constant_name) else {
|
||||
return Err(CodegenError::MissingConstantMetadata(
|
||||
constant_name.into(),
|
||||
pallet_name.into(),
|
||||
));
|
||||
};
|
||||
|
||||
let return_ty = type_gen
|
||||
.resolve_type_path(constant.ty())?
|
||||
.to_token_stream(type_gen.settings());
|
||||
let docs = constant.docs();
|
||||
let docs = type_gen
|
||||
.settings()
|
||||
.should_gen_docs
|
||||
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||
.unwrap_or_default();
|
||||
|
||||
Ok(quote! {
|
||||
#docs
|
||||
pub fn #fn_name(&self) -> #crate_path::constants::address::StaticAddress<#return_ty> {
|
||||
#crate_path::constants::address::StaticAddress::new_static(
|
||||
#pallet_name,
|
||||
#constant_name,
|
||||
[#(#constant_hash,)*]
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
let types_mod_ident = type_gen.types_mod_ident();
|
||||
|
||||
Ok(quote! {
|
||||
pub mod constants {
|
||||
use super::#types_mod_ident;
|
||||
|
||||
pub struct ConstantsApi;
|
||||
|
||||
impl ConstantsApi {
|
||||
#(#constant_fns)*
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -1,78 +0,0 @@
|
||||
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use heck::ToSnakeCase as _;
|
||||
use scale_typegen::TypeGenerator;
|
||||
use scale_typegen::typegen::ir::ToTokensWithSettings;
|
||||
use std::collections::HashSet;
|
||||
use pezkuwi_subxt_metadata::{CustomValueMetadata, Metadata};
|
||||
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
|
||||
/// Generate the custom values mod, if there are any custom values in the metadata. Else returns None.
|
||||
pub fn generate_custom_values(
|
||||
metadata: &Metadata,
|
||||
type_gen: &TypeGenerator,
|
||||
crate_path: &syn::Path,
|
||||
) -> TokenStream2 {
|
||||
let mut fn_names_taken = HashSet::new();
|
||||
let custom = metadata.custom();
|
||||
let custom_values_fns = custom.iter().filter_map(|custom_value| {
|
||||
generate_custom_value_fn(custom_value, type_gen, crate_path, &mut fn_names_taken)
|
||||
});
|
||||
|
||||
quote! {
|
||||
pub struct CustomValuesApi;
|
||||
|
||||
impl CustomValuesApi {
|
||||
#(#custom_values_fns)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates runtime functions for the given API metadata.
|
||||
/// Returns None, if the name would not make for a valid identifier.
|
||||
fn generate_custom_value_fn(
|
||||
custom_value: CustomValueMetadata,
|
||||
type_gen: &TypeGenerator,
|
||||
crate_path: &syn::Path,
|
||||
fn_names_taken: &mut HashSet<String>,
|
||||
) -> Option<TokenStream2> {
|
||||
// names are transformed to snake case to make for good function identifiers.
|
||||
let name = custom_value.name();
|
||||
let fn_name = name.to_snake_case();
|
||||
if fn_names_taken.contains(&fn_name) {
|
||||
return None;
|
||||
}
|
||||
// if the fn_name would be an invalid ident, return None:
|
||||
let fn_name_ident = syn::parse_str::<syn::Ident>(&fn_name).ok()?;
|
||||
fn_names_taken.insert(fn_name);
|
||||
|
||||
let custom_value_hash = custom_value.hash();
|
||||
|
||||
// for custom values it is important to check if the type id is actually in the metadata:
|
||||
let type_is_valid = custom_value
|
||||
.types()
|
||||
.resolve(custom_value.type_id())
|
||||
.is_some();
|
||||
|
||||
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(type_gen.settings());
|
||||
let decodable = quote!(#crate_path::utils::Maybe);
|
||||
(return_ty, decodable)
|
||||
} else {
|
||||
// if type registry does not contain the type, we can just return the Encoded scale bytes.
|
||||
(quote!(()), quote!(#crate_path::utils::No))
|
||||
};
|
||||
|
||||
Some(quote!(
|
||||
pub fn #fn_name_ident(&self) -> #crate_path::custom_values::address::StaticAddress<#return_ty, #decodable> {
|
||||
#crate_path::custom_values::address::StaticAddress::new_static(#name, [#(#custom_value_hash,)*])
|
||||
}
|
||||
))
|
||||
}
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
use scale_typegen::TypeGenerator;
|
||||
use pezkuwi_subxt_metadata::PalletMetadata;
|
||||
|
||||
use super::CodegenError;
|
||||
use scale_typegen::typegen::ir::ToTokensWithSettings;
|
||||
|
||||
/// Generate error type alias from the provided pallet metadata.
|
||||
pub fn generate_error_type_alias(
|
||||
type_gen: &TypeGenerator,
|
||||
pallet: &PalletMetadata,
|
||||
) -> 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)?
|
||||
.to_token_stream(type_gen.settings());
|
||||
let error_ty = type_gen.resolve_type(error_ty)?;
|
||||
let docs = &error_ty.docs;
|
||||
let docs = type_gen
|
||||
.settings()
|
||||
.should_gen_docs
|
||||
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||
.unwrap_or_default();
|
||||
Ok(quote! {
|
||||
#docs
|
||||
pub type Error = #error_type;
|
||||
})
|
||||
}
|
||||
-93
@@ -1,93 +0,0 @@
|
||||
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use super::CodegenError;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
use scale_typegen::TypeGenerator;
|
||||
use scale_typegen::typegen::ir::ToTokensWithSettings;
|
||||
use pezkuwi_subxt_metadata::PalletMetadata;
|
||||
|
||||
/// Generate events from the provided pallet metadata.
|
||||
///
|
||||
/// The function creates a new module named `events` under the pallet's module.
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// pub mod PalletName {
|
||||
/// pub mod events {
|
||||
/// ...
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The function generates the events as rust structs that implement the `subxt::event::StaticEvent` trait
|
||||
/// to uniquely identify the event's identity when creating the extrinsic.
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// pub struct EventName {
|
||||
/// pub event_param: type,
|
||||
/// }
|
||||
/// impl ::pezkuwi_subxt::events::StaticEvent for EventName {
|
||||
/// ...
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `type_gen` - [`scale_typegen::TypeGenerator`] that contains settings and all types from the runtime metadata.
|
||||
/// - `pallet` - Pallet metadata from which the events are generated.
|
||||
/// - `crate_path` - The crate path under which the `subxt-core` crate is located, e.g. `::pezkuwi_subxt::ext::pezkuwi_subxt_core` when using subxt as a dependency.
|
||||
pub fn generate_events(
|
||||
type_gen: &TypeGenerator,
|
||||
pallet: &PalletMetadata,
|
||||
crate_path: &syn::Path,
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
// Early return if the pallet has no events.
|
||||
let Some(event_ty) = pallet.event_ty_id() else {
|
||||
return Ok(quote!());
|
||||
};
|
||||
|
||||
let variant_names_and_struct_defs =
|
||||
super::generate_structs_from_variants(type_gen, event_ty, |name| name.into(), "Event")?;
|
||||
|
||||
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)
|
||||
.to_token_stream(type_gen.settings());
|
||||
quote! {
|
||||
#struct_def
|
||||
#alias_mod
|
||||
|
||||
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)?
|
||||
.to_token_stream(type_gen.settings());
|
||||
let event_ty = type_gen.resolve_type(event_ty)?;
|
||||
let docs = &event_ty.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
|
||||
pub type Event = #event_type;
|
||||
pub mod events {
|
||||
use super::#types_mod_ident;
|
||||
#( #event_structs )*
|
||||
}
|
||||
})
|
||||
}
|
||||
-475
@@ -1,475 +0,0 @@
|
||||
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
//! Generate code for submitting extrinsics and query storage of a Substrate runtime.
|
||||
|
||||
mod calls;
|
||||
mod constants;
|
||||
mod custom_values;
|
||||
mod errors;
|
||||
mod events;
|
||||
mod pallet_view_functions;
|
||||
mod runtime_apis;
|
||||
mod storage;
|
||||
|
||||
use scale_typegen::TypeGenerator;
|
||||
use scale_typegen::typegen::ir::ToTokensWithSettings;
|
||||
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 pezkuwi_subxt_metadata::Metadata;
|
||||
use syn::{Ident, parse_quote};
|
||||
|
||||
use crate::error::CodegenError;
|
||||
use crate::subxt_type_gen_settings;
|
||||
use crate::{api::custom_values::generate_custom_values, ir};
|
||||
|
||||
use heck::{ToSnakeCase as _, ToUpperCamelCase};
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
/// Create the API for interacting with a Substrate runtime.
|
||||
pub struct RuntimeGenerator {
|
||||
metadata: Metadata,
|
||||
}
|
||||
|
||||
impl RuntimeGenerator {
|
||||
/// Create a new runtime generator from the provided metadata.
|
||||
///
|
||||
/// **Note:** If you have the metadata path, URL or bytes to hand, prefer to use
|
||||
/// `GenerateRuntimeApi` for generating the runtime API from that.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the runtime metadata version is not supported.
|
||||
///
|
||||
/// Supported versions: v14 and v15.
|
||||
pub fn new(mut metadata: Metadata) -> Self {
|
||||
scale_typegen::utils::ensure_unique_type_paths(metadata.types_mut())
|
||||
.expect("Duplicate type paths in metadata; this is bug please file an issue.");
|
||||
RuntimeGenerator { metadata }
|
||||
}
|
||||
|
||||
/// 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_types(
|
||||
&self,
|
||||
item_mod: syn::ItemMod,
|
||||
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()?
|
||||
.to_token_stream(type_gen.settings());
|
||||
let mod_ident = &item_mod_ir.ident;
|
||||
let rust_items = item_mod_ir.rust_items();
|
||||
|
||||
Ok(quote! {
|
||||
#( #item_mod_attrs )*
|
||||
#[allow(dead_code, unused_imports, non_camel_case_types, unreachable_patterns)]
|
||||
#[allow(clippy::all)]
|
||||
#[allow(rustdoc::broken_intra_doc_links)]
|
||||
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 items via `root_mod` at different levels
|
||||
// without reaching out of this module.
|
||||
#[allow(unused_imports)]
|
||||
mod root_mod {
|
||||
pub use super::*;
|
||||
}
|
||||
|
||||
#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,
|
||||
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()?
|
||||
.to_token_stream(type_gen.settings());
|
||||
let types_mod_ident = type_gen.types_mod_ident();
|
||||
let pallets_with_mod_names = self
|
||||
.metadata
|
||||
.pallets()
|
||||
.map(|pallet| {
|
||||
(
|
||||
pallet,
|
||||
format_ident!("{}", pallet.name().to_string().to_snake_case()),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// Pallet names and their length are used to create PALLETS array.
|
||||
// The array is used to identify the pallets composing the metadata for
|
||||
// validation of just those pallets.
|
||||
let pallet_names: Vec<_> = self
|
||||
.metadata
|
||||
.pallets()
|
||||
.map(|pallet| pallet.name())
|
||||
.collect();
|
||||
let pallet_names_len = pallet_names.len();
|
||||
|
||||
let runtime_api_names: Vec<_> = self
|
||||
.metadata
|
||||
.runtime_api_traits()
|
||||
.map(|api| api.name().to_string())
|
||||
.collect();
|
||||
let runtime_api_names_len = runtime_api_names.len();
|
||||
|
||||
let modules = pallets_with_mod_names
|
||||
.iter()
|
||||
.map(|(pallet, mod_name)| {
|
||||
let calls = calls::generate_calls(&type_gen, pallet, &crate_path)?;
|
||||
|
||||
let event = events::generate_events(&type_gen, pallet, &crate_path)?;
|
||||
|
||||
let storage_mod = storage::generate_storage(&type_gen, pallet, &crate_path)?;
|
||||
|
||||
let constants_mod = constants::generate_constants(&type_gen, pallet, &crate_path)?;
|
||||
|
||||
let errors = errors::generate_error_type_alias(&type_gen, pallet)?;
|
||||
|
||||
let view_functions = pallet_view_functions::generate_pallet_view_functions(
|
||||
&type_gen,
|
||||
pallet,
|
||||
&crate_path,
|
||||
)?;
|
||||
|
||||
Ok(quote! {
|
||||
pub mod #mod_name {
|
||||
use super::root_mod;
|
||||
use super::#types_mod_ident;
|
||||
#errors
|
||||
#calls
|
||||
#view_functions
|
||||
#event
|
||||
#storage_mod
|
||||
#constants_mod
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>, CodegenError>>()?;
|
||||
|
||||
let mod_ident = &item_mod_ir.ident;
|
||||
let pallets_with_constants: Vec<_> = pallets_with_mod_names
|
||||
.iter()
|
||||
.filter_map(|(pallet, pallet_mod_name)| {
|
||||
pallet
|
||||
.constants()
|
||||
.next()
|
||||
.is_some()
|
||||
.then_some(pallet_mod_name)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let pallets_with_storage: Vec<_> = pallets_with_mod_names
|
||||
.iter()
|
||||
.filter_map(|(pallet, pallet_mod_name)| pallet.storage().map(|_| pallet_mod_name))
|
||||
.collect();
|
||||
|
||||
let pallets_with_calls: Vec<_> = pallets_with_mod_names
|
||||
.iter()
|
||||
.filter_map(|(pallet, pallet_mod_name)| pallet.call_ty_id().map(|_| pallet_mod_name))
|
||||
.collect();
|
||||
|
||||
let pallets_with_view_functions: Vec<_> = pallets_with_mod_names
|
||||
.iter()
|
||||
.filter(|(pallet, _pallet_mod_name)| pallet.has_view_functions())
|
||||
.map(|(_, pallet_mod_name)| pallet_mod_name)
|
||||
.collect();
|
||||
|
||||
let rust_items = item_mod_ir.rust_items();
|
||||
|
||||
let apis_mod = runtime_apis::generate_runtime_apis(
|
||||
&self.metadata,
|
||||
&type_gen,
|
||||
types_mod_ident,
|
||||
&crate_path,
|
||||
)?;
|
||||
|
||||
// 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())?
|
||||
.to_token_stream(type_gen.settings());
|
||||
let event_path = type_gen
|
||||
.resolve_type_path(self.metadata.outer_enums().event_enum_ty())?
|
||||
.to_token_stream(type_gen.settings());
|
||||
let error_path = type_gen
|
||||
.resolve_type_path(self.metadata.outer_enums().error_enum_ty())?
|
||||
.to_token_stream(type_gen.settings());
|
||||
|
||||
let metadata_hash = self.metadata.hasher().hash();
|
||||
|
||||
let custom_values = generate_custom_values(&self.metadata, &type_gen, &crate_path);
|
||||
|
||||
Ok(quote! {
|
||||
#( #item_mod_attrs )*
|
||||
#[allow(dead_code, unused_imports, non_camel_case_types, unreachable_patterns)]
|
||||
#[allow(clippy::all)]
|
||||
#[allow(rustdoc::broken_intra_doc_links)]
|
||||
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 items via `root_mod` at different levels
|
||||
// without reaching out of this module.
|
||||
#[allow(unused_imports)]
|
||||
mod root_mod {
|
||||
pub use super::*;
|
||||
}
|
||||
|
||||
// Identify the pallets composing the static metadata by name.
|
||||
pub static PALLETS: [&str; #pallet_names_len] = [ #(#pallet_names,)* ];
|
||||
|
||||
// Runtime APIs in the metadata by name.
|
||||
pub static RUNTIME_APIS: [&str; #runtime_api_names_len] = [ #(#runtime_api_names,)* ];
|
||||
|
||||
/// The error type that is returned when there is a runtime issue.
|
||||
pub type DispatchError = ::pezsp_runtime::DispatchError;
|
||||
|
||||
/// The outer event enum.
|
||||
pub type Event = #event_path;
|
||||
|
||||
/// The outer extrinsic enum.
|
||||
pub type Call = #call_path;
|
||||
|
||||
/// The outer error enum represents the DispatchError's Module variant.
|
||||
pub type Error = #error_path;
|
||||
|
||||
pub fn constants() -> ConstantsApi {
|
||||
ConstantsApi
|
||||
}
|
||||
|
||||
pub fn storage() -> StorageApi {
|
||||
StorageApi
|
||||
}
|
||||
|
||||
pub fn tx() -> TransactionApi {
|
||||
TransactionApi
|
||||
}
|
||||
|
||||
pub fn apis() -> runtime_apis::RuntimeApi {
|
||||
runtime_apis::RuntimeApi
|
||||
}
|
||||
|
||||
#apis_mod
|
||||
|
||||
pub fn view_functions() -> ViewFunctionsApi {
|
||||
ViewFunctionsApi
|
||||
}
|
||||
|
||||
pub fn custom() -> CustomValuesApi {
|
||||
CustomValuesApi
|
||||
}
|
||||
|
||||
#custom_values
|
||||
|
||||
pub struct ConstantsApi;
|
||||
impl ConstantsApi {
|
||||
#(
|
||||
pub fn #pallets_with_constants(&self) -> #pallets_with_constants::constants::ConstantsApi {
|
||||
#pallets_with_constants::constants::ConstantsApi
|
||||
}
|
||||
)*
|
||||
}
|
||||
|
||||
pub struct StorageApi;
|
||||
impl StorageApi {
|
||||
#(
|
||||
pub fn #pallets_with_storage(&self) -> #pallets_with_storage::storage::StorageApi {
|
||||
#pallets_with_storage::storage::StorageApi
|
||||
}
|
||||
)*
|
||||
}
|
||||
|
||||
pub struct TransactionApi;
|
||||
impl TransactionApi {
|
||||
#(
|
||||
pub fn #pallets_with_calls(&self) -> #pallets_with_calls::calls::TransactionApi {
|
||||
#pallets_with_calls::calls::TransactionApi
|
||||
}
|
||||
)*
|
||||
}
|
||||
|
||||
pub struct ViewFunctionsApi;
|
||||
impl ViewFunctionsApi {
|
||||
#(
|
||||
pub fn #pallets_with_view_functions(&self) -> #pallets_with_view_functions::view_functions::ViewFunctionsApi {
|
||||
#pallets_with_view_functions::view_functions::ViewFunctionsApi
|
||||
}
|
||||
)*
|
||||
}
|
||||
|
||||
/// check whether the metadata provided is aligned with this statically generated code.
|
||||
pub fn is_codegen_valid_for(metadata: &#crate_path::Metadata) -> bool {
|
||||
let runtime_metadata_hash = metadata
|
||||
.hasher()
|
||||
.only_these_pallets(&PALLETS)
|
||||
.only_these_runtime_apis(&RUNTIME_APIS)
|
||||
.hash();
|
||||
runtime_metadata_hash == [ #(#metadata_hash,)* ]
|
||||
}
|
||||
|
||||
#( #modules )*
|
||||
#types_mod
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a vector of tuples of variant names and corresponding struct definitions.
|
||||
pub fn generate_structs_from_variants<F>(
|
||||
type_gen: &TypeGenerator,
|
||||
type_id: u32,
|
||||
variant_to_struct_name: F,
|
||||
error_message_type_name: &str,
|
||||
) -> Result<Vec<StructFromVariant>, CodegenError>
|
||||
where
|
||||
F: Fn(&str) -> std::borrow::Cow<str>,
|
||||
{
|
||||
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()));
|
||||
};
|
||||
|
||||
variant
|
||||
.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 type_alias_mod = generate_type_alias_mod(&mut composite, type_gen);
|
||||
Ok(StructFromVariant {
|
||||
variant_name: var.name.to_string(),
|
||||
composite,
|
||||
type_alias_mod,
|
||||
})
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
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.
|
||||
///
|
||||
/// E.g a struct like this:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// pub struct SetMaxCodeSize {
|
||||
/// pub new: ::core::primitive::u32,
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// will be made into this:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// pub struct SetMaxCodeSize {
|
||||
/// pub new: set_max_code_size::New,
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// And the type alias module will look like this:
|
||||
///
|
||||
/// ```rust,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");
|
||||
|
||||
let mut modify_field_to_be_type_alias = |field: &mut CompositeFieldIR, alias_name: Ident| {
|
||||
let type_path = field.type_path.to_token_stream(type_gen.settings());
|
||||
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.
|
||||
}
|
||||
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 )*
|
||||
})
|
||||
}
|
||||
@@ -1,184 +0,0 @@
|
||||
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use heck::ToUpperCamelCase as _;
|
||||
|
||||
use crate::CodegenError;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote};
|
||||
use scale_typegen::TypeGenerator;
|
||||
use scale_typegen::typegen::ir::ToTokensWithSettings;
|
||||
use std::collections::HashSet;
|
||||
use pezkuwi_subxt_metadata::{PalletMetadata, ViewFunctionMetadata};
|
||||
|
||||
pub fn generate_pallet_view_functions(
|
||||
type_gen: &TypeGenerator,
|
||||
pallet: &PalletMetadata,
|
||||
crate_path: &syn::Path,
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
if !pallet.has_view_functions() {
|
||||
// If there are no view functions in this pallet, we
|
||||
// don't generate anything.
|
||||
return Ok(quote! {});
|
||||
}
|
||||
|
||||
let view_functions: Vec<_> = pallet
|
||||
.view_functions()
|
||||
.map(|vf| generate_pallet_view_function(pallet.name(), vf, type_gen, crate_path))
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
let view_functions_types = view_functions.iter().map(|(apis, _)| apis);
|
||||
let view_functions_methods = view_functions.iter().map(|(_, getters)| getters);
|
||||
|
||||
let types_mod_ident = type_gen.types_mod_ident();
|
||||
|
||||
Ok(quote! {
|
||||
pub mod view_functions {
|
||||
use super::root_mod;
|
||||
use super::#types_mod_ident;
|
||||
|
||||
pub struct ViewFunctionsApi;
|
||||
|
||||
impl ViewFunctionsApi {
|
||||
#( #view_functions_methods )*
|
||||
}
|
||||
|
||||
#( #view_functions_types )*
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn generate_pallet_view_function(
|
||||
pallet_name: &str,
|
||||
view_function: ViewFunctionMetadata<'_>,
|
||||
type_gen: &TypeGenerator,
|
||||
crate_path: &syn::Path,
|
||||
) -> Result<(TokenStream2, TokenStream2), CodegenError> {
|
||||
let types_mod_ident = type_gen.types_mod_ident();
|
||||
|
||||
let view_function_name_str = view_function.name();
|
||||
let view_function_name_ident = format_ident!("{view_function_name_str}");
|
||||
let validation_hash = view_function.hash();
|
||||
|
||||
let docs = view_function.docs();
|
||||
let docs: TokenStream2 = type_gen
|
||||
.settings()
|
||||
.should_gen_docs
|
||||
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||
.unwrap_or_default();
|
||||
|
||||
struct Input {
|
||||
name: syn::Ident,
|
||||
type_alias: syn::Ident,
|
||||
type_path: TokenStream2,
|
||||
}
|
||||
|
||||
let view_function_inputs: Vec<Input> = {
|
||||
let mut unique_names = HashSet::new();
|
||||
let mut unique_aliases = HashSet::new();
|
||||
|
||||
view_function
|
||||
.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 = format!("{name}_param{idx}");
|
||||
}
|
||||
|
||||
// The alias type name is based on the name, above.
|
||||
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!("{alias}Param{idx}");
|
||||
}
|
||||
|
||||
// Path to the actual type we'll have generated for this input.
|
||||
let type_path = type_gen
|
||||
.resolve_type_path(input.id)
|
||||
.expect("view function input type is in metadata; qed")
|
||||
.to_token_stream(type_gen.settings());
|
||||
|
||||
Input {
|
||||
name: format_ident!("{name}"),
|
||||
type_alias: format_ident!("{alias}"),
|
||||
type_path,
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
|
||||
let input_tuple_types = view_function_inputs
|
||||
.iter()
|
||||
.map(|i| {
|
||||
let ty = &i.type_alias;
|
||||
quote!(#view_function_name_ident::#ty)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let input_args = view_function_inputs
|
||||
.iter()
|
||||
.map(|i| {
|
||||
let arg = &i.name;
|
||||
let ty = &i.type_alias;
|
||||
quote!(#arg: #view_function_name_ident::#ty)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let input_type_aliases = view_function_inputs.iter().map(|i| {
|
||||
let ty = &i.type_alias;
|
||||
let path = &i.type_path;
|
||||
quote!(pub type #ty = #path;)
|
||||
});
|
||||
|
||||
let input_param_names = view_function_inputs.iter().map(|i| &i.name);
|
||||
|
||||
let output_type_path = type_gen
|
||||
.resolve_type_path(view_function.output_ty())?
|
||||
.to_token_stream(type_gen.settings());
|
||||
|
||||
// Define the input and output type bits.
|
||||
let view_function_types = quote!(
|
||||
pub mod #view_function_name_ident {
|
||||
use super::root_mod;
|
||||
use super::#types_mod_ident;
|
||||
|
||||
#(#input_type_aliases)*
|
||||
|
||||
pub mod output {
|
||||
use super::#types_mod_ident;
|
||||
pub type Output = #output_type_path;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Define the getter method that will live on the `ViewFunctionApi` type.
|
||||
let view_function_method = quote!(
|
||||
#docs
|
||||
pub fn #view_function_name_ident(
|
||||
&self,
|
||||
#(#input_args),*
|
||||
) -> #crate_path::view_functions::payload::StaticPayload<
|
||||
(#(#input_tuple_types,)*),
|
||||
#view_function_name_ident::output::Output
|
||||
> {
|
||||
#crate_path::view_functions::payload::StaticPayload::new_static(
|
||||
#pallet_name,
|
||||
#view_function_name_str,
|
||||
(#(#input_param_names,)*),
|
||||
[#(#validation_hash,)*],
|
||||
)
|
||||
}
|
||||
);
|
||||
|
||||
Ok((view_function_types, view_function_method))
|
||||
}
|
||||
@@ -1,433 +0,0 @@
|
||||
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use heck::ToSnakeCase as _;
|
||||
use heck::ToUpperCamelCase as _;
|
||||
|
||||
use scale_typegen::TypeGenerator;
|
||||
use scale_typegen::typegen::ir::ToTokensWithSettings;
|
||||
use pezkuwi_subxt_metadata::{Metadata, RuntimeApiMetadata};
|
||||
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote};
|
||||
|
||||
use crate::CodegenError;
|
||||
|
||||
/// Generate the runtime APIs.
|
||||
pub fn generate_runtime_apis(
|
||||
metadata: &Metadata,
|
||||
type_gen: &TypeGenerator,
|
||||
types_mod_ident: &syn::Ident,
|
||||
crate_path: &syn::Path,
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
let runtime_fns: Vec<_> = metadata
|
||||
.runtime_api_traits()
|
||||
.map(|api| generate_runtime_api(api, type_gen, crate_path))
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
let trait_defs = runtime_fns.iter().map(|(apis, _)| apis);
|
||||
let trait_getters = runtime_fns.iter().map(|(_, getters)| getters);
|
||||
|
||||
Ok(quote! {
|
||||
pub mod runtime_apis {
|
||||
use super::root_mod;
|
||||
use super::#types_mod_ident;
|
||||
|
||||
use #crate_path::ext::codec::Encode;
|
||||
|
||||
pub struct RuntimeApi;
|
||||
|
||||
impl RuntimeApi {
|
||||
#( #trait_getters )*
|
||||
}
|
||||
|
||||
#( #trait_defs )*
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Generates runtime functions for the given API metadata.
|
||||
fn generate_runtime_api(
|
||||
api: RuntimeApiMetadata,
|
||||
type_gen: &TypeGenerator,
|
||||
crate_path: &syn::Path,
|
||||
) -> Result<(TokenStream2, TokenStream2), CodegenError> {
|
||||
let types_mod_ident = type_gen.types_mod_ident();
|
||||
// Trait name must remain as is (upper case) to identify 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 = type_gen
|
||||
.settings()
|
||||
.should_gen_docs
|
||||
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||
.unwrap_or_default();
|
||||
|
||||
let types_and_methods = api
|
||||
.methods()
|
||||
.map(|method| {
|
||||
let method_name = format_ident!("{}", method.name());
|
||||
let method_name_str = method.name();
|
||||
let validation_hash = method.hash();
|
||||
|
||||
let docs = method.docs();
|
||||
let docs: TokenStream2 = type_gen
|
||||
.settings()
|
||||
.should_gen_docs
|
||||
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||
.unwrap_or_default();
|
||||
|
||||
struct Input {
|
||||
name: syn::Ident,
|
||||
type_alias: syn::Ident,
|
||||
type_path: TokenStream2,
|
||||
}
|
||||
|
||||
let runtime_api_inputs: Vec<Input> = {
|
||||
let mut unique_names = HashSet::new();
|
||||
let mut unique_aliases = HashSet::new();
|
||||
|
||||
method
|
||||
.inputs()
|
||||
.enumerate()
|
||||
.map(|(idx, input)| {
|
||||
// The method argument name is either the input name or the
|
||||
// index (eg _1, _2 etc) if one isn't provided.
|
||||
// if we get unlucky we'll end up with param_param1 etc.
|
||||
let mut name = input.name.trim_start_matches('_').to_string();
|
||||
if name.is_empty() {
|
||||
name = format!("_{idx}");
|
||||
}
|
||||
while !unique_names.insert(name.clone()) {
|
||||
name = format!("{name}_param{idx}");
|
||||
}
|
||||
|
||||
// The alias is either InputName if provided, or Param1, Param2 etc if not.
|
||||
// If we get unlucky we may even end up with ParamParam1 etc.
|
||||
let mut alias = name.trim_start_matches('_').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!("{alias}Param{idx}");
|
||||
}
|
||||
|
||||
// Generate alias for runtime type.
|
||||
let type_path = type_gen
|
||||
.resolve_type_path(input.id)
|
||||
.expect("runtime api input type is in metadata; qed")
|
||||
.to_token_stream(type_gen.settings());
|
||||
|
||||
Input {
|
||||
name: format_ident!("{name}"),
|
||||
type_alias: format_ident!("{alias}"),
|
||||
type_path,
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
};
|
||||
|
||||
let input_tuple_types = runtime_api_inputs
|
||||
.iter()
|
||||
.map(|i| {
|
||||
let ty = &i.type_alias;
|
||||
quote!(#method_name::#ty)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let input_args = runtime_api_inputs
|
||||
.iter()
|
||||
.map(|i| {
|
||||
let arg = &i.name;
|
||||
let ty = &i.type_alias;
|
||||
quote!(#arg: #method_name::#ty)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let input_param_names = runtime_api_inputs.iter().map(|i| &i.name);
|
||||
|
||||
let input_type_aliases = runtime_api_inputs.iter().map(|i| {
|
||||
let ty = &i.type_alias;
|
||||
let path = &i.type_path;
|
||||
quote!(pub type #ty = #path;)
|
||||
});
|
||||
|
||||
let output_type_path = type_gen
|
||||
.resolve_type_path(method.output_ty())?
|
||||
.to_token_stream(type_gen.settings());
|
||||
|
||||
// Define the input and output type bits for the method.
|
||||
let runtime_api_types = quote! {
|
||||
pub mod #method_name {
|
||||
use super::root_mod;
|
||||
use super::#types_mod_ident;
|
||||
|
||||
#(#input_type_aliases)*
|
||||
|
||||
pub mod output {
|
||||
use super::#types_mod_ident;
|
||||
pub type Output = #output_type_path;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Define the getter method that will live on the `ViewFunctionApi` type.
|
||||
let runtime_api_method = quote!(
|
||||
#docs
|
||||
pub fn #method_name(
|
||||
&self,
|
||||
#(#input_args),*
|
||||
) -> #crate_path::runtime_api::payload::StaticPayload<
|
||||
(#(#input_tuple_types,)*),
|
||||
#method_name::output::Output
|
||||
> {
|
||||
#crate_path::runtime_api::payload::StaticPayload::new_static(
|
||||
#trait_name_str,
|
||||
#method_name_str,
|
||||
(#(#input_param_names,)*),
|
||||
[#(#validation_hash,)*],
|
||||
)
|
||||
}
|
||||
);
|
||||
|
||||
Ok((runtime_api_types, runtime_api_method))
|
||||
})
|
||||
.collect::<Result<Vec<_>, CodegenError>>()?;
|
||||
|
||||
let trait_name = format_ident!("{}", trait_name_str);
|
||||
let types = types_and_methods.iter().map(|(types, _)| types);
|
||||
let methods = types_and_methods.iter().map(|(_, methods)| methods);
|
||||
|
||||
// The runtime API definition and types.
|
||||
let trait_defs = quote!(
|
||||
pub mod #trait_name_snake {
|
||||
use super::root_mod;
|
||||
use super::#types_mod_ident;
|
||||
|
||||
#docs
|
||||
pub struct #trait_name;
|
||||
|
||||
impl #trait_name {
|
||||
#( #methods )*
|
||||
}
|
||||
|
||||
#( #types )*
|
||||
}
|
||||
);
|
||||
|
||||
// A getter for the `RuntimeApi` to get the trait structure.
|
||||
let trait_getter = quote!(
|
||||
pub fn #trait_name_snake(&self) -> #trait_name_snake::#trait_name {
|
||||
#trait_name_snake::#trait_name
|
||||
}
|
||||
);
|
||||
|
||||
Ok((trait_defs, trait_getter))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::RuntimeGenerator;
|
||||
use frame_metadata::v15::{
|
||||
self, RuntimeApiMetadata, RuntimeApiMethodMetadata, RuntimeApiMethodParamMetadata,
|
||||
};
|
||||
use quote::quote;
|
||||
use scale_info::meta_type;
|
||||
use pezkuwi_subxt_metadata::Metadata;
|
||||
|
||||
fn metadata_with_runtime_apis(runtime_apis: Vec<RuntimeApiMetadata>) -> Metadata {
|
||||
let extrinsic_metadata = v15::ExtrinsicMetadata {
|
||||
version: 0,
|
||||
signed_extensions: vec![],
|
||||
address_ty: meta_type::<()>(),
|
||||
call_ty: meta_type::<()>(),
|
||||
signature_ty: meta_type::<()>(),
|
||||
extra_ty: meta_type::<()>(),
|
||||
};
|
||||
|
||||
let metadata: Metadata = v15::RuntimeMetadataV15::new(
|
||||
vec![],
|
||||
extrinsic_metadata,
|
||||
meta_type::<()>(),
|
||||
runtime_apis,
|
||||
v15::OuterEnums {
|
||||
call_enum_ty: meta_type::<()>(),
|
||||
event_enum_ty: meta_type::<()>(),
|
||||
error_enum_ty: meta_type::<()>(),
|
||||
},
|
||||
v15::CustomMetadata {
|
||||
map: Default::default(),
|
||||
},
|
||||
)
|
||||
.try_into()
|
||||
.expect("can build valid metadata");
|
||||
metadata
|
||||
}
|
||||
|
||||
fn generate_code(runtime_apis: Vec<RuntimeApiMetadata>) -> String {
|
||||
let metadata = metadata_with_runtime_apis(runtime_apis);
|
||||
let item_mod = syn::parse_quote!(
|
||||
pub mod api {}
|
||||
);
|
||||
let generator = RuntimeGenerator::new(metadata);
|
||||
let generated = generator
|
||||
.generate_runtime(
|
||||
item_mod,
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
syn::parse_str("::subxt_path").unwrap(),
|
||||
false,
|
||||
)
|
||||
.expect("should be able to generate runtime");
|
||||
generated.to_string()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unique_param_names() {
|
||||
let runtime_apis = vec![RuntimeApiMetadata {
|
||||
name: "Test",
|
||||
methods: vec![RuntimeApiMethodMetadata {
|
||||
name: "test",
|
||||
inputs: vec![
|
||||
RuntimeApiMethodParamMetadata {
|
||||
name: "foo",
|
||||
ty: meta_type::<bool>(),
|
||||
},
|
||||
RuntimeApiMethodParamMetadata {
|
||||
name: "bar",
|
||||
ty: meta_type::<bool>(),
|
||||
},
|
||||
],
|
||||
output: meta_type::<bool>(),
|
||||
docs: vec![],
|
||||
}],
|
||||
|
||||
docs: vec![],
|
||||
}];
|
||||
|
||||
let code = generate_code(runtime_apis);
|
||||
|
||||
let expected_alias = quote!(
|
||||
pub mod test {
|
||||
use super::root_mod;
|
||||
use super::runtime_types;
|
||||
pub type Foo = ::core::primitive::bool;
|
||||
pub type Bar = ::core::primitive::bool;
|
||||
pub mod output {
|
||||
use super::runtime_types;
|
||||
pub type Output = ::core::primitive::bool;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
assert!(code.contains(&expected_alias.to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duplicate_param_names() {
|
||||
let runtime_apis = vec![RuntimeApiMetadata {
|
||||
name: "Test",
|
||||
methods: vec![RuntimeApiMethodMetadata {
|
||||
name: "test",
|
||||
inputs: vec![
|
||||
RuntimeApiMethodParamMetadata {
|
||||
name: "_a",
|
||||
ty: meta_type::<bool>(),
|
||||
},
|
||||
RuntimeApiMethodParamMetadata {
|
||||
name: "a",
|
||||
ty: meta_type::<bool>(),
|
||||
},
|
||||
RuntimeApiMethodParamMetadata {
|
||||
name: "__a",
|
||||
ty: meta_type::<bool>(),
|
||||
},
|
||||
],
|
||||
output: meta_type::<bool>(),
|
||||
docs: vec![],
|
||||
}],
|
||||
|
||||
docs: vec![],
|
||||
}];
|
||||
|
||||
let code = generate_code(runtime_apis);
|
||||
|
||||
let expected_alias = quote!(
|
||||
pub mod test {
|
||||
use super::root_mod;
|
||||
use super::runtime_types;
|
||||
pub type A = ::core::primitive::bool;
|
||||
pub type AParam1 = ::core::primitive::bool;
|
||||
pub type AParam2 = ::core::primitive::bool;
|
||||
pub mod output {
|
||||
use super::runtime_types;
|
||||
pub type Output = ::core::primitive::bool;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
assert!(code.contains(&expected_alias.to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duplicate_param_and_alias_names() {
|
||||
let runtime_apis = vec![RuntimeApiMetadata {
|
||||
name: "Test",
|
||||
methods: vec![RuntimeApiMethodMetadata {
|
||||
name: "test",
|
||||
inputs: vec![
|
||||
RuntimeApiMethodParamMetadata {
|
||||
name: "_",
|
||||
ty: meta_type::<bool>(),
|
||||
},
|
||||
RuntimeApiMethodParamMetadata {
|
||||
name: "_a",
|
||||
ty: meta_type::<bool>(),
|
||||
},
|
||||
RuntimeApiMethodParamMetadata {
|
||||
name: "_param_0",
|
||||
ty: meta_type::<bool>(),
|
||||
},
|
||||
RuntimeApiMethodParamMetadata {
|
||||
name: "__",
|
||||
ty: meta_type::<bool>(),
|
||||
},
|
||||
RuntimeApiMethodParamMetadata {
|
||||
name: "___param_0_param_2",
|
||||
ty: meta_type::<bool>(),
|
||||
},
|
||||
],
|
||||
output: meta_type::<bool>(),
|
||||
docs: vec![],
|
||||
}],
|
||||
|
||||
docs: vec![],
|
||||
}];
|
||||
|
||||
let code = generate_code(runtime_apis);
|
||||
|
||||
let expected_alias = quote!(
|
||||
pub mod test {
|
||||
use super::root_mod;
|
||||
use super::runtime_types;
|
||||
pub type Param0 = ::core::primitive::bool;
|
||||
pub type A = ::core::primitive::bool;
|
||||
pub type Param0Param2 = ::core::primitive::bool;
|
||||
pub type Param3 = ::core::primitive::bool;
|
||||
pub type Param0Param2Param4 = ::core::primitive::bool;
|
||||
pub mod output {
|
||||
use super::runtime_types;
|
||||
pub type Output = ::core::primitive::bool;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
assert!(code.contains(&expected_alias.to_string()));
|
||||
}
|
||||
}
|
||||
-239
@@ -1,239 +0,0 @@
|
||||
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use heck::ToSnakeCase as _;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote};
|
||||
use scale_typegen::TypeGenerator;
|
||||
use pezkuwi_subxt_metadata::{PalletMetadata, StorageEntryMetadata};
|
||||
|
||||
use super::CodegenError;
|
||||
|
||||
use scale_typegen::typegen::ir::ToTokensWithSettings;
|
||||
|
||||
/// Generate functions which create storage addresses from the provided pallet's metadata.
|
||||
/// These addresses can be used to access and iterate over storage values.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// - `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 the `subxt-core` crate is located, e.g. `::pezkuwi_subxt::ext::pezkuwi_subxt_core` when using subxt as a dependency.
|
||||
pub fn generate_storage(
|
||||
type_gen: &TypeGenerator,
|
||||
pallet: &PalletMetadata,
|
||||
crate_path: &syn::Path,
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
let Some(storage) = pallet.storage() else {
|
||||
// If there are no storage entries in this pallet, we
|
||||
// don't generate anything.
|
||||
return Ok(quote!());
|
||||
};
|
||||
|
||||
let storage_entries = storage
|
||||
.entries()
|
||||
.iter()
|
||||
.map(|entry| generate_storage_entry_fns(type_gen, pallet, entry, crate_path))
|
||||
.collect::<Result<Vec<_>, CodegenError>>()?;
|
||||
|
||||
let storage_entry_types = storage_entries.iter().map(|(types, _)| types);
|
||||
let storage_entry_methods = storage_entries.iter().map(|(_, method)| method);
|
||||
|
||||
let types_mod_ident = type_gen.types_mod_ident();
|
||||
|
||||
Ok(quote! {
|
||||
pub mod storage {
|
||||
use super::root_mod;
|
||||
use super::#types_mod_ident;
|
||||
|
||||
pub struct StorageApi;
|
||||
|
||||
impl StorageApi {
|
||||
#( #storage_entry_methods )*
|
||||
}
|
||||
|
||||
#( #storage_entry_types )*
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns storage entry functions and alias modules.
|
||||
fn generate_storage_entry_fns(
|
||||
type_gen: &TypeGenerator,
|
||||
pallet: &PalletMetadata,
|
||||
storage_entry: &StorageEntryMetadata,
|
||||
crate_path: &syn::Path,
|
||||
) -> Result<(TokenStream2, TokenStream2), CodegenError> {
|
||||
let types_mod_ident = type_gen.types_mod_ident();
|
||||
|
||||
let pallet_name = pallet.name();
|
||||
let storage_entry_name_str = storage_entry.name();
|
||||
let storage_entry_snake_case_name = storage_entry_name_str.to_snake_case();
|
||||
let storage_entry_snake_case_ident = format_ident!("{storage_entry_snake_case_name}");
|
||||
let Some(validation_hash) = pallet.storage_hash(storage_entry_name_str) else {
|
||||
return Err(CodegenError::MissingStorageMetadata(
|
||||
pallet_name.into(),
|
||||
storage_entry_name_str.into(),
|
||||
));
|
||||
};
|
||||
|
||||
let docs = storage_entry.docs();
|
||||
let docs: TokenStream2 = type_gen
|
||||
.settings()
|
||||
.should_gen_docs
|
||||
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||
.unwrap_or_default();
|
||||
|
||||
struct Input {
|
||||
type_alias: syn::Ident,
|
||||
type_path: TokenStream2,
|
||||
}
|
||||
|
||||
let storage_key_types: Vec<Input> = storage_entry
|
||||
.keys()
|
||||
.enumerate()
|
||||
.map(|(idx, key)| {
|
||||
// Storage key aliases are just indexes; no names to use.
|
||||
let type_alias = format_ident!("Param{}", idx);
|
||||
|
||||
// Path to the actual type we'll have generated for this input.
|
||||
let type_path = type_gen
|
||||
.resolve_type_path(key.key_id)
|
||||
.expect("view function input type is in metadata; qed")
|
||||
.to_token_stream(type_gen.settings());
|
||||
|
||||
Input {
|
||||
type_alias,
|
||||
type_path,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let storage_key_tuple_types = storage_key_types
|
||||
.iter()
|
||||
.map(|i| {
|
||||
let ty = &i.type_alias;
|
||||
quote!(#storage_entry_snake_case_ident::#ty)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let storage_key_type_aliases = storage_key_types
|
||||
.iter()
|
||||
.map(|i| {
|
||||
let ty = &i.type_alias;
|
||||
let path = &i.type_path;
|
||||
quote!(pub type #ty = #path;)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let storage_value_type_path = type_gen
|
||||
.resolve_type_path(storage_entry.value_ty())?
|
||||
.to_token_stream(type_gen.settings());
|
||||
|
||||
let is_plain = if storage_entry.keys().len() == 0 {
|
||||
quote!(#crate_path::utils::Yes)
|
||||
} else {
|
||||
quote!(#crate_path::utils::Maybe)
|
||||
};
|
||||
|
||||
let storage_entry_types = quote!(
|
||||
pub mod #storage_entry_snake_case_ident {
|
||||
use super::root_mod;
|
||||
use super::#types_mod_ident;
|
||||
|
||||
#(#storage_key_type_aliases)*
|
||||
|
||||
pub mod output {
|
||||
use super::#types_mod_ident;
|
||||
pub type Output = #storage_value_type_path;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
let storage_entry_method = quote!(
|
||||
#docs
|
||||
pub fn #storage_entry_snake_case_ident(&self) -> #crate_path::storage::address::StaticAddress<
|
||||
(#(#storage_key_tuple_types,)*),
|
||||
#storage_entry_snake_case_ident::output::Output,
|
||||
#is_plain
|
||||
> {
|
||||
#crate_path::storage::address::StaticAddress::new_static(
|
||||
#pallet_name,
|
||||
#storage_entry_name_str,
|
||||
[#(#validation_hash,)*],
|
||||
)
|
||||
}
|
||||
);
|
||||
|
||||
Ok((storage_entry_types, storage_entry_method))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use frame_metadata::v15;
|
||||
use scale_info::{MetaType, meta_type};
|
||||
use pezkuwi_subxt_metadata::Metadata;
|
||||
|
||||
// TODO: Think about adding tests for storage codegen which can use this sort of function.
|
||||
#[allow(dead_code)]
|
||||
fn metadata_with_storage_entries(
|
||||
storage_entries: impl IntoIterator<Item = (&'static str, MetaType)>,
|
||||
) -> Metadata {
|
||||
let storage_entries: Vec<v15::StorageEntryMetadata> = storage_entries
|
||||
.into_iter()
|
||||
.map(|(name, key)| v15::StorageEntryMetadata {
|
||||
name,
|
||||
modifier: v15::StorageEntryModifier::Optional,
|
||||
ty: v15::StorageEntryType::Map {
|
||||
hashers: vec![v15::StorageHasher::Blake2_128Concat],
|
||||
key,
|
||||
value: meta_type::<bool>(),
|
||||
},
|
||||
default: vec![],
|
||||
docs: vec![],
|
||||
})
|
||||
.collect();
|
||||
|
||||
let pallet_1 = v15::PalletMetadata {
|
||||
name: "Pallet1",
|
||||
storage: Some(v15::PalletStorageMetadata {
|
||||
prefix: Default::default(),
|
||||
entries: storage_entries,
|
||||
}),
|
||||
calls: None,
|
||||
event: None,
|
||||
constants: vec![],
|
||||
error: None,
|
||||
index: 0,
|
||||
docs: vec![],
|
||||
};
|
||||
|
||||
let extrinsic_metadata = v15::ExtrinsicMetadata {
|
||||
version: 0,
|
||||
signed_extensions: vec![],
|
||||
address_ty: meta_type::<()>(),
|
||||
call_ty: meta_type::<()>(),
|
||||
signature_ty: meta_type::<()>(),
|
||||
extra_ty: meta_type::<()>(),
|
||||
};
|
||||
|
||||
let metadata: Metadata = v15::RuntimeMetadataV15::new(
|
||||
vec![pallet_1],
|
||||
extrinsic_metadata,
|
||||
meta_type::<()>(),
|
||||
vec![],
|
||||
v15::OuterEnums {
|
||||
call_enum_ty: meta_type::<()>(),
|
||||
event_enum_ty: meta_type::<()>(),
|
||||
error_enum_ty: meta_type::<()>(),
|
||||
},
|
||||
v15::CustomMetadata {
|
||||
map: Default::default(),
|
||||
},
|
||||
)
|
||||
.try_into()
|
||||
.expect("can build valid metadata");
|
||||
metadata
|
||||
}
|
||||
}
|
||||
-101
@@ -1,101 +0,0 @@
|
||||
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
//! Errors that can be emitted from codegen.
|
||||
|
||||
use proc_macro2::{Span, TokenStream as TokenStream2};
|
||||
use scale_typegen::TypegenError;
|
||||
|
||||
/// Error returned when the Codegen cannot generate the runtime API.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[non_exhaustive]
|
||||
pub enum CodegenError {
|
||||
/// Cannot decode the metadata bytes.
|
||||
#[error("Could not decode metadata, only V14 and V15 metadata are supported: {0}")]
|
||||
Decode(#[from] codec::Error),
|
||||
/// Out of line modules are not supported.
|
||||
#[error(
|
||||
"Out-of-line subxt modules are not supported, make sure you are providing a body to your module: pub mod polkadot {{ ... }}"
|
||||
)]
|
||||
InvalidModule(Span),
|
||||
/// Invalid type path.
|
||||
#[error("Invalid type path {0}: {1}")]
|
||||
InvalidTypePath(String, syn::Error),
|
||||
/// Metadata for constant could not be found.
|
||||
#[error(
|
||||
"Metadata for constant entry {0}_{1} could not be found. Make sure you are providing a valid substrate-based metadata"
|
||||
)]
|
||||
MissingConstantMetadata(String, String),
|
||||
/// Metadata for storage could not be found.
|
||||
#[error(
|
||||
"Metadata for storage entry {0}_{1} could not be found. Make sure you are providing a valid substrate-based metadata"
|
||||
)]
|
||||
MissingStorageMetadata(String, String),
|
||||
/// Metadata for call could not be found.
|
||||
#[error(
|
||||
"Metadata for call entry {0}_{1} could not be found. Make sure you are providing a valid substrate-based metadata"
|
||||
)]
|
||||
MissingCallMetadata(String, String),
|
||||
/// Metadata for call could not be found.
|
||||
#[error(
|
||||
"Metadata for runtime API entry {0}_{1} could not be found. Make sure you are providing a valid substrate-based metadata"
|
||||
)]
|
||||
MissingRuntimeApiMetadata(String, String),
|
||||
/// Call variant must have all named fields.
|
||||
#[error(
|
||||
"Call variant for type {0} must have all named fields. Make sure you are providing a valid substrate-based metadata"
|
||||
)]
|
||||
InvalidCallVariant(u32),
|
||||
/// Type should be an variant/enum.
|
||||
#[error(
|
||||
"{0} type should be an variant/enum type. Make sure you are providing a valid substrate-based metadata"
|
||||
)]
|
||||
InvalidType(String),
|
||||
/// Extrinsic call type could not be found.
|
||||
#[error(
|
||||
"Extrinsic call type could not be found. Make sure you are providing a valid substrate-based metadata"
|
||||
)]
|
||||
MissingCallType,
|
||||
/// There are too many or too few hashers.
|
||||
#[error(
|
||||
"Could not generate functions for storage entry {storage_entry_name}. There are {key_count} keys, but only {hasher_count} hashers. The number of hashers must equal the number of keys or be exactly 1."
|
||||
)]
|
||||
InvalidStorageHasherCount {
|
||||
/// The name of the storage entry
|
||||
storage_entry_name: String,
|
||||
/// Number of keys
|
||||
key_count: usize,
|
||||
/// Number of hashers
|
||||
hasher_count: usize,
|
||||
},
|
||||
/// Cannot generate types.
|
||||
#[error("Type Generation failed: {0}")]
|
||||
TypeGeneration(#[from] TypegenError),
|
||||
/// Error when generating metadata from Wasm-runtime
|
||||
#[error("Failed to generate metadata from wasm file. reason: {0}")]
|
||||
Wasm(String),
|
||||
/// Other error.
|
||||
#[error("Other error: {0}")]
|
||||
Other(String),
|
||||
}
|
||||
|
||||
impl CodegenError {
|
||||
/// Fetch the location for this error.
|
||||
// Todo: Probably worth storing location outside of the variant,
|
||||
// so that there's a common way to set a location for some error.
|
||||
fn get_location(&self) -> Span {
|
||||
match self {
|
||||
Self::InvalidModule(span) => *span,
|
||||
Self::TypeGeneration(TypegenError::InvalidSubstitute(err)) => err.span,
|
||||
Self::InvalidTypePath(_, err) => err.span(),
|
||||
_ => proc_macro2::Span::call_site(),
|
||||
}
|
||||
}
|
||||
/// Render the error as an invocation of syn::compile_error!.
|
||||
pub fn into_compile_error(self) -> TokenStream2 {
|
||||
let msg = self.to_string();
|
||||
let span = self.get_location();
|
||||
syn::Error::new(span, msg).into_compile_error()
|
||||
}
|
||||
}
|
||||
-40
@@ -1,40 +0,0 @@
|
||||
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use crate::error::CodegenError;
|
||||
use syn::token;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct ItemMod {
|
||||
vis: syn::Visibility,
|
||||
mod_token: token::Mod,
|
||||
pub ident: syn::Ident,
|
||||
brace: token::Brace,
|
||||
items: Vec<syn::Item>,
|
||||
}
|
||||
|
||||
impl TryFrom<syn::ItemMod> for ItemMod {
|
||||
type Error = CodegenError;
|
||||
|
||||
fn try_from(module: syn::ItemMod) -> Result<Self, Self::Error> {
|
||||
let (brace, items) = match module.content {
|
||||
Some((brace, items)) => (brace, items),
|
||||
None => return Err(CodegenError::InvalidModule(module.ident.span())),
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
vis: module.vis,
|
||||
mod_token: module.mod_token,
|
||||
ident: module.ident,
|
||||
brace,
|
||||
items,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ItemMod {
|
||||
pub fn rust_items(&self) -> impl Iterator<Item = &syn::Item> {
|
||||
self.items.iter()
|
||||
}
|
||||
}
|
||||
-445
@@ -1,445 +0,0 @@
|
||||
// Copyright 2019-2025 Parity Technologies (UK) Ltd.
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
//! Generate a type safe Subxt interface for a Substrate runtime from its metadata.
|
||||
//! This is used by the `#[subxt]` macro and `subxt codegen` CLI command, but can also
|
||||
//! be used directly if preferable.
|
||||
|
||||
#![deny(missing_docs)]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
|
||||
mod api;
|
||||
pub mod error;
|
||||
mod ir;
|
||||
|
||||
#[cfg(feature = "web")]
|
||||
use getrandom as _;
|
||||
|
||||
use api::RuntimeGenerator;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use scale_typegen::typegen::settings::AllocCratePath;
|
||||
use scale_typegen::{
|
||||
DerivesRegistry, TypeGeneratorSettings, TypeSubstitutes, TypegenError,
|
||||
typegen::settings::substitutes::absolute_path,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
use syn::parse_quote;
|
||||
|
||||
// Part of the public interface, so expose:
|
||||
pub use error::CodegenError;
|
||||
pub use pezkuwi_subxt_metadata::Metadata;
|
||||
pub use syn;
|
||||
|
||||
/// Generate a type safe interface to use with `subxt`.
|
||||
/// The options exposed here are similar to those exposed via
|
||||
/// the `#[subxt]` macro or via the `subxt codegen` CLI command.
|
||||
/// Both use this under the hood.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Generating an interface using all of the defaults:
|
||||
///
|
||||
/// ```rust,standalone_crate
|
||||
/// use codec::Decode;
|
||||
/// use pezkuwi_subxt_codegen::{ Metadata, CodegenBuilder };
|
||||
///
|
||||
/// // Get hold of and decode some metadata:
|
||||
/// let encoded = std::fs::read("../artifacts/polkadot_metadata_full.scale").unwrap();
|
||||
/// let metadata = Metadata::decode(&mut &*encoded).unwrap();
|
||||
///
|
||||
/// // Generate a TokenStream representing the code for the interface.
|
||||
/// // This can be converted to a string, displayed as-is or output from a macro.
|
||||
/// let token_stream = CodegenBuilder::new().generate(metadata);
|
||||
/// ````
|
||||
pub struct CodegenBuilder {
|
||||
crate_path: syn::Path,
|
||||
use_default_derives: bool,
|
||||
use_default_substitutions: bool,
|
||||
generate_docs: bool,
|
||||
runtime_types_only: bool,
|
||||
item_mod: syn::ItemMod,
|
||||
extra_global_derives: Vec<syn::Path>,
|
||||
extra_global_attributes: Vec<syn::Attribute>,
|
||||
type_substitutes: HashMap<syn::Path, syn::Path>,
|
||||
derives_for_type: HashMap<syn::TypePath, Vec<syn::Path>>,
|
||||
attributes_for_type: HashMap<syn::TypePath, Vec<syn::Attribute>>,
|
||||
derives_for_type_recursive: HashMap<syn::TypePath, Vec<syn::Path>>,
|
||||
attributes_for_type_recursive: HashMap<syn::TypePath, Vec<syn::Attribute>>,
|
||||
}
|
||||
|
||||
impl Default for CodegenBuilder {
|
||||
fn default() -> Self {
|
||||
CodegenBuilder {
|
||||
crate_path: syn::parse_quote!(::pezkuwi_subxt::ext::pezkuwi_subxt_core),
|
||||
use_default_derives: true,
|
||||
use_default_substitutions: true,
|
||||
generate_docs: true,
|
||||
runtime_types_only: false,
|
||||
item_mod: syn::parse_quote!(
|
||||
pub mod api {}
|
||||
),
|
||||
extra_global_derives: Vec::new(),
|
||||
extra_global_attributes: Vec::new(),
|
||||
type_substitutes: HashMap::new(),
|
||||
derives_for_type: HashMap::new(),
|
||||
attributes_for_type: HashMap::new(),
|
||||
derives_for_type_recursive: HashMap::new(),
|
||||
attributes_for_type_recursive: HashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CodegenBuilder {
|
||||
/// Construct a builder to configure and generate a type-safe interface for Subxt.
|
||||
pub fn new() -> Self {
|
||||
CodegenBuilder::default()
|
||||
}
|
||||
|
||||
/// Disable the default derives that are applied to all types.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// This is not recommended, and is highly likely to break some part of the
|
||||
/// generated interface. Expect compile errors.
|
||||
pub fn disable_default_derives(&mut self) {
|
||||
self.use_default_derives = false;
|
||||
}
|
||||
|
||||
/// Disable the default type substitutions that are applied to the generated
|
||||
/// code.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// This is not recommended, and is highly likely to break some part of the
|
||||
/// generated interface. Expect compile errors.
|
||||
pub fn disable_default_substitutes(&mut self) {
|
||||
self.use_default_substitutions = false;
|
||||
}
|
||||
|
||||
/// Disable the output of doc comments associated with the generated types and
|
||||
/// methods. This can reduce the generated code size at the expense of losing
|
||||
/// documentation.
|
||||
pub fn no_docs(&mut self) {
|
||||
self.generate_docs = false;
|
||||
}
|
||||
|
||||
/// Only generate the types, and don't generate the rest of the Subxt specific
|
||||
/// interface.
|
||||
pub fn runtime_types_only(&mut self) {
|
||||
self.runtime_types_only = true;
|
||||
}
|
||||
|
||||
/// Set the additional derives that will be applied to all types. By default,
|
||||
/// a set of derives required for Subxt are automatically added for all types.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// Invalid derives, or derives that cannot be applied to _all_ of the generated
|
||||
/// types (taking into account that some types are substituted for hand written ones
|
||||
/// that we cannot add extra derives for) will lead to compile errors in the
|
||||
/// generated code.
|
||||
pub fn set_additional_global_derives(&mut self, derives: Vec<syn::Path>) {
|
||||
self.extra_global_derives = derives;
|
||||
}
|
||||
|
||||
/// Set the additional attributes that will be applied to all types. By default,
|
||||
/// a set of attributes required for Subxt are automatically added for all types.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// Invalid attributes can very easily lead to compile errors in the generated code.
|
||||
pub fn set_additional_global_attributes(&mut self, attributes: Vec<syn::Attribute>) {
|
||||
self.extra_global_attributes = attributes;
|
||||
}
|
||||
|
||||
/// Set additional derives for a specific type at the path given.
|
||||
///
|
||||
/// If you want to set the additional derives on all contained types recursively as well,
|
||||
/// you can set the `recursive` argument to `true`. If you don't do that,
|
||||
/// there might be compile errors in the generated code, if the derived trait
|
||||
/// relies on the fact that contained types also implement that trait.
|
||||
pub fn add_derives_for_type(
|
||||
&mut self,
|
||||
ty: syn::TypePath,
|
||||
derives: impl IntoIterator<Item = syn::Path>,
|
||||
recursive: bool,
|
||||
) {
|
||||
if recursive {
|
||||
self.derives_for_type_recursive
|
||||
.entry(ty)
|
||||
.or_default()
|
||||
.extend(derives);
|
||||
} else {
|
||||
self.derives_for_type.entry(ty).or_default().extend(derives);
|
||||
}
|
||||
}
|
||||
|
||||
/// Set additional attributes for a specific type at the path given.
|
||||
///
|
||||
/// Setting the `recursive` argument to `true` will additionally add the specified
|
||||
/// attributes to all contained types recursively.
|
||||
pub fn add_attributes_for_type(
|
||||
&mut self,
|
||||
ty: syn::TypePath,
|
||||
attributes: impl IntoIterator<Item = syn::Attribute>,
|
||||
recursive: bool,
|
||||
) {
|
||||
if recursive {
|
||||
self.attributes_for_type_recursive
|
||||
.entry(ty)
|
||||
.or_default()
|
||||
.extend(attributes);
|
||||
} else {
|
||||
self.attributes_for_type
|
||||
.entry(ty)
|
||||
.or_default()
|
||||
.extend(attributes);
|
||||
}
|
||||
}
|
||||
|
||||
/// Substitute a type at the given path with some type at the second path. During codegen,
|
||||
/// we will avoid generating the type at the first path given, and instead point any references
|
||||
/// to that type to the second path given.
|
||||
///
|
||||
/// The substituted type will need to implement the relevant traits to be compatible with the
|
||||
/// original, and it will need to SCALE encode and SCALE decode in a compatible way.
|
||||
pub fn set_type_substitute(&mut self, ty: syn::Path, with: syn::Path) {
|
||||
self.type_substitutes.insert(ty, with);
|
||||
}
|
||||
|
||||
/// By default, all of the code is generated inside a module `pub mod api {}`. We decorate
|
||||
/// this module with a few attributes to reduce compile warnings and things. You can provide a
|
||||
/// target module here, allowing you to add additional attributes or inner code items (with the
|
||||
/// warning that duplicate identifiers will lead to compile errors).
|
||||
pub fn set_target_module(&mut self, item_mod: syn::ItemMod) {
|
||||
self.item_mod = item_mod;
|
||||
}
|
||||
|
||||
/// Set the path to the `subxt` crate. By default, we expect it to be at `::pezkuwi_subxt::ext::pezkuwi_subxt_core`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the path provided is not an absolute path.
|
||||
pub fn set_subxt_crate_path(&mut self, crate_path: syn::Path) {
|
||||
if absolute_path(crate_path.clone()).is_err() {
|
||||
// Throw an error here, because otherwise we end up with a harder to comprehend error when
|
||||
// substitute types don't begin with an absolute path.
|
||||
panic!(
|
||||
"The provided crate path must be an absolute path, ie prefixed with '::' or 'crate'"
|
||||
);
|
||||
}
|
||||
self.crate_path = crate_path;
|
||||
}
|
||||
|
||||
/// Generate an interface, assuming that the default path to the `subxt` crate is `::pezkuwi_subxt::ext::pezkuwi_subxt_core`.
|
||||
/// If the `subxt` crate is not available as a top level dependency, use `generate` and provide
|
||||
/// a valid path to the `subxt¦ crate.
|
||||
pub fn generate(self, metadata: Metadata) -> Result<TokenStream2, CodegenError> {
|
||||
let crate_path = self.crate_path;
|
||||
|
||||
let mut derives_registry: DerivesRegistry = if self.use_default_derives {
|
||||
default_derives(&crate_path)
|
||||
} else {
|
||||
DerivesRegistry::new()
|
||||
};
|
||||
|
||||
derives_registry.add_derives_for_all(self.extra_global_derives);
|
||||
derives_registry.add_attributes_for_all(self.extra_global_attributes);
|
||||
|
||||
for (ty, derives) in self.derives_for_type {
|
||||
derives_registry.add_derives_for(ty, derives, false);
|
||||
}
|
||||
for (ty, derives) in self.derives_for_type_recursive {
|
||||
derives_registry.add_derives_for(ty, derives, true);
|
||||
}
|
||||
for (ty, attributes) in self.attributes_for_type {
|
||||
derives_registry.add_attributes_for(ty, attributes, false);
|
||||
}
|
||||
for (ty, attributes) in self.attributes_for_type_recursive {
|
||||
derives_registry.add_attributes_for(ty, attributes, true);
|
||||
}
|
||||
|
||||
let mut type_substitutes: TypeSubstitutes = if self.use_default_substitutions {
|
||||
default_substitutes(&crate_path)
|
||||
} else {
|
||||
TypeSubstitutes::new()
|
||||
};
|
||||
|
||||
for (from, with) in self.type_substitutes {
|
||||
let abs_path = absolute_path(with).map_err(TypegenError::from)?;
|
||||
type_substitutes
|
||||
.insert(from, abs_path)
|
||||
.map_err(TypegenError::from)?;
|
||||
}
|
||||
|
||||
let item_mod = self.item_mod;
|
||||
let generator = RuntimeGenerator::new(metadata);
|
||||
let should_gen_docs = self.generate_docs;
|
||||
|
||||
if self.runtime_types_only {
|
||||
generator.generate_runtime_types(
|
||||
item_mod,
|
||||
derives_registry,
|
||||
type_substitutes,
|
||||
crate_path,
|
||||
should_gen_docs,
|
||||
)
|
||||
} else {
|
||||
generator.generate_runtime(
|
||||
item_mod,
|
||||
derives_registry,
|
||||
type_substitutes,
|
||||
crate_path,
|
||||
should_gen_docs,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The default [`scale_typegen::TypeGeneratorSettings`], subxt is using for generating code.
|
||||
/// Useful for emulating subxt's code generation settings from e.g. subxt-explorer.
|
||||
pub fn default_subxt_type_gen_settings() -> TypeGeneratorSettings {
|
||||
let crate_path: syn::Path = parse_quote!(::pezkuwi_subxt::ext::pezkuwi_subxt_core);
|
||||
let derives = default_derives(&crate_path);
|
||||
let substitutes = default_substitutes(&crate_path);
|
||||
subxt_type_gen_settings(derives, substitutes, &crate_path, true)
|
||||
}
|
||||
|
||||
fn subxt_type_gen_settings(
|
||||
derives: scale_typegen::DerivesRegistry,
|
||||
substitutes: scale_typegen::TypeSubstitutes,
|
||||
crate_path: &syn::Path,
|
||||
should_gen_docs: bool,
|
||||
) -> TypeGeneratorSettings {
|
||||
// Are we using codec::Encode or codec::Decode derives?
|
||||
let are_codec_derives_used = derives.default_derives().derives().iter().any(|path| {
|
||||
let mut segments_backwards = path.segments.iter().rev();
|
||||
let ident = segments_backwards.next();
|
||||
let module = segments_backwards.next();
|
||||
|
||||
let is_ident_match = ident.is_some_and(|s| s.ident == "Encode" || s.ident == "Decode");
|
||||
let is_module_match = module.is_some_and(|s| s.ident == "codec");
|
||||
|
||||
is_ident_match && is_module_match
|
||||
});
|
||||
|
||||
// If we're inserting the codec derives, we also should use `CompactAs` where necessary.
|
||||
let compact_as_type_path =
|
||||
are_codec_derives_used.then(|| parse_quote!(#crate_path::ext::codec::CompactAs));
|
||||
|
||||
TypeGeneratorSettings {
|
||||
types_mod_ident: parse_quote!(runtime_types),
|
||||
should_gen_docs,
|
||||
derives,
|
||||
substitutes,
|
||||
decoded_bits_type_path: Some(parse_quote!(#crate_path::utils::bits::DecodedBits)),
|
||||
compact_as_type_path,
|
||||
compact_type_path: Some(parse_quote!(#crate_path::ext::codec::Compact)),
|
||||
alloc_crate_path: AllocCratePath::Custom(parse_quote!(#crate_path::alloc)),
|
||||
// Note: even when we don't use codec::Encode and codec::Decode, we need to keep #[codec(...)]
|
||||
// attributes because `#[codec(skip)]` is still used/important with `EncodeAsType` and `DecodeAsType`.
|
||||
insert_codec_attributes: true,
|
||||
}
|
||||
}
|
||||
|
||||
fn default_derives(crate_path: &syn::Path) -> DerivesRegistry {
|
||||
let encode_crate_path = quote::quote! { #crate_path::ext::scale_encode }.to_string();
|
||||
let decode_crate_path = quote::quote! { #crate_path::ext::scale_decode }.to_string();
|
||||
|
||||
let derives: [syn::Path; 3] = [
|
||||
parse_quote!(#crate_path::ext::scale_encode::EncodeAsType),
|
||||
parse_quote!(#crate_path::ext::scale_decode::DecodeAsType),
|
||||
parse_quote!(Debug),
|
||||
];
|
||||
|
||||
let attributes: [syn::Attribute; 2] = [
|
||||
parse_quote!(#[encode_as_type(crate_path = #encode_crate_path)]),
|
||||
parse_quote!(#[decode_as_type(crate_path = #decode_crate_path)]),
|
||||
];
|
||||
|
||||
let mut derives_registry = DerivesRegistry::new();
|
||||
derives_registry.add_derives_for_all(derives);
|
||||
derives_registry.add_attributes_for_all(attributes);
|
||||
derives_registry
|
||||
}
|
||||
|
||||
fn default_substitutes(crate_path: &syn::Path) -> TypeSubstitutes {
|
||||
let mut type_substitutes = TypeSubstitutes::new();
|
||||
|
||||
let defaults: [(syn::Path, syn::Path); 13] = [
|
||||
(
|
||||
parse_quote!(bitvec::order::Lsb0),
|
||||
parse_quote!(#crate_path::utils::bits::Lsb0),
|
||||
),
|
||||
(
|
||||
parse_quote!(bitvec::order::Msb0),
|
||||
parse_quote!(#crate_path::utils::bits::Msb0),
|
||||
),
|
||||
(
|
||||
parse_quote!(pezsp_core::crypto::AccountId32),
|
||||
parse_quote!(#crate_path::utils::AccountId32),
|
||||
),
|
||||
(
|
||||
parse_quote!(fp_account::AccountId20),
|
||||
parse_quote!(#crate_path::utils::AccountId20),
|
||||
),
|
||||
(
|
||||
parse_quote!(pezsp_runtime::multiaddress::MultiAddress),
|
||||
parse_quote!(#crate_path::utils::MultiAddress),
|
||||
),
|
||||
(
|
||||
parse_quote!(primitive_types::H160),
|
||||
parse_quote!(#crate_path::utils::H160),
|
||||
),
|
||||
(
|
||||
parse_quote!(primitive_types::H256),
|
||||
parse_quote!(#crate_path::utils::H256),
|
||||
),
|
||||
(
|
||||
parse_quote!(primitive_types::H512),
|
||||
parse_quote!(#crate_path::utils::H512),
|
||||
),
|
||||
(
|
||||
parse_quote!(pezframe_support::traits::misc::WrapperKeepOpaque),
|
||||
parse_quote!(#crate_path::utils::WrapperKeepOpaque),
|
||||
),
|
||||
// BTreeMap and BTreeSet impose an `Ord` constraint on their key types. This
|
||||
// can cause an issue with generated code that doesn't impl `Ord` by default.
|
||||
// Decoding them to Vec by default (KeyedVec is just an alias for Vec with
|
||||
// suitable type params) avoids these issues.
|
||||
(
|
||||
parse_quote!(BTreeMap),
|
||||
parse_quote!(#crate_path::utils::KeyedVec),
|
||||
),
|
||||
(
|
||||
parse_quote!(BinaryHeap),
|
||||
parse_quote!(#crate_path::alloc::vec::Vec),
|
||||
),
|
||||
(
|
||||
parse_quote!(BTreeSet),
|
||||
parse_quote!(#crate_path::alloc::vec::Vec),
|
||||
),
|
||||
// The `UncheckedExtrinsic(pub Vec<u8>)` is part of the runtime API calls.
|
||||
// The inner bytes represent the encoded extrinsic, however when deriving the
|
||||
// `EncodeAsType` the bytes would be re-encoded. This leads to the bytes
|
||||
// being altered by adding the length prefix in front of them.
|
||||
|
||||
// Note: Not sure if this is appropriate or not. The most recent polkadot.rs file does not have these.
|
||||
(
|
||||
parse_quote!(pezsp_runtime::generic::unchecked_extrinsic::UncheckedExtrinsic),
|
||||
parse_quote!(#crate_path::utils::UncheckedExtrinsic),
|
||||
),
|
||||
];
|
||||
|
||||
let defaults = defaults.into_iter().map(|(from, to)| {
|
||||
(
|
||||
from,
|
||||
absolute_path(to).expect("default substitutes above are absolute paths; qed"),
|
||||
)
|
||||
});
|
||||
type_substitutes
|
||||
.extend(defaults)
|
||||
.expect("default substitutes can always be parsed; qed");
|
||||
type_substitutes
|
||||
}
|
||||
Reference in New Issue
Block a user