openzeppelin_monitor/services/filter/expression/
ast.rs

1//! This module defines the abstract syntax tree (AST) for the filter expressions.
2//! Parsing module will convert the input string into this AST structure.
3//! This AST is then traversed and interpreted by the evaluation module (via helpers::evaluate) to determine the result of the filter expression.
4//!
5//! The AST is designed to be a direct representation of the parsed filter expression, capturing it's structure, operators and literal values.
6//! Lifetime annotations (`'a`) are used to ensure that the references to string literals are valid for the duration of the expression evaluation.
7
8/// Represents the possible literal values that can be used in filter expressions.
9/// The `LiteralValue` enum captures the different constant values that are used on the right side of a condition (RHS).
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub enum LiteralValue<'a> {
12	/// A boolean literal value.
13	Bool(bool),
14	/// A string literal value. Includes both single-quoted and unquoted strings, includes hexadecimal strings.
15	/// e.g., "abc", 'abc', '0x123ABC'
16	Str(&'a str),
17	/// A numeric literal value. e.g., "123", "-123.456", "0x123" or hexadecimal
18	/// Store as string slice to preserve original form until evaluation phase.
19	/// Conversion to specific type is done within chain context during evaluation.
20	Number(&'a str),
21}
22
23/// Represents the possible comparison operators that can be used in filter expressions.
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum ComparisonOperator {
26	/// Equality operator (==)
27	Eq,
28	/// Inequality operator (!=)
29	Ne,
30	/// Greater than operator (>)
31	Gt,
32	/// Greater than or equal to operator (>=)
33	Gte,
34	/// Less than operator (<)
35	Lt,
36	/// Less than or equal to operator (<=)
37	Lte,
38	/// String/collection comparison operators:
39	/// - StartsWith: Checks if the string/collection starts with a given item.
40	StartsWith,
41	/// - EndsWith: Checks if the string/collection ends with a given item.
42	EndsWith,
43	/// - Contains: Checks if the string/collection contains a given item.
44	Contains,
45}
46
47/// Represents the possible logical operators that can be used in filter expressions.
48#[derive(Debug, Clone, PartialEq, Eq)]
49pub enum LogicalOperator {
50	/// Logical AND operator (&&)
51	And,
52	/// Logical OR operator (||)
53	Or,
54}
55
56/// Represents the possible accessors that can be used in filter expressions.
57/// Accessors are used to access elements in collections or properties in objects.
58#[derive(Debug, Clone, PartialEq, Eq)]
59pub enum Accessor<'a> {
60	/// Accessor for a collection index (e.g., [0], [1], etc.)
61	Index(usize),
62	/// Accessor for a property name (e.g., .name, .age, etc.)
63	Key(&'a str),
64}
65
66#[derive(Debug, Clone, PartialEq, Eq)]
67pub struct VariablePath<'a> {
68	pub base: &'a str,
69	pub accessors: Vec<Accessor<'a>>,
70}
71
72/// Represents the left side of a condition (LHS) in a filter expression.
73/// The left side can either be a simple variable name or a path to a variable.
74#[derive(Debug, Clone, PartialEq, Eq)]
75pub enum ConditionLeft<'a> {
76	/// A simple variable name (e.g., "name", "age", etc.)
77	/// This is a direct reference to a variable in the data structure.
78	Simple(&'a str),
79	/// A sequence of accessors that form a path to a variable (e.g., "person.name", "person[0].age", etc.)
80	Path(VariablePath<'a>),
81}
82
83impl<'a> ConditionLeft<'a> {
84	/// Helper method get the base name of the variable or path.
85	pub fn base_name(&self) -> &'a str {
86		match self {
87			ConditionLeft::Simple(name) => name,
88			ConditionLeft::Path(path) => path.base,
89		}
90	}
91
92	/// Helper method to get the accessors of the variable path.
93	/// If ConditionLeft is a simple variable, it returns an empty slice.
94	/// If it is a path, it returns the accessors of that path.
95	/// Used during evaluation to traverse nested structures.
96	pub fn accessors(&self) -> &[Accessor] {
97		match self {
98			ConditionLeft::Simple(_) => &[],
99			ConditionLeft::Path(path) => &path.accessors,
100		}
101	}
102}
103
104/// Represents a condition in a filter expression.
105/// A condition consists of a left side (LHS), an operator, and a right side (RHS).
106#[derive(Debug, Clone, PartialEq, Eq)]
107pub struct Condition<'a> {
108	/// The left side of the condition (LHS).
109	/// This can be a simple variable name or a path to a variable.
110	pub left: ConditionLeft<'a>,
111	/// The operator used in the condition (e.g., ==, !=, >, <, etc.)
112	pub operator: ComparisonOperator,
113	/// The right side of the condition (RHS).
114	pub right: LiteralValue<'a>,
115}
116
117/// Represents a complete filter expression.
118/// An expression can be a single condition or a logical combination of multiple conditions.
119#[derive(Debug, Clone, PartialEq, Eq)]
120pub enum Expression<'a> {
121	/// A simple condition (e.g., "age > 30")
122	Condition(Condition<'a>),
123	/// A logical combination of two expressions (e.g., "age > 30 && name == 'John'")
124	/// `Box` is used to avoid infinite type recursion, as `Expression` can contain other `Expression`s.
125	Logical {
126		/// The left side sub-expression.
127		left: Box<Expression<'a>>,
128		/// The logical operator used to combine the two expressions: AND or OR.
129		operator: LogicalOperator,
130		/// The right side sub-expression.
131		right: Box<Expression<'a>>,
132	},
133}