referrerpolicy=no-referrer-when-downgrade

frame_support_procedural/pallet/parse/
composite.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
18use super::helper;
19use quote::ToTokens;
20use syn::spanned::Spanned;
21
22pub mod keyword {
23	use super::*;
24
25	syn::custom_keyword!(FreezeReason);
26	syn::custom_keyword!(HoldReason);
27	syn::custom_keyword!(LockId);
28	syn::custom_keyword!(SlashReason);
29	syn::custom_keyword!(Task);
30
31	pub enum CompositeKeyword {
32		FreezeReason(FreezeReason),
33		HoldReason(HoldReason),
34		LockId(LockId),
35		SlashReason(SlashReason),
36		Task(Task),
37	}
38
39	impl ToTokens for CompositeKeyword {
40		fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
41			use CompositeKeyword::*;
42			match self {
43				FreezeReason(inner) => inner.to_tokens(tokens),
44				HoldReason(inner) => inner.to_tokens(tokens),
45				LockId(inner) => inner.to_tokens(tokens),
46				SlashReason(inner) => inner.to_tokens(tokens),
47				Task(inner) => inner.to_tokens(tokens),
48			}
49		}
50	}
51
52	impl syn::parse::Parse for CompositeKeyword {
53		fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
54			let lookahead = input.lookahead1();
55			if lookahead.peek(FreezeReason) {
56				Ok(Self::FreezeReason(input.parse()?))
57			} else if lookahead.peek(HoldReason) {
58				Ok(Self::HoldReason(input.parse()?))
59			} else if lookahead.peek(LockId) {
60				Ok(Self::LockId(input.parse()?))
61			} else if lookahead.peek(SlashReason) {
62				Ok(Self::SlashReason(input.parse()?))
63			} else if lookahead.peek(Task) {
64				Ok(Self::Task(input.parse()?))
65			} else {
66				Err(lookahead.error())
67			}
68		}
69	}
70
71	impl std::fmt::Display for CompositeKeyword {
72		fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73			use CompositeKeyword::*;
74			write!(
75				f,
76				"{}",
77				match self {
78					FreezeReason(_) => "FreezeReason",
79					HoldReason(_) => "HoldReason",
80					Task(_) => "Task",
81					LockId(_) => "LockId",
82					SlashReason(_) => "SlashReason",
83				}
84			)
85		}
86	}
87}
88
89pub struct CompositeDef {
90	/// The composite keyword used (contains span).
91	pub composite_keyword: keyword::CompositeKeyword,
92	/// Name of the associated type.
93	pub ident: syn::Ident,
94	/// Type parameters and where clause attached to a declaration of the pallet::composite_enum.
95	pub generics: syn::Generics,
96	/// The span of the pallet::composite_enum attribute.
97	pub attr_span: proc_macro2::Span,
98	/// Variant count of the pallet::composite_enum.
99	pub variant_count: u32,
100}
101
102impl CompositeDef {
103	pub fn try_from(
104		attr_span: proc_macro2::Span,
105		scrate: &syn::Path,
106		item: &mut syn::Item,
107	) -> syn::Result<Self> {
108		let item = if let syn::Item::Enum(item) = item {
109			// check variants: composite enums support only field-less enum variants. This is
110			// because fields can introduce too many possibilities, making it challenging to compute
111			// a fixed variant count.
112			for variant in &item.variants {
113				match variant.fields {
114					syn::Fields::Named(_) | syn::Fields::Unnamed(_) =>
115						return Err(syn::Error::new(
116							variant.ident.span(),
117							"The composite enum does not support variants with fields!",
118						)),
119					syn::Fields::Unit => (),
120				}
121			}
122			item
123		} else {
124			return Err(syn::Error::new(
125				item.span(),
126				"Invalid pallet::composite_enum, expected enum item",
127			))
128		};
129
130		if !matches!(item.vis, syn::Visibility::Public(_)) {
131			let msg = format!("Invalid pallet::composite_enum, `{}` must be public", item.ident);
132			return Err(syn::Error::new(item.span(), msg))
133		}
134
135		let has_instance = if item.generics.params.first().is_some() {
136			helper::check_config_def_gen(&item.generics, item.ident.span())?;
137			true
138		} else {
139			false
140		};
141
142		let has_derive_attr = item.attrs.iter().any(|attr| {
143			if let syn::Meta::List(syn::MetaList { path, .. }) = &attr.meta {
144				path.get_ident().map(|ident| ident == "derive").unwrap_or(false)
145			} else {
146				false
147			}
148		});
149
150		if !has_derive_attr {
151			let derive_attr: syn::Attribute = syn::parse_quote! {
152				#[derive(
153					Copy, Clone, Eq, PartialEq,
154					#scrate::__private::codec::Encode,
155					#scrate::__private::codec::Decode,
156					#scrate::__private::codec::DecodeWithMemTracking,
157					#scrate::__private::codec::MaxEncodedLen,
158					#scrate::__private::scale_info::TypeInfo,
159					#scrate::__private::RuntimeDebug,
160				)]
161			};
162			item.attrs.push(derive_attr);
163		}
164
165		if has_instance {
166			item.attrs.push(syn::parse_quote! {
167				#[scale_info(skip_type_params(I))]
168			});
169
170			item.variants.push(syn::parse_quote! {
171				#[doc(hidden)]
172				#[codec(skip)]
173				__Ignore(
174					::core::marker::PhantomData<I>,
175				)
176			});
177		}
178
179		let composite_keyword =
180			syn::parse2::<keyword::CompositeKeyword>(item.ident.to_token_stream())?;
181
182		Ok(CompositeDef {
183			composite_keyword,
184			attr_span,
185			generics: item.generics.clone(),
186			variant_count: item.variants.len() as u32,
187			ident: item.ident.clone(),
188		})
189	}
190}