mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-23 00:18:00 +00:00
codegen: Add codegen error (#841)
* codegen: Add codegen error Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * codegen: Use codegen error instead of aborts Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * codegen: Remove `proc-macro-error` dependency Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * macro/subxt: Transform codegen error into compile_error! Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cli: Pretty printing for `CodegenError` Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * Update cargo.lock Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * tests: Adjust testing for codegen error Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * codegen: Fix documentation example Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * codegen: Export `CodegenError` Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * codegen: Use collect::<Result<_>, _>() Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * cli: Adjust comment regarding error printing Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> * codegen: Improve error messages Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io> --------- Signed-off-by: Alexandru Vasile <alexandru.vasile@parity.io>
This commit is contained in:
+19
-24
@@ -2,6 +2,7 @@
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use super::CodegenError;
|
||||
use crate::{
|
||||
types::{
|
||||
CompositeDefFields,
|
||||
@@ -18,7 +19,6 @@ use heck::{
|
||||
ToUpperCamelCase as _,
|
||||
};
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use proc_macro_error::abort_call_site;
|
||||
use quote::{
|
||||
format_ident,
|
||||
quote,
|
||||
@@ -41,12 +41,10 @@ pub fn generate_calls(
|
||||
types_mod_ident: &syn::Ident,
|
||||
crate_path: &CratePath,
|
||||
should_gen_docs: bool,
|
||||
) -> TokenStream2 {
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
// Early return if the pallet has no calls.
|
||||
let call = if let Some(ref calls) = pallet.calls {
|
||||
calls
|
||||
} else {
|
||||
return quote!()
|
||||
let Some(call) = &pallet.calls else {
|
||||
return Ok(quote!());
|
||||
};
|
||||
|
||||
let mut struct_defs = super::generate_structs_from_variants(
|
||||
@@ -56,7 +54,7 @@ pub fn generate_calls(
|
||||
"Call",
|
||||
crate_path,
|
||||
should_gen_docs,
|
||||
);
|
||||
)?;
|
||||
let (call_structs, call_fns): (Vec<_>, Vec<_>) = struct_defs
|
||||
.iter_mut()
|
||||
.map(|(variant_name, struct_def)| {
|
||||
@@ -77,26 +75,20 @@ pub fn generate_calls(
|
||||
}
|
||||
CompositeDefFields::NoFields => Default::default(),
|
||||
CompositeDefFields::Unnamed(_) => {
|
||||
abort_call_site!(
|
||||
"Call variant for type {} must have all named fields",
|
||||
call.ty.id()
|
||||
)
|
||||
return Err(CodegenError::InvalidCallVariant(call.ty.id()))
|
||||
}
|
||||
};
|
||||
|
||||
let pallet_name = &pallet.name;
|
||||
let call_name = &variant_name;
|
||||
let struct_name = &struct_def.name;
|
||||
let call_hash =
|
||||
subxt_metadata::get_call_hash(metadata, pallet_name, call_name)
|
||||
.unwrap_or_else(|_| {
|
||||
abort_call_site!(
|
||||
"Metadata information for the call {}_{} could not be found",
|
||||
pallet_name,
|
||||
call_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 fn_name = format_ident!("{}", variant_name.to_snake_case());
|
||||
// Propagate the documentation just to `TransactionApi` methods, while
|
||||
// draining the documentation of inner call structures.
|
||||
@@ -121,8 +113,11 @@ pub fn generate_calls(
|
||||
)
|
||||
}
|
||||
};
|
||||
(call_struct, client_fn)
|
||||
|
||||
Ok((call_struct, client_fn))
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?
|
||||
.into_iter()
|
||||
.unzip();
|
||||
|
||||
let call_ty = type_gen.resolve_type(call.ty.id());
|
||||
@@ -131,7 +126,7 @@ pub fn generate_calls(
|
||||
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||
.unwrap_or_default();
|
||||
|
||||
quote! {
|
||||
Ok(quote! {
|
||||
#docs
|
||||
pub mod calls {
|
||||
use super::root_mod;
|
||||
@@ -147,5 +142,5 @@ pub fn generate_calls(
|
||||
#( #call_fns )*
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -12,13 +12,14 @@ use frame_metadata::{
|
||||
};
|
||||
use heck::ToSnakeCase as _;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use proc_macro_error::abort_call_site;
|
||||
use quote::{
|
||||
format_ident,
|
||||
quote,
|
||||
};
|
||||
use scale_info::form::PortableForm;
|
||||
|
||||
use super::CodegenError;
|
||||
|
||||
/// Generate constants from the provided pallet's metadata.
|
||||
///
|
||||
/// The function creates a new module named `constants` under the pallet's module.
|
||||
@@ -49,10 +50,10 @@ pub fn generate_constants(
|
||||
types_mod_ident: &syn::Ident,
|
||||
crate_path: &CratePath,
|
||||
should_gen_docs: bool,
|
||||
) -> TokenStream2 {
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
// Early return if the pallet has no constants.
|
||||
if pallet.constants.is_empty() {
|
||||
return quote!()
|
||||
return Ok(quote!())
|
||||
}
|
||||
let constants = &pallet.constants;
|
||||
|
||||
@@ -60,8 +61,9 @@ pub fn generate_constants(
|
||||
let fn_name = format_ident!("{}", constant.name.to_snake_case());
|
||||
let pallet_name = &pallet.name;
|
||||
let constant_name = &constant.name;
|
||||
let constant_hash = subxt_metadata::get_constant_hash(metadata, pallet_name, constant_name)
|
||||
.unwrap_or_else(|_| abort_call_site!("Metadata information for the constant {}_{} could not be found", pallet_name, constant_name));
|
||||
let Ok(constant_hash) = subxt_metadata::get_constant_hash(metadata, pallet_name, 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;
|
||||
@@ -69,7 +71,7 @@ pub fn generate_constants(
|
||||
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||
.unwrap_or_default();
|
||||
|
||||
quote! {
|
||||
Ok(quote! {
|
||||
#docs
|
||||
pub fn #fn_name(&self) -> #crate_path::constants::StaticConstantAddress<#crate_path::metadata::DecodeStaticType<#return_ty>> {
|
||||
#crate_path::constants::StaticConstantAddress::new(
|
||||
@@ -78,10 +80,10 @@ pub fn generate_constants(
|
||||
[#(#constant_hash,)*]
|
||||
)
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
}).collect::<Result<Vec<_>, _>>()?;
|
||||
|
||||
quote! {
|
||||
Ok(quote! {
|
||||
pub mod constants {
|
||||
use super::#types_mod_ident;
|
||||
|
||||
@@ -91,5 +93,5 @@ pub fn generate_constants(
|
||||
#(#constant_fns)*
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ use proc_macro2::TokenStream as TokenStream2;
|
||||
use quote::quote;
|
||||
use scale_info::form::PortableForm;
|
||||
|
||||
use super::CodegenError;
|
||||
|
||||
/// Generate events from the provided pallet metadata.
|
||||
///
|
||||
/// The function creates a new module named `events` under the pallet's module.
|
||||
@@ -46,12 +48,10 @@ pub fn generate_events(
|
||||
types_mod_ident: &syn::Ident,
|
||||
crate_path: &CratePath,
|
||||
should_gen_docs: bool,
|
||||
) -> TokenStream2 {
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
// Early return if the pallet has no events.
|
||||
let event = if let Some(ref event) = pallet.event {
|
||||
event
|
||||
} else {
|
||||
return quote!()
|
||||
let Some(event) = &pallet.event else {
|
||||
return Ok(quote!())
|
||||
};
|
||||
|
||||
let struct_defs = super::generate_structs_from_variants(
|
||||
@@ -61,7 +61,8 @@ pub fn generate_events(
|
||||
"Event",
|
||||
crate_path,
|
||||
should_gen_docs,
|
||||
);
|
||||
)?;
|
||||
|
||||
let event_structs = struct_defs.iter().map(|(variant_name, struct_def)| {
|
||||
let pallet_name = &pallet.name;
|
||||
let event_struct = &struct_def.name;
|
||||
@@ -83,12 +84,12 @@ pub fn generate_events(
|
||||
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||
.unwrap_or_default();
|
||||
|
||||
quote! {
|
||||
Ok(quote! {
|
||||
#docs
|
||||
pub type Event = #event_type;
|
||||
pub mod events {
|
||||
use super::#types_mod_ident;
|
||||
#( #event_structs )*
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
+159
-95
@@ -22,6 +22,7 @@ use crate::{
|
||||
},
|
||||
utils::{
|
||||
fetch_metadata_bytes_blocking,
|
||||
FetchMetadataError,
|
||||
Uri,
|
||||
},
|
||||
CratePath,
|
||||
@@ -33,8 +34,10 @@ use frame_metadata::{
|
||||
RuntimeMetadataPrefixed,
|
||||
};
|
||||
use heck::ToSnakeCase as _;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use proc_macro_error::abort_call_site;
|
||||
use proc_macro2::{
|
||||
Span,
|
||||
TokenStream as TokenStream2,
|
||||
};
|
||||
use quote::{
|
||||
format_ident,
|
||||
quote,
|
||||
@@ -47,6 +50,67 @@ use std::{
|
||||
};
|
||||
use syn::parse_quote;
|
||||
|
||||
/// Error returned when the Codegen cannot generate the runtime API.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum CodegenError {
|
||||
/// Cannot fetch the metadata bytes.
|
||||
#[error("Failed to fetch metadata, make sure that you're pointing at a node which is providing V14 metadata: {0}")]
|
||||
Fetch(#[from] FetchMetadataError),
|
||||
/// Failed IO for the metadata file.
|
||||
#[error("Failed IO for {0}, make sure that you are providing the correct file path for metadata V14: {1}")]
|
||||
Io(String, std::io::Error),
|
||||
/// Cannot decode the metadata bytes.
|
||||
#[error("Could not decode metadata, only V14 metadata is 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),
|
||||
/// Expected named or unnamed fields.
|
||||
#[error("Fields should either be all named or all unnamed, make sure you are providing a valid metadata V14: {0}")]
|
||||
InvalidFields(String),
|
||||
/// Substitute types must have a valid path.
|
||||
#[error("Substitute types must have a valid path")]
|
||||
EmptySubstitutePath(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 metadata V14")]
|
||||
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 metadata V14")]
|
||||
MissingStorageMetadata(String, String),
|
||||
/// StorageNMap should have N hashers.
|
||||
#[error("Number of hashers ({0}) does not equal 1 for StorageMap, or match number of fields ({1}) for StorageNMap. Make sure you are providing a valid metadata V14")]
|
||||
MismatchHashers(usize, usize),
|
||||
/// Expected to find one hasher for StorageMap.
|
||||
#[error("No hasher found for single key. Make sure you are providing a valid metadata V14")]
|
||||
MissingHasher,
|
||||
/// 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 metadata V14")]
|
||||
MissingCallMetadata(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 metadata V14")]
|
||||
InvalidCallVariant(u32),
|
||||
/// Type should be an variant/enum.
|
||||
#[error("{0} type should be an variant/enum type. Make sure you are providing a valid metadata V14")]
|
||||
InvalidType(String),
|
||||
}
|
||||
|
||||
impl CodegenError {
|
||||
/// Render the error as an invocation of syn::compile_error!.
|
||||
pub fn into_compile_error(self) -> TokenStream2 {
|
||||
let msg = self.to_string();
|
||||
let span = match self {
|
||||
Self::InvalidModule(span) => span,
|
||||
Self::EmptySubstitutePath(span) => span,
|
||||
Self::InvalidTypePath(_, err) => err.span(),
|
||||
_ => proc_macro2::Span::call_site(),
|
||||
};
|
||||
syn::Error::new(span, msg).into_compile_error()
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates the API for interacting with a Substrate runtime.
|
||||
///
|
||||
/// # Arguments
|
||||
@@ -66,17 +130,15 @@ pub fn generate_runtime_api_from_path<P>(
|
||||
type_substitutes: TypeSubstitutes,
|
||||
crate_path: CratePath,
|
||||
should_gen_docs: bool,
|
||||
) -> TokenStream2
|
||||
) -> Result<TokenStream2, CodegenError>
|
||||
where
|
||||
P: AsRef<path::Path>,
|
||||
{
|
||||
let mut file = fs::File::open(&path).unwrap_or_else(|e| {
|
||||
abort_call_site!("Failed to open {}: {}", path.as_ref().to_string_lossy(), e)
|
||||
});
|
||||
let to_err = |err| CodegenError::Io(path.as_ref().to_string_lossy().into(), err);
|
||||
|
||||
let mut file = fs::File::open(&path).map_err(to_err)?;
|
||||
let mut bytes = Vec::new();
|
||||
file.read_to_end(&mut bytes)
|
||||
.unwrap_or_else(|e| abort_call_site!("Failed to read metadata file: {}", e));
|
||||
file.read_to_end(&mut bytes).map_err(to_err)?;
|
||||
|
||||
generate_runtime_api_from_bytes(
|
||||
item_mod,
|
||||
@@ -109,9 +171,8 @@ pub fn generate_runtime_api_from_url(
|
||||
type_substitutes: TypeSubstitutes,
|
||||
crate_path: CratePath,
|
||||
should_gen_docs: bool,
|
||||
) -> TokenStream2 {
|
||||
let bytes = fetch_metadata_bytes_blocking(url)
|
||||
.unwrap_or_else(|e| abort_call_site!("Failed to obtain metadata: {}", e));
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
let bytes = fetch_metadata_bytes_blocking(url)?;
|
||||
|
||||
generate_runtime_api_from_bytes(
|
||||
item_mod,
|
||||
@@ -142,9 +203,8 @@ pub fn generate_runtime_api_from_bytes(
|
||||
type_substitutes: TypeSubstitutes,
|
||||
crate_path: CratePath,
|
||||
should_gen_docs: bool,
|
||||
) -> TokenStream2 {
|
||||
let metadata = frame_metadata::RuntimeMetadataPrefixed::decode(&mut &bytes[..])
|
||||
.unwrap_or_else(|e| abort_call_site!("Failed to decode metadata: {}", e));
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
let metadata = frame_metadata::RuntimeMetadataPrefixed::decode(&mut &bytes[..])?;
|
||||
|
||||
let generator = RuntimeGenerator::new(metadata);
|
||||
generator.generate_runtime(
|
||||
@@ -187,9 +247,9 @@ impl RuntimeGenerator {
|
||||
type_substitutes: TypeSubstitutes,
|
||||
crate_path: CratePath,
|
||||
should_gen_docs: bool,
|
||||
) -> TokenStream2 {
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
let item_mod_attrs = item_mod.attrs.clone();
|
||||
let item_mod_ir = ir::ItemMod::from(item_mod);
|
||||
let item_mod_ir = ir::ItemMod::try_from(item_mod)?;
|
||||
let default_derives = derives.default_derives();
|
||||
|
||||
let type_gen = TypeGenerator::new(
|
||||
@@ -200,7 +260,7 @@ impl RuntimeGenerator {
|
||||
crate_path.clone(),
|
||||
should_gen_docs,
|
||||
);
|
||||
let types_mod = type_gen.generate_types_mod();
|
||||
let types_mod = type_gen.generate_types_mod()?;
|
||||
let types_mod_ident = types_mod.ident();
|
||||
let pallets_with_mod_names = self
|
||||
.metadata
|
||||
@@ -227,53 +287,56 @@ impl RuntimeGenerator {
|
||||
|
||||
let metadata_hash = get_metadata_per_pallet_hash(&self.metadata, &pallet_names);
|
||||
|
||||
let modules = pallets_with_mod_names.iter().map(|(pallet, mod_name)| {
|
||||
let calls = calls::generate_calls(
|
||||
&self.metadata,
|
||||
&type_gen,
|
||||
pallet,
|
||||
types_mod_ident,
|
||||
&crate_path,
|
||||
should_gen_docs,
|
||||
);
|
||||
let modules = pallets_with_mod_names
|
||||
.iter()
|
||||
.map(|(pallet, mod_name)| {
|
||||
let calls = calls::generate_calls(
|
||||
&self.metadata,
|
||||
&type_gen,
|
||||
pallet,
|
||||
types_mod_ident,
|
||||
&crate_path,
|
||||
should_gen_docs,
|
||||
)?;
|
||||
|
||||
let event = events::generate_events(
|
||||
&type_gen,
|
||||
pallet,
|
||||
types_mod_ident,
|
||||
&crate_path,
|
||||
should_gen_docs,
|
||||
);
|
||||
let event = events::generate_events(
|
||||
&type_gen,
|
||||
pallet,
|
||||
types_mod_ident,
|
||||
&crate_path,
|
||||
should_gen_docs,
|
||||
)?;
|
||||
|
||||
let storage_mod = storage::generate_storage(
|
||||
&self.metadata,
|
||||
&type_gen,
|
||||
pallet,
|
||||
types_mod_ident,
|
||||
&crate_path,
|
||||
should_gen_docs,
|
||||
);
|
||||
let storage_mod = storage::generate_storage(
|
||||
&self.metadata,
|
||||
&type_gen,
|
||||
pallet,
|
||||
types_mod_ident,
|
||||
&crate_path,
|
||||
should_gen_docs,
|
||||
)?;
|
||||
|
||||
let constants_mod = constants::generate_constants(
|
||||
&self.metadata,
|
||||
&type_gen,
|
||||
pallet,
|
||||
types_mod_ident,
|
||||
&crate_path,
|
||||
should_gen_docs,
|
||||
);
|
||||
let constants_mod = constants::generate_constants(
|
||||
&self.metadata,
|
||||
&type_gen,
|
||||
pallet,
|
||||
types_mod_ident,
|
||||
&crate_path,
|
||||
should_gen_docs,
|
||||
)?;
|
||||
|
||||
quote! {
|
||||
pub mod #mod_name {
|
||||
use super::root_mod;
|
||||
use super::#types_mod_ident;
|
||||
#calls
|
||||
#event
|
||||
#storage_mod
|
||||
#constants_mod
|
||||
}
|
||||
}
|
||||
});
|
||||
Ok(quote! {
|
||||
pub mod #mod_name {
|
||||
use super::root_mod;
|
||||
use super::#types_mod_ident;
|
||||
#calls
|
||||
#event
|
||||
#storage_mod
|
||||
#constants_mod
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect::<Result<Vec<_>, CodegenError>>()?;
|
||||
|
||||
let outer_event_variants = self.metadata.pallets.iter().filter_map(|p| {
|
||||
let variant_name = format_ident!("{}", p.name);
|
||||
@@ -319,7 +382,7 @@ impl RuntimeGenerator {
|
||||
|
||||
let rust_items = item_mod_ir.rust_items();
|
||||
|
||||
quote! {
|
||||
Ok(quote! {
|
||||
#( #item_mod_attrs )*
|
||||
#[allow(dead_code, unused_imports, non_camel_case_types)]
|
||||
#[allow(clippy::all)]
|
||||
@@ -389,7 +452,7 @@ impl RuntimeGenerator {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -401,41 +464,42 @@ pub fn generate_structs_from_variants<F>(
|
||||
error_message_type_name: &str,
|
||||
crate_path: &CratePath,
|
||||
should_gen_docs: bool,
|
||||
) -> Vec<(String, CompositeDef)>
|
||||
) -> Result<Vec<(String, CompositeDef)>, CodegenError>
|
||||
where
|
||||
F: Fn(&str) -> std::borrow::Cow<str>,
|
||||
{
|
||||
let ty = type_gen.resolve_type(type_id);
|
||||
if let scale_info::TypeDef::Variant(variant) = ty.type_def() {
|
||||
variant
|
||||
.variants()
|
||||
.iter()
|
||||
.map(|var| {
|
||||
let struct_name = variant_to_struct_name(var.name());
|
||||
let fields = CompositeDefFields::from_scale_info_fields(
|
||||
struct_name.as_ref(),
|
||||
var.fields(),
|
||||
&[],
|
||||
type_gen,
|
||||
);
|
||||
let docs = should_gen_docs.then_some(var.docs()).unwrap_or_default();
|
||||
let struct_def = CompositeDef::struct_def(
|
||||
&ty,
|
||||
struct_name.as_ref(),
|
||||
Default::default(),
|
||||
fields,
|
||||
Some(parse_quote!(pub)),
|
||||
type_gen,
|
||||
docs,
|
||||
crate_path,
|
||||
);
|
||||
(var.name().to_string(), struct_def)
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
abort_call_site!(
|
||||
"{} type should be an variant/enum type",
|
||||
error_message_type_name
|
||||
)
|
||||
}
|
||||
|
||||
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 struct_name = variant_to_struct_name(var.name());
|
||||
|
||||
let fields = CompositeDefFields::from_scale_info_fields(
|
||||
struct_name.as_ref(),
|
||||
var.fields(),
|
||||
&[],
|
||||
type_gen,
|
||||
)?;
|
||||
|
||||
let docs = should_gen_docs.then_some(var.docs()).unwrap_or_default();
|
||||
let struct_def = CompositeDef::struct_def(
|
||||
&ty,
|
||||
struct_name.as_ref(),
|
||||
Default::default(),
|
||||
fields,
|
||||
Some(parse_quote!(pub)),
|
||||
type_gen,
|
||||
docs,
|
||||
crate_path,
|
||||
)?;
|
||||
|
||||
Ok((var.name.to_string(), struct_def))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
+26
-31
@@ -16,7 +16,6 @@ use frame_metadata::{
|
||||
};
|
||||
use heck::ToSnakeCase as _;
|
||||
use proc_macro2::TokenStream as TokenStream2;
|
||||
use proc_macro_error::abort_call_site;
|
||||
use quote::{
|
||||
format_ident,
|
||||
quote,
|
||||
@@ -26,6 +25,8 @@ use scale_info::{
|
||||
TypeDef,
|
||||
};
|
||||
|
||||
use super::CodegenError;
|
||||
|
||||
/// Generate functions which create storage addresses from the provided pallet's metadata.
|
||||
/// These addresses can be used to access and iterate over storage values.
|
||||
///
|
||||
@@ -42,14 +43,12 @@ pub fn generate_storage(
|
||||
types_mod_ident: &syn::Ident,
|
||||
crate_path: &CratePath,
|
||||
should_gen_docs: bool,
|
||||
) -> TokenStream2 {
|
||||
let storage = if let Some(ref storage) = pallet.storage {
|
||||
storage
|
||||
} else {
|
||||
return quote!()
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
let Some(storage) = &pallet.storage else {
|
||||
return Ok(quote!())
|
||||
};
|
||||
|
||||
let storage_fns: Vec<_> = storage
|
||||
let storage_fns = storage
|
||||
.entries
|
||||
.iter()
|
||||
.map(|entry| {
|
||||
@@ -62,9 +61,9 @@ pub fn generate_storage(
|
||||
should_gen_docs,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
.collect::<Result<Vec<_>, CodegenError>>()?;
|
||||
|
||||
quote! {
|
||||
Ok(quote! {
|
||||
pub mod storage {
|
||||
use super::#types_mod_ident;
|
||||
|
||||
@@ -74,7 +73,7 @@ pub fn generate_storage(
|
||||
#( #storage_fns )*
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn generate_storage_entry_fns(
|
||||
@@ -84,7 +83,7 @@ fn generate_storage_entry_fns(
|
||||
storage_entry: &StorageEntryMetadata<PortableForm>,
|
||||
crate_path: &CratePath,
|
||||
should_gen_docs: bool,
|
||||
) -> TokenStream2 {
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
let (fields, key_impl) = match storage_entry.ty {
|
||||
StorageEntryType::Plain(_) => (vec![], quote!(vec![])),
|
||||
StorageEntryType::Map {
|
||||
@@ -146,13 +145,10 @@ fn generate_storage_entry_fns(
|
||||
vec![ #crate_path::storage::address::StorageMapKey::new(&(#( #items.borrow() ),*), #hasher) ]
|
||||
}
|
||||
} else {
|
||||
// If we hit this condition, we don't know how to handle the number of hashes vs fields
|
||||
// that we've been handed, so abort.
|
||||
abort_call_site!(
|
||||
"Number of hashers ({}) does not equal 1 for StorageMap, or match number of fields ({}) for StorageNMap",
|
||||
return Err(CodegenError::MismatchHashers(
|
||||
hashers.len(),
|
||||
fields.len()
|
||||
)
|
||||
fields.len(),
|
||||
))
|
||||
};
|
||||
|
||||
(fields, key_impl)
|
||||
@@ -160,9 +156,9 @@ fn generate_storage_entry_fns(
|
||||
_ => {
|
||||
let ty_path = type_gen.resolve_type_path(key.id());
|
||||
let fields = vec![(format_ident!("_0"), ty_path)];
|
||||
let hasher = hashers.get(0).unwrap_or_else(|| {
|
||||
abort_call_site!("No hasher found for single key")
|
||||
});
|
||||
let Some(hasher) = hashers.get(0) else {
|
||||
return Err(CodegenError::MissingHasher)
|
||||
};
|
||||
let key_impl = quote! {
|
||||
vec![ #crate_path::storage::address::StorageMapKey::new(_0.borrow(), #hasher) ]
|
||||
};
|
||||
@@ -174,15 +170,14 @@ 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)
|
||||
.unwrap_or_else(|_| {
|
||||
abort_call_site!(
|
||||
"Metadata information for the storage entry {}_{} could not be found",
|
||||
pallet_name,
|
||||
storage_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 {
|
||||
@@ -251,7 +246,7 @@ fn generate_storage_entry_fns(
|
||||
quote!()
|
||||
};
|
||||
|
||||
quote! {
|
||||
Ok(quote! {
|
||||
// Access a specific value from a storage entry
|
||||
#docs
|
||||
pub fn #fn_name(
|
||||
@@ -267,5 +262,5 @@ fn generate_storage_entry_fns(
|
||||
}
|
||||
|
||||
#root_entry_fn
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user