1use 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#[derive(Debug)]
59pub enum RuntimeDeclaration {
60 Implicit(ImplicitRuntimeDeclaration),
61 Explicit(ExplicitRuntimeDeclaration),
62 ExplicitExpanded(ExplicitRuntimeDeclaration),
63}
64
65#[derive(Debug)]
67pub struct ImplicitRuntimeDeclaration {
68 pub pallets: Vec<PalletDeclaration>,
69}
70
71#[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 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#[derive(Debug, Clone)]
188pub struct PalletDeclaration {
189 pub is_expanded: bool,
191 pub name: Ident,
193 pub attrs: Vec<Attribute>,
195 pub index: Option<u8>,
197 pub path: PalletPath,
199 pub instance: Option<Ident>,
201 pub pallet_parts: Option<Vec<PalletPart>>,
205 pub specified_parts: SpecifiedParts,
207}
208
209#[derive(Debug, Clone)]
211pub enum SpecifiedParts {
212 Exclude(Vec<PalletPartNoGeneric>),
214 Use(Vec<PalletPartNoGeneric>),
216 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 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 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 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 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 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#[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
360fn 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 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 fn allows_generic(&self) -> bool {
459 Self::all_generic_arg().iter().any(|n| *n == self.name())
460 }
461
462 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 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#[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
555fn 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#[derive(Debug, Clone)]
578pub struct Pallet {
579 pub is_expanded: bool,
581 pub name: Ident,
583 pub index: u8,
585 pub path: PalletPath,
587 pub instance: Option<Ident>,
589 pub pallet_parts: Vec<PalletPart>,
591 pub cfg_pattern: Vec<cfg_expr::Expression>,
593 pub docs: Vec<syn::Expr>,
595}
596
597impl Pallet {
598 pub fn pallet_parts(&self) -> &[PalletPart] {
600 &self.pallet_parts
601 }
602
603 pub fn find_part(&self, name: &str) -> Option<&PalletPart> {
605 self.pallet_parts.iter().find(|part| part.name() == name)
606 }
607
608 pub fn exists_part(&self, name: &str) -> bool {
610 self.find_part(name).is_some()
611 }
612}
613
614enum PalletsConversion {
624 Implicit(Vec<PalletDeclaration>),
628 Explicit(Vec<Pallet>),
635 ExplicitExpanded(Vec<Pallet>),
644}
645
646fn 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 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 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 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}