1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
use syn::bracketed;
use syn::parse::Parse;
use syn::punctuated::Punctuated;
use syn::Attribute;
use syn::Token;

use crate::Params;
use crate::Proxy;

impl Parse for Proxy {
    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
        // if input.peek(Token![struct]) {
        //     let _kwd: Token![struct] = input.parse()?;
        //     Ok(Self::Struct(input.parse()?))
        // } else 
        if input.peek(Token![trait]) {
            let kwd: Token![trait] = input.parse()?;
            Ok(Self::Trait(kwd, input.parse()?))
        // } else if input.peek(Token![impl]) {
        //     let _kwd: Token![impl] = input.parse()?;
        //     let generics = input.parse()?;
        //     let path = input.parse()?;
        //     Ok(Self::Impl(ProxyImpl { generics, path }))
        } else {
            Err(syn::Error::new(
                input.span(),
                "Expected `struct`, `trait` or `impl`",
            ))
        }
    }
}

impl Parse for Params {
    fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
        let mut enum_attr = Attribute::parse_outer(input)?;

        let visibility: syn::Visibility = input
            .fork()
            .parse::<syn::Visibility>()
            .ok()
            .and_then(|_| input.parse().ok())
            .unwrap_or(syn::Visibility::Inherited);

        let _: Token![enum] = input.parse()?;

        let enum_name: syn::Ident = input.parse()?;
        let mut returnval = None;
        let mut proxies = Vec::new();

        while input.peek(Token![,]) {
            let _comma: Token![,] = input.parse()?;
            if input.is_empty() {
                // Allow trailing comma
                break;
            }
            if input.peek(Token![trait]) {
                proxies.extend(input.parse());
                continue;
            }
            
            let arg: syn::Ident = input.parse()?;
            match arg.to_string().as_str() {
                "enum_attr" => {
                    let content;
                    enum_attr.push(Attribute {
                        pound_token: Token![#](input.span()),
                        style: syn::AttrStyle::Outer,
                        bracket_token: bracketed!(content in input),
                        path: content.call(syn::Path::parse_mod_style)?,
                        tokens: content.parse()?,
                    })
                }
                "returnval" => {
                    if returnval.is_some() {
                        return Err(syn::Error::new_spanned(
                            arg,
                            "Argument `returnval` specified twice",
                        ));
                    }
                    let _eq: Token![=] = input.parse()?;
                    returnval = Some(input.parse()?)
                }
                "proxy" => {
                    let contents;
                    if input.peek(syn::token::Paren) {
                        let _paren = syn::parenthesized!(contents in input);
                    } else {
                        let _paren = syn::braced!(contents in input);
                    }
                    let punct: Punctuated<Proxy, Token![;]> =
                        Punctuated::parse_terminated(&contents)?;
                    proxies.extend(punct)
                }
                _ => {
                    return Err(syn::Error::new(
                        arg.span(),
                        format!("Unknown argument `{arg}` to ctrlgen"),
                    ))
                }
            };
        }

        Ok(Self {
            visibility,
            enum_name,
            returnval,
            proxies,
            enum_attr,
        })
    }
}