orchestra_proc_macro/parse/
parse_subsystem_attr.rs1use super::kw;
17use proc_macro2::Span;
18use quote::{quote, ToTokens};
19use std::collections::{hash_map::RandomState, HashMap};
20use syn::{
21 parse::{Parse, ParseBuffer},
22 punctuated::Punctuated,
23 spanned::Spanned,
24 Error, Ident, Path, Result, Token,
25};
26
27#[derive(Clone, Debug)]
28enum SubsystemAttrItem {
29 Error { tag: kw::error, eq_token: Token![=], value: Path },
31 Subsystem { tag: Option<kw::subsystem>, eq_token: Option<Token![=]>, value: Ident },
36 TraitPrefix { tag: kw::prefix, eq_token: Token![=], value: Path },
41}
42
43impl ToTokens for SubsystemAttrItem {
44 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
45 let ts = match self {
46 Self::TraitPrefix { tag, eq_token, value } => {
47 quote! { #tag #eq_token, #value }
48 },
49 Self::Error { tag, eq_token, value } => {
50 quote! { #tag #eq_token, #value }
51 },
52 Self::Subsystem { tag, eq_token, value } => {
53 quote! { #tag #eq_token, #value }
54 },
55 };
56 tokens.extend(ts.into_iter());
57 }
58}
59
60impl Parse for SubsystemAttrItem {
61 fn parse(input: &ParseBuffer) -> Result<Self> {
62 let lookahead = input.lookahead1();
63 if lookahead.peek(kw::error) {
64 Ok(SubsystemAttrItem::Error {
65 tag: input.parse::<kw::error>()?,
66 eq_token: input.parse()?,
67 value: input.parse()?,
68 })
69 } else if lookahead.peek(kw::prefix) {
70 Ok(SubsystemAttrItem::TraitPrefix {
71 tag: input.parse::<kw::prefix>()?,
72 eq_token: input.parse()?,
73 value: input.parse()?,
74 })
75 } else if lookahead.peek(kw::subsystem) {
76 Ok(SubsystemAttrItem::Subsystem {
77 tag: Some(input.parse::<kw::subsystem>()?),
78 eq_token: Some(input.parse()?),
79 value: input.parse()?,
80 })
81 } else {
82 Ok(SubsystemAttrItem::Subsystem { tag: None, eq_token: None, value: input.parse()? })
83 }
84 }
85}
86
87#[derive(Clone, Debug)]
89pub(crate) struct SubsystemAttrArgs {
90 span: Span,
91 pub(crate) error_path: Option<Path>,
92 pub(crate) subsystem_ident: Ident,
93 pub(crate) trait_prefix_path: Option<Path>,
94}
95
96impl Spanned for SubsystemAttrArgs {
97 fn span(&self) -> Span {
98 self.span.clone()
99 }
100}
101
102macro_rules! extract_variant {
103 ($unique:expr, $variant:ident ; default = $fallback:expr) => {
104 extract_variant!($unique, $variant).unwrap_or_else(|| $fallback)
105 };
106 ($unique:expr, $variant:ident ; err = $err:expr) => {
107 extract_variant!($unique, $variant).ok_or_else(|| Error::new(Span::call_site(), $err))
108 };
109 ($unique:expr, $variant:ident) => {
110 $unique.values().find_map(|item| match item {
111 SubsystemAttrItem::$variant { value, .. } => Some(value.clone()),
112 _ => None,
113 })
114 };
115}
116
117impl Parse for SubsystemAttrArgs {
118 fn parse(input: &ParseBuffer) -> Result<Self> {
119 let span = input.span();
120 let items: Punctuated<SubsystemAttrItem, Token![,]> =
121 input.parse_terminated(SubsystemAttrItem::parse)?;
122
123 let mut unique = HashMap::<
124 std::mem::Discriminant<SubsystemAttrItem>,
125 SubsystemAttrItem,
126 RandomState,
127 >::default();
128 for item in items {
129 if let Some(first) = unique.insert(std::mem::discriminant(&item), item.clone()) {
130 let mut e = Error::new(
131 item.span(),
132 format!("Duplicate definition of subsystem generation type found"),
133 );
134 e.combine(Error::new(first.span(), "previously defined here."));
135 return Err(e)
136 }
137 }
138 let error_path = extract_variant!(unique, Error);
139 let subsystem_ident = extract_variant!(unique, Subsystem; err = "Must annotate the identical orchestra error type via `subsystem=..` or plainly as `Subsystem` as specified in the orchestra declaration.")?;
140 let trait_prefix_path = extract_variant!(unique, TraitPrefix);
141 Ok(SubsystemAttrArgs { span, error_path, subsystem_ident, trait_prefix_path })
142 }
143}