mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-11 02:31:06 +00:00
codegen for root level error (#930)
* code gen for root error * cargo fmt * polkadot.rs regenerated * use pallet name and decode with metadata * remove pallet by name fn * test that we can decode a ModuleError via as_root_error * nits --------- Co-authored-by: James Wilson <james@jsdw.me>
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
// Copyright 2019-2023 Parity Technologies (UK) Ltd.
|
||||
// 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 crate::types::TypeGenerator;
|
||||
|
||||
use super::CodegenError;
|
||||
|
||||
/// Generate error type alias from the provided pallet metadata.
|
||||
pub fn generate_error_type_alias(
|
||||
type_gen: &TypeGenerator,
|
||||
pallet: &PalletMetadata<PortableForm>,
|
||||
should_gen_docs: bool,
|
||||
) -> Result<TokenStream2, CodegenError> {
|
||||
let Some(error) = &pallet.error 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 docs = &error_ty.docs;
|
||||
let docs = should_gen_docs
|
||||
.then_some(quote! { #( #[doc = #docs ] )* })
|
||||
.unwrap_or_default();
|
||||
Ok(quote! {
|
||||
#docs
|
||||
pub type Error = #error_type;
|
||||
})
|
||||
}
|
||||
+52
-1
@@ -6,6 +6,7 @@
|
||||
|
||||
mod calls;
|
||||
mod constants;
|
||||
mod errors;
|
||||
mod events;
|
||||
mod storage;
|
||||
|
||||
@@ -320,10 +321,13 @@ impl RuntimeGenerator {
|
||||
should_gen_docs,
|
||||
)?;
|
||||
|
||||
let errors = errors::generate_error_type_alias(&type_gen, pallet, should_gen_docs)?;
|
||||
|
||||
Ok(quote! {
|
||||
pub mod #mod_name {
|
||||
use super::root_mod;
|
||||
use super::#types_mod_ident;
|
||||
#errors
|
||||
#calls
|
||||
#event
|
||||
#storage_mod
|
||||
@@ -371,6 +375,43 @@ 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);
|
||||
|
||||
p.error.as_ref().map(|_| {
|
||||
quote! {
|
||||
#[codec(index = #index)]
|
||||
#variant_name(#mod_name::Error),
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
let outer_error = quote! {
|
||||
#default_derives
|
||||
pub enum Error {
|
||||
#( #outer_error_variants )*
|
||||
}
|
||||
};
|
||||
|
||||
let root_error_if_arms = self.metadata.pallets.iter().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! {
|
||||
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()
|
||||
@@ -424,6 +465,16 @@ impl RuntimeGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
#outer_error
|
||||
impl #crate_path::error::RootError for Error {
|
||||
fn root_error(pallet_bytes: &[u8], pallet_name: &str, metadata: &#crate_path::Metadata) -> Result<Self, #crate_path::Error> {
|
||||
use #crate_path::metadata::DecodeWithMetadata;
|
||||
let cursor = &mut &pallet_bytes[..];
|
||||
#( #root_error_if_arms )*
|
||||
Err(#crate_path::ext::scale_decode::Error::custom(format!("Pallet name '{}' not found in root Error enum", pallet_name)).into())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn constants() -> ConstantsApi {
|
||||
ConstantsApi
|
||||
}
|
||||
@@ -495,7 +546,7 @@ where
|
||||
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()))
|
||||
return Err(CodegenError::InvalidType(error_message_type_name.into()));
|
||||
};
|
||||
|
||||
variant
|
||||
|
||||
@@ -10,6 +10,9 @@ use core::fmt::Debug;
|
||||
use scale_decode::visitor::DecodeAsTypeResult;
|
||||
use std::borrow::Cow;
|
||||
|
||||
use super::Error;
|
||||
use crate::error::RootError;
|
||||
|
||||
/// An error dispatching a transaction.
|
||||
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
@@ -133,12 +136,13 @@ impl PartialEq for ModuleError {
|
||||
self.raw == other.raw
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for ModuleError {}
|
||||
|
||||
impl std::fmt::Display for ModuleError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let Ok(details) = self.details() else {
|
||||
return f.write_str("Unknown pallet error (pallet and error details cannot be retrieved)")
|
||||
return f.write_str("Unknown pallet error (pallet and error details cannot be retrieved)");
|
||||
};
|
||||
|
||||
let pallet = details.pallet();
|
||||
@@ -159,6 +163,12 @@ impl ModuleError {
|
||||
pub fn raw(&self) -> RawModuleError {
|
||||
self.raw
|
||||
}
|
||||
|
||||
/// Attempts to decode the ModuleError into a value implementing the trait `RootError`
|
||||
/// where the actual type of value is the generated top level enum `Error`.
|
||||
pub fn as_root_error<E: RootError>(&self) -> Result<E, Error> {
|
||||
E::root_error(&self.raw.error, self.details()?.pallet(), &self.metadata)
|
||||
}
|
||||
}
|
||||
|
||||
/// The error details about a module error that has occurred.
|
||||
|
||||
+13
-1
@@ -14,7 +14,7 @@ pub use dispatch_error::{
|
||||
};
|
||||
|
||||
// Re-expose the errors we use from other crates here:
|
||||
pub use crate::metadata::{InvalidMetadataError, MetadataError};
|
||||
pub use crate::metadata::{InvalidMetadataError, Metadata, MetadataError};
|
||||
pub use scale_decode::Error as DecodeError;
|
||||
pub use scale_encode::Error as EncodeError;
|
||||
|
||||
@@ -162,3 +162,15 @@ pub enum StorageAddressError {
|
||||
fields: usize,
|
||||
},
|
||||
}
|
||||
|
||||
/// This trait is implemented on the statically generated root ModuleError type
|
||||
#[doc(hidden)]
|
||||
pub trait RootError: Sized {
|
||||
/// Given details of the pallet error we want to decode
|
||||
fn root_error(
|
||||
// typically a [u8; 4] encodes the error of a pallet
|
||||
pallet_bytes: &[u8],
|
||||
pallet_name: &str,
|
||||
metadata: &Metadata,
|
||||
) -> Result<Self, Error>;
|
||||
}
|
||||
|
||||
@@ -350,6 +350,43 @@ async fn submit_large_extrinsic() {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn decode_a_module_error() {
|
||||
use node_runtime::runtime_types::pallet_assets::pallet as assets;
|
||||
|
||||
let ctx = test_context().await;
|
||||
let api = ctx.client();
|
||||
|
||||
let alice = pair_signer(AccountKeyring::Alice.pair());
|
||||
let alice_addr = alice.account_id().clone().into();
|
||||
|
||||
// Trying to work with an asset ID 1 which doesn't exist should return an
|
||||
// "unknown" module error from the assets pallet.
|
||||
let freeze_unknown_asset = node_runtime::tx().assets().freeze(1, alice_addr);
|
||||
|
||||
let err = api
|
||||
.tx()
|
||||
.sign_and_submit_then_watch_default(&freeze_unknown_asset, &alice)
|
||||
.await
|
||||
.unwrap()
|
||||
.wait_for_finalized_success()
|
||||
.await
|
||||
.expect_err("an 'unknown asset' error");
|
||||
|
||||
let Error::Runtime(DispatchError::Module(module_err)) = err else {
|
||||
panic!("Expected a ModuleError, got {err:?}");
|
||||
};
|
||||
|
||||
// Decode the error into our generated Error type.
|
||||
let decoded_err = module_err.as_root_error::<node_runtime::Error>().unwrap();
|
||||
|
||||
// Decoding should result in an Assets.Unknown error:
|
||||
assert_eq!(
|
||||
decoded_err,
|
||||
node_runtime::Error::Assets(assets::Error::Unknown)
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn unsigned_extrinsic_is_same_shape_as_polkadotjs() {
|
||||
let ctx = test_context().await;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user