mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-23 03:47:59 +00:00
Use the generated DispatchError instead of the hardcoded Substrate one (#394)
* WIP DispatchError generic param * main crate now compiling with new E generic param for DispatchError * Remove E param from RpcClient since it doesn't really need it * Point to generated DispatchError in codegen so no need for additional param there * More error hunting; cargo check --all-targets passes now * Use our own RuntimeVersion struct (for now) to avoid error decoding into sp_version::RuntimeVersion * cargo fmt * fix docs and expose private documented thing that I think should be pub * move error info to compile time so that we can make DispatchError a little nicer to work with * cargo fmt * clippy * Rework error handling to remove <E> param in most cases * fix Error doc ambiguity (hopefully) * doc tweaks * docs: remove dismbiguation thing that isn't needed now * One more Error<E> that can be a BasicError * rewrite pallet errors thing into normal loops to tidy * tidy errors codegen a little * tidy examples/custom_type_derives.rs a little * cargo fmt * silcnce clippy in example
This commit is contained in:
@@ -68,7 +68,7 @@ pub fn generate_calls(
|
||||
pub fn #fn_name(
|
||||
&self,
|
||||
#( #call_fn_args, )*
|
||||
) -> ::subxt::SubmittableExtrinsic<'a, T, E, A, #call_struct_name> {
|
||||
) -> ::subxt::SubmittableExtrinsic<'a, T, X, A, #call_struct_name, DispatchError> {
|
||||
let call = #call_struct_name { #( #call_args, )* };
|
||||
::subxt::SubmittableExtrinsic::new(self.client, call)
|
||||
}
|
||||
@@ -80,17 +80,20 @@ pub fn generate_calls(
|
||||
quote! {
|
||||
pub mod calls {
|
||||
use super::#types_mod_ident;
|
||||
|
||||
type DispatchError = #types_mod_ident::sp_runtime::DispatchError;
|
||||
|
||||
#( #call_structs )*
|
||||
|
||||
pub struct TransactionApi<'a, T: ::subxt::Config, E, A> {
|
||||
pub struct TransactionApi<'a, T: ::subxt::Config, X, A> {
|
||||
client: &'a ::subxt::Client<T>,
|
||||
marker: ::core::marker::PhantomData<(E, A)>,
|
||||
marker: ::core::marker::PhantomData<(X, A)>,
|
||||
}
|
||||
|
||||
impl<'a, T, E, A> TransactionApi<'a, T, E, A>
|
||||
impl<'a, T, X, A> TransactionApi<'a, T, X, A>
|
||||
where
|
||||
T: ::subxt::Config,
|
||||
E: ::subxt::SignedExtra<T>,
|
||||
X: ::subxt::SignedExtra<T>,
|
||||
A: ::subxt::AccountData<T>,
|
||||
{
|
||||
pub fn new(client: &'a ::subxt::Client<T>) -> Self {
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// This file is part of subxt.
|
||||
//
|
||||
// subxt is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// subxt is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use frame_metadata::v14::RuntimeMetadataV14;
|
||||
use proc_macro2::{
|
||||
Span as Span2,
|
||||
TokenStream as TokenStream2,
|
||||
};
|
||||
use quote::quote;
|
||||
|
||||
/// Tokens which allow us to provide static error information in the generated output.
|
||||
pub struct ErrorDetails {
|
||||
/// This type definition will be used in the `dispatch_error_impl_fn` and is
|
||||
/// expected to be generated somewhere in scope for that to be possible.
|
||||
pub type_def: TokenStream2,
|
||||
// A function which will live in an impl block for our `DispatchError`,
|
||||
// to statically return details for known error types:
|
||||
pub dispatch_error_impl_fn: TokenStream2,
|
||||
}
|
||||
|
||||
impl ErrorDetails {
|
||||
fn emit_compile_error(err: &str) -> ErrorDetails {
|
||||
let err_lit_str = syn::LitStr::new(err, Span2::call_site());
|
||||
ErrorDetails {
|
||||
type_def: quote!(),
|
||||
dispatch_error_impl_fn: quote!(compile_error!(#err_lit_str)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The purpose of this is to enumerate all of the possible `(module_index, error_index)` error
|
||||
/// variants, so that we can convert `u8` error codes inside a generated `DispatchError` into
|
||||
/// nicer error strings with documentation. To do this, we emit the type we'll return instances of,
|
||||
/// and a function that returns such an instance for all of the error codes seen in the metadata.
|
||||
pub fn generate_error_details(metadata: &RuntimeMetadataV14) -> ErrorDetails {
|
||||
let errors = match pallet_errors(metadata) {
|
||||
Ok(errors) => errors,
|
||||
Err(e) => {
|
||||
let err_string =
|
||||
format!("Failed to generate error details from metadata: {}", e);
|
||||
return ErrorDetails::emit_compile_error(&err_string)
|
||||
}
|
||||
};
|
||||
|
||||
let match_body_items = errors.into_iter().map(|err| {
|
||||
let docs = err.docs;
|
||||
let pallet_index = err.pallet_index;
|
||||
let error_index = err.error_index;
|
||||
let pallet_name = err.pallet;
|
||||
let error_name = err.error;
|
||||
|
||||
quote! {
|
||||
(#pallet_index, #error_index) => Some(ErrorDetails {
|
||||
pallet: #pallet_name,
|
||||
error: #error_name,
|
||||
docs: #docs
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
ErrorDetails {
|
||||
type_def: quote! {
|
||||
pub struct ErrorDetails {
|
||||
pub pallet: &'static str,
|
||||
pub error: &'static str,
|
||||
pub docs: &'static str,
|
||||
}
|
||||
},
|
||||
dispatch_error_impl_fn: quote! {
|
||||
pub fn details(&self) -> Option<ErrorDetails> {
|
||||
if let Self::Module { index, error } = self {
|
||||
match (index, error) {
|
||||
#( #match_body_items ),*,
|
||||
_ => None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn pallet_errors(
|
||||
metadata: &RuntimeMetadataV14,
|
||||
) -> Result<Vec<ErrorMetadata>, InvalidMetadataError> {
|
||||
let get_type_def_variant = |type_id: u32| {
|
||||
let ty = metadata
|
||||
.types
|
||||
.resolve(type_id)
|
||||
.ok_or(InvalidMetadataError::MissingType(type_id))?;
|
||||
if let scale_info::TypeDef::Variant(var) = ty.type_def() {
|
||||
Ok(var)
|
||||
} else {
|
||||
Err(InvalidMetadataError::TypeDefNotVariant(type_id))
|
||||
}
|
||||
};
|
||||
|
||||
let mut pallet_errors = vec![];
|
||||
for pallet in &metadata.pallets {
|
||||
let error = match &pallet.error {
|
||||
Some(err) => err,
|
||||
None => continue,
|
||||
};
|
||||
|
||||
let type_def_variant = get_type_def_variant(error.ty.id())?;
|
||||
for var in type_def_variant.variants().iter() {
|
||||
pallet_errors.push(ErrorMetadata {
|
||||
pallet_index: pallet.index,
|
||||
error_index: var.index(),
|
||||
pallet: pallet.name.clone(),
|
||||
error: var.name().clone(),
|
||||
docs: var.docs().join("\n"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(pallet_errors)
|
||||
}
|
||||
|
||||
/// Information about each error that we find in the metadata;
|
||||
/// used to generate the static error information.
|
||||
#[derive(Clone, Debug)]
|
||||
struct ErrorMetadata {
|
||||
pub pallet_index: u8,
|
||||
pub error_index: u8,
|
||||
pub pallet: String,
|
||||
pub error: String,
|
||||
pub docs: String,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum InvalidMetadataError {
|
||||
MissingType(u32),
|
||||
TypeDefNotVariant(u32),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for InvalidMetadataError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
InvalidMetadataError::MissingType(n) => {
|
||||
write!(f, "Type {} missing from type registry", n)
|
||||
}
|
||||
InvalidMetadataError::TypeDefNotVariant(n) => {
|
||||
write!(f, "Type {} was not a variant/enum type", n)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
-12
@@ -15,6 +15,7 @@
|
||||
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
mod calls;
|
||||
mod errors;
|
||||
mod events;
|
||||
mod storage;
|
||||
|
||||
@@ -216,6 +217,10 @@ impl RuntimeGenerator {
|
||||
pallet.calls.as_ref().map(|_| pallet_mod_name)
|
||||
});
|
||||
|
||||
let error_details = errors::generate_error_details(&self.metadata);
|
||||
let error_type = error_details.type_def;
|
||||
let error_fn = error_details.dispatch_error_impl_fn;
|
||||
|
||||
quote! {
|
||||
#[allow(dead_code, unused_imports, non_camel_case_types)]
|
||||
pub mod #mod_ident {
|
||||
@@ -227,6 +232,15 @@ impl RuntimeGenerator {
|
||||
/// constructing a transaction.
|
||||
pub type DefaultAccountData = self::system::storage::Account;
|
||||
|
||||
/// The default error type returned when there is a runtime issue.
|
||||
pub type DispatchError = self::runtime_types::sp_runtime::DispatchError;
|
||||
|
||||
// Statically generate error information so that we don't need runtime metadata for it.
|
||||
#error_type
|
||||
impl DispatchError {
|
||||
#error_fn
|
||||
}
|
||||
|
||||
impl ::subxt::AccountData<::subxt::DefaultConfig> for DefaultAccountData {
|
||||
fn nonce(result: &<Self as ::subxt::StorageEntry>::Value) -> <::subxt::DefaultConfig as ::subxt::Config>::Index {
|
||||
result.nonce
|
||||
@@ -236,31 +250,31 @@ impl RuntimeGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RuntimeApi<T: ::subxt::Config, E> {
|
||||
pub struct RuntimeApi<T: ::subxt::Config, X> {
|
||||
pub client: ::subxt::Client<T>,
|
||||
marker: ::core::marker::PhantomData<E>,
|
||||
marker: ::core::marker::PhantomData<X>,
|
||||
}
|
||||
|
||||
impl<T, E> ::core::convert::From<::subxt::Client<T>> for RuntimeApi<T, E>
|
||||
impl<T, X> ::core::convert::From<::subxt::Client<T>> for RuntimeApi<T, X>
|
||||
where
|
||||
T: ::subxt::Config,
|
||||
E: ::subxt::SignedExtra<T>,
|
||||
X: ::subxt::SignedExtra<T>,
|
||||
{
|
||||
fn from(client: ::subxt::Client<T>) -> Self {
|
||||
Self { client, marker: ::core::marker::PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T, E> RuntimeApi<T, E>
|
||||
impl<'a, T, X> RuntimeApi<T, X>
|
||||
where
|
||||
T: ::subxt::Config,
|
||||
E: ::subxt::SignedExtra<T>,
|
||||
X: ::subxt::SignedExtra<T>,
|
||||
{
|
||||
pub fn storage(&'a self) -> StorageApi<'a, T> {
|
||||
StorageApi { client: &self.client }
|
||||
}
|
||||
|
||||
pub fn tx(&'a self) -> TransactionApi<'a, T, E, DefaultAccountData> {
|
||||
pub fn tx(&'a self) -> TransactionApi<'a, T, X, DefaultAccountData> {
|
||||
TransactionApi { client: &self.client, marker: ::core::marker::PhantomData }
|
||||
}
|
||||
}
|
||||
@@ -280,19 +294,19 @@ impl RuntimeGenerator {
|
||||
)*
|
||||
}
|
||||
|
||||
pub struct TransactionApi<'a, T: ::subxt::Config, E, A> {
|
||||
pub struct TransactionApi<'a, T: ::subxt::Config, X, A> {
|
||||
client: &'a ::subxt::Client<T>,
|
||||
marker: ::core::marker::PhantomData<(E, A)>,
|
||||
marker: ::core::marker::PhantomData<(X, A)>,
|
||||
}
|
||||
|
||||
impl<'a, T, E, A> TransactionApi<'a, T, E, A>
|
||||
impl<'a, T, X, A> TransactionApi<'a, T, X, A>
|
||||
where
|
||||
T: ::subxt::Config,
|
||||
E: ::subxt::SignedExtra<T>,
|
||||
X: ::subxt::SignedExtra<T>,
|
||||
A: ::subxt::AccountData<T>,
|
||||
{
|
||||
#(
|
||||
pub fn #pallets_with_calls(&self) -> #pallets_with_calls::calls::TransactionApi<'a, T, E, A> {
|
||||
pub fn #pallets_with_calls(&self) -> #pallets_with_calls::calls::TransactionApi<'a, T, X, A> {
|
||||
#pallets_with_calls::calls::TransactionApi::new(self.client)
|
||||
}
|
||||
)*
|
||||
|
||||
@@ -50,6 +50,7 @@ pub fn generate_storage(
|
||||
quote! {
|
||||
pub mod storage {
|
||||
use super::#types_mod_ident;
|
||||
|
||||
#( #storage_structs )*
|
||||
|
||||
pub struct StorageApi<'a, T: ::subxt::Config> {
|
||||
@@ -195,7 +196,7 @@ fn generate_storage_entry_fns(
|
||||
pub async fn #fn_name_iter(
|
||||
&self,
|
||||
hash: ::core::option::Option<T::Hash>,
|
||||
) -> ::core::result::Result<::subxt::KeyIter<'a, T, #entry_struct_ident>, ::subxt::Error> {
|
||||
) -> ::core::result::Result<::subxt::KeyIter<'a, T, #entry_struct_ident>, ::subxt::BasicError> {
|
||||
self.client.storage().iter(hash).await
|
||||
}
|
||||
)
|
||||
@@ -211,7 +212,7 @@ fn generate_storage_entry_fns(
|
||||
&self,
|
||||
#( #key_args, )*
|
||||
hash: ::core::option::Option<T::Hash>,
|
||||
) -> ::core::result::Result<#return_ty, ::subxt::Error> {
|
||||
) -> ::core::result::Result<#return_ty, ::subxt::BasicError> {
|
||||
let entry = #constructor;
|
||||
self.client.storage().#fetch(&entry, hash).await
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user