1mod ctx;
4mod error;
5mod expr;
6mod parser;
7mod token;
8
9pub use ctx::{SimpleContext, Context};
10pub use error::Error;
11
12use crate::expr::{Expr, ExprValue};
13
14pub struct FilterExpr {
16 #[allow(unused)]
18 expr: Option<Expr>,
19}
20
21impl FilterExpr {
22 pub fn parse(expr: &str) -> Result<Self, Error> {
23 let expr = parse_expr(expr)?;
24 Ok(Self { expr: Some(expr) })
25 }
26
27 pub async fn eval(&self, ctx: &dyn Context) -> Result<bool, Error> {
28 if let Some(expr) = &self.expr {
29 let value = expr.eval(ctx).await?;
30 match value {
31 ExprValue::Bool(b) => Ok(b),
32 _ => Err(Error::InvalidValue(format!("{:?}", value))),
33 }
34 } else {
35 Ok(true)
36 }
37 }
38}
39
40fn parse_expr(input: &str) -> Result<Expr, Error> {
41 let tokens = token::parse_token(input)?;
42 let mut parser = parser::Parser::new(tokens);
43 Ok(parser.parse_expr()?)
44}
45
46#[cfg(test)]
47mod tests {
48 use crate::expr::{ExprFn, ExprFnContext};
49
50 use super::*;
51
52 #[tokio::test]
53 async fn test_parse_expr_and_then_eval() {
54 let input = "name = 'John' AND age > 18 AND 1 > 0";
60 let filter_expr = FilterExpr::parse(input).unwrap();
61
62 let ctx = simple_context! {
63 "name": "John",
64 "age": 19,
65 };
66 let result = filter_expr.eval(&ctx).await.unwrap();
67 assert_eq!(result, true);
68
69 let ctx = simple_context! {
70 "name": "John",
71 "age": 18,
72 };
73 let result = filter_expr.eval(&ctx).await.unwrap();
74 assert_eq!(result, false);
75
76 let input = r#"name = "John" AND age IN [18, 19, 20, 22] AND 1 > 0"#;
82 let filter_expr = FilterExpr::parse(input).unwrap();
83
84 let ctx = simple_context! {
85 "name": "John",
86 "age": 19,
87 };
88 let result = filter_expr.eval(&ctx).await.unwrap();
89 assert_eq!(result, true.into());
90
91 let ctx = simple_context! {
92 "name": "John",
93 "age": 23,
94 };
95 let result = filter_expr.eval(&ctx).await.unwrap();
96 assert_eq!(result, false.into());
97
98 let input = r#"matches(name, "^J.*n$")"#;
104 let filter_expr = FilterExpr::parse(input).unwrap();
105
106 let ctx = simple_context! {
107 "name": "John",
108 };
109 let result = filter_expr.eval(&ctx).await.unwrap();
110 assert_eq!(result, true);
111
112 let ctx = simple_context! {
113 "name": "Jane",
114 };
115 let result = filter_expr.eval(&ctx).await.unwrap();
116 assert_eq!(result, false);
117
118 let input = r#"custom_add(1, 2) = a"#;
124 let filter_expr = FilterExpr::parse(input).unwrap();
125
126 struct CustomAddFn;
127 #[async_trait::async_trait]
128 impl ExprFn for CustomAddFn {
129 async fn call(&self, ctx: ExprFnContext) -> Result<ExprValue, Error> {
130 if ctx.args.len() != 2 {
131 return Err(Error::InvalidArgumentCount {
132 expected: 2,
133 got: ctx.args.len(),
134 });
135 }
136 let a = match ctx.args[0] {
137 ExprValue::Int(a) => a,
138 _ => {
139 return Err(Error::InvalidArgumentType {
140 expected: "integer".to_string(),
141 got: format!("{:?}", ctx.args[0]),
142 });
143 }
144 };
145 let b = match ctx.args[1] {
146 ExprValue::Int(b) => b,
147 _ => {
148 return Err(Error::InvalidArgumentType {
149 expected: "integer".to_string(),
150 got: format!("{:?}", ctx.args[1]),
151 });
152 }
153 };
154 Ok((a + b).into())
155 }
156 }
157
158 let mut ctx = simple_context! {
159 "a": 3,
160 };
161 ctx.add_fn("custom_add".to_string(), Box::new(CustomAddFn));
162 let result = filter_expr.eval(&ctx).await.unwrap();
163 assert_eq!(result, true);
164
165 let mut ctx = simple_context! {
166 "a": 4,
167 };
168 ctx.add_fn("custom_add".to_string(), Box::new(CustomAddFn));
169 let result = filter_expr.eval(&ctx).await.unwrap();
170 assert_eq!(result, false);
171 }
172}