1use frame_support_procedural_tools::generate_access_from_frame_or_crate;
22use inflector::Inflector;
23use proc_macro2::{Span, TokenStream};
24use quote::{format_ident, quote, ToTokens};
25use syn::{parse2, spanned::Spanned, visit_mut, visit_mut::VisitMut, Result, Token};
26
27pub fn dynamic_params(attr: TokenStream, item: TokenStream) -> Result<TokenStream> {
29 DynamicParamModAttr::parse(attr, item).map(ToTokens::into_token_stream)
30}
31
32pub fn dynamic_pallet_params(attr: TokenStream, item: TokenStream) -> Result<TokenStream> {
34 DynamicPalletParamAttr::parse(attr, item).map(ToTokens::into_token_stream)
35}
36
37pub fn dynamic_aggregated_params_internal(
39 _attr: TokenStream,
40 item: TokenStream,
41) -> Result<TokenStream> {
42 parse2::<DynamicParamAggregatedEnum>(item).map(ToTokens::into_token_stream)
43}
44
45#[derive(derive_syn_parse::Parse)]
47pub struct DynamicParamModAttr {
48 params_mod: syn::ItemMod,
49 meta: DynamicParamModAttrMeta,
50}
51
52#[derive(derive_syn_parse::Parse)]
54pub struct DynamicParamModAttrMeta {
55 name: syn::Ident,
56 _comma: Option<Token![,]>,
57 #[parse_if(_comma.is_some())]
58 params_pallet: Option<syn::Type>,
59}
60
61impl DynamicParamModAttr {
62 pub fn parse(attr: TokenStream, item: TokenStream) -> Result<Self> {
63 let params_mod = parse2(item)?;
64 let meta = parse2(attr)?;
65 Ok(Self { params_mod, meta })
66 }
67
68 pub fn inner_mods(&self) -> Vec<syn::ItemMod> {
69 self.params_mod.content.as_ref().map_or(Vec::new(), |(_, items)| {
70 items
71 .iter()
72 .filter_map(|i| match i {
73 syn::Item::Mod(m) => Some(m),
74 _ => None,
75 })
76 .cloned()
77 .collect()
78 })
79 }
80}
81
82impl ToTokens for DynamicParamModAttr {
83 fn to_tokens(&self, tokens: &mut TokenStream) {
84 let scrate = match crate_access() {
85 Ok(path) => path,
86 Err(err) => return tokens.extend(err),
87 };
88 let (mut params_mod, name) = (self.params_mod.clone(), &self.meta.name);
89 let dynam_params_ident = ¶ms_mod.ident;
90
91 let mut quoted_enum = quote! {};
92 for m in self.inner_mods() {
93 let aggregate_name =
94 syn::Ident::new(&m.ident.to_string().to_pascal_case(), m.ident.span());
95 let mod_name = &m.ident;
96
97 let mut attrs = m.attrs.clone();
98 attrs.retain(|attr| !attr.path().is_ident("dynamic_pallet_params"));
99 if let Err(err) = ensure_codec_index(&attrs, m.span()) {
100 tokens.extend(err.into_compile_error());
101 return
102 }
103
104 quoted_enum.extend(quote! {
105 #(#attrs)*
106 #aggregate_name(#dynam_params_ident::#mod_name::Parameters),
107 });
108 }
109
110 if let Some(params_pallet) = &self.meta.params_pallet {
112 MacroInjectArgs { runtime_params: name.clone(), params_pallet: params_pallet.clone() }
113 .visit_item_mod_mut(&mut params_mod);
114 }
115
116 tokens.extend(quote! {
117 #params_mod
118
119 #[#scrate::dynamic_params::dynamic_aggregated_params_internal]
120 pub enum #name {
121 #quoted_enum
122 }
123 });
124 }
125}
126
127fn ensure_codec_index(attrs: &Vec<syn::Attribute>, span: Span) -> Result<()> {
129 let mut found = false;
130
131 for attr in attrs.iter() {
132 if attr.path().is_ident("codec") {
133 let meta: syn::ExprAssign = attr.parse_args()?;
134 if meta.left.to_token_stream().to_string() == "index" {
135 found = true;
136 break
137 }
138 }
139 }
140
141 if !found {
142 Err(syn::Error::new(span, "Missing explicit `#[codec(index = ..)]` attribute"))
143 } else {
144 Ok(())
145 }
146}
147
148struct MacroInjectArgs {
153 runtime_params: syn::Ident,
154 params_pallet: syn::Type,
155}
156impl VisitMut for MacroInjectArgs {
157 fn visit_item_mod_mut(&mut self, item: &mut syn::ItemMod) {
158 let attr = item.attrs.iter_mut().find(|attr| attr.path().is_ident("dynamic_pallet_params"));
160
161 if let Some(attr) = attr {
162 match &attr.meta {
163 syn::Meta::Path(path) =>
164 assert_eq!(path.to_token_stream().to_string(), "dynamic_pallet_params"),
165 _ => (),
166 }
167
168 let runtime_params = &self.runtime_params;
169 let params_pallet = &self.params_pallet;
170
171 attr.meta = syn::parse2::<syn::Meta>(quote! {
172 dynamic_pallet_params(#runtime_params, #params_pallet)
173 })
174 .unwrap()
175 .into();
176 }
177
178 visit_mut::visit_item_mod_mut(self, item);
179 }
180}
181#[derive(derive_syn_parse::Parse)]
184pub struct DynamicPalletParamAttr {
185 inner_mod: syn::ItemMod,
186 meta: DynamicPalletParamAttrMeta,
187}
188
189#[derive(derive_syn_parse::Parse)]
191pub struct DynamicPalletParamAttrMeta {
192 runtime_params: syn::Ident,
193 _comma: Token![,],
194 parameter_pallet: syn::Type,
195}
196
197impl DynamicPalletParamAttr {
198 pub fn parse(attr: TokenStream, item: TokenStream) -> Result<Self> {
199 Ok(Self { inner_mod: parse2(item)?, meta: parse2(attr)? })
200 }
201
202 pub fn statics(&self) -> Vec<syn::ItemStatic> {
203 self.inner_mod.content.as_ref().map_or(Vec::new(), |(_, items)| {
204 items
205 .iter()
206 .filter_map(|i| match i {
207 syn::Item::Static(s) => Some(s),
208 _ => None,
209 })
210 .cloned()
211 .collect()
212 })
213 }
214}
215
216impl ToTokens for DynamicPalletParamAttr {
217 fn to_tokens(&self, tokens: &mut TokenStream) {
218 let scrate = match crate_access() {
219 Ok(path) => path,
220 Err(err) => return tokens.extend(err),
221 };
222 let (params_mod, parameter_pallet, runtime_params) =
223 (&self.inner_mod, &self.meta.parameter_pallet, &self.meta.runtime_params);
224
225 let aggregate_name = syn::Ident::new(
226 ¶ms_mod.ident.to_string().to_pascal_case(),
227 params_mod.ident.span(),
228 );
229 let (mod_name, vis) = (¶ms_mod.ident, ¶ms_mod.vis);
230 let statics = self.statics();
231
232 let (mut key_names, mut key_values, mut defaults, mut attrs, mut value_types): (
233 Vec<_>,
234 Vec<_>,
235 Vec<_>,
236 Vec<_>,
237 Vec<_>,
238 ) = Default::default();
239
240 for s in statics.iter() {
241 if let Err(err) = ensure_codec_index(&s.attrs, s.span()) {
242 tokens.extend(err.into_compile_error());
243 return
244 }
245
246 key_names.push(&s.ident);
247 key_values.push(format_ident!("{}Value", &s.ident));
248 defaults.push(&s.expr);
249 attrs.push(&s.attrs);
250 value_types.push(&s.ty);
251 }
252
253 let key_ident = syn::Ident::new("ParametersKey", params_mod.ident.span());
254 let value_ident = syn::Ident::new("ParametersValue", params_mod.ident.span());
255 let runtime_key_ident = format_ident!("{}Key", runtime_params);
256 let runtime_value_ident = format_ident!("{}Value", runtime_params);
257
258 tokens.extend(quote! {
259 pub mod #mod_name {
260 use super::*;
261
262 #[doc(hidden)]
263 #[derive(
264 Clone,
265 PartialEq,
266 Eq,
267 #scrate::__private::codec::Encode,
268 #scrate::__private::codec::Decode,
269 #scrate::__private::codec::DecodeWithMemTracking,
270 #scrate::__private::codec::MaxEncodedLen,
271 #scrate::__private::RuntimeDebug,
272 #scrate::__private::scale_info::TypeInfo
273 )]
274 #vis enum Parameters {
275 #(
276 #(#attrs)*
277 #key_names(#key_names, Option<#value_types>),
278 )*
279 }
280
281 #[doc(hidden)]
282 #[derive(
283 Clone,
284 PartialEq,
285 Eq,
286 #scrate::__private::codec::Encode,
287 #scrate::__private::codec::Decode,
288 #scrate::__private::codec::DecodeWithMemTracking,
289 #scrate::__private::codec::MaxEncodedLen,
290 #scrate::__private::RuntimeDebug,
291 #scrate::__private::scale_info::TypeInfo
292 )]
293 #vis enum #key_ident {
294 #(
295 #(#attrs)*
296 #key_names(#key_names),
297 )*
298 }
299
300 #[doc(hidden)]
301 #[derive(
302 Clone,
303 PartialEq,
304 Eq,
305 #scrate::__private::codec::Encode,
306 #scrate::__private::codec::Decode,
307 #scrate::__private::codec::DecodeWithMemTracking,
308 #scrate::__private::codec::MaxEncodedLen,
309 #scrate::__private::RuntimeDebug,
310 #scrate::__private::scale_info::TypeInfo
311 )]
312 #vis enum #value_ident {
313 #(
314 #(#attrs)*
315 #key_names(#value_types),
316 )*
317 }
318
319 impl #scrate::traits::dynamic_params::AggregatedKeyValue for Parameters {
320 type Key = #key_ident;
321 type Value = #value_ident;
322
323 fn into_parts(self) -> (Self::Key, Option<Self::Value>) {
324 match self {
325 #(
326 Parameters::#key_names(key, value) => {
327 (#key_ident::#key_names(key), value.map(#value_ident::#key_names))
328 },
329 )*
330 }
331 }
332 }
333
334 #(
335 #[doc(hidden)]
336 #[derive(
337 Clone,
338 PartialEq,
339 Eq,
340 #scrate::__private::codec::Encode,
341 #scrate::__private::codec::Decode,
342 #scrate::__private::codec::DecodeWithMemTracking,
343 #scrate::__private::codec::MaxEncodedLen,
344 #scrate::__private::RuntimeDebug,
345 #scrate::__private::scale_info::TypeInfo
346 )]
347 #vis struct #key_names;
348
349 impl #scrate::__private::Get<#value_types> for #key_names {
350 fn get() -> #value_types {
351 match
352 <#parameter_pallet as
353 #scrate::storage::StorageMap<#runtime_key_ident, #runtime_value_ident>
354 >::get(#runtime_key_ident::#aggregate_name(#key_ident::#key_names(#key_names)))
355 {
356 Some(#runtime_value_ident::#aggregate_name(
357 #value_ident::#key_names(inner))) => inner,
358 Some(_) => {
359 #scrate::defensive!("Unexpected value type at key - returning default");
360 #defaults
361 },
362 None => #defaults,
363 }
364 }
365 }
366
367 impl #scrate::traits::dynamic_params::Key for #key_names {
368 type Value = #value_types;
369 type WrappedValue = #key_values;
370 }
371
372 impl From<#key_names> for #key_ident {
373 fn from(key: #key_names) -> Self {
374 #key_ident::#key_names(key)
375 }
376 }
377
378 impl TryFrom<#key_ident> for #key_names {
379 type Error = ();
380
381 fn try_from(key: #key_ident) -> Result<Self, Self::Error> {
382 match key {
383 #key_ident::#key_names(key) => Ok(key),
384 _ => Err(()),
385 }
386 }
387 }
388
389 #[doc(hidden)]
390 #[derive(
391 Clone,
392 PartialEq,
393 Eq,
394 #scrate::sp_runtime::RuntimeDebug,
395 )]
396 #vis struct #key_values(pub #value_types);
397
398 impl From<#key_values> for #value_ident {
399 fn from(value: #key_values) -> Self {
400 #value_ident::#key_names(value.0)
401 }
402 }
403
404 impl From<(#key_names, #value_types)> for Parameters {
405 fn from((key, value): (#key_names, #value_types)) -> Self {
406 Parameters::#key_names(key, Some(value))
407 }
408 }
409
410 impl From<#key_names> for Parameters {
411 fn from(key: #key_names) -> Self {
412 Parameters::#key_names(key, None)
413 }
414 }
415
416 impl TryFrom<#value_ident> for #key_values {
417 type Error = ();
418
419 fn try_from(value: #value_ident) -> Result<Self, Self::Error> {
420 match value {
421 #value_ident::#key_names(value) => Ok(#key_values(value)),
422 _ => Err(()),
423 }
424 }
425 }
426
427 impl From<#key_values> for #value_types {
428 fn from(value: #key_values) -> Self {
429 value.0
430 }
431 }
432 )*
433 }
434 });
435 }
436}
437
438#[derive(derive_syn_parse::Parse)]
439pub struct DynamicParamAggregatedEnum {
440 aggregated_enum: syn::ItemEnum,
441}
442
443impl ToTokens for DynamicParamAggregatedEnum {
444 fn to_tokens(&self, tokens: &mut TokenStream) {
445 let scrate = match crate_access() {
446 Ok(path) => path,
447 Err(err) => return tokens.extend(err),
448 };
449 let params_enum = &self.aggregated_enum;
450 let (name, vis) = (¶ms_enum.ident, ¶ms_enum.vis);
451
452 let (mut indices, mut param_names, mut param_types): (Vec<_>, Vec<_>, Vec<_>) =
453 Default::default();
454 let mut attributes = Vec::new();
455 for (i, variant) in params_enum.variants.iter().enumerate() {
456 indices.push(i);
457 param_names.push(&variant.ident);
458 attributes.push(&variant.attrs);
459
460 param_types.push(match &variant.fields {
461 syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty,
462 _ => {
463 *tokens = quote! { compile_error!("Only unnamed enum variants with one inner item are supported") };
464 return
465 },
466 });
467 }
468
469 let params_key_ident = format_ident!("{}Key", params_enum.ident);
470 let params_value_ident = format_ident!("{}Value", params_enum.ident);
471
472 tokens.extend(quote! {
473 #[doc(hidden)]
474 #[derive(
475 Clone,
476 PartialEq,
477 Eq,
478 #scrate::__private::codec::Encode,
479 #scrate::__private::codec::Decode,
480 #scrate::__private::codec::DecodeWithMemTracking,
481 #scrate::__private::codec::MaxEncodedLen,
482 #scrate::sp_runtime::RuntimeDebug,
483 #scrate::__private::scale_info::TypeInfo
484 )]
485 #vis enum #name {
486 #(
487 #(#attributes)*
489 #param_names(#param_types),
490 )*
491 }
492
493 #[doc(hidden)]
494 #[derive(
495 Clone,
496 PartialEq,
497 Eq,
498 #scrate::__private::codec::Encode,
499 #scrate::__private::codec::Decode,
500 #scrate::__private::codec::DecodeWithMemTracking,
501 #scrate::__private::codec::MaxEncodedLen,
502 #scrate::sp_runtime::RuntimeDebug,
503 #scrate::__private::scale_info::TypeInfo
504 )]
505 #vis enum #params_key_ident {
506 #(
507 #(#attributes)*
508 #param_names(<#param_types as #scrate::traits::dynamic_params::AggregatedKeyValue>::Key),
509 )*
510 }
511
512 #[doc(hidden)]
513 #[derive(
514 Clone,
515 PartialEq,
516 Eq,
517 #scrate::__private::codec::Encode,
518 #scrate::__private::codec::Decode,
519 #scrate::__private::codec::DecodeWithMemTracking,
520 #scrate::__private::codec::MaxEncodedLen,
521 #scrate::sp_runtime::RuntimeDebug,
522 #scrate::__private::scale_info::TypeInfo
523 )]
524 #vis enum #params_value_ident {
525 #(
526 #(#attributes)*
527 #param_names(<#param_types as #scrate::traits::dynamic_params::AggregatedKeyValue>::Value),
528 )*
529 }
530
531 impl #scrate::traits::dynamic_params::AggregatedKeyValue for #name {
532 type Key = #params_key_ident;
533 type Value = #params_value_ident;
534
535 fn into_parts(self) -> (Self::Key, Option<Self::Value>) {
536 match self {
537 #(
538 #name::#param_names(parameter) => {
539 let (key, value) = parameter.into_parts();
540 (#params_key_ident::#param_names(key), value.map(#params_value_ident::#param_names))
541 },
542 )*
543 }
544 }
545 }
546
547 #(
548 impl ::core::convert::From<<#param_types as #scrate::traits::dynamic_params::AggregatedKeyValue>::Key> for #params_key_ident {
549 fn from(key: <#param_types as #scrate::traits::dynamic_params::AggregatedKeyValue>::Key) -> Self {
550 #params_key_ident::#param_names(key)
551 }
552 }
553
554 impl ::core::convert::TryFrom<#params_value_ident> for <#param_types as #scrate::traits::dynamic_params::AggregatedKeyValue>::Value {
555 type Error = ();
556
557 fn try_from(value: #params_value_ident) -> Result<Self, Self::Error> {
558 match value {
559 #params_value_ident::#param_names(value) => Ok(value),
560 _ => Err(()),
561 }
562 }
563 }
564 )*
565 });
566 }
567}
568
569fn crate_access() -> core::result::Result<syn::Path, TokenStream> {
571 generate_access_from_frame_or_crate("frame-support").map_err(|e| e.to_compile_error())
572}