diff --git a/codegen/src/api/errors.rs b/codegen/src/api/errors.rs index e2034c7e2d..2043a8238e 100644 --- a/codegen/src/api/errors.rs +++ b/codegen/src/api/errors.rs @@ -19,7 +19,9 @@ use proc_macro2::{ Span as Span2, TokenStream as TokenStream2, }; +use proc_macro_error::abort_call_site; use quote::quote; +use scale_info::TypeDef; /// Tokens which allow us to provide static error information in the generated output. pub struct ErrorDetails { @@ -71,17 +73,48 @@ pub fn generate_error_details(metadata: &RuntimeMetadataV14) -> ErrorDetails { } }); - ErrorDetails { - type_def: quote! { - pub struct ErrorDetails { - pub pallet: &'static str, - pub error: &'static str, - pub docs: &'static str, - } - }, - dispatch_error_impl_fn: quote! { + let dispatch_error_def = metadata + .types + .types() + .iter() + .find(|&ty| ty.ty().path().segments() == ["sp_runtime", "DispatchError"]) + .unwrap_or_else(|| { + abort_call_site!("sp_runtime::DispatchError type expected in metadata") + }) + .ty() + .type_def(); + + // Slightly older versions of substrate have a `DispatchError::Module { index, error }` + // variant. Newer versions have something like a `DispatchError::Module (Details)` variant. + // We check to see which type of variant we're dealing with based on the metadata, and + // generate the correct code to handle either older or newer substrate versions. + let module_variant_is_struct = if let TypeDef::Variant(details) = dispatch_error_def { + let module_variant = details + .variants() + .iter() + .find(|variant| variant.name() == "Module") + .unwrap_or_else(|| { + abort_call_site!("DispatchError::Module variant expected in metadata") + }); + let are_fields_named = module_variant + .fields() + .get(0) + .unwrap_or_else(|| { + abort_call_site!( + "DispatchError::Module expected to contain 1 or more fields" + ) + }) + .name() + .is_some(); + are_fields_named + } else { + false + }; + + let dispatch_error_impl_fn = if module_variant_is_struct { + quote! { pub fn details(&self) -> Option { - if let Self::Module { index, error } = self { + if let Self::Module { error, index } = self { match (index, error) { #( #match_body_items ),*, _ => None @@ -90,7 +123,31 @@ pub fn generate_error_details(metadata: &RuntimeMetadataV14) -> ErrorDetails { None } } + } + } else { + quote! { + pub fn details(&self) -> Option { + if let Self::Module (module_error) = self { + match (module_error.index, module_error.error) { + #( #match_body_items ),*, + _ => None + } + } else { + None + } + } + } + }; + + ErrorDetails { + type_def: quote! { + pub struct ErrorDetails { + pub pallet: &'static str, + pub error: &'static str, + pub docs: &'static str, + } }, + dispatch_error_impl_fn, } } diff --git a/codegen/src/api/mod.rs b/codegen/src/api/mod.rs index 2381bf0087..1681ea9919 100644 --- a/codegen/src/api/mod.rs +++ b/codegen/src/api/mod.rs @@ -284,7 +284,7 @@ impl RuntimeGenerator { #types_mod /// The default error type returned when there is a runtime issue. - pub type DispatchError = self::runtime_types::sp_runtime::DispatchError; + pub type DispatchError = #types_mod_ident::sp_runtime::DispatchError; // Statically generate error information so that we don't need runtime metadata for it. #error_type