1use serde::{Deserialize, Serialize};
2
3use nom::branch::alt;
4use nom::bytes::complete::tag;
5use nom::character::complete::char;
6use nom::combinator::map;
7use nom::sequence::{preceded, separated_pair};
8use nom::IResult;
9
10use crate::FieldValue;
11use rapidquery::parse::util::{identifier, integer, string};
12
13#[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]
14#[serde(untagged)]
15pub enum ExpressionField {
16 Tag { tag: String },
17 Field { key: String, value: FieldValue },
18 HasField { key: String },
19}
20
21impl ExpressionField {
22 fn tag_node(i: &str) -> IResult<&str, Self> {
23 map(alt((identifier, string)), |tag| ExpressionField::Tag {
24 tag,
25 })(i)
26 }
27
28 fn string_field_value(i: &str) -> IResult<&str, FieldValue> {
29 map(alt((identifier, string)), FieldValue::Str)(i)
30 }
31
32 fn int_field_value(i: &str) -> IResult<&str, FieldValue> {
33 map(integer, FieldValue::Numeric)(i)
34 }
35
36 fn field_value(i: &str) -> IResult<&str, FieldValue> {
37 alt((Self::int_field_value, Self::string_field_value))(i)
38 }
39
40 fn key_value_node(i: &str) -> IResult<&str, Self> {
41 map(
42 separated_pair(identifier, tag("="), Self::field_value),
43 |(key, value)| ExpressionField::Field { key, value },
44 )(i)
45 }
46
47 fn hasfield_node(i: &str) -> IResult<&str, Self> {
48 map(preceded(char('@'), identifier), |key| {
49 ExpressionField::HasField { key }
50 })(i)
51 }
52}
53
54impl rapidquery::Parse for ExpressionField {
55 fn parse(i: &str) -> IResult<&str, Self> {
56 alt((Self::hasfield_node, Self::key_value_node, Self::tag_node))(i)
57 }
58}
59
60#[cfg(test)]
61mod test {
62 use std::{collections::HashMap, convert::Infallible};
63
64 use rapidquery::Expression;
65
66 use super::*;
67
68 #[test]
69 fn parse_basic_tag() {
70 let e = Expression::parse(" bing ").unwrap();
71 assert_eq!(
72 e,
73 Expression::Field(ExpressionField::Tag { tag: "bing".into() })
74 );
75 }
76
77 #[test]
78 fn parse_basic_hasfield() {
79 let e = Expression::parse(" @type").unwrap();
80 assert_eq!(
81 e,
82 Expression::Field(ExpressionField::HasField { key: "type".into() })
83 )
84 }
85
86 #[test]
87 fn parse_underscore_tag() {
88 let e = Expression::parse(" bing_bong ").unwrap();
89 assert_eq!(
90 e,
91 Expression::Field(ExpressionField::Tag {
92 tag: "bing_bong".into()
93 })
94 );
95 }
96
97 #[test]
98 fn parse_multiword_tag() {
99 let e = Expression::parse(" \"hello world\" ").unwrap();
100 assert_eq!(
101 e,
102 Expression::Field(ExpressionField::Tag {
103 tag: "hello world".into()
104 })
105 );
106 }
107
108 #[test]
109 fn parse_basic_kv() {
110 let e = Expression::parse("bing=bong").unwrap();
111 assert_eq!(
112 e,
113 Expression::Field(ExpressionField::Field {
114 key: "bing".into(),
115 value: "bong".into(),
116 })
117 );
118 }
119
120 #[test]
121 fn parse_int_kv() {
122 let e = Expression::parse("bing=42").unwrap();
123 assert_eq!(
124 e,
125 Expression::Field(ExpressionField::Field {
126 key: "bing".into(),
127 value: FieldValue::Numeric(42)
128 })
129 )
130 }
131
132 #[test]
133 fn parse_multiword_kv() {
134 let e = Expression::parse("bing = \"bada boom\"").unwrap();
135 assert_eq!(
136 e,
137 Expression::Field(ExpressionField::Field {
138 key: "bing".into(),
139 value: "bada boom".into(),
140 })
141 );
142 }
143
144 #[test]
145 fn parse_basic_and() {
146 let e = Expression::parse("bing && type=image").unwrap();
147 assert_eq!(
148 e,
149 Expression::And {
150 and: (
151 Box::new(Expression::Field(ExpressionField::Tag {
152 tag: "bing".into()
153 })),
154 Box::new(Expression::Field(ExpressionField::Field {
155 key: "type".into(),
156 value: "image".into(),
157 }))
158 )
159 }
160 );
161 }
162
163 #[test]
164 fn chained_and() {
165 let e = Expression::parse("hello && there && world").unwrap();
166
167 let expected_expr = Expression::And {
168 and: (
169 Box::from(Expression::And {
170 and: (
171 Box::from(Expression::Field(ExpressionField::Tag {
172 tag: "hello".into(),
173 })),
174 Box::from(Expression::Field(ExpressionField::Tag {
175 tag: "there".into(),
176 })),
177 ),
178 }),
179 Box::from(Expression::Field(ExpressionField::Tag {
180 tag: "world".into(),
181 })),
182 ),
183 };
184 assert_eq!(e, expected_expr);
185 }
186
187 #[test]
188 fn parse_basic_or() {
189 let e = Expression::parse("type=image || bing").unwrap();
190 assert_eq!(
191 e,
192 Expression::Or {
193 or: (
194 Box::from(Expression::Field(ExpressionField::Field {
195 key: "type".into(),
196 value: "image".into(),
197 })),
198 Box::from(Expression::Field(ExpressionField::Tag {
199 tag: "bing".into()
200 }))
201 )
202 }
203 )
204 }
205
206 #[test]
207 fn parse_not_after_or() {
208 let e = Expression::parse("to_b || !to_b").unwrap();
209 assert_eq!(
210 e,
211 Expression::Or {
212 or: (
213 Box::from(Expression::Field(ExpressionField::Tag {
214 tag: "to_b".into()
215 })),
216 Box::from(Expression::Not {
217 not: Box::from(Expression::Field(ExpressionField::Tag {
218 tag: "to_b".into()
219 }))
220 })
221 )
222 }
223 )
224 }
225
226 #[test]
227 fn parse_basic_not() {
228 let e = Expression::parse("!type=image").unwrap();
229 assert_eq!(
230 e,
231 Expression::Not {
232 not: Box::from(Expression::Field(ExpressionField::Field {
233 key: "type".into(),
234 value: "image".into(),
235 }))
236 }
237 );
238 }
239
240 #[test]
241 fn parse_basic_subexpr() {
242 let e = Expression::parse("a && (b || c)").unwrap();
243 let expected_expression = Expression::And {
244 and: (
245 Box::from(Expression::Field(ExpressionField::Tag { tag: "a".into() })),
246 Box::from(Expression::Or {
247 or: (
248 Box::from(Expression::Field(ExpressionField::Tag { tag: "b".into() })),
249 Box::from(Expression::Field(ExpressionField::Tag { tag: "c".into() })),
250 ),
251 }),
252 ),
253 };
254
255 assert_eq!(e, expected_expression);
256 }
257
258 #[derive(Default)]
259 struct MockResolver {
260 tags: Vec<String>,
261 kv: HashMap<String, FieldValue>,
262 keys: Vec<String>,
263 }
264
265 impl MockResolver {
266 pub fn with_tag<S: Into<String>>(mut self, tag: S) -> Self {
267 self.tags.push(tag.into());
268 self
269 }
270
271 pub fn with_field<K: Into<String>, V: Into<FieldValue>>(mut self, k: K, v: V) -> Self {
272 let key: String = k.into();
273 self.keys.push(key.clone());
274 self.kv.insert(key, v.into());
275 self
276 }
277 }
278
279 impl rapidquery::FieldResolver<bool> for MockResolver {
280 type FieldType = ExpressionField;
281 type Error = Infallible;
282
283 fn resolve_empty(&self) -> Result<bool, Self::Error> {
284 Ok(true)
285 }
286
287 fn resolve(&self, field: &Self::FieldType) -> Result<bool, Self::Error> {
288 let val = match field {
289 ExpressionField::HasField { key } => self.keys.contains(key),
290 ExpressionField::Field { key, value } => self.kv.get(key) == Some(value),
291 ExpressionField::Tag { tag } => self.tags.contains(tag),
292 };
293
294 Ok(val)
295 }
296 }
297
298 #[test]
299 fn eval_empty_query() {
300 assert!(Expression::Empty
301 .evaluate(&MockResolver::default())
302 .unwrap())
303 }
304
305 #[test]
306 fn eval_tag_query() {
307 assert!(Expression::Field(ExpressionField::Tag {
308 tag: "hello".into()
309 })
310 .evaluate(&MockResolver::default().with_tag("hello"))
311 .unwrap());
312 }
313
314 #[test]
315 fn eval_tag_nomatch() {
316 assert!(!Expression::Field(ExpressionField::Tag {
317 tag: "yayeet".into()
318 })
319 .evaluate(&MockResolver::default().with_tag("Hello"))
320 .unwrap())
321 }
322
323 #[test]
324 fn eval_kv_query() {
325 assert!(Expression::Field(ExpressionField::Field {
326 key: "key".into(),
327 value: "val".into(),
328 })
329 .evaluate(&MockResolver::default().with_field("key", "val"))
330 .unwrap())
331 }
332
333 #[test]
334 fn eval_kv_nomatch() {
335 assert!(!Expression::Field(ExpressionField::Field {
336 key: "key".into(),
337 value: "val".into(),
338 })
339 .evaluate(&MockResolver::default().with_field("key", "yayeet"))
340 .unwrap())
341 }
342
343 #[test]
344 fn eval_and() {
345 assert!(Expression::parse("a && b")
346 .unwrap()
347 .evaluate(&MockResolver::default().with_tag("a").with_tag("b"))
348 .unwrap())
349 }
350
351 #[test]
352 fn eval_and_nomatch() {
353 assert!(!Expression::parse("a && b")
354 .unwrap()
355 .evaluate(&MockResolver::default().with_tag("a").with_tag("c"))
356 .unwrap())
357 }
358
359 #[test]
360 fn eval_or() {
361 assert!(Expression::parse("a || b")
362 .unwrap()
363 .evaluate(&MockResolver::default().with_tag("b"))
364 .unwrap())
365 }
366
367 #[test]
368 fn eval_or_nomatch() {
369 assert!(!Expression::parse("a || b")
370 .unwrap()
371 .evaluate(&MockResolver::default().with_tag("c"))
372 .unwrap())
373 }
374
375 #[test]
376 fn eval_not() {
377 assert!(Expression::parse("!a")
378 .unwrap()
379 .evaluate(&MockResolver::default().with_tag("b"))
380 .unwrap())
381 }
382
383 #[test]
384 fn eval_not_nomatch() {
385 assert!(!Expression::parse("!a")
386 .unwrap()
387 .evaluate(&MockResolver::default().with_tag("a"))
388 .unwrap())
389 }
390
391 #[test]
392 fn eval_and_or_nested() {
393 assert!(Expression::parse("(a || b) && c")
394 .unwrap()
395 .evaluate(&MockResolver::default().with_tag("a").with_tag("c"))
396 .unwrap())
397 }
398
399 #[test]
400 fn eval_not_nested() {
401 assert!(Expression::parse("!(a && b) && !(!c)")
402 .unwrap()
403 .evaluate(&MockResolver::default().with_tag("a").with_tag("c"))
404 .unwrap())
405 }
406}