derive_deftly_macros/
accum.rs

1//! `derive_deftly_engine!()`, parsing accumulations
2
3use super::framework::*;
4use adviseable::*;
5
6/// `derive_deftly_engine! accumulated form, accumulated information
7///
8/// We don't reify the whole input;
9/// instead, we accumulate directly in the `Parse` impl.
10#[derive(Debug)]
11pub struct EngineFinalInput {
12    driver: syn::DeriveInput,
13    accum: Accumulated,
14}
15
16#[derive(Debug)]
17pub struct Accumulated {
18    metas: meta::CheckUsed<meta::Accum>,
19}
20
21impl EngineFinalInput {
22    pub fn parse_adviseable_remainder(
23        driver: syn::DeriveInput,
24        input: ParseStream,
25    ) -> AdviseableResult<Self> {
26        let _empty_next_brackets_contents;
27        let _ = bracketed!(_empty_next_brackets_contents in input);
28
29        let accum;
30        let _ = bracketed!(accum in input);
31        let accum = accum.parse()?;
32
33        let _: TokenStream = input.parse()?;
34
35        Ok(AOk(EngineFinalInput { driver, accum }))
36    }
37}
38
39impl Parse for Accumulated {
40    fn parse(input: ParseStream) -> syn::Result<Self> {
41        use meta::CheckUsed as mCU;
42
43        let mut metas = mCU::Check(meta::Accum::default());
44
45        struct Ignore;
46
47        while !input.is_empty() {
48            let kind: syn::Ident = input.parse()?;
49            match if kind == "_meta_used" {
50                if let mCU::Check(m) = &mut metas {
51                    match input.parse()? {
52                        mCU::Check(y) => m.used.push(y),
53                        mCU::Unchecked => metas = mCU::Unchecked,
54                    }
55                    continue;
56                } else {
57                    Ignore
58                }
59            } else if kind == "_meta_recog" {
60                if let mCU::Check(m) = &mut metas {
61                    let content;
62                    let _brackets = bracketed!(content in input);
63                    let input = content;
64                    while !input.is_empty() {
65                        let allow = match input.parse()? {
66                            Some::<proc_macro2::Punct>(p) => {
67                                match p.as_char() {
68                                    '?' => meta::Usage::BOOL_ONLY,
69                                    '+' => meta::Usage::VALUE_ONLY,
70                                    _other => return Err(p.error(
71                                        "unrecognised _meta_recog usage mode",
72                                    )),
73                                }
74                            }
75                            None => meta::Usage::VALUE,
76                        };
77                        let desig = input.parse()?;
78                        m.recog.update(desig, allow);
79                    }
80                    continue;
81                } else {
82                    Ignore
83                }
84            } else if kind == "error" {
85                metas = mCU::Unchecked;
86                Ignore
87            } else if kind.to_string().starts_with('_') {
88                Ignore
89            } else {
90                return Err(
91                    kind.error("unrecognised mandatory accumulation kind")
92                );
93            } {
94                Ignore => {
95                    let _: TokenTree = input.parse()?;
96                }
97            }
98        }
99        Ok(Accumulated { metas })
100    }
101}
102
103impl EngineFinalInput {
104    pub fn process(self) -> syn::Result<TokenStream> {
105        let r = Context::call(
106            &self.driver,
107            &dummy_path(), // template_crate, not used by our f
108            None,          // template_name
109            |ctx| {
110                if let mCU::Check(m) = &self.accum.metas {
111                    for group in &m.used {
112                        adviseable_parse2_call(
113                            group.content.clone(),
114                            |input| {
115                                ctx.decode_update_metas_used(input)?;
116                                Ok(AOk(()))
117                            },
118                        )?
119                    }
120                }
121
122                let mut errors = ErrorAccumulator::default();
123
124                if let mCU::Check(m) = &self.accum.metas {
125                    ctx.check_metas_used(&mut errors, &m.recog);
126                }
127                errors.finish()
128            },
129        );
130
131        let mut out = TokenStream::new();
132
133        match r {
134            Ok(()) => {}
135            Err(e) => e.into_compile_error().to_tokens(&mut out),
136        }
137
138        Ok(out)
139    }
140}