mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-22 05:37:58 +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:
+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()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user