mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-22 20:48:01 +00:00
Introduce Metadata type (#974)
* WIP new Metadata type * Finish basic Metadata impl inc hashing and validation * remove caching from metadata; can add that higher up * remove caches * update retain to use Metadata * clippy fixes * update codegen to use Metadata * clippy * WIP fixing subxt lib * WIP fixing tests, rebuild artifacts, fix OrderedMap::retain * get --all-targets compiling * move DispatchError type lookup back to being optional * cargo clippy * fix docs * re-use VariantIndex to get variants * add docs and enforce docs on metadata crate * fix docs * add test and fix docs * cargo fmt * address review comments * update lockfiles * ExactSizeIter so we can ask for len() of things (and hopefully soon is_empty()
This commit is contained in:
+14
-17
@@ -7,11 +7,10 @@ use crate::{
|
||||
types::{CompositeDefFields, TypeGenerator},
|
||||
CratePath,
|
||||
};
|
||||
use frame_metadata::v15::{PalletMetadata, RuntimeMetadataV15};
|
||||
use heck::{ToSnakeCase as _, ToUpperCamelCase as _};
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote};
|
||||
use scale_info::form::PortableForm;
|
||||
use subxt_metadata::PalletMetadata;
|
||||
|
||||
/// Generate calls from the provided pallet's metadata. Each call returns a `StaticTxPayload`
|
||||
/// that can be passed to the subxt client to submit/sign/encode.
|
||||
@@ -23,21 +22,20 @@ use scale_info::form::PortableForm;
|
||||
/// - `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.
|
||||
pub fn generate_calls(
|
||||
metadata: &RuntimeMetadataV15,
|
||||
type_gen: &TypeGenerator,
|
||||
pallet: &PalletMetadata<PortableForm>,
|
||||
pallet: &PalletMetadata,
|
||||
types_mod_ident: &syn::Ident,
|
||||
crate_path: &CratePath,
|
||||
should_gen_docs: bool,
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
// Early return if the pallet has no calls.
|
||||
let Some(call) = &pallet.calls else {
|
||||
let Some(call_ty) = pallet.call_ty_id() else {
|
||||
return Ok(quote!());
|
||||
};
|
||||
|
||||
let mut struct_defs = super::generate_structs_from_variants(
|
||||
type_gen,
|
||||
call.ty.id,
|
||||
call_ty,
|
||||
|name| name.to_upper_camel_case().into(),
|
||||
"Call",
|
||||
crate_path,
|
||||
@@ -61,20 +59,19 @@ pub fn generate_calls(
|
||||
.unzip(),
|
||||
CompositeDefFields::NoFields => Default::default(),
|
||||
CompositeDefFields::Unnamed(_) => {
|
||||
return Err(CodegenError::InvalidCallVariant(call.ty.id))
|
||||
return Err(CodegenError::InvalidCallVariant(call_ty))
|
||||
}
|
||||
};
|
||||
|
||||
let pallet_name = &pallet.name;
|
||||
let pallet_name = pallet.name();
|
||||
let call_name = &variant_name;
|
||||
let struct_name = &struct_def.name;
|
||||
let Ok(call_hash) =
|
||||
subxt_metadata::get_call_hash(metadata, pallet_name, call_name) else {
|
||||
return Err(CodegenError::MissingCallMetadata(
|
||||
pallet_name.into(),
|
||||
call_name.to_string(),
|
||||
))
|
||||
};
|
||||
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!("{}", variant_name.to_snake_case());
|
||||
// Propagate the documentation just to `TransactionApi` methods, while
|
||||
// draining the documentation of inner call structures.
|
||||
@@ -111,8 +108,8 @@ pub fn generate_calls(
|
||||
.into_iter()
|
||||
.unzip();
|
||||
|
||||
let call_type = type_gen.resolve_type_path(call.ty.id);
|
||||
let call_ty = type_gen.resolve_type(call.ty.id);
|
||||
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 ] )* })
|
||||
|
||||
@@ -3,11 +3,10 @@
|
||||
// see LICENSE for license details.
|
||||
|
||||
use crate::{types::TypeGenerator, CratePath};
|
||||
use frame_metadata::v15::{PalletMetadata, RuntimeMetadataV15};
|
||||
use heck::ToSnakeCase as _;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote};
|
||||
use scale_info::form::PortableForm;
|
||||
use subxt_metadata::PalletMetadata;
|
||||
|
||||
use super::CodegenError;
|
||||
|
||||
@@ -35,29 +34,27 @@ use super::CodegenError;
|
||||
/// - `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.
|
||||
pub fn generate_constants(
|
||||
metadata: &RuntimeMetadataV15,
|
||||
type_gen: &TypeGenerator,
|
||||
pallet: &PalletMetadata<PortableForm>,
|
||||
pallet: &PalletMetadata,
|
||||
types_mod_ident: &syn::Ident,
|
||||
crate_path: &CratePath,
|
||||
should_gen_docs: bool,
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
// Early return if the pallet has no constants.
|
||||
if pallet.constants.is_empty() {
|
||||
if pallet.constants().len() == 0 {
|
||||
return Ok(quote!());
|
||||
}
|
||||
let constants = &pallet.constants;
|
||||
|
||||
let constant_fns = constants.iter().map(|constant| {
|
||||
let fn_name = format_ident!("{}", constant.name.to_snake_case());
|
||||
let pallet_name = &pallet.name;
|
||||
let constant_name = &constant.name;
|
||||
let Ok(constant_hash) = subxt_metadata::get_constant_hash(metadata, pallet_name, constant_name) else {
|
||||
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.id);
|
||||
let docs = &constant.docs;
|
||||
let return_ty = type_gen.resolve_type_path(constant.ty());
|
||||
let docs = constant.docs();
|
||||
let docs = should_gen_docs
|
||||
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||
.unwrap_or_default();
|
||||
|
||||
@@ -2,10 +2,9 @@
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use frame_metadata::v15::PalletMetadata;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
use scale_info::form::PortableForm;
|
||||
use subxt_metadata::PalletMetadata;
|
||||
|
||||
use crate::types::TypeGenerator;
|
||||
|
||||
@@ -14,15 +13,15 @@ use super::CodegenError;
|
||||
/// Generate error type alias from the provided pallet metadata.
|
||||
pub fn generate_error_type_alias(
|
||||
type_gen: &TypeGenerator,
|
||||
pallet: &PalletMetadata<PortableForm>,
|
||||
pallet: &PalletMetadata,
|
||||
should_gen_docs: bool,
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
let Some(error) = &pallet.error else {
|
||||
let Some(error_ty) = pallet.error_ty_id() else {
|
||||
return Ok(quote!());
|
||||
};
|
||||
|
||||
let error_type = type_gen.resolve_type_path(error.ty.id);
|
||||
let error_ty = type_gen.resolve_type(error.ty.id);
|
||||
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
|
||||
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||
|
||||
@@ -3,10 +3,9 @@
|
||||
// see LICENSE for license details.
|
||||
|
||||
use crate::{types::TypeGenerator, CratePath};
|
||||
use frame_metadata::v15::PalletMetadata;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
use scale_info::form::PortableForm;
|
||||
use subxt_metadata::PalletMetadata;
|
||||
|
||||
use super::CodegenError;
|
||||
|
||||
@@ -41,19 +40,19 @@ use super::CodegenError;
|
||||
/// - `types_mod_ident` - The ident of the base module that we can use to access the generated types from.
|
||||
pub fn generate_events(
|
||||
type_gen: &TypeGenerator,
|
||||
pallet: &PalletMetadata<PortableForm>,
|
||||
pallet: &PalletMetadata,
|
||||
types_mod_ident: &syn::Ident,
|
||||
crate_path: &CratePath,
|
||||
should_gen_docs: bool,
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
// Early return if the pallet has no events.
|
||||
let Some(event) = &pallet.event else {
|
||||
let Some(event_ty) = pallet.event_ty_id() else {
|
||||
return Ok(quote!())
|
||||
};
|
||||
|
||||
let struct_defs = super::generate_structs_from_variants(
|
||||
type_gen,
|
||||
event.ty.id,
|
||||
event_ty,
|
||||
|name| name.into(),
|
||||
"Event",
|
||||
crate_path,
|
||||
@@ -61,7 +60,7 @@ pub fn generate_events(
|
||||
)?;
|
||||
|
||||
let event_structs = struct_defs.iter().map(|(variant_name, struct_def)| {
|
||||
let pallet_name = &pallet.name;
|
||||
let pallet_name = pallet.name();
|
||||
let event_struct = &struct_def.name;
|
||||
let event_name = variant_name;
|
||||
|
||||
@@ -74,8 +73,8 @@ pub fn generate_events(
|
||||
}
|
||||
}
|
||||
});
|
||||
let event_type = type_gen.resolve_type_path(event.ty.id);
|
||||
let event_ty = type_gen.resolve_type(event.ty.id);
|
||||
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
|
||||
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||
|
||||
+53
-64
@@ -11,8 +11,7 @@ mod events;
|
||||
mod runtime_apis;
|
||||
mod storage;
|
||||
|
||||
use frame_metadata::v15::RuntimeMetadataV15;
|
||||
use subxt_metadata::{metadata_v14_to_latest, MetadataHasher};
|
||||
use subxt_metadata::Metadata;
|
||||
|
||||
use super::DerivesRegistry;
|
||||
use crate::error::CodegenError;
|
||||
@@ -23,7 +22,6 @@ use crate::{
|
||||
CratePath,
|
||||
};
|
||||
use codec::Decode;
|
||||
use frame_metadata::{RuntimeMetadata, RuntimeMetadataPrefixed};
|
||||
use heck::ToSnakeCase as _;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote};
|
||||
@@ -135,7 +133,7 @@ pub fn generate_runtime_api_from_bytes(
|
||||
should_gen_docs: bool,
|
||||
runtime_types_only: bool,
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
let metadata = frame_metadata::RuntimeMetadataPrefixed::decode(&mut &bytes[..])?;
|
||||
let metadata = Metadata::decode(&mut &bytes[..])?;
|
||||
|
||||
let generator = RuntimeGenerator::new(metadata);
|
||||
if runtime_types_only {
|
||||
@@ -159,7 +157,7 @@ pub fn generate_runtime_api_from_bytes(
|
||||
|
||||
/// Create the API for interacting with a Substrate runtime.
|
||||
pub struct RuntimeGenerator {
|
||||
metadata: RuntimeMetadataV15,
|
||||
metadata: Metadata,
|
||||
}
|
||||
|
||||
impl RuntimeGenerator {
|
||||
@@ -174,15 +172,8 @@ impl RuntimeGenerator {
|
||||
/// Panics if the runtime metadata version is not supported.
|
||||
///
|
||||
/// Supported versions: v14 and v15.
|
||||
pub fn new(metadata: RuntimeMetadataPrefixed) -> Self {
|
||||
let mut metadata = match metadata.1 {
|
||||
RuntimeMetadata::V14(v14) => metadata_v14_to_latest(v14),
|
||||
RuntimeMetadata::V15(v15) => v15,
|
||||
_ => panic!("Unsupported metadata version {:?}", metadata.1),
|
||||
};
|
||||
|
||||
pub fn new(mut metadata: Metadata) -> Self {
|
||||
Self::ensure_unique_type_paths(&mut metadata);
|
||||
|
||||
RuntimeGenerator { metadata }
|
||||
}
|
||||
|
||||
@@ -190,9 +181,9 @@ impl RuntimeGenerator {
|
||||
/// unique path, so that types with matching paths don't end up overwriting each other
|
||||
/// in the codegen. We ignore any types with generics; Subxt actually endeavours to
|
||||
/// de-duplicate those into single types with a generic.
|
||||
fn ensure_unique_type_paths(metadata: &mut RuntimeMetadataV15) {
|
||||
fn ensure_unique_type_paths(metadata: &mut Metadata) {
|
||||
let mut visited_path_counts = HashMap::<Vec<String>, usize>::new();
|
||||
for ty in &mut metadata.types.types {
|
||||
for ty in metadata.types_mut().types.iter_mut() {
|
||||
// Ignore types without a path (ie prelude types).
|
||||
if ty.ty.path.namespace().is_empty() {
|
||||
continue;
|
||||
@@ -249,7 +240,7 @@ impl RuntimeGenerator {
|
||||
let rust_items = item_mod_ir.rust_items();
|
||||
|
||||
let type_gen = TypeGenerator::new(
|
||||
&self.metadata.types,
|
||||
self.metadata.types(),
|
||||
"runtime_types",
|
||||
type_substitutes,
|
||||
derives,
|
||||
@@ -300,7 +291,7 @@ impl RuntimeGenerator {
|
||||
let default_derives = derives.default_derives();
|
||||
|
||||
let type_gen = TypeGenerator::new(
|
||||
&self.metadata.types,
|
||||
self.metadata.types(),
|
||||
"runtime_types",
|
||||
type_substitutes,
|
||||
derives.clone(),
|
||||
@@ -311,12 +302,11 @@ impl RuntimeGenerator {
|
||||
let types_mod_ident = types_mod.ident();
|
||||
let pallets_with_mod_names = self
|
||||
.metadata
|
||||
.pallets
|
||||
.iter()
|
||||
.pallets()
|
||||
.map(|pallet| {
|
||||
(
|
||||
pallet,
|
||||
format_ident!("{}", pallet.name.to_string().to_snake_case()),
|
||||
format_ident!("{}", pallet.name().to_string().to_snake_case()),
|
||||
)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
@@ -326,21 +316,21 @@ impl RuntimeGenerator {
|
||||
// validation of just those pallets.
|
||||
let pallet_names: Vec<_> = self
|
||||
.metadata
|
||||
.pallets
|
||||
.iter()
|
||||
.map(|pallet| &pallet.name)
|
||||
.pallets()
|
||||
.map(|pallet| pallet.name())
|
||||
.collect();
|
||||
let pallet_names_len = pallet_names.len();
|
||||
|
||||
let metadata_hash = MetadataHasher::new()
|
||||
let metadata_hash = self
|
||||
.metadata
|
||||
.hasher()
|
||||
.only_these_pallets(&pallet_names)
|
||||
.hash(&self.metadata);
|
||||
.hash();
|
||||
|
||||
let modules = pallets_with_mod_names
|
||||
.iter()
|
||||
.map(|(pallet, mod_name)| {
|
||||
let calls = calls::generate_calls(
|
||||
&self.metadata,
|
||||
&type_gen,
|
||||
pallet,
|
||||
types_mod_ident,
|
||||
@@ -357,7 +347,6 @@ impl RuntimeGenerator {
|
||||
)?;
|
||||
|
||||
let storage_mod = storage::generate_storage(
|
||||
&self.metadata,
|
||||
&type_gen,
|
||||
pallet,
|
||||
types_mod_ident,
|
||||
@@ -366,7 +355,6 @@ impl RuntimeGenerator {
|
||||
)?;
|
||||
|
||||
let constants_mod = constants::generate_constants(
|
||||
&self.metadata,
|
||||
&type_gen,
|
||||
pallet,
|
||||
types_mod_ident,
|
||||
@@ -390,12 +378,12 @@ impl RuntimeGenerator {
|
||||
})
|
||||
.collect::<Result<Vec<_>, CodegenError>>()?;
|
||||
|
||||
let outer_event_variants = self.metadata.pallets.iter().filter_map(|p| {
|
||||
let variant_name = format_ident!("{}", p.name);
|
||||
let mod_name = format_ident!("{}", p.name.to_string().to_snake_case());
|
||||
let index = proc_macro2::Literal::u8_unsuffixed(p.index);
|
||||
let outer_event_variants = self.metadata.pallets().filter_map(|p| {
|
||||
let variant_name = format_ident!("{}", p.name());
|
||||
let mod_name = format_ident!("{}", p.name().to_string().to_snake_case());
|
||||
let index = proc_macro2::Literal::u8_unsuffixed(p.index());
|
||||
|
||||
p.event.as_ref().map(|_| {
|
||||
p.event_ty_id().map(|_| {
|
||||
quote! {
|
||||
#[codec(index = #index)]
|
||||
#variant_name(#mod_name::Event),
|
||||
@@ -410,12 +398,12 @@ impl RuntimeGenerator {
|
||||
}
|
||||
};
|
||||
|
||||
let outer_extrinsic_variants = self.metadata.pallets.iter().filter_map(|p| {
|
||||
let variant_name = format_ident!("{}", p.name);
|
||||
let mod_name = format_ident!("{}", p.name.to_string().to_snake_case());
|
||||
let index = proc_macro2::Literal::u8_unsuffixed(p.index);
|
||||
let outer_extrinsic_variants = self.metadata.pallets().filter_map(|p| {
|
||||
let variant_name = format_ident!("{}", p.name());
|
||||
let mod_name = format_ident!("{}", p.name().to_string().to_snake_case());
|
||||
let index = proc_macro2::Literal::u8_unsuffixed(p.index());
|
||||
|
||||
p.calls.as_ref().map(|_| {
|
||||
p.call_ty_id().map(|_| {
|
||||
quote! {
|
||||
#[codec(index = #index)]
|
||||
#variant_name(#mod_name::Call),
|
||||
@@ -430,11 +418,12 @@ impl RuntimeGenerator {
|
||||
}
|
||||
};
|
||||
|
||||
let root_event_if_arms = self.metadata.pallets.iter().filter_map(|p| {
|
||||
let variant_name_str = &p.name;
|
||||
let root_event_if_arms = self.metadata.pallets().filter_map(|p| {
|
||||
let variant_name_str = &p.name();
|
||||
let variant_name = format_ident!("{}", variant_name_str);
|
||||
let mod_name = format_ident!("{}", variant_name_str.to_string().to_snake_case());
|
||||
p.event.as_ref().map(|_| {
|
||||
|
||||
p.event_ty_id().map(|_| {
|
||||
// An 'if' arm for the RootEvent impl to match this variant name:
|
||||
quote! {
|
||||
if pallet_name == #variant_name_str {
|
||||
@@ -448,11 +437,11 @@ impl RuntimeGenerator {
|
||||
})
|
||||
});
|
||||
|
||||
let root_extrinsic_if_arms = self.metadata.pallets.iter().filter_map(|p| {
|
||||
let variant_name_str = &p.name;
|
||||
let root_extrinsic_if_arms = self.metadata.pallets().filter_map(|p| {
|
||||
let variant_name_str = p.name();
|
||||
let variant_name = format_ident!("{}", variant_name_str);
|
||||
let mod_name = format_ident!("{}", variant_name_str.to_string().to_snake_case());
|
||||
p.calls.as_ref().map(|_| {
|
||||
p.call_ty_id().map(|_| {
|
||||
// An 'if' arm for the RootExtrinsic impl to match this variant name:
|
||||
quote! {
|
||||
if pallet_name == #variant_name_str {
|
||||
@@ -466,12 +455,12 @@ impl RuntimeGenerator {
|
||||
})
|
||||
});
|
||||
|
||||
let outer_error_variants = self.metadata.pallets.iter().filter_map(|p| {
|
||||
let variant_name = format_ident!("{}", p.name);
|
||||
let mod_name = format_ident!("{}", p.name.to_string().to_snake_case());
|
||||
let index = proc_macro2::Literal::u8_unsuffixed(p.index);
|
||||
let outer_error_variants = self.metadata.pallets().filter_map(|p| {
|
||||
let variant_name = format_ident!("{}", p.name());
|
||||
let mod_name = format_ident!("{}", p.name().to_string().to_snake_case());
|
||||
let index = proc_macro2::Literal::u8_unsuffixed(p.index());
|
||||
|
||||
p.error.as_ref().map(|_| {
|
||||
p.error_ty_id().map(|_| {
|
||||
quote! {
|
||||
#[codec(index = #index)]
|
||||
#variant_name(#mod_name::Error),
|
||||
@@ -486,41 +475,41 @@ impl RuntimeGenerator {
|
||||
}
|
||||
};
|
||||
|
||||
let root_error_if_arms = self.metadata.pallets.iter().filter_map(|p| {
|
||||
let variant_name_str = &p.name;
|
||||
let root_error_if_arms = self.metadata.pallets().filter_map(|p| {
|
||||
let variant_name_str = &p.name();
|
||||
let variant_name = format_ident!("{}", variant_name_str);
|
||||
let mod_name = format_ident!("{}", variant_name_str.to_string().to_snake_case());
|
||||
p.error.as_ref().map(|err|
|
||||
{
|
||||
let type_id = err.ty.id;
|
||||
quote! {
|
||||
|
||||
p.error_ty_id().map(|type_id| {
|
||||
quote! {
|
||||
if pallet_name == #variant_name_str {
|
||||
let variant_error = #mod_name::Error::decode_with_metadata(cursor, #type_id, metadata)?;
|
||||
return Ok(Error::#variant_name(variant_error));
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
});
|
||||
|
||||
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.is_empty()).then_some(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.as_ref().map(|_| pallet_mod_name)
|
||||
})
|
||||
.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.calls.as_ref().map(|_| pallet_mod_name))
|
||||
.filter_map(|(pallet, pallet_mod_name)| pallet.call_ty_id().map(|_| pallet_mod_name))
|
||||
.collect();
|
||||
|
||||
let rust_items = item_mod_ir.rust_items();
|
||||
@@ -632,9 +621,9 @@ impl RuntimeGenerator {
|
||||
|
||||
/// check whether the Client you are using is aligned with the statically generated codegen.
|
||||
pub fn validate_codegen<T: #crate_path::Config, C: #crate_path::client::OfflineClientT<T>>(client: &C) -> Result<(), #crate_path::error::MetadataError> {
|
||||
let runtime_metadata_hash = client.metadata().metadata_hash(&PALLETS);
|
||||
let runtime_metadata_hash = client.metadata().hasher().only_these_pallets(&PALLETS).hash();
|
||||
if runtime_metadata_hash != [ #(#metadata_hash,)* ] {
|
||||
Err(#crate_path::error::MetadataError::IncompatibleMetadata)
|
||||
Err(#crate_path::error::MetadataError::IncompatibleCodegen)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -3,45 +3,42 @@
|
||||
// see LICENSE for license details.
|
||||
|
||||
use crate::{types::TypeGenerator, CodegenError, CratePath};
|
||||
use frame_metadata::v15::{RuntimeApiMetadata, RuntimeMetadataV15};
|
||||
use heck::ToSnakeCase as _;
|
||||
use heck::ToUpperCamelCase as _;
|
||||
use subxt_metadata::{Metadata, RuntimeApiMetadata};
|
||||
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote};
|
||||
use scale_info::form::PortableForm;
|
||||
|
||||
/// Generates runtime functions for the given API metadata.
|
||||
fn generate_runtime_api(
|
||||
metadata: &RuntimeMetadataV15,
|
||||
api: &RuntimeApiMetadata<PortableForm>,
|
||||
api: RuntimeApiMetadata,
|
||||
type_gen: &TypeGenerator,
|
||||
types_mod_ident: &syn::Ident,
|
||||
crate_path: &CratePath,
|
||||
should_gen_docs: bool,
|
||||
) -> Result<(TokenStream2, TokenStream2), CodegenError> {
|
||||
// Trait name must remain as is (upper case) to identity the runtime call.
|
||||
let trait_name = &api.name;
|
||||
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 trait_name_snake = format_ident!("{}", api.name().to_snake_case());
|
||||
let docs = api.docs();
|
||||
let docs: TokenStream2 = should_gen_docs
|
||||
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||
.unwrap_or_default();
|
||||
|
||||
let structs_and_methods: Vec<_> = api.methods.iter().map(|method| {
|
||||
let method_name = format_ident!("{}", method.name);
|
||||
let structs_and_methods: Vec<_> = api.methods().map(|method| {
|
||||
let method_name = format_ident!("{}", method.name());
|
||||
let method_name_str = method.name();
|
||||
|
||||
// Runtime function name is `TraitName_MethodName`.
|
||||
let runtime_fn_name = format!("{}_{}", trait_name, method_name);
|
||||
let docs = &method.docs;
|
||||
let docs = method.docs();
|
||||
let docs: TokenStream2 = should_gen_docs
|
||||
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||
.unwrap_or_default();
|
||||
|
||||
let inputs: Vec<_> = method.inputs.iter().map(|input| {
|
||||
let inputs: Vec<_> = method.inputs().map(|input| {
|
||||
let name = format_ident!("{}", &input.name);
|
||||
let ty = type_gen.resolve_type_path(input.ty.id);
|
||||
let ty = type_gen.resolve_type_path(input.ty);
|
||||
|
||||
let param = quote!(#name: #ty);
|
||||
(param, name)
|
||||
@@ -54,7 +51,7 @@ fn generate_runtime_api(
|
||||
// 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_name = format_ident!("{}", method.name().to_upper_camel_case());
|
||||
let struct_params = params.clone();
|
||||
let struct_input = quote!(
|
||||
#derives
|
||||
@@ -63,21 +60,21 @@ fn generate_runtime_api(
|
||||
}
|
||||
);
|
||||
|
||||
let output = type_gen.resolve_type_path(method.output.id);
|
||||
let output = type_gen.resolve_type_path(method.output_ty());
|
||||
|
||||
let Ok(call_hash) =
|
||||
subxt_metadata::get_runtime_api_hash(metadata, trait_name, &method.name) else {
|
||||
return Err(CodegenError::MissingRuntimeApiMetadata(
|
||||
trait_name.into(),
|
||||
method.name.clone(),
|
||||
))
|
||||
};
|
||||
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, #( #params, )* ) -> #crate_path::runtime_api::Payload<types::#struct_name, #output> {
|
||||
#crate_path::runtime_api::Payload::new_static(
|
||||
#runtime_fn_name,
|
||||
#trait_name_str,
|
||||
#method_name_str,
|
||||
types::#struct_name { #( #param_names, )* },
|
||||
[#(#call_hash,)*],
|
||||
)
|
||||
@@ -87,7 +84,7 @@ fn generate_runtime_api(
|
||||
Ok((struct_input, method))
|
||||
}).collect::<Result<_, _>>()?;
|
||||
|
||||
let trait_name = format_ident!("{}", trait_name);
|
||||
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);
|
||||
@@ -124,25 +121,16 @@ fn generate_runtime_api(
|
||||
|
||||
/// Generate the runtime APIs.
|
||||
pub fn generate_runtime_apis(
|
||||
metadata: &RuntimeMetadataV15,
|
||||
metadata: &Metadata,
|
||||
type_gen: &TypeGenerator,
|
||||
types_mod_ident: &syn::Ident,
|
||||
crate_path: &CratePath,
|
||||
should_gen_docs: bool,
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
let apis = &metadata.apis;
|
||||
|
||||
let runtime_fns: Vec<_> = apis
|
||||
.iter()
|
||||
let runtime_fns: Vec<_> = metadata
|
||||
.runtime_api_traits()
|
||||
.map(|api| {
|
||||
generate_runtime_api(
|
||||
metadata,
|
||||
api,
|
||||
type_gen,
|
||||
types_mod_ident,
|
||||
crate_path,
|
||||
should_gen_docs,
|
||||
)
|
||||
generate_runtime_api(api, type_gen, types_mod_ident, crate_path, should_gen_docs)
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
|
||||
+28
-41
@@ -3,14 +3,13 @@
|
||||
// see LICENSE for license details.
|
||||
|
||||
use crate::{types::TypeGenerator, CratePath};
|
||||
use frame_metadata::v15::{
|
||||
PalletMetadata, RuntimeMetadataV15, StorageEntryMetadata, StorageEntryModifier,
|
||||
StorageEntryType,
|
||||
};
|
||||
use heck::ToSnakeCase as _;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::{format_ident, quote};
|
||||
use scale_info::{form::PortableForm, TypeDef};
|
||||
use scale_info::TypeDef;
|
||||
use subxt_metadata::{
|
||||
PalletMetadata, StorageEntryMetadata, StorageEntryModifier, StorageEntryType,
|
||||
};
|
||||
|
||||
use super::CodegenError;
|
||||
|
||||
@@ -24,29 +23,20 @@ use super::CodegenError;
|
||||
/// - `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.
|
||||
pub fn generate_storage(
|
||||
metadata: &RuntimeMetadataV15,
|
||||
type_gen: &TypeGenerator,
|
||||
pallet: &PalletMetadata<PortableForm>,
|
||||
pallet: &PalletMetadata,
|
||||
types_mod_ident: &syn::Ident,
|
||||
crate_path: &CratePath,
|
||||
should_gen_docs: bool,
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
let Some(storage) = &pallet.storage else {
|
||||
let Some(storage) = pallet.storage() else {
|
||||
return Ok(quote!())
|
||||
};
|
||||
|
||||
let storage_fns = storage
|
||||
.entries
|
||||
.iter()
|
||||
.entries()
|
||||
.map(|entry| {
|
||||
generate_storage_entry_fns(
|
||||
metadata,
|
||||
type_gen,
|
||||
pallet,
|
||||
entry,
|
||||
crate_path,
|
||||
should_gen_docs,
|
||||
)
|
||||
generate_storage_entry_fns(type_gen, pallet, entry, crate_path, should_gen_docs)
|
||||
})
|
||||
.collect::<Result<Vec<_>, CodegenError>>()?;
|
||||
|
||||
@@ -64,18 +54,16 @@ pub fn generate_storage(
|
||||
}
|
||||
|
||||
fn generate_storage_entry_fns(
|
||||
metadata: &RuntimeMetadataV15,
|
||||
type_gen: &TypeGenerator,
|
||||
pallet: &PalletMetadata<PortableForm>,
|
||||
storage_entry: &StorageEntryMetadata<PortableForm>,
|
||||
pallet: &PalletMetadata,
|
||||
storage_entry: &StorageEntryMetadata,
|
||||
crate_path: &CratePath,
|
||||
should_gen_docs: bool,
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
let (fields, key_impl) = match &storage_entry.ty {
|
||||
let (fields, key_impl) = match storage_entry.entry_type() {
|
||||
StorageEntryType::Plain(_) => (vec![], quote!(vec![])),
|
||||
StorageEntryType::Map { key, .. } => {
|
||||
let key_ty = type_gen.resolve_type(key.id);
|
||||
match &key_ty.type_def {
|
||||
StorageEntryType::Map { key_ty, .. } => {
|
||||
match &type_gen.resolve_type(*key_ty).type_def {
|
||||
// An N-map; return each of the keys separately.
|
||||
TypeDef::Tuple(tuple) => {
|
||||
let fields = tuple
|
||||
@@ -102,7 +90,7 @@ fn generate_storage_entry_fns(
|
||||
}
|
||||
// A map with a single key; return the single key.
|
||||
_ => {
|
||||
let ty_path = type_gen.resolve_type_path(key.id);
|
||||
let ty_path = type_gen.resolve_type_path(*key_ty);
|
||||
let fields = vec![(format_ident!("_0"), ty_path)];
|
||||
let key_impl = quote! {
|
||||
vec![ #crate_path::storage::address::make_static_storage_map_key(_0.borrow()) ]
|
||||
@@ -113,21 +101,20 @@ fn generate_storage_entry_fns(
|
||||
}
|
||||
};
|
||||
|
||||
let pallet_name = &pallet.name;
|
||||
let storage_name = &storage_entry.name;
|
||||
let storage_hash = subxt_metadata::get_storage_hash(metadata, pallet_name, storage_name)
|
||||
.map_err(|_| {
|
||||
CodegenError::MissingStorageMetadata(pallet_name.into(), storage_name.into())
|
||||
})?;
|
||||
|
||||
let fn_name = format_ident!("{}", storage_entry.name.to_snake_case());
|
||||
let storage_entry_ty = match storage_entry.ty {
|
||||
StorageEntryType::Plain(ref ty) => ty,
|
||||
StorageEntryType::Map { ref value, .. } => value,
|
||||
let pallet_name = pallet.name();
|
||||
let storage_name = storage_entry.name();
|
||||
let Some(storage_hash) = pallet.storage_hash(storage_name) else {
|
||||
return Err(CodegenError::MissingStorageMetadata(pallet_name.into(), storage_name.into()))
|
||||
};
|
||||
let storage_entry_value_ty = type_gen.resolve_type_path(storage_entry_ty.id);
|
||||
|
||||
let docs = &storage_entry.docs;
|
||||
let fn_name = format_ident!("{}", storage_entry.name().to_snake_case());
|
||||
let storage_entry_ty = match storage_entry.entry_type() {
|
||||
StorageEntryType::Plain(ty) => *ty,
|
||||
StorageEntryType::Map { value_ty, .. } => *value_ty,
|
||||
};
|
||||
let storage_entry_value_ty = type_gen.resolve_type_path(storage_entry_ty);
|
||||
|
||||
let docs = storage_entry.docs();
|
||||
let docs = should_gen_docs
|
||||
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||
.unwrap_or_default();
|
||||
@@ -145,7 +132,7 @@ fn generate_storage_entry_fns(
|
||||
quote!( #field_name: impl ::std::borrow::Borrow<#field_ty> )
|
||||
});
|
||||
|
||||
let is_map_type = matches!(storage_entry.ty, StorageEntryType::Map { .. });
|
||||
let is_map_type = matches!(storage_entry.entry_type(), StorageEntryType::Map { .. });
|
||||
|
||||
// Is the entry iterable?
|
||||
let is_iterable_type = if is_map_type {
|
||||
@@ -154,7 +141,7 @@ fn generate_storage_entry_fns(
|
||||
quote!(())
|
||||
};
|
||||
|
||||
let has_default_value = match storage_entry.modifier {
|
||||
let has_default_value = match storage_entry.modifier() {
|
||||
StorageEntryModifier::Default => true,
|
||||
StorageEntryModifier::Optional => false,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user