filter_expr/
expr.rs

1use crate::{Error, ctx::Context};
2
3#[derive(Debug, Clone, PartialEq)]
4pub(crate) enum Expr {
5    Field(String),
6    Value(ExprValue),
7
8    FuncCall(String, Vec<Expr>),
9
10    Gt(Box<Expr>, Box<Expr>),
11    Lt(Box<Expr>, Box<Expr>),
12    Ge(Box<Expr>, Box<Expr>),
13    Le(Box<Expr>, Box<Expr>),
14    Eq(Box<Expr>, Box<Expr>),
15    Ne(Box<Expr>, Box<Expr>),
16    In(Box<Expr>, Box<Expr>),
17
18    And(Vec<Expr>),
19    Or(Vec<Expr>),
20    Not(Box<Expr>),
21}
22
23impl Expr {
24    pub(crate) fn field<T: Into<String>>(field: T) -> Self {
25        Self::Field(field.into())
26    }
27
28    #[cfg(test)]
29    pub(crate) fn field_boxed<T: Into<String>>(field: T) -> Box<Self> {
30        Box::new(Self::field(field))
31    }
32
33    pub(crate) fn value<T: Into<ExprValue>>(value: T) -> Self {
34        Self::Value(value.into())
35    }
36
37    #[cfg(test)]
38    pub(crate) fn value_boxed<T: Into<ExprValue>>(value: T) -> Box<Self> {
39        Box::new(Self::value(value))
40    }
41
42    pub(crate) async fn eval(&self, ctx: &dyn Context) -> Result<ExprValue, Error> {
43        match self {
44            Self::Field(field) => {
45                let value = ctx.get_var(field).await?;
46                Ok(value)
47            }
48            Self::Value(value) => Ok(value.clone()),
49
50            Self::FuncCall(func, args) => self.eval_func_call(func, args, ctx).await,
51
52            Self::Gt(left, right) => {
53                let left_value = Box::pin(left.eval(ctx)).await?;
54                let right_value = Box::pin(right.eval(ctx)).await?;
55                match left_value.partial_cmp(&right_value) {
56                    Some(ordering) => Ok(ExprValue::Bool(ordering == std::cmp::Ordering::Greater)),
57                    None => Err(Error::TypeMismatch(
58                        format!("{:?}", left_value),
59                        format!("{:?}", right_value),
60                    )),
61                }
62            }
63            Self::Lt(left, right) => {
64                let left_value = Box::pin(left.eval(ctx)).await?;
65                let right_value = Box::pin(right.eval(ctx)).await?;
66                match left_value.partial_cmp(&right_value) {
67                    Some(ordering) => Ok(ExprValue::Bool(ordering == std::cmp::Ordering::Less)),
68                    None => Err(Error::TypeMismatch(
69                        format!("{:?}", left_value),
70                        format!("{:?}", right_value),
71                    )),
72                }
73            }
74            Self::Ge(left, right) => {
75                let left_value = Box::pin(left.eval(ctx)).await?;
76                let right_value = Box::pin(right.eval(ctx)).await?;
77                match left_value.partial_cmp(&right_value) {
78                    Some(ordering) => Ok(ExprValue::Bool(
79                        ordering == std::cmp::Ordering::Greater
80                            || ordering == std::cmp::Ordering::Equal,
81                    )),
82                    None => Err(Error::TypeMismatch(
83                        format!("{:?}", left_value),
84                        format!("{:?}", right_value),
85                    )),
86                }
87            }
88            Self::Le(left, right) => {
89                let left_value = Box::pin(left.eval(ctx)).await?;
90                let right_value = Box::pin(right.eval(ctx)).await?;
91                match left_value.partial_cmp(&right_value) {
92                    Some(ordering) => Ok(ExprValue::Bool(
93                        ordering == std::cmp::Ordering::Less
94                            || ordering == std::cmp::Ordering::Equal,
95                    )),
96                    None => Err(Error::TypeMismatch(
97                        format!("{:?}", left_value),
98                        format!("{:?}", right_value),
99                    )),
100                }
101            }
102            Self::Eq(left, right) => {
103                let left_value = Box::pin(left.eval(ctx)).await?;
104                let right_value = Box::pin(right.eval(ctx)).await?;
105                Ok(ExprValue::Bool(left_value == right_value))
106            }
107            Self::Ne(left, right) => {
108                let left_value = Box::pin(left.eval(ctx)).await?;
109                let right_value = Box::pin(right.eval(ctx)).await?;
110                Ok(ExprValue::Bool(left_value != right_value))
111            }
112            Self::In(left, right) => {
113                let left_value = Box::pin(left.eval(ctx)).await?;
114                let right_value = Box::pin(right.eval(ctx)).await?;
115                match right_value {
116                    ExprValue::Array(array) => Ok(ExprValue::Bool(array.contains(&left_value))),
117                    _ => Err(Error::TypeMismatch(
118                        format!("{:?}", right_value),
119                        format!("{:?}", left_value),
120                    )),
121                }
122            }
123
124            Self::And(exprs) => {
125                let mut result = true;
126                for expr in exprs {
127                    let value = Box::pin(expr.eval(ctx)).await?;
128                    match value {
129                        ExprValue::Bool(b) => result = result && b,
130                        _ => {
131                            return Err(Error::InvalidValue(format!(
132                                "expected bool, got {:?}",
133                                value
134                            )));
135                        }
136                    }
137                }
138                Ok(ExprValue::Bool(result))
139            }
140            Self::Or(exprs) => {
141                let mut result = false;
142                for expr in exprs {
143                    let value = Box::pin(expr.eval(ctx)).await?;
144                    match value {
145                        ExprValue::Bool(b) => result = result || b,
146                        _ => {
147                            return Err(Error::InvalidValue(format!(
148                                "expected bool, got {:?}",
149                                value
150                            )));
151                        }
152                    }
153                }
154                Ok(ExprValue::Bool(result))
155            }
156            Self::Not(expr) => {
157                let value = Box::pin(expr.eval(ctx)).await?;
158                match value {
159                    ExprValue::Bool(b) => Ok(ExprValue::Bool(!b)),
160                    _ => Err(Error::TypeMismatch(format!("{:?}", value), format!("bool"))),
161                }
162            }
163        }
164    }
165
166    pub(crate) async fn eval_func_call(
167        &self,
168        func: &str,
169        args: &[Expr],
170        ctx: &dyn Context,
171    ) -> Result<ExprValue, Error> {
172        // Evaluate the arguments.
173        let mut args_values = vec![];
174        for arg in args {
175            let value = Box::pin(arg.eval(ctx)).await?;
176            args_values.push(value);
177        }
178
179        // Get the function to call.
180        let func_name = func;
181        let func = ctx.get_fn(func).await?;
182
183        // Call the function or call the builtin function.
184        if let Some(func) = func {
185            return func.call(ExprFnContext { args: args_values }).await;
186        } else {
187            match func_name {
188                "matches" => self.eval_builtin_func_call_matches(&args_values).await,
189                _ => Err(Error::NoSuchFunction(func_name.to_string())),
190            }
191        }
192    }
193
194    pub(crate) async fn eval_builtin_func_call_matches(
195        &self,
196        args: &[ExprValue],
197    ) -> Result<ExprValue, Error> {
198        if args.len() != 2 {
199            return Err(Error::InvalidArgumentCount {
200                expected: 2,
201                got: args.len(),
202            });
203        }
204        let text = match &args[0] {
205            ExprValue::Str(s) => s,
206            _ => {
207                return Err(Error::InvalidArgumentType {
208                    expected: "string".to_string(),
209                    got: format!("{:?}", args[0]),
210                });
211            }
212        };
213        let pattern = match &args[1] {
214            ExprValue::Str(s) => s,
215            _ => {
216                return Err(Error::InvalidArgumentType {
217                    expected: "string".to_string(),
218                    got: format!("{:?}", args[1]),
219                });
220            }
221        };
222        let pattern = regex::Regex::new(&pattern);
223        let pattern = match pattern {
224            Ok(pattern) => pattern,
225            Err(e) => {
226                return Err(Error::Internal(format!("failed to compile regex: {}", e)));
227            }
228        };
229
230        let matches = pattern.is_match(&text);
231        Ok(ExprValue::Bool(matches))
232    }
233}
234
235#[derive(Debug, Clone, PartialEq)]
236pub enum ExprValue {
237    Str(String),
238    Int(i64),
239    Float(f64),
240    Bool(bool),
241    Null,
242
243    Array(Vec<ExprValue>),
244}
245
246impl PartialOrd for ExprValue {
247    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
248        match (self, other) {
249            (ExprValue::Str(a), ExprValue::Str(b)) => a.partial_cmp(b),
250            (ExprValue::Int(a), ExprValue::Int(b)) => a.partial_cmp(b),
251            (ExprValue::Float(a), ExprValue::Float(b)) => a.partial_cmp(b),
252            (ExprValue::Bool(a), ExprValue::Bool(b)) => a.partial_cmp(b),
253            _ => None, // Different types cannot be compared
254        }
255    }
256}
257
258impl Into<ExprValue> for String {
259    fn into(self) -> ExprValue {
260        ExprValue::Str(self)
261    }
262}
263
264impl Into<ExprValue> for &str {
265    fn into(self) -> ExprValue {
266        ExprValue::Str(self.to_string())
267    }
268}
269
270impl Into<ExprValue> for i64 {
271    fn into(self) -> ExprValue {
272        ExprValue::Int(self)
273    }
274}
275
276impl Into<ExprValue> for f64 {
277    fn into(self) -> ExprValue {
278        ExprValue::Float(self)
279    }
280}
281
282impl Into<ExprValue> for bool {
283    fn into(self) -> ExprValue {
284        ExprValue::Bool(self)
285    }
286}
287
288impl<T: Into<ExprValue>> Into<ExprValue> for Vec<T> {
289    fn into(self) -> ExprValue {
290        ExprValue::Array(self.into_iter().map(|item| item.into()).collect())
291    }
292}
293
294pub struct ExprFnContext {
295    pub args: Vec<ExprValue>,
296}
297
298#[async_trait::async_trait]
299pub trait ExprFn: Send + Sync {
300    async fn call(&self, ctx: ExprFnContext) -> Result<ExprValue, Error>;
301}
302
303pub type BoxedExprFn = Box<dyn ExprFn>;