pyro_macro/module/
parse.rs1use proc_macro2;
2use syn::{
3 Expr, Ident, Meta, Result, Token,
4 parse::{Parse, ParseStream},
5 punctuated::Punctuated,
6};
7
8pub enum OutputSpec {
10 SingleField(Ident),
12 TupleFields(Vec<Ident>),
14 Struct,
16}
17
18pub struct ModuleAttrs {
20 pub session: bool,
21 pub output: OutputSpec,
22}
23
24impl ModuleAttrs {
25 fn from_metas(metas: Punctuated<Meta, Token![,]>) -> Result<Self> {
26 let mut session = false;
27 let mut output = None;
28
29 for meta in metas {
30 match meta {
31 Meta::Path(path) if path.is_ident("session") => {
32 session = true;
33 }
34 Meta::NameValue(nv) if nv.path.is_ident("output") => {
35 output = Some(parse_output_spec(&nv.value)?);
36 }
37 _ => {
38 return Err(syn::Error::new_spanned(
39 meta,
40 "Unexpected attribute. Expected 'session' or 'output = ...'",
41 ));
42 }
43 }
44 }
45
46 let output = output.ok_or_else(|| {
47 syn::Error::new(proc_macro2::Span::call_site(), "Missing `output` attribute")
48 })?;
49
50 Ok(ModuleAttrs { session, output })
51 }
52}
53
54fn parse_output_spec(expr: &Expr) -> Result<OutputSpec> {
55 match expr {
56 Expr::Tuple(tuple) => {
57 let mut fields = Vec::new();
58 for e in &tuple.elems {
59 if let Expr::Path(path) = e {
60 if let Some(ident) = path.path.get_ident() {
61 fields.push(ident.clone());
62 } else {
63 return Err(syn::Error::new_spanned(
64 path,
65 "Expected identifier in tuple",
66 ));
67 }
68 } else {
69 return Err(syn::Error::new_spanned(e, "Expected identifier in tuple"));
70 }
71 }
72 Ok(OutputSpec::TupleFields(fields))
73 }
74 Expr::Path(path) => {
75 if let Some(ident) = path.path.get_ident() {
76 let name_str = ident.to_string();
77 if name_str
78 .chars()
79 .next()
80 .map(|c| c.is_uppercase())
81 .unwrap_or(false)
82 {
83 Ok(OutputSpec::Struct)
84 } else {
85 Ok(OutputSpec::SingleField(ident.clone()))
86 }
87 } else {
88 Err(syn::Error::new_spanned(
89 path,
90 "Expected identifier for output",
91 ))
92 }
93 }
94 _ => Err(syn::Error::new_spanned(
95 expr,
96 "Expected identifier or tuple of identifiers for output",
97 )),
98 }
99}
100
101impl Parse for ModuleAttrs {
102 fn parse(input: ParseStream) -> Result<Self> {
103 let metas = Punctuated::<Meta, Token![,]>::parse_terminated(input)?;
104 Self::from_metas(metas)
105 }
106}