frame_support_procedural/construct_runtime/expand/
outer_enums.rs1use crate::construct_runtime::Pallet;
19use proc_macro2::{Span, TokenStream};
20use quote::{quote, ToTokens};
21use std::str::FromStr;
22use syn::{Generics, Ident};
23
24#[derive(Clone, Copy, PartialEq)]
26pub enum OuterEnumType {
27 Event,
29 Error,
31}
32
33impl OuterEnumType {
34 fn struct_name(&self) -> &str {
36 match self {
37 OuterEnumType::Event => "RuntimeEvent",
38 OuterEnumType::Error => "RuntimeError",
39 }
40 }
41
42 fn variant_name(&self) -> &str {
44 match self {
45 OuterEnumType::Event => "Event",
46 OuterEnumType::Error => "Error",
47 }
48 }
49}
50
51impl ToTokens for OuterEnumType {
52 fn to_tokens(&self, tokens: &mut TokenStream) {
53 match self {
54 OuterEnumType::Event => quote!(Event).to_tokens(tokens),
55 OuterEnumType::Error => quote!(Error).to_tokens(tokens),
56 }
57 }
58}
59
60pub fn expand_outer_enum(
87 runtime: &Ident,
88 pallet_decls: &[Pallet],
89 scrate: &TokenStream,
90 enum_ty: OuterEnumType,
91) -> syn::Result<TokenStream> {
92 let mut enum_variants = TokenStream::new();
94 let mut enum_conversions = TokenStream::new();
96 let mut query_enum_part_macros = Vec::new();
98
99 let enum_name_str = enum_ty.variant_name();
100 let enum_name_ident = Ident::new(enum_ty.struct_name(), Span::call_site());
101
102 for pallet_decl in pallet_decls {
103 let Some(pallet_entry) = pallet_decl.find_part(enum_name_str) else { continue };
104
105 let path = &pallet_decl.path;
106 let pallet_name = &pallet_decl.name;
107 let index = pallet_decl.index;
108 let instance = pallet_decl.instance.as_ref();
109 let generics = &pallet_entry.generics;
110
111 if instance.is_some() && generics.params.is_empty() {
112 let msg = format!(
113 "Instantiable pallet with no generic `{}` cannot \
114 be constructed: pallet `{}` must have generic `{}`",
115 enum_name_str, pallet_name, enum_name_str,
116 );
117 return Err(syn::Error::new(pallet_name.span(), msg))
118 }
119
120 let part_is_generic = !generics.params.is_empty();
121 let pallet_enum = match (instance, part_is_generic) {
122 (Some(inst), true) => quote!(#path::#enum_ty::<#runtime, #path::#inst>),
123 (Some(inst), false) => quote!(#path::#enum_ty::<#path::#inst>),
124 (None, true) => quote!(#path::#enum_ty::<#runtime>),
125 (None, false) => quote!(#path::#enum_ty),
126 };
127
128 enum_variants.extend(expand_enum_variant(
129 runtime,
130 pallet_decl,
131 index,
132 instance,
133 generics,
134 enum_ty,
135 ));
136 enum_conversions.extend(expand_enum_conversion(
137 pallet_decl,
138 &pallet_enum,
139 &enum_name_ident,
140 ));
141
142 if enum_ty == OuterEnumType::Event {
143 query_enum_part_macros.push(quote! {
144 #path::__substrate_event_check::is_event_part_defined!(#pallet_name);
145 });
146 }
147 }
148
149 let event_custom_derives =
151 if enum_ty == OuterEnumType::Event { quote!(Clone, PartialEq, Eq,) } else { quote!() };
152
153 let error_custom_impl = generate_error_impl(scrate, enum_ty);
155
156 Ok(quote! {
157 #( #query_enum_part_macros )*
158
159 #[derive(
160 #event_custom_derives
161 #scrate::__private::codec::Encode,
162 #scrate::__private::codec::Decode,
163 #scrate::__private::scale_info::TypeInfo,
164 #scrate::__private::RuntimeDebug,
165 )]
166 #[allow(non_camel_case_types)]
167 pub enum #enum_name_ident {
168 #enum_variants
169 }
170
171 #enum_conversions
172
173 #error_custom_impl
174 })
175}
176
177fn expand_enum_variant(
178 runtime: &Ident,
179 pallet: &Pallet,
180 index: u8,
181 instance: Option<&Ident>,
182 generics: &Generics,
183 enum_ty: OuterEnumType,
184) -> TokenStream {
185 let path = &pallet.path;
186 let variant_name = &pallet.name;
187 let part_is_generic = !generics.params.is_empty();
188 let attr = pallet.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| {
189 let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original()))
190 .expect("was successfully parsed before; qed");
191 quote! {
192 #acc
193 #attr
194 }
195 });
196
197 match instance {
198 Some(inst) if part_is_generic => quote! {
199 #attr
200 #[codec(index = #index)]
201 #variant_name(#path::#enum_ty<#runtime, #path::#inst>),
202 },
203 Some(inst) => quote! {
204 #attr
205 #[codec(index = #index)]
206 #variant_name(#path::#enum_ty<#path::#inst>),
207 },
208 None if part_is_generic => quote! {
209 #attr
210 #[codec(index = #index)]
211 #variant_name(#path::#enum_ty<#runtime>),
212 },
213 None => quote! {
214 #attr
215 #[codec(index = #index)]
216 #variant_name(#path::#enum_ty),
217 },
218 }
219}
220
221fn expand_enum_conversion(
222 pallet: &Pallet,
223 pallet_enum: &TokenStream,
224 enum_name_ident: &Ident,
225) -> TokenStream {
226 let variant_name = &pallet.name;
227 let attr = pallet.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| {
228 let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original()))
229 .expect("was successfully parsed before; qed");
230 quote! {
231 #acc
232 #attr
233 }
234 });
235
236 quote! {
237 #attr
238 impl From<#pallet_enum> for #enum_name_ident {
239 fn from(x: #pallet_enum) -> Self {
240 #enum_name_ident
241 ::#variant_name(x)
242 }
243 }
244 #attr
245 impl TryInto<#pallet_enum> for #enum_name_ident {
246 type Error = ();
247
248 fn try_into(self) -> ::core::result::Result<#pallet_enum, Self::Error> {
249 match self {
250 Self::#variant_name(evt) => Ok(evt),
251 _ => Err(()),
252 }
253 }
254 }
255 }
256}
257
258fn generate_error_impl(scrate: &TokenStream, enum_ty: OuterEnumType) -> TokenStream {
259 if enum_ty == OuterEnumType::Event {
261 return quote! {}
262 }
263
264 let enum_name_ident = Ident::new(enum_ty.struct_name(), Span::call_site());
265
266 quote! {
267 impl #enum_name_ident {
268 pub fn from_dispatch_error(err: #scrate::sp_runtime::DispatchError) -> Option<Self> {
272 let #scrate::sp_runtime::DispatchError::Module(module_error) = err else { return None };
273
274 let bytes = #scrate::__private::codec::Encode::encode(&module_error);
275 #scrate::__private::codec::Decode::decode(&mut &bytes[..]).ok()
276 }
277 }
278 }
279}