referrerpolicy=no-referrer-when-downgrade

frame_support_procedural/construct_runtime/
parse.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 core::str::FromStr;
19use frame_support_procedural_tools::syn_ext as ext;
20use proc_macro2::{Span, TokenStream};
21use quote::ToTokens;
22use std::collections::{HashMap, HashSet};
23use syn::{
24	ext::IdentExt,
25	parse::{Parse, ParseStream},
26	punctuated::Punctuated,
27	spanned::Spanned,
28	token, Attribute, Error, Ident, Path, Result, Token,
29};
30
31mod keyword {
32	syn::custom_keyword!(Block);
33	syn::custom_keyword!(NodeBlock);
34	syn::custom_keyword!(UncheckedExtrinsic);
35	syn::custom_keyword!(Pallet);
36	syn::custom_keyword!(Call);
37	syn::custom_keyword!(Storage);
38	syn::custom_keyword!(Event);
39	syn::custom_keyword!(Error);
40	syn::custom_keyword!(Config);
41	syn::custom_keyword!(Origin);
42	syn::custom_keyword!(Inherent);
43	syn::custom_keyword!(ValidateUnsigned);
44	syn::custom_keyword!(FreezeReason);
45	syn::custom_keyword!(HoldReason);
46	syn::custom_keyword!(Task);
47	syn::custom_keyword!(LockId);
48	syn::custom_keyword!(SlashReason);
49	syn::custom_keyword!(exclude_parts);
50	syn::custom_keyword!(use_parts);
51	syn::custom_keyword!(expanded);
52}
53
54/// Declaration of a runtime.
55///
56/// Pallet declare their part either explicitly or implicitly (using no part declaration)
57/// If all pallet have explicit parts then the runtime declaration is explicit, otherwise it is
58/// implicit.
59#[derive(Debug)]
60pub enum RuntimeDeclaration {
61	Implicit(ImplicitRuntimeDeclaration),
62	Explicit(ExplicitRuntimeDeclaration),
63	ExplicitExpanded(ExplicitRuntimeDeclaration),
64}
65
66/// Declaration of a runtime with some pallet with implicit declaration of parts.
67#[derive(Debug)]
68pub struct ImplicitRuntimeDeclaration {
69	pub pallets: Vec<PalletDeclaration>,
70}
71
72/// Declaration of a runtime with all pallet having explicit declaration of parts.
73#[derive(Debug)]
74pub struct ExplicitRuntimeDeclaration {
75	pub name: Ident,
76	pub where_section: Option<WhereSection>,
77	pub pallets: Vec<Pallet>,
78	pub pallets_token: token::Brace,
79}
80
81impl Parse for RuntimeDeclaration {
82	fn parse(input: ParseStream) -> Result<Self> {
83		input.parse::<Token![pub]>()?;
84
85		// Support either `enum` or `struct`.
86		if input.peek(Token![struct]) {
87			input.parse::<Token![struct]>()?;
88		} else {
89			input.parse::<Token![enum]>()?;
90		}
91
92		let name = input.parse::<syn::Ident>()?;
93		let where_section = if input.peek(token::Where) { Some(input.parse()?) } else { None };
94		let pallets =
95			input.parse::<ext::Braces<ext::Punctuated<PalletDeclaration, Token![,]>>>()?;
96		let pallets_token = pallets.token;
97
98		match convert_pallets(pallets.content.inner.into_iter().collect())? {
99			PalletsConversion::Implicit(pallets) =>
100				Ok(RuntimeDeclaration::Implicit(ImplicitRuntimeDeclaration { pallets })),
101			PalletsConversion::Explicit(pallets) =>
102				Ok(RuntimeDeclaration::Explicit(ExplicitRuntimeDeclaration {
103					name,
104					where_section,
105					pallets,
106					pallets_token,
107				})),
108			PalletsConversion::ExplicitExpanded(pallets) =>
109				Ok(RuntimeDeclaration::ExplicitExpanded(ExplicitRuntimeDeclaration {
110					name,
111					where_section,
112					pallets,
113					pallets_token,
114				})),
115		}
116	}
117}
118
119#[derive(Debug)]
120pub struct WhereSection {
121	pub span: Span,
122}
123
124impl Parse for WhereSection {
125	fn parse(input: ParseStream) -> Result<Self> {
126		input.parse::<token::Where>()?;
127
128		let mut definitions = Vec::new();
129		while !input.peek(token::Brace) {
130			let definition: WhereDefinition = input.parse()?;
131			definitions.push(definition);
132			if !input.peek(Token![,]) {
133				if !input.peek(token::Brace) {
134					return Err(input.error("Expected `,` or `{`"));
135				}
136				break;
137			}
138			input.parse::<Token![,]>()?;
139		}
140		remove_kind(input, WhereKind::Block, &mut definitions)?;
141		remove_kind(input, WhereKind::NodeBlock, &mut definitions)?;
142		remove_kind(input, WhereKind::UncheckedExtrinsic, &mut definitions)?;
143		if let Some(WhereDefinition { ref kind_span, ref kind, .. }) = definitions.first() {
144			let msg = format!(
145				"`{:?}` was declared above. Please use exactly one declaration for `{:?}`.",
146				kind, kind
147			);
148			return Err(Error::new(*kind_span, msg));
149		}
150		Ok(Self { span: input.span() })
151	}
152}
153
154#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
155pub enum WhereKind {
156	Block,
157	NodeBlock,
158	UncheckedExtrinsic,
159}
160
161#[derive(Debug)]
162pub struct WhereDefinition {
163	pub kind_span: Span,
164	pub kind: WhereKind,
165}
166
167impl Parse for WhereDefinition {
168	fn parse(input: ParseStream) -> Result<Self> {
169		let lookahead = input.lookahead1();
170		let (kind_span, kind) = if lookahead.peek(keyword::Block) {
171			(input.parse::<keyword::Block>()?.span(), WhereKind::Block)
172		} else if lookahead.peek(keyword::NodeBlock) {
173			(input.parse::<keyword::NodeBlock>()?.span(), WhereKind::NodeBlock)
174		} else if lookahead.peek(keyword::UncheckedExtrinsic) {
175			(input.parse::<keyword::UncheckedExtrinsic>()?.span(), WhereKind::UncheckedExtrinsic)
176		} else {
177			return Err(lookahead.error());
178		};
179
180		let _: Token![=] = input.parse()?;
181		let _: syn::TypePath = input.parse()?;
182
183		Ok(Self { kind_span, kind })
184	}
185}
186
187/// The declaration of a pallet.
188#[derive(Debug, Clone)]
189pub struct PalletDeclaration {
190	/// Is this pallet fully expanded?
191	pub is_expanded: bool,
192	/// The name of the pallet, e.g.`System` in `System: frame_system`.
193	pub name: Ident,
194	/// Optional attributes tagged right above a pallet declaration.
195	pub attrs: Vec<Attribute>,
196	/// Optional fixed index, e.g. `MyPallet ...  = 3,`.
197	pub index: Option<u8>,
198	/// The path of the pallet, e.g. `frame_system` in `System: frame_system`.
199	pub path: PalletPath,
200	/// The instance of the pallet, e.g. `Instance1` in `Council: pallet_collective::<Instance1>`.
201	pub instance: Option<Ident>,
202	/// The declared pallet parts,
203	/// e.g. `Some([Pallet, Call])` for `System: system::{Pallet, Call}`
204	/// or `None` for `System: system`.
205	pub pallet_parts: Option<Vec<PalletPart>>,
206	/// The specified parts, either use_parts or exclude_parts.
207	pub specified_parts: SpecifiedParts,
208}
209
210/// The possible declaration of pallet parts to use.
211#[derive(Debug, Clone)]
212pub enum SpecifiedParts {
213	/// Use all the pallet parts except those specified.
214	Exclude(Vec<PalletPartNoGeneric>),
215	/// Use only the specified pallet parts.
216	Use(Vec<PalletPartNoGeneric>),
217	/// Use the all the pallet parts.
218	All,
219}
220
221impl Parse for PalletDeclaration {
222	fn parse(input: ParseStream) -> Result<Self> {
223		let attrs = input.call(Attribute::parse_outer)?;
224
225		let name = input.parse()?;
226		let _: Token![:] = input.parse()?;
227		let path = input.parse()?;
228
229		// Parse for instance.
230		let instance = if input.peek(Token![::]) && input.peek3(Token![<]) {
231			let _: Token![::] = input.parse()?;
232			let _: Token![<] = input.parse()?;
233			let res = Some(input.parse()?);
234			let _: Token![>] = input.parse()?;
235			res
236		} else if !(input.peek(Token![::]) && input.peek3(token::Brace)) &&
237			!input.peek(keyword::expanded) &&
238			!input.peek(keyword::exclude_parts) &&
239			!input.peek(keyword::use_parts) &&
240			!input.peek(Token![=]) &&
241			!input.peek(Token![,]) &&
242			!input.is_empty()
243		{
244			return Err(input.error(
245				"Unexpected tokens, expected one of `::$ident` `::{`, `exclude_parts`, `use_parts`, `=`, `,`",
246			));
247		} else {
248			None
249		};
250
251		// Check if the pallet is fully expanded.
252		let (is_expanded, extra_parts) = if input.peek(keyword::expanded) {
253			let _: keyword::expanded = input.parse()?;
254			let _: Token![::] = input.parse()?;
255			(true, parse_pallet_parts(input)?)
256		} else {
257			(false, vec![])
258		};
259
260		// Parse for explicit parts
261		let pallet_parts = if input.peek(Token![::]) && input.peek3(token::Brace) {
262			let _: Token![::] = input.parse()?;
263			let mut parts = parse_pallet_parts(input)?;
264			parts.extend(extra_parts.into_iter());
265			Some(parts)
266		} else if !input.peek(keyword::exclude_parts) &&
267			!input.peek(keyword::use_parts) &&
268			!input.peek(Token![=]) &&
269			!input.peek(Token![,]) &&
270			!input.is_empty()
271		{
272			return Err(input.error(
273				"Unexpected tokens, expected one of `::{`, `exclude_parts`, `use_parts`, `=`, `,`",
274			));
275		} else {
276			is_expanded.then_some(extra_parts)
277		};
278
279		// Parse for specified parts
280		let specified_parts = if input.peek(keyword::exclude_parts) {
281			let _: keyword::exclude_parts = input.parse()?;
282			SpecifiedParts::Exclude(parse_pallet_parts_no_generic(input)?)
283		} else if input.peek(keyword::use_parts) {
284			let _: keyword::use_parts = input.parse()?;
285			SpecifiedParts::Use(parse_pallet_parts_no_generic(input)?)
286		} else if !input.peek(Token![=]) && !input.peek(Token![,]) && !input.is_empty() {
287			return Err(input.error("Unexpected tokens, expected one of `exclude_parts`, `=`, `,`"));
288		} else {
289			SpecifiedParts::All
290		};
291
292		// Parse for pallet index
293		let index = if input.peek(Token![=]) {
294			input.parse::<Token![=]>()?;
295			let index = input.parse::<syn::LitInt>()?;
296			let index = index.base10_parse::<u8>()?;
297			Some(index)
298		} else if !input.peek(Token![,]) && !input.is_empty() {
299			return Err(input.error("Unexpected tokens, expected one of `=`, `,`"));
300		} else {
301			None
302		};
303
304		Ok(Self { is_expanded, attrs, name, path, instance, pallet_parts, specified_parts, index })
305	}
306}
307
308/// A struct representing a path to a pallet. `PalletPath` is almost identical to the standard
309/// Rust path with a few restrictions:
310/// - No leading colons allowed
311/// - Path segments can only consist of identifiers separated by colons
312#[derive(Debug, Clone)]
313pub struct PalletPath {
314	pub inner: Path,
315}
316
317impl PalletPath {
318	pub fn module_name(&self) -> String {
319		self.inner.segments.iter().fold(String::new(), |mut acc, segment| {
320			if !acc.is_empty() {
321				acc.push_str("::");
322			}
323			acc.push_str(&segment.ident.to_string());
324			acc
325		})
326	}
327}
328
329impl Parse for PalletPath {
330	fn parse(input: ParseStream) -> Result<Self> {
331		let mut res =
332			PalletPath { inner: Path { leading_colon: None, segments: Punctuated::new() } };
333
334		let lookahead = input.lookahead1();
335		if lookahead.peek(Token![crate]) ||
336			lookahead.peek(Token![self]) ||
337			lookahead.peek(Token![super]) ||
338			lookahead.peek(Ident)
339		{
340			let ident = input.call(Ident::parse_any)?;
341			res.inner.segments.push(ident.into());
342		} else {
343			return Err(lookahead.error());
344		}
345
346		while input.peek(Token![::]) && input.peek3(Ident) {
347			input.parse::<Token![::]>()?;
348			let ident = input.parse::<Ident>()?;
349			res.inner.segments.push(ident.into());
350		}
351		Ok(res)
352	}
353}
354
355impl quote::ToTokens for PalletPath {
356	fn to_tokens(&self, tokens: &mut TokenStream) {
357		self.inner.to_tokens(tokens);
358	}
359}
360
361/// Parse [`PalletPart`]'s from a braces enclosed list that is split by commas, e.g.
362///
363/// `{ Call, Event }`
364fn parse_pallet_parts(input: ParseStream) -> Result<Vec<PalletPart>> {
365	let pallet_parts: ext::Braces<ext::Punctuated<PalletPart, Token![,]>> = input.parse()?;
366
367	let mut resolved = HashSet::new();
368	for part in pallet_parts.content.inner.iter() {
369		if !resolved.insert(part.name()) {
370			let msg = format!(
371				"`{}` was already declared before. Please remove the duplicate declaration",
372				part.name(),
373			);
374			return Err(Error::new(part.keyword.span(), msg));
375		}
376	}
377
378	Ok(pallet_parts.content.inner.into_iter().collect())
379}
380
381#[derive(Debug, Clone)]
382pub enum PalletPartKeyword {
383	Pallet(keyword::Pallet),
384	Call(keyword::Call),
385	Storage(keyword::Storage),
386	Event(keyword::Event),
387	Error(keyword::Error),
388	Config(keyword::Config),
389	Origin(keyword::Origin),
390	Inherent(keyword::Inherent),
391	ValidateUnsigned(keyword::ValidateUnsigned),
392	FreezeReason(keyword::FreezeReason),
393	HoldReason(keyword::HoldReason),
394	Task(keyword::Task),
395	LockId(keyword::LockId),
396	SlashReason(keyword::SlashReason),
397}
398
399impl Parse for PalletPartKeyword {
400	fn parse(input: ParseStream) -> Result<Self> {
401		let lookahead = input.lookahead1();
402
403		if lookahead.peek(keyword::Pallet) {
404			Ok(Self::Pallet(input.parse()?))
405		} else if lookahead.peek(keyword::Call) {
406			Ok(Self::Call(input.parse()?))
407		} else if lookahead.peek(keyword::Storage) {
408			Ok(Self::Storage(input.parse()?))
409		} else if lookahead.peek(keyword::Event) {
410			Ok(Self::Event(input.parse()?))
411		} else if lookahead.peek(keyword::Error) {
412			Ok(Self::Error(input.parse()?))
413		} else if lookahead.peek(keyword::Config) {
414			Ok(Self::Config(input.parse()?))
415		} else if lookahead.peek(keyword::Origin) {
416			Ok(Self::Origin(input.parse()?))
417		} else if lookahead.peek(keyword::Inherent) {
418			Ok(Self::Inherent(input.parse()?))
419		} else if lookahead.peek(keyword::ValidateUnsigned) {
420			Ok(Self::ValidateUnsigned(input.parse()?))
421		} else if lookahead.peek(keyword::FreezeReason) {
422			Ok(Self::FreezeReason(input.parse()?))
423		} else if lookahead.peek(keyword::HoldReason) {
424			Ok(Self::HoldReason(input.parse()?))
425		} else if lookahead.peek(keyword::Task) {
426			Ok(Self::Task(input.parse()?))
427		} else if lookahead.peek(keyword::LockId) {
428			Ok(Self::LockId(input.parse()?))
429		} else if lookahead.peek(keyword::SlashReason) {
430			Ok(Self::SlashReason(input.parse()?))
431		} else {
432			Err(lookahead.error())
433		}
434	}
435}
436
437impl PalletPartKeyword {
438	/// Returns the name of `Self`.
439	fn name(&self) -> &'static str {
440		match self {
441			Self::Pallet(_) => "Pallet",
442			Self::Call(_) => "Call",
443			Self::Storage(_) => "Storage",
444			Self::Event(_) => "Event",
445			Self::Error(_) => "Error",
446			Self::Config(_) => "Config",
447			Self::Origin(_) => "Origin",
448			Self::Inherent(_) => "Inherent",
449			Self::ValidateUnsigned(_) => "ValidateUnsigned",
450			Self::FreezeReason(_) => "FreezeReason",
451			Self::HoldReason(_) => "HoldReason",
452			Self::Task(_) => "Task",
453			Self::LockId(_) => "LockId",
454			Self::SlashReason(_) => "SlashReason",
455		}
456	}
457
458	/// Returns `true` if this pallet part is allowed to have generic arguments.
459	fn allows_generic(&self) -> bool {
460		Self::all_generic_arg().iter().any(|n| *n == self.name())
461	}
462
463	/// Returns the names of all pallet parts that allow to have a generic argument.
464	fn all_generic_arg() -> &'static [&'static str] {
465		&["Event", "Error", "Origin", "Config", "Task"]
466	}
467}
468
469impl ToTokens for PalletPartKeyword {
470	fn to_tokens(&self, tokens: &mut TokenStream) {
471		match self {
472			Self::Pallet(inner) => inner.to_tokens(tokens),
473			Self::Call(inner) => inner.to_tokens(tokens),
474			Self::Storage(inner) => inner.to_tokens(tokens),
475			Self::Event(inner) => inner.to_tokens(tokens),
476			Self::Error(inner) => inner.to_tokens(tokens),
477			Self::Config(inner) => inner.to_tokens(tokens),
478			Self::Origin(inner) => inner.to_tokens(tokens),
479			Self::Inherent(inner) => inner.to_tokens(tokens),
480			Self::ValidateUnsigned(inner) => inner.to_tokens(tokens),
481			Self::FreezeReason(inner) => inner.to_tokens(tokens),
482			Self::HoldReason(inner) => inner.to_tokens(tokens),
483			Self::Task(inner) => inner.to_tokens(tokens),
484			Self::LockId(inner) => inner.to_tokens(tokens),
485			Self::SlashReason(inner) => inner.to_tokens(tokens),
486		}
487	}
488}
489
490#[derive(Debug, Clone)]
491pub struct PalletPart {
492	pub keyword: PalletPartKeyword,
493	pub generics: syn::Generics,
494}
495
496impl Parse for PalletPart {
497	fn parse(input: ParseStream) -> Result<Self> {
498		let keyword: PalletPartKeyword = input.parse()?;
499
500		let generics: syn::Generics = input.parse()?;
501		if !generics.params.is_empty() && !keyword.allows_generic() {
502			let valid_generics = PalletPart::format_names(PalletPartKeyword::all_generic_arg());
503			let msg = format!(
504				"`{}` is not allowed to have generics. \
505				 Only the following pallets are allowed to have generics: {}.",
506				keyword.name(),
507				valid_generics,
508			);
509			return Err(syn::Error::new(keyword.span(), msg));
510		}
511
512		Ok(Self { keyword, generics })
513	}
514}
515
516impl PalletPart {
517	pub fn format_names(names: &[&'static str]) -> String {
518		let res: Vec<_> = names.iter().map(|s| format!("`{}`", s)).collect();
519		res.join(", ")
520	}
521
522	/// The name of this pallet part.
523	pub fn name(&self) -> &'static str {
524		self.keyword.name()
525	}
526}
527
528fn remove_kind(
529	input: ParseStream,
530	kind: WhereKind,
531	definitions: &mut Vec<WhereDefinition>,
532) -> Result<WhereDefinition> {
533	if let Some(pos) = definitions.iter().position(|d| d.kind == kind) {
534		Ok(definitions.remove(pos))
535	} else {
536		let msg = format!(
537			"Missing associated type for `{:?}`. Add `{:?}` = ... to where section.",
538			kind, kind
539		);
540		Err(input.error(msg))
541	}
542}
543
544/// The declaration of a part without its generics
545#[derive(Debug, Clone)]
546pub struct PalletPartNoGeneric {
547	keyword: PalletPartKeyword,
548}
549
550impl Parse for PalletPartNoGeneric {
551	fn parse(input: ParseStream) -> Result<Self> {
552		Ok(Self { keyword: input.parse()? })
553	}
554}
555
556/// Parse [`PalletPartNoGeneric`]'s from a braces enclosed list that is split by commas, e.g.
557///
558/// `{ Call, Event }`
559fn parse_pallet_parts_no_generic(input: ParseStream) -> Result<Vec<PalletPartNoGeneric>> {
560	let pallet_parts: ext::Braces<ext::Punctuated<PalletPartNoGeneric, Token![,]>> =
561		input.parse()?;
562
563	let mut resolved = HashSet::new();
564	for part in pallet_parts.content.inner.iter() {
565		if !resolved.insert(part.keyword.name()) {
566			let msg = format!(
567				"`{}` was already declared before. Please remove the duplicate declaration",
568				part.keyword.name(),
569			);
570			return Err(Error::new(part.keyword.span(), msg));
571		}
572	}
573
574	Ok(pallet_parts.content.inner.into_iter().collect())
575}
576
577/// The final definition of a pallet with the resulting fixed index and explicit parts.
578#[derive(Debug, Clone)]
579pub struct Pallet {
580	/// Is this pallet fully expanded?
581	pub is_expanded: bool,
582	/// The name of the pallet, e.g.`System` in `System: frame_system`.
583	pub name: Ident,
584	/// Either automatically inferred, or defined (e.g. `MyPallet ...  = 3,`).
585	pub index: u8,
586	/// The path of the pallet, e.g. `frame_system` in `System: frame_system`.
587	pub path: PalletPath,
588	/// The instance of the pallet, e.g. `Instance1` in `Council: pallet_collective::<Instance1>`.
589	pub instance: Option<Ident>,
590	/// The pallet parts to use for the pallet.
591	pub pallet_parts: Vec<PalletPart>,
592	/// Expressions specified inside of a #[cfg] attribute.
593	pub cfg_pattern: Vec<cfg_expr::Expression>,
594	/// The doc literals
595	pub docs: Vec<syn::Expr>,
596}
597
598impl Pallet {
599	/// Get resolved pallet parts
600	pub fn pallet_parts(&self) -> &[PalletPart] {
601		&self.pallet_parts
602	}
603
604	/// Find matching parts
605	pub fn find_part(&self, name: &str) -> Option<&PalletPart> {
606		self.pallet_parts.iter().find(|part| part.name() == name)
607	}
608
609	/// Return whether pallet contains part
610	pub fn exists_part(&self, name: &str) -> bool {
611		self.find_part(name).is_some()
612	}
613
614	// Get runtime attributes for the pallet, mostly used for macros
615	pub fn get_attributes(&self) -> TokenStream {
616		self.cfg_pattern.iter().fold(TokenStream::new(), |acc, pattern| {
617			let attr = TokenStream::from_str(&format!("#[cfg({})]", pattern.original()))
618				.expect("was successfully parsed before; qed");
619			quote::quote! {
620				#acc
621				#attr
622			}
623		})
624	}
625}
626
627/// Result of a conversion of a declaration of pallets.
628///
629/// # State Transitions
630///
631/// ```ignore
632/// +----------+    +----------+    +------------------+
633/// | Implicit | -> | Explicit | -> | ExplicitExpanded |
634/// +----------+    +----------+    +------------------+
635/// ```
636enum PalletsConversion {
637	/// Pallets implicitly declare parts.
638	///
639	/// `System: frame_system`.
640	Implicit(Vec<PalletDeclaration>),
641	/// Pallets explicitly declare parts.
642	///
643	/// `System: frame_system::{Pallet, Call}`
644	///
645	/// However, for backwards compatibility with Polkadot/Kusama
646	/// we must propagate some other parts to the pallet by default.
647	Explicit(Vec<Pallet>),
648	/// Pallets explicitly declare parts that are fully expanded.
649	///
650	/// This is the end state that contains extra parts included by
651	/// default by Substrate.
652	///
653	/// `System: frame_system expanded::{Error} ::{Pallet, Call}`
654	///
655	/// For this example, the `Pallet`, `Call` and `Error` parts are collected.
656	ExplicitExpanded(Vec<Pallet>),
657}
658
659/// Convert from the parsed pallet declaration to their final information.
660///
661/// Check if all pallet have explicit declaration of their parts, if so then assign index to each
662/// pallet using same rules as rust for fieldless enum. I.e. implicit are assigned number
663/// incrementally from last explicit or 0.
664fn convert_pallets(pallets: Vec<PalletDeclaration>) -> syn::Result<PalletsConversion> {
665	if pallets.iter().any(|pallet| pallet.pallet_parts.is_none()) {
666		return Ok(PalletsConversion::Implicit(pallets));
667	}
668
669	let mut indices = HashMap::new();
670	let mut last_index: Option<u8> = None;
671	let mut names = HashMap::new();
672	let mut is_expanded = true;
673
674	let pallets = pallets
675		.into_iter()
676		.map(|pallet| {
677			let final_index = match pallet.index {
678				Some(i) => i,
679				None => last_index.map_or(Some(0), |i| i.checked_add(1)).ok_or_else(|| {
680					let msg = "Pallet index doesn't fit into u8, index is 256";
681					syn::Error::new(pallet.name.span(), msg)
682				})?,
683			};
684
685			last_index = Some(final_index);
686
687			if let Some(used_pallet) = indices.insert(final_index, pallet.name.clone()) {
688				let msg = format!(
689					"Pallet indices are conflicting: Both pallets {} and {} are at index {}",
690					used_pallet, pallet.name, final_index,
691				);
692				let mut err = syn::Error::new(used_pallet.span(), &msg);
693				err.combine(syn::Error::new(pallet.name.span(), msg));
694				return Err(err);
695			}
696
697			if let Some(used_pallet) = names.insert(pallet.name.clone(), pallet.name.span()) {
698				let msg = "Two pallets with the same name!";
699
700				let mut err = syn::Error::new(used_pallet, &msg);
701				err.combine(syn::Error::new(pallet.name.span(), &msg));
702				return Err(err);
703			}
704
705			let mut pallet_parts = pallet.pallet_parts.expect("Checked above");
706
707			let available_parts =
708				pallet_parts.iter().map(|part| part.keyword.name()).collect::<HashSet<_>>();
709
710			// Check parts are correctly specified
711			match &pallet.specified_parts {
712				SpecifiedParts::Exclude(parts) | SpecifiedParts::Use(parts) =>
713					for part in parts {
714						if !available_parts.contains(part.keyword.name()) {
715							let msg = format!(
716								"Invalid pallet part specified, the pallet `{}` doesn't have the \
717								`{}` part. Available parts are: {}.",
718								pallet.name,
719								part.keyword.name(),
720								pallet_parts.iter().fold(String::new(), |fold, part| {
721									if fold.is_empty() {
722										format!("`{}`", part.keyword.name())
723									} else {
724										format!("{}, `{}`", fold, part.keyword.name())
725									}
726								})
727							);
728							return Err(syn::Error::new(part.keyword.span(), msg));
729						}
730					},
731				SpecifiedParts::All => (),
732			}
733
734			// Set only specified parts.
735			match pallet.specified_parts {
736				SpecifiedParts::Exclude(excluded_parts) => pallet_parts.retain(|part| {
737					!excluded_parts
738						.iter()
739						.any(|excluded_part| excluded_part.keyword.name() == part.keyword.name())
740				}),
741				SpecifiedParts::Use(used_parts) => pallet_parts.retain(|part| {
742					used_parts.iter().any(|use_part| use_part.keyword.name() == part.keyword.name())
743				}),
744				SpecifiedParts::All => (),
745			}
746
747			let cfg_pattern = pallet
748				.attrs
749				.iter()
750				.map(|attr| {
751					if attr.path().segments.first().map_or(false, |s| s.ident != "cfg") {
752						let msg = "Unsupported attribute, only #[cfg] is supported on pallet \
753						declarations in `construct_runtime`";
754						return Err(syn::Error::new(attr.span(), msg));
755					}
756
757					attr.parse_args_with(|input: syn::parse::ParseStream| {
758						// Required, otherwise the parse stream doesn't advance and will result in
759						// an error.
760						let input = input.parse::<proc_macro2::TokenStream>()?;
761						cfg_expr::Expression::parse(&input.to_string())
762							.map_err(|e| syn::Error::new(attr.span(), e.to_string()))
763					})
764				})
765				.collect::<Result<Vec<_>>>()?;
766
767			is_expanded &= pallet.is_expanded;
768
769			Ok(Pallet {
770				is_expanded: pallet.is_expanded,
771				name: pallet.name,
772				index: final_index,
773				path: pallet.path,
774				instance: pallet.instance,
775				cfg_pattern,
776				pallet_parts,
777				docs: vec![],
778			})
779		})
780		.collect::<Result<Vec<_>>>()?;
781
782	if is_expanded {
783		Ok(PalletsConversion::ExplicitExpanded(pallets))
784	} else {
785		Ok(PalletsConversion::Explicit(pallets))
786	}
787}