filter_expr_evaler/
ctx.rs

1use std::{
2    collections::BTreeMap,
3    fmt::{self, Debug},
4};
5
6use crate::Value;
7
8use crate::Error;
9
10/// The context of the filter for evaluation.
11#[async_trait::async_trait]
12pub trait Context: Send + Sync {
13    /// Get the value of a variable/field.
14    ///
15    /// It is a async function so that the callee can even request the database
16    /// or network to get the value.
17    ///
18    /// If the variable is not found, return `None`.
19    ///
20    /// If some error occurs, return the error.
21    async fn get_var(&self, name: &str) -> Result<Option<Value>, Error>;
22
23    /// Get the function by name.
24    ///
25    /// If the function is not found, return `None`.
26    fn get_fn(&self, name: &str) -> Option<&BoxedExprFn> {
27        let _name = name;
28
29        None
30    }
31}
32
33/// A macro to create a `SimpleContext` from key-value pairs.
34///
35/// # Example
36///
37/// ```rust
38/// use filter_expr::simple_context;
39///
40/// let ctx = simple_context! {
41///     "name": "John",
42///     "age": 19,
43/// };
44/// ```
45#[macro_export]
46macro_rules! simple_context {
47    // Empty case.
48    () => {
49        $crate::SimpleContext::new(std::collections::BTreeMap::new())
50    };
51
52    // Single key-value pair.
53    ($key:literal : $value:expr $(,)?) => {
54        $crate::SimpleContext::new(std::collections::BTreeMap::from([
55            ($key.to_string(), ($value).into()),
56        ]))
57    };
58
59    // Multiple key-value pairs.
60    ($key:literal : $value:expr, $($rest_key:literal : $rest_value:expr),* $(,)?) => {
61        $crate::SimpleContext::new(std::collections::BTreeMap::from([
62            ($key.to_string(), ($value).into()),
63            $(($rest_key.to_string(), ($rest_value).into())),*
64        ]))
65    };
66}
67
68/// A simple context that stores the variables in a hash map.
69///
70/// For those who don't want to implement the `Context` trait, this is a simple
71/// implementation that can be used.
72pub struct SimpleContext {
73    vars: BTreeMap<String, Value>,
74    fns: BTreeMap<String, BoxedExprFn>,
75}
76
77impl Debug for SimpleContext {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        let fns_len = self.fns.len();
80        write!(
81            f,
82            "SimpleContext {{ vars: {:?}, fns: [{:?} fns] }}",
83            self.vars, fns_len
84        )?;
85        Ok(())
86    }
87}
88
89impl SimpleContext {
90    pub fn new(vars: BTreeMap<String, Value>) -> Self {
91        Self {
92            vars,
93            fns: BTreeMap::new(),
94        }
95    }
96
97    pub fn add_fn(&mut self, name: String, func: BoxedExprFn) {
98        self.fns.insert(name, func);
99    }
100}
101
102#[async_trait::async_trait]
103impl Context for SimpleContext {
104    async fn get_var(&self, name: &str) -> Result<Option<Value>, Error> {
105        Ok(self.vars.get(name).cloned())
106    }
107
108    fn get_fn(&self, name: &str) -> Option<&BoxedExprFn> {
109        self.fns.get(name)
110    }
111}
112
113pub struct ExprFnContext {
114    pub args: Vec<Value>,
115}
116
117#[async_trait::async_trait]
118pub trait ExprFn: Send + Sync {
119    async fn call(&self, ctx: ExprFnContext) -> Result<Value, Error>;
120}
121
122pub type BoxedExprFn = Box<dyn ExprFn>;