cel_parser/
ast.rs

1use std::collections::HashSet;
2use std::sync::Arc;
3
4#[derive(Debug, Eq, PartialEq, Clone)]
5pub enum RelationOp {
6    LessThan,
7    LessThanEq,
8    GreaterThan,
9    GreaterThanEq,
10    Equals,
11    NotEquals,
12    In,
13}
14
15#[derive(Debug, Eq, PartialEq, Clone)]
16pub enum ArithmeticOp {
17    Add,
18    Subtract,
19    Divide,
20    Multiply,
21    Modulus,
22}
23
24#[derive(Debug, Eq, PartialEq, Clone)]
25pub enum UnaryOp {
26    Not,
27    DoubleNot,
28    Minus,
29    DoubleMinus,
30}
31
32#[derive(Debug, PartialEq, Clone)]
33pub enum Expression {
34    Arithmetic(Box<Expression>, ArithmeticOp, Box<Expression>),
35    Relation(Box<Expression>, RelationOp, Box<Expression>),
36
37    Ternary(Box<Expression>, Box<Expression>, Box<Expression>),
38    Or(Box<Expression>, Box<Expression>),
39    And(Box<Expression>, Box<Expression>),
40    Unary(UnaryOp, Box<Expression>),
41
42    Member(Box<Expression>, Box<Member>),
43    FunctionCall(Box<Expression>, Option<Box<Expression>>, Vec<Expression>),
44
45    List(Vec<Expression>),
46    Map(Vec<(Expression, Expression)>),
47
48    Atom(Atom),
49    Ident(Arc<String>),
50}
51
52#[derive(Debug, PartialEq, Clone)]
53pub enum Member {
54    Attribute(Arc<String>),
55    Index(Box<Expression>),
56    Fields(Vec<(Arc<String>, Expression)>),
57}
58
59#[derive(Debug, PartialEq, Clone)]
60pub enum Atom {
61    Int(i64),
62    UInt(u64),
63    Float(f64),
64    String(Arc<String>),
65    Bytes(Arc<Vec<u8>>),
66    Bool(bool),
67    Null,
68}
69
70/// A collection of all the references that an expression makes to variables and functions.
71pub struct ExpressionReferences<'expr> {
72    variables: HashSet<&'expr str>,
73    functions: HashSet<&'expr str>,
74}
75
76impl<'expr> ExpressionReferences<'expr> {
77    /// Returns true if the expression references the provided variable name.
78    ///
79    /// # Example
80    /// ```rust
81    /// # use cel_parser::parse;
82    /// let expression = parse("foo.bar == true").unwrap();
83    /// let references = expression.references();
84    /// assert!(references.has_variable("foo"));
85    /// ```
86    pub fn has_variable(&self, name: impl AsRef<str>) -> bool {
87        self.variables.contains(name.as_ref())
88    }
89
90    /// Returns true if the expression references the provided variable name.
91    ///
92    /// # Example
93    /// ```rust
94    /// # use cel_parser::parse;
95    /// let expression = parse("size(foo) > 0").unwrap();
96    /// let references = expression.references();
97    /// assert!(references.has_function("size"));
98    /// ```
99    pub fn has_function(&self, name: impl AsRef<str>) -> bool {
100        self.functions.contains(name.as_ref())
101    }
102
103    /// Returns a list of all variables referenced in the expression.
104    ///
105    /// # Example
106    /// ```rust
107    /// # use cel_parser::parse;
108    /// let expression = parse("foo.bar == true").unwrap();
109    /// let references = expression.references();
110    /// assert_eq!(vec!["foo"], references.variables());
111    /// ```
112    pub fn variables(&self) -> Vec<&str> {
113        self.variables.iter().copied().collect()
114    }
115
116    /// Returns a list of all functions referenced in the expression.
117    ///
118    /// # Example
119    /// ```rust
120    /// # use cel_parser::parse;
121    /// let expression = parse("size(foo) > 0").unwrap();
122    /// let references = expression.references();
123    /// assert_eq!(vec!["size"], references.functions());
124    /// ```
125    pub fn functions(&self) -> Vec<&str> {
126        self.functions.iter().copied().collect()
127    }
128}
129
130impl Expression {
131    /// Returns a set of all variables referenced in the expression. Variable identifiers
132    /// are represented internally as [`Arc<String>`] and this function simply clones those
133    /// references into a [`HashSet`].
134    ///
135    /// # Example
136    /// ```rust
137    /// # use cel_parser::parse;
138    /// let expression = parse("foo && size(foo) > 0").unwrap();
139    /// let references = expression.references();
140    ///
141    /// assert!(references.has_variable("foo"));
142    /// assert!(references.has_function("size"));
143    /// ```
144    pub fn references(&self) -> ExpressionReferences {
145        let mut variables = HashSet::new();
146        let mut functions = HashSet::new();
147        self._references(&mut variables, &mut functions);
148        ExpressionReferences {
149            variables,
150            functions,
151        }
152    }
153
154    /// Internal recursive function to collect all variable and function references in the expression.
155    fn _references<'expr>(
156        &'expr self,
157        variables: &mut HashSet<&'expr str>,
158        functions: &mut HashSet<&'expr str>,
159    ) {
160        match self {
161            Expression::Arithmetic(e1, _, e2)
162            | Expression::Relation(e1, _, e2)
163            | Expression::Ternary(e1, _, e2)
164            | Expression::Or(e1, e2)
165            | Expression::And(e1, e2) => {
166                e1._references(variables, functions);
167                e2._references(variables, functions);
168            }
169            Expression::Unary(_, e) => {
170                e._references(variables, functions);
171            }
172            Expression::Member(e, _) => {
173                e._references(variables, functions);
174            }
175            Expression::FunctionCall(name, target, args) => {
176                if let Expression::Ident(v) = &**name {
177                    functions.insert(v.as_str());
178                }
179                if let Some(target) = target {
180                    target._references(variables, functions);
181                }
182                for e in args {
183                    e._references(variables, functions);
184                }
185            }
186            Expression::List(e) => {
187                for e in e {
188                    e._references(variables, functions);
189                }
190            }
191            Expression::Map(v) => {
192                for (e1, e2) in v {
193                    e1._references(variables, functions);
194                    e2._references(variables, functions);
195                }
196            }
197            Expression::Atom(_) => {}
198            Expression::Ident(v) => {
199                variables.insert(v.as_str());
200            }
201        }
202    }
203}
204
205#[cfg(test)]
206mod tests {
207    use crate::parse;
208
209    #[test]
210    fn test_references() {
211        let expr =
212            parse("foo.bar.baz == true && size(foo) > 0 && foo[0] == 1 && bar.startsWith('a')")
213                .unwrap();
214        let refs = expr.references();
215        assert!(refs.has_variable("foo"));
216        assert!(refs.has_variable("bar"));
217        assert_eq!(refs.variables.len(), 2);
218
219        assert!(refs.has_function("size"));
220        assert!(refs.has_function("startsWith"));
221        assert_eq!(refs.functions.len(), 2);
222    }
223}