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