use crate::construct_runtime::Pallet;
use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens};
use std::str::FromStr;
use syn::{Generics, Ident};
#[derive(Clone, Copy, PartialEq)]
pub enum OuterEnumType {
Event,
Error,
}
impl OuterEnumType {
fn struct_name(&self) -> &str {
match self {
OuterEnumType::Event => "RuntimeEvent",
OuterEnumType::Error => "RuntimeError",
}
}
fn variant_name(&self) -> &str {
match self {
OuterEnumType::Event => "Event",
OuterEnumType::Error => "Error",
}
}
}
impl ToTokens for OuterEnumType {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
OuterEnumType::Event => quote!(Event).to_tokens(tokens),
OuterEnumType::Error => quote!(Error).to_tokens(tokens),
}
}
}
pub fn expand_outer_enum(
runtime: &Ident,
pallet_decls: &[Pallet],
scrate: &TokenStream,
enum_ty: OuterEnumType,
) -> syn::Result<TokenStream> {
let mut enum_variants = TokenStream::new();
let mut enum_conversions = TokenStream::new();
let mut query_enum_part_macros = Vec::new();
let enum_name_str = enum_ty.variant_name();
let enum_name_ident = Ident::new(enum_ty.struct_name(), Span::call_site());
for pallet_decl in pallet_decls {
let Some(pallet_entry) = pallet_decl.find_part(enum_name_str) else { continue };
let path = &pallet_decl.path;
let pallet_name = &pallet_decl.name;
let index = pallet_decl.index;
let instance = pallet_decl.instance.as_ref();
let generics = &pallet_entry.generics;
if instance.is_some() && generics.params.is_empty() {
let msg = format!(
"Instantiable pallet with no generic `{}` cannot \
be constructed: pallet `{}` must have generic `{}`",
enum_name_str, pallet_name, enum_name_str,
);
return Err(syn::Error::new(pallet_name.span(), msg))
}
let part_is_generic = !generics.params.is_empty();
let pallet_enum = match (instance, part_is_generic) {
(Some(inst), true) => quote!(#path::#enum_ty::<#runtime, #path::#inst>),
(Some(inst), false) => quote!(#path::#enum_ty::<#path::#inst>),
(None, true) => quote!(#path::#enum_ty::<#runtime>),
(None, false) => quote!(#path::#enum_ty),
};
enum_variants.extend(expand_enum_variant(
runtime,
pallet_decl,
index,
instance,
generics,
enum_ty,
));
enum_conversions.extend(expand_enum_conversion(
pallet_decl,
&pallet_enum,
&enum_name_ident,
));
if enum_ty == OuterEnumType::Event {
query_enum_part_macros.push(quote! {
#path::__substrate_event_check::is_event_part_defined!(#pallet_name);
});
}
}
let event_custom_derives =
if enum_ty == OuterEnumType::Event { quote!(Clone, PartialEq, Eq,) } else { quote!() };
let error_custom_impl = generate_error_impl(scrate, enum_ty);
Ok(quote! {
#( #query_enum_part_macros )*
#[derive(
#event_custom_derives
#scrate::__private::codec::Encode,
#scrate::__private::codec::Decode,
#scrate::__private::scale_info::TypeInfo,
#scrate::__private::RuntimeDebug,
)]
#[allow(non_camel_case_types)]
pub enum #enum_name_ident {
#enum_variants
}
#enum_conversions
#error_custom_impl
})
}
fn expand_enum_variant(
runtime: &Ident,
pallet: &Pallet,
index: u8,
instance: Option<&Ident>,
generics: &Generics,
enum_ty: OuterEnumType,
) -> TokenStream {
let path = &pallet.path;
let variant_name = &pallet.name;
let part_is_generic = !generics.params.is_empty();
let attr = pallet.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| {
let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original()))
.expect("was successfully parsed before; qed");
quote! {
#acc
#attr
}
});
match instance {
Some(inst) if part_is_generic => quote! {
#attr
#[codec(index = #index)]
#variant_name(#path::#enum_ty<#runtime, #path::#inst>),
},
Some(inst) => quote! {
#attr
#[codec(index = #index)]
#variant_name(#path::#enum_ty<#path::#inst>),
},
None if part_is_generic => quote! {
#attr
#[codec(index = #index)]
#variant_name(#path::#enum_ty<#runtime>),
},
None => quote! {
#attr
#[codec(index = #index)]
#variant_name(#path::#enum_ty),
},
}
}
fn expand_enum_conversion(
pallet: &Pallet,
pallet_enum: &TokenStream,
enum_name_ident: &Ident,
) -> TokenStream {
let variant_name = &pallet.name;
let attr = pallet.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| {
let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original()))
.expect("was successfully parsed before; qed");
quote! {
#acc
#attr
}
});
quote! {
#attr
impl From<#pallet_enum> for #enum_name_ident {
fn from(x: #pallet_enum) -> Self {
#enum_name_ident
::#variant_name(x)
}
}
#attr
impl TryInto<#pallet_enum> for #enum_name_ident {
type Error = ();
fn try_into(self) -> ::core::result::Result<#pallet_enum, Self::Error> {
match self {
Self::#variant_name(evt) => Ok(evt),
_ => Err(()),
}
}
}
}
}
fn generate_error_impl(scrate: &TokenStream, enum_ty: OuterEnumType) -> TokenStream {
if enum_ty == OuterEnumType::Event {
return quote! {}
}
let enum_name_ident = Ident::new(enum_ty.struct_name(), Span::call_site());
quote! {
impl #enum_name_ident {
pub fn from_dispatch_error(err: #scrate::sp_runtime::DispatchError) -> Option<Self> {
let #scrate::sp_runtime::DispatchError::Module(module_error) = err else { return None };
let bytes = #scrate::__private::codec::Encode::encode(&module_error);
#scrate::__private::codec::Decode::decode(&mut &bytes[..]).ok()
}
}
}
}