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