referrerpolicy=no-referrer-when-downgrade

frame_support_procedural/runtime/parse/
pallet.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 crate::{
19	construct_runtime::parse::{Pallet, PalletPart, PalletPartKeyword, PalletPath},
20	runtime::parse::PalletDeclaration,
21};
22use frame_support_procedural_tools::get_doc_literals;
23use quote::ToTokens;
24use syn::{punctuated::Punctuated, spanned::Spanned, token, Error};
25
26impl Pallet {
27	pub fn try_from(
28		attr_span: proc_macro2::Span,
29		item: &syn::ItemType,
30		pallet_index: u8,
31		disable_call: bool,
32		disable_unsigned: bool,
33		bounds: &Punctuated<syn::TypeParamBound, token::Plus>,
34	) -> syn::Result<Self> {
35		let name = item.ident.clone();
36
37		let mut pallet_path = None;
38		let mut pallet_parts = vec![];
39
40		for (index, bound) in bounds.into_iter().enumerate() {
41			if let syn::TypeParamBound::Trait(syn::TraitBound { path, .. }) = bound {
42				if index == 0 {
43					pallet_path = Some(PalletPath { inner: path.clone() });
44				} else {
45					let pallet_part = syn::parse2::<PalletPart>(bound.into_token_stream())?;
46					pallet_parts.push(pallet_part);
47				}
48			} else {
49				return Err(Error::new(
50					attr_span,
51					"Invalid pallet declaration, expected a path or a trait object",
52				));
53			};
54		}
55
56		let mut path = pallet_path.ok_or(Error::new(
57			attr_span,
58			"Invalid pallet declaration, expected a path or a trait object",
59		))?;
60
61		let PalletDeclaration { path: inner, instance, .. } =
62			PalletDeclaration::try_from(attr_span, item, &path.inner)?;
63
64		path = PalletPath { inner };
65
66		pallet_parts = pallet_parts
67			.into_iter()
68			.filter(|part| {
69				if let (true, &PalletPartKeyword::Call(_)) = (disable_call, &part.keyword) {
70					false
71				} else if let (true, &PalletPartKeyword::ValidateUnsigned(_)) =
72					(disable_unsigned, &part.keyword)
73				{
74					false
75				} else {
76					true
77				}
78			})
79			.collect();
80
81		let cfg_pattern = item
82			.attrs
83			.iter()
84			.filter(|attr| attr.path().segments.first().map_or(false, |s| s.ident == "cfg"))
85			.map(|attr| {
86				attr.parse_args_with(|input: syn::parse::ParseStream| {
87					let input = input.parse::<proc_macro2::TokenStream>()?;
88					cfg_expr::Expression::parse(&input.to_string())
89						.map_err(|e| syn::Error::new(attr.span(), e.to_string()))
90				})
91			})
92			.collect::<syn::Result<Vec<_>>>()?;
93
94		let docs = get_doc_literals(&item.attrs);
95
96		Ok(Pallet {
97			is_expanded: true,
98			name,
99			index: pallet_index,
100			path,
101			instance,
102			cfg_pattern,
103			pallet_parts,
104			docs,
105		})
106	}
107}
108
109#[test]
110fn pallet_parsing_works() {
111	use syn::{parse_quote, ItemType};
112
113	let item: ItemType = parse_quote! {
114		pub type System = frame_system + Call;
115	};
116	let ItemType { ty, .. } = item.clone();
117	let syn::Type::TraitObject(syn::TypeTraitObject { bounds, .. }) = *ty else {
118		panic!("Expected a trait object");
119	};
120
121	let index = 0;
122	let pallet =
123		Pallet::try_from(proc_macro2::Span::call_site(), &item, index, false, false, &bounds)
124			.unwrap();
125
126	assert_eq!(pallet.name.to_string(), "System");
127	assert_eq!(pallet.index, index);
128	assert_eq!(pallet.path.to_token_stream().to_string(), "frame_system");
129	assert_eq!(pallet.instance, None);
130}
131
132#[test]
133fn pallet_parsing_works_with_instance() {
134	use syn::{parse_quote, ItemType};
135
136	let item: ItemType = parse_quote! {
137		pub type System = frame_system<Instance1> + Call;
138	};
139	let ItemType { ty, .. } = item.clone();
140	let syn::Type::TraitObject(syn::TypeTraitObject { bounds, .. }) = *ty else {
141		panic!("Expected a trait object");
142	};
143
144	let index = 0;
145	let pallet =
146		Pallet::try_from(proc_macro2::Span::call_site(), &item, index, false, false, &bounds)
147			.unwrap();
148
149	assert_eq!(pallet.name.to_string(), "System");
150	assert_eq!(pallet.index, index);
151	assert_eq!(pallet.path.to_token_stream().to_string(), "frame_system");
152	assert_eq!(pallet.instance, Some(parse_quote! { Instance1 }));
153}
154
155#[test]
156fn pallet_parsing_works_with_pallet() {
157	use syn::{parse_quote, ItemType};
158
159	let item: ItemType = parse_quote! {
160		pub type System = frame_system::Pallet<Runtime> + Call;
161	};
162	let ItemType { ty, .. } = item.clone();
163	let syn::Type::TraitObject(syn::TypeTraitObject { bounds, .. }) = *ty else {
164		panic!("Expected a trait object");
165	};
166
167	let index = 0;
168	let pallet =
169		Pallet::try_from(proc_macro2::Span::call_site(), &item, index, false, false, &bounds)
170			.unwrap();
171
172	assert_eq!(pallet.name.to_string(), "System");
173	assert_eq!(pallet.index, index);
174	assert_eq!(pallet.path.to_token_stream().to_string(), "frame_system");
175	assert_eq!(pallet.instance, None);
176}
177
178#[test]
179fn pallet_parsing_works_with_instance_and_pallet() {
180	use syn::{parse_quote, ItemType};
181
182	let item: ItemType = parse_quote! {
183		pub type System = frame_system::Pallet<Runtime, Instance1> + Call;
184	};
185	let ItemType { ty, .. } = item.clone();
186	let syn::Type::TraitObject(syn::TypeTraitObject { bounds, .. }) = *ty else {
187		panic!("Expected a trait object");
188	};
189
190	let index = 0;
191	let pallet =
192		Pallet::try_from(proc_macro2::Span::call_site(), &item, index, false, false, &bounds)
193			.unwrap();
194
195	assert_eq!(pallet.name.to_string(), "System");
196	assert_eq!(pallet.index, index);
197	assert_eq!(pallet.path.to_token_stream().to_string(), "frame_system");
198	assert_eq!(pallet.instance, Some(parse_quote! { Instance1 }));
199}