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, 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 = vec![];
82
83		let docs = get_doc_literals(&item.attrs);
84
85		Ok(Pallet {
86			is_expanded: true,
87			name,
88			index: pallet_index,
89			path,
90			instance,
91			cfg_pattern,
92			pallet_parts,
93			docs,
94		})
95	}
96}
97
98#[test]
99fn pallet_parsing_works() {
100	use syn::{parse_quote, ItemType};
101
102	let item: ItemType = parse_quote! {
103		pub type System = frame_system + Call;
104	};
105	let ItemType { ty, .. } = item.clone();
106	let syn::Type::TraitObject(syn::TypeTraitObject { bounds, .. }) = *ty else {
107		panic!("Expected a trait object");
108	};
109
110	let index = 0;
111	let pallet =
112		Pallet::try_from(proc_macro2::Span::call_site(), &item, index, false, false, &bounds)
113			.unwrap();
114
115	assert_eq!(pallet.name.to_string(), "System");
116	assert_eq!(pallet.index, index);
117	assert_eq!(pallet.path.to_token_stream().to_string(), "frame_system");
118	assert_eq!(pallet.instance, None);
119}
120
121#[test]
122fn pallet_parsing_works_with_instance() {
123	use syn::{parse_quote, ItemType};
124
125	let item: ItemType = parse_quote! {
126		pub type System = frame_system<Instance1> + Call;
127	};
128	let ItemType { ty, .. } = item.clone();
129	let syn::Type::TraitObject(syn::TypeTraitObject { bounds, .. }) = *ty else {
130		panic!("Expected a trait object");
131	};
132
133	let index = 0;
134	let pallet =
135		Pallet::try_from(proc_macro2::Span::call_site(), &item, index, false, false, &bounds)
136			.unwrap();
137
138	assert_eq!(pallet.name.to_string(), "System");
139	assert_eq!(pallet.index, index);
140	assert_eq!(pallet.path.to_token_stream().to_string(), "frame_system");
141	assert_eq!(pallet.instance, Some(parse_quote! { Instance1 }));
142}
143
144#[test]
145fn pallet_parsing_works_with_pallet() {
146	use syn::{parse_quote, ItemType};
147
148	let item: ItemType = parse_quote! {
149		pub type System = frame_system::Pallet<Runtime> + Call;
150	};
151	let ItemType { ty, .. } = item.clone();
152	let syn::Type::TraitObject(syn::TypeTraitObject { bounds, .. }) = *ty else {
153		panic!("Expected a trait object");
154	};
155
156	let index = 0;
157	let pallet =
158		Pallet::try_from(proc_macro2::Span::call_site(), &item, index, false, false, &bounds)
159			.unwrap();
160
161	assert_eq!(pallet.name.to_string(), "System");
162	assert_eq!(pallet.index, index);
163	assert_eq!(pallet.path.to_token_stream().to_string(), "frame_system");
164	assert_eq!(pallet.instance, None);
165}
166
167#[test]
168fn pallet_parsing_works_with_instance_and_pallet() {
169	use syn::{parse_quote, ItemType};
170
171	let item: ItemType = parse_quote! {
172		pub type System = frame_system::Pallet<Runtime, Instance1> + Call;
173	};
174	let ItemType { ty, .. } = item.clone();
175	let syn::Type::TraitObject(syn::TypeTraitObject { bounds, .. }) = *ty else {
176		panic!("Expected a trait object");
177	};
178
179	let index = 0;
180	let pallet =
181		Pallet::try_from(proc_macro2::Span::call_site(), &item, index, false, false, &bounds)
182			.unwrap();
183
184	assert_eq!(pallet.name.to_string(), "System");
185	assert_eq!(pallet.index, index);
186	assert_eq!(pallet.path.to_token_stream().to_string(), "frame_system");
187	assert_eq!(pallet.instance, Some(parse_quote! { Instance1 }));
188}