substruct/
expr.rs

1use proc_macro2::TokenStream;
2use quote::ToTokens;
3use syn::parse::{Parse, ParseStream};
4use syn::punctuated::Punctuated;
5
6pub(crate) enum Expr {
7    Ident(syn::Ident),
8    Not(NotExpr),
9    All(AllExpr),
10    Any(AnyExpr),
11}
12
13impl Expr {
14    pub fn evaluate(&self, ident: &syn::Ident) -> bool {
15        match self {
16            Self::Ident(lit) => ident == lit,
17            Self::Not(e) => e.evaluate(ident),
18            Self::Any(e) => e.evaluate(ident),
19            Self::All(e) => e.evaluate(ident),
20        }
21    }
22}
23
24impl Parse for Expr {
25    fn parse(input: ParseStream) -> syn::Result<Self> {
26        if !input.peek2(syn::token::Paren) {
27            return Ok(Self::Ident(input.parse()?));
28        }
29
30        let ident: syn::Ident = input.fork().parse()?;
31
32        match () {
33            _ if ident == "not" => input.parse().map(Self::Not),
34            _ if ident == "any" => input.parse().map(Self::Any),
35            _ if ident == "all" => input.parse().map(Self::All),
36            _ => Err(syn::Error::new(
37                ident.span(),
38                format!("unexpected operator `{ident}`, expected `not`, `any`, or `all`"),
39            )),
40        }
41    }
42}
43
44impl ToTokens for Expr {
45    fn to_tokens(&self, tokens: &mut TokenStream) {
46        match self {
47            Self::Ident(ident) => ident.to_tokens(tokens),
48            Self::Not(e) => e.to_tokens(tokens),
49            Self::All(e) => e.to_tokens(tokens),
50            Self::Any(e) => e.to_tokens(tokens),
51        }
52    }
53}
54
55pub(crate) struct NotExpr {
56    pub ident: syn::Ident,
57    pub paren: syn::token::Paren,
58    pub expr: Box<Expr>,
59}
60
61impl NotExpr {
62    pub fn evaluate(&self, ident: &syn::Ident) -> bool {
63        !self.expr.evaluate(ident)
64    }
65}
66
67impl Parse for NotExpr {
68    fn parse(input: ParseStream) -> syn::Result<Self> {
69        let content;
70
71        Ok(Self {
72            ident: input.parse()?,
73            paren: syn::parenthesized!(content in input),
74            expr: content.parse()?,
75        })
76    }
77}
78
79impl ToTokens for NotExpr {
80    fn to_tokens(&self, tokens: &mut TokenStream) {
81        self.ident.to_tokens(tokens);
82        self.paren
83            .surround(tokens, |tokens| self.expr.to_tokens(tokens));
84    }
85}
86
87pub(crate) struct AnyExpr {
88    pub ident: syn::Ident,
89    pub paren: syn::token::Paren,
90    pub exprs: Punctuated<Expr, syn::Token![,]>,
91}
92
93impl AnyExpr {
94    pub fn evaluate(&self, ident: &syn::Ident) -> bool {
95        self.exprs.iter().any(|e| e.evaluate(ident))
96    }
97}
98
99impl Parse for AnyExpr {
100    fn parse(input: ParseStream) -> syn::Result<Self> {
101        let content;
102
103        let ident: syn::Ident = input.parse()?;
104        if ident != "any" {
105            return Err(syn::Error::new(
106                ident.span(),
107                format_args!("expected `any`, got `{ident}` instead"),
108            ));
109        }
110
111        Ok(Self {
112            ident,
113            paren: syn::parenthesized!(content in input),
114            exprs: Punctuated::parse_terminated(&content)?,
115        })
116    }
117}
118
119impl ToTokens for AnyExpr {
120    fn to_tokens(&self, tokens: &mut TokenStream) {
121        self.ident.to_tokens(tokens);
122        self.paren
123            .surround(tokens, |tokens| self.exprs.to_tokens(tokens));
124    }
125}
126
127pub(crate) struct AllExpr {
128    pub ident: syn::Ident,
129    pub paren: syn::token::Paren,
130    pub exprs: Punctuated<Expr, syn::Token![,]>,
131}
132
133impl AllExpr {
134    pub fn evaluate(&self, ident: &syn::Ident) -> bool {
135        self.exprs.iter().all(|e| e.evaluate(ident))
136    }
137}
138
139impl Parse for AllExpr {
140    fn parse(input: ParseStream) -> syn::Result<Self> {
141        let content;
142
143        let ident: syn::Ident = input.parse()?;
144        if ident != "all" {
145            return Err(syn::Error::new(
146                ident.span(),
147                format_args!("expected `all`, got `{ident}` instead"),
148            ));
149        }
150
151        Ok(Self {
152            ident,
153            paren: syn::parenthesized!(content in input),
154            exprs: Punctuated::parse_terminated(&content)?,
155        })
156    }
157}
158
159impl ToTokens for AllExpr {
160    fn to_tokens(&self, tokens: &mut TokenStream) {
161        self.ident.to_tokens(tokens);
162        self.paren
163            .surround(tokens, |tokens| self.exprs.to_tokens(tokens));
164    }
165}