referrerpolicy=no-referrer-when-downgrade

frame_support_procedural/
dynamic_params.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Code for the `#[dynamic_params]`, `#[dynamic_pallet_params]` and
19//! `#[dynamic_aggregated_params_internal]` macros.
20
21use 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
27/// Parse and expand a `#[dynamic_params(..)]` module.
28pub fn dynamic_params(attr: TokenStream, item: TokenStream) -> Result<TokenStream> {
29	DynamicParamModAttr::parse(attr, item).map(ToTokens::into_token_stream)
30}
31
32/// Parse and expand `#[dynamic_pallet_params(..)]` attribute.
33pub fn dynamic_pallet_params(attr: TokenStream, item: TokenStream) -> Result<TokenStream> {
34	DynamicPalletParamAttr::parse(attr, item).map(ToTokens::into_token_stream)
35}
36
37/// Parse and expand `#[dynamic_aggregated_params_internal]` attribute.
38pub 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/// A top `#[dynamic_params(..)]` attribute together with a mod.
46#[derive(derive_syn_parse::Parse)]
47pub struct DynamicParamModAttr {
48	params_mod: syn::ItemMod,
49	meta: DynamicParamModAttrMeta,
50}
51
52/// The inner meta of a `#[dynamic_params(..)]` attribute.
53#[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 = &params_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		// Inject the outer args into the inner `#[dynamic_pallet_params(..)]` attribute.
111		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
127/// Ensure there is a `#[codec(index = ..)]` attribute.
128fn 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
148/// Used to inject arguments into the inner `#[dynamic_pallet_params(..)]` attribute.
149///
150/// This allows the outer `#[dynamic_params(..)]` attribute to specify some arguments that don't
151/// need to be repeated every time.
152struct 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		// Check if the mod has a `#[dynamic_pallet_params(..)]` attribute.
159		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/// The helper attribute of a `#[dynamic_pallet_params(runtime_params, params_pallet)]`
182/// attribute.
183#[derive(derive_syn_parse::Parse)]
184pub struct DynamicPalletParamAttr {
185	inner_mod: syn::ItemMod,
186	meta: DynamicPalletParamAttrMeta,
187}
188
189/// The inner meta of a `#[dynamic_pallet_params(..)]` attribute.
190#[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			&params_mod.ident.to_string().to_pascal_case(),
227			params_mod.ident.span(),
228		);
229		let (mod_name, vis) = (&params_mod.ident, &params_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) = (&params_enum.ident, &params_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					//#[codec(index = #indices)]
488					#(#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
569/// Get access to the current crate and convert the error to a compile error.
570fn 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}