1use std::any::Any;
2
3use crate::{
4 evaluators::{FieldEvaluator, LengthEvaluator, PathEvaluator, TypeEvaluator, ValueEvaluator},
5 matchable::Matchable,
6 result::ConditionResult,
7 traits::Predicate,
8};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
12#[cfg_attr(any(feature = "serde", feature = "json_condition"), derive(serde::Serialize, serde::Deserialize))]
13pub enum ConditionMode {
14 #[default]
16 AND,
17 OR,
19 XOR,
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
25#[cfg_attr(any(feature = "serde", feature = "json_condition"), derive(serde::Serialize, serde::Deserialize))]
26#[cfg_attr(any(feature = "serde", feature = "json_condition"), serde(rename_all = "snake_case"))]
27pub enum ConditionOperator {
28 Equals,
30 NotEquals,
32 GreaterThan,
34 LessThan,
36 GreaterThanOrEqual,
38 LessThanOrEqual,
40 Contains,
42 NotContains,
44 StartsWith,
46 EndsWith,
48 Regex,
50 IsNone,
52 IsSome,
54 IsEmpty,
56 IsNotEmpty,
58}
59
60#[derive(Debug)]
66pub enum ConditionSelector<'a, T> {
67 Length(usize),
69 Type(String),
71 Value(T),
73 FieldValue(&'a str, &'a dyn Any),
75 FieldPath(&'a [&'a str], &'a dyn Any),
77 Not(Box<Condition<'a, T>>),
79 Nested(Box<NestedCondition<'a, T>>),
81}
82
83#[derive(Debug)]
85pub struct Condition<'a, T> {
86 pub operator: ConditionOperator,
87 pub selector: ConditionSelector<'a, T>,
88}
89
90#[derive(Debug)]
92pub struct NestedCondition<'a, T> {
93 pub mode: ConditionMode,
95 pub rules: Vec<Condition<'a, T>>,
97 pub nested: Vec<Box<NestedCondition<'a, T>>>,
99}
100
101#[cfg(feature = "json_condition")]
114#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
115pub struct JsonCondition {
116 pub field: String,
118 pub operator: ConditionOperator,
120 pub value: serde_json::Value,
122}
123
124#[cfg(feature = "json_condition")]
139#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
140pub struct JsonNestedCondition {
141 #[serde(alias = "logic", alias = "comparator", default)]
143 pub mode: ConditionMode,
144 #[serde(default, alias = "conditions")]
146 pub rules: Vec<JsonCondition>,
147 #[serde(default, alias = "nested_rules", alias = "nested_conditions")]
149 pub nested: Vec<Box<JsonNestedCondition>>,
150}
151
152impl<'a, T: Matchable + 'static> Predicate<T> for Condition<'a, T> {
157 fn test(&self, value: &T) -> bool {
158 self.test_detailed(value).passed
159 }
160
161 fn test_detailed(&self, value: &T) -> ConditionResult {
162 match &self.selector {
163 ConditionSelector::Length(expected) => {
164 LengthEvaluator::evaluate(value, *expected, &self.operator)
165 }
166 ConditionSelector::Type(type_name) => {
167 TypeEvaluator::evaluate(value, type_name, &self.operator)
168 }
169 ConditionSelector::Value(expected) => {
170 ValueEvaluator::evaluate(value, expected, &self.operator)
171 }
172 ConditionSelector::FieldValue(field, expected) => {
173 FieldEvaluator::evaluate(value, field, *expected, &self.operator)
174 }
175 ConditionSelector::FieldPath(path, expected) => {
176 PathEvaluator::evaluate(value, path, *expected, &self.operator)
177 }
178 ConditionSelector::Not(inner) => {
179 let mut result = inner.test_detailed(value);
180 result.passed = !result.passed;
181 result.description = format!("NOT({})", result.description);
182 result
183 }
184 ConditionSelector::Nested(group) => evaluate_nested(value, group),
185 }
186 }
187}
188
189fn evaluate_nested<'a, T: Matchable + 'static>(
191 value: &T,
192 group: &NestedCondition<'a, T>,
193) -> ConditionResult {
194 let mut results = Vec::new();
195
196 for condition in &group.rules {
198 results.push(condition.test_detailed(value));
199 }
200
201 for nested_group in &group.nested {
203 results.push(evaluate_nested(value, nested_group));
204 }
205
206 let passed = match group.mode {
207 ConditionMode::AND => results.iter().all(|r| r.passed),
208 ConditionMode::OR => results.iter().any(|r| r.passed),
209 ConditionMode::XOR => results.iter().filter(|r| r.passed).count() == 1,
210 };
211
212 ConditionResult {
213 passed,
214 description: format!(
215 "{:?} group ({} rules, {} nested)",
216 group.mode,
217 group.rules.len(),
218 group.nested.len()
219 ),
220 actual_value: None,
221 expected_value: None,
222 error: None,
223 }
224}