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}