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