1use std::{any::Any, fmt};
29
30use crate::{
31 MatchError, Matchable,
32 condition::{Condition, ConditionMode, ConditionOperator, ConditionSelector, NestedCondition},
33 result::{ConditionResult, MatchResult},
34};
35
36#[derive(Debug)]
59pub struct Matcher<'a, T: Matchable> {
60 pub mode: ConditionMode,
61 pub conditions: Vec<Condition<'a, T>>,
62}
63
64impl<'a, T: Matchable + 'static> Matcher<'a, T> {
65 pub fn new(mode: ConditionMode) -> Self {
67 Self {
68 mode,
69 conditions: Vec::new(),
70 }
71 }
72
73 pub fn and() -> Self {
75 Self::new(ConditionMode::AND)
76 }
77
78 pub fn or() -> Self {
80 Self::new(ConditionMode::OR)
81 }
82
83 pub fn xor() -> Self {
85 Self::new(ConditionMode::XOR)
86 }
87
88 pub fn add_condition(&mut self, condition: Condition<'a, T>) -> &mut Self {
90 self.conditions.push(condition);
91 self
92 }
93
94 pub fn add_conditions(
96 &mut self,
97 conditions: impl IntoIterator<Item = Condition<'a, T>>,
98 ) -> &mut Self {
99 self.conditions.extend(conditions);
100 self
101 }
102
103 pub fn run(&self, value: &T) -> Result<bool, MatchError> {
106 let result = self.run_detailed(value)?;
107 Ok(result.matched)
108 }
109
110 pub fn run_batch(&self, values: impl Iterator<Item = &'a T>) -> Result<Vec<bool>, MatchError> {
113 values.into_iter().map(|value| self.run(value)).collect()
114 }
115
116 pub fn run_detailed(&self, value: &T) -> Result<MatchResult, MatchError> {
118 let mut condition_results = Vec::new();
119
120 for condition in self.conditions.iter() {
121 let result = self.evaluate_condition(condition, value);
122 condition_results.push(result);
123 }
124
125 let matched = match self.mode {
126 ConditionMode::AND => condition_results.iter().all(|r| r.passed),
127 ConditionMode::OR => condition_results.iter().any(|r| r.passed),
128 ConditionMode::XOR => condition_results.iter().filter(|r| r.passed).count() == 1,
129 };
130
131 Ok(MatchResult {
132 matched,
133 condition_results,
134 mode: self.mode,
135 })
136 }
137
138 pub fn run_detailed_batch(
140 &self,
141 values: impl Iterator<Item = &'a T>,
142 ) -> Result<Vec<MatchResult>, MatchError> {
143 values
144 .into_iter()
145 .map(|value| self.run_detailed(value))
146 .collect()
147 }
148
149 fn evaluate_condition(&self, condition: &Condition<'a, T>, value: &T) -> ConditionResult {
150 match &condition.selector {
151 ConditionSelector::Length(expected_length) => {
152 self.eval_length(value, *expected_length, &condition.operator)
153 }
154 ConditionSelector::Type(type_name) => {
155 self.eval_type(value, type_name, &condition.operator)
156 }
157 ConditionSelector::Value(value_to_check) => {
158 self.eval_value(value, value_to_check, &condition.operator)
159 }
160 ConditionSelector::FieldValue(field_name, expected_value) => {
161 self.eval_field_value(value, field_name, *expected_value, &condition.operator)
162 }
163 ConditionSelector::FieldPath(path, expected_value) => {
164 self.eval_field_path(value, path, *expected_value, &condition.operator)
165 }
166 ConditionSelector::Not(inner_condition) => {
167 let mut result = self.evaluate_condition(inner_condition, value);
168 result.passed = !result.passed;
169 result.description = format!("NOT({})", result.description);
170 result
171 }
172 ConditionSelector::Nested(nested_group) => self.eval_nested(value, nested_group),
173 }
174 }
175
176 fn eval_nested(&self, value: &T, group: &NestedCondition<'a, T>) -> ConditionResult {
177 let mut results = Vec::new();
178
179 for condition in &group.rules {
181 results.push(self.evaluate_condition(condition, value));
182 }
183
184 for nested_group in &group.nested {
186 results.push(self.eval_nested(value, nested_group));
187 }
188
189 let passed = match group.mode {
190 ConditionMode::AND => results.iter().all(|r| r.passed),
191 ConditionMode::OR => results.iter().any(|r| r.passed),
192 ConditionMode::XOR => results.iter().filter(|r| r.passed).count() == 1,
193 };
194
195 ConditionResult {
196 passed,
197 description: format!(
198 "{:?} group ({} rules, {} nested)",
199 group.mode,
200 group.rules.len(),
201 group.nested.len()
202 ),
203 actual_value: None,
204 expected_value: None,
205 error: None,
206 }
207 }
208
209 pub fn evaluate_nested(&self, value: &T, group: &NestedCondition<'a, T>) -> ConditionResult {
211 self.eval_nested(value, group)
212 }
213
214 fn eval_length(
215 &self,
216 value: &T,
217 expected: usize,
218 operator: &ConditionOperator,
219 ) -> ConditionResult {
220 match value.get_length() {
221 Some(actual) => {
222 let passed = compare_numeric(actual, expected, operator);
223 ConditionResult {
224 passed,
225 description: format!("length {:?} {}", operator, expected),
226 actual_value: Some(actual.to_string()),
227 expected_value: Some(expected.to_string()),
228 error: None,
229 }
230 }
231 None => ConditionResult {
232 passed: false,
233 description: format!("length {:?} {}", operator, expected),
234 actual_value: None,
235 expected_value: Some(expected.to_string()),
236 error: Some(MatchError::LengthNotSupported {
237 type_name: value.type_name().to_string(),
238 }),
239 },
240 }
241 }
242
243 fn eval_type(
244 &self,
245 value: &T,
246 expected_type: &str,
247 operator: &ConditionOperator,
248 ) -> ConditionResult {
249 let actual_type = value.type_name();
250 let passed = match operator {
251 ConditionOperator::Equals => actual_type == expected_type,
252 ConditionOperator::NotEquals => actual_type != expected_type,
253 ConditionOperator::Contains => actual_type.contains(expected_type),
254 _ => false,
255 };
256
257 ConditionResult {
258 passed,
259 description: format!("type {:?} {}", operator, expected_type),
260 actual_value: Some(actual_type.to_string()),
261 expected_value: Some(expected_type.to_string()),
262 error: None,
263 }
264 }
265
266 fn eval_value(&self, value: &T, expected: &T, operator: &ConditionOperator) -> ConditionResult {
267 let passed = match operator {
268 ConditionOperator::Equals => value == expected,
269 ConditionOperator::NotEquals => value != expected,
270 _ => false,
271 };
272
273 ConditionResult {
274 passed,
275 description: format!("value {:?}", operator),
276 actual_value: None,
277 expected_value: None,
278 error: None,
279 }
280 }
281
282 fn eval_field_value(
283 &self,
284 value: &T,
285 field: &str,
286 expected: &dyn Any,
287 operator: &ConditionOperator,
288 ) -> ConditionResult {
289 match value.get_field(field) {
290 Some(actual) => {
291 let (passed, actual_str, expected_str) =
292 compare_any_values(actual, expected, operator);
293 ConditionResult {
294 passed,
295 description: format!("field '{}' {:?}", field, operator),
296 actual_value: actual_str,
297 expected_value: expected_str,
298 error: None,
299 }
300 }
301 None => ConditionResult {
302 passed: false,
303 description: format!("field '{}' {:?}", field, operator),
304 actual_value: None,
305 expected_value: None,
306 error: Some(MatchError::FieldNotFound {
307 field: field.to_string(),
308 type_name: value.type_name().to_string(),
309 }),
310 },
311 }
312 }
313
314 fn eval_field_path(
315 &self,
316 value: &T,
317 path: &[&str],
318 expected: &dyn Any,
319 operator: &ConditionOperator,
320 ) -> ConditionResult {
321 if path.is_empty() {
322 return ConditionResult {
323 passed: false,
324 description: "field path".to_string(),
325 actual_value: None,
326 expected_value: None,
327 error: Some(MatchError::EmptyFieldPath),
328 };
329 }
330
331 if let Some(actual) = value.get_field_path(path) {
333 let (passed, actual_str, expected_str) = compare_any_values(actual, expected, operator);
334 return ConditionResult {
335 passed,
336 description: format!("field path '{:?}' {:?}", path, operator),
337 actual_value: actual_str,
338 expected_value: expected_str,
339 error: None,
340 };
341 }
342
343 match value.get_field(path[0]) {
345 Some(actual) if path.len() == 1 => {
346 let (passed, actual_str, expected_str) =
347 compare_any_values(actual, expected, operator);
348 ConditionResult {
349 passed,
350 description: format!("field path '{:?}' {:?}", path, operator),
351 actual_value: actual_str,
352 expected_value: expected_str,
353 error: None,
354 }
355 }
356 _ => ConditionResult {
357 passed: false,
358 description: format!("field path '{:?}' {:?}", path, operator),
359 actual_value: None,
360 expected_value: None,
361 error: Some(MatchError::NestedFieldNotFound {
362 path: path.iter().map(|s| s.to_string()).collect(),
363 failed_at: path[0].to_string(),
364 }),
365 },
366 }
367 }
368}
369
370fn compare_numeric<N: PartialOrd>(actual: N, expected: N, operator: &ConditionOperator) -> bool {
375 match operator {
376 ConditionOperator::Equals => actual == expected,
377 ConditionOperator::NotEquals => actual != expected,
378 ConditionOperator::GreaterThan => actual > expected,
379 ConditionOperator::LessThan => actual < expected,
380 ConditionOperator::GreaterThanOrEqual => actual >= expected,
381 ConditionOperator::LessThanOrEqual => actual <= expected,
382 _ => false,
383 }
384}
385
386fn compare_any_values(
387 actual: &dyn Any,
388 expected: &dyn Any,
389 operator: &ConditionOperator,
390) -> (bool, Option<String>, Option<String>) {
391 if let Some(result) = try_compare::<i8>(actual, expected, operator) {
393 return result;
394 }
395 if let Some(result) = try_compare::<i16>(actual, expected, operator) {
396 return result;
397 }
398 if let Some(result) = try_compare::<i32>(actual, expected, operator) {
399 return result;
400 }
401 if let Some(result) = try_compare::<i64>(actual, expected, operator) {
402 return result;
403 }
404 if let Some(result) = try_compare::<i128>(actual, expected, operator) {
405 return result;
406 }
407 if let Some(result) = try_compare::<isize>(actual, expected, operator) {
408 return result;
409 }
410
411 if let Some(result) = try_compare::<u8>(actual, expected, operator) {
413 return result;
414 }
415 if let Some(result) = try_compare::<u16>(actual, expected, operator) {
416 return result;
417 }
418 if let Some(result) = try_compare::<u32>(actual, expected, operator) {
419 return result;
420 }
421 if let Some(result) = try_compare::<u64>(actual, expected, operator) {
422 return result;
423 }
424 if let Some(result) = try_compare::<u128>(actual, expected, operator) {
425 return result;
426 }
427 if let Some(result) = try_compare::<usize>(actual, expected, operator) {
428 return result;
429 }
430
431 if let Some(result) = try_compare::<f32>(actual, expected, operator) {
433 return result;
434 }
435 if let Some(result) = try_compare::<f64>(actual, expected, operator) {
436 return result;
437 }
438
439 if let Some(result) = try_compare::<bool>(actual, expected, operator) {
441 return result;
442 }
443
444 if let Some(result) = try_compare_strings(actual, expected, operator) {
446 return result;
447 }
448
449 if let Some(result) = try_compare::<char>(actual, expected, operator) {
451 return result;
452 }
453
454 (false, None, None)
456}
457
458fn try_compare<T: PartialOrd + PartialEq + fmt::Display + 'static>(
459 actual: &dyn Any,
460 expected: &dyn Any,
461 operator: &ConditionOperator,
462) -> Option<(bool, Option<String>, Option<String>)> {
463 if let (Some(a), Some(e)) = (actual.downcast_ref::<T>(), expected.downcast_ref::<T>()) {
464 let passed = match operator {
465 ConditionOperator::Equals => a == e,
466 ConditionOperator::NotEquals => a != e,
467 ConditionOperator::GreaterThan => a > e,
468 ConditionOperator::LessThan => a < e,
469 ConditionOperator::GreaterThanOrEqual => a >= e,
470 ConditionOperator::LessThanOrEqual => a <= e,
471 _ => return None,
472 };
473 Some((passed, Some(a.to_string()), Some(e.to_string())))
474 } else {
475 None
476 }
477}
478
479fn try_compare_strings(
480 actual: &dyn Any,
481 expected: &dyn Any,
482 operator: &ConditionOperator,
483) -> Option<(bool, Option<String>, Option<String>)> {
484 let actual_str: Option<&str> = actual
486 .downcast_ref::<String>()
487 .map(|s| s.as_str())
488 .or_else(|| actual.downcast_ref::<&str>().copied());
489
490 let expected_str: Option<&str> = expected
492 .downcast_ref::<String>()
493 .map(|s| s.as_str())
494 .or_else(|| expected.downcast_ref::<&str>().copied());
495
496 match (actual_str, expected_str) {
497 (Some(a), Some(e)) => {
498 let passed = match operator {
499 ConditionOperator::Equals => a == e,
500 ConditionOperator::NotEquals => a != e,
501 ConditionOperator::Contains => a.contains(e),
502 ConditionOperator::NotContains => !a.contains(e),
503 ConditionOperator::StartsWith => a.starts_with(e),
504 ConditionOperator::EndsWith => a.ends_with(e),
505 ConditionOperator::GreaterThan => a > e,
506 ConditionOperator::LessThan => a < e,
507 ConditionOperator::GreaterThanOrEqual => a >= e,
508 ConditionOperator::LessThanOrEqual => a <= e,
509 ConditionOperator::IsEmpty => a.is_empty(),
510 ConditionOperator::IsNotEmpty => !a.is_empty(),
511 #[cfg(feature = "regex")]
512 ConditionOperator::Regex => regex::Regex::new(e)
513 .map(|re| re.is_match(a))
514 .unwrap_or(false),
515 #[cfg(not(feature = "regex"))]
516 ConditionOperator::Regex => false,
517 _ => return None,
518 };
519 Some((passed, Some(a.to_string()), Some(e.to_string())))
520 }
521 _ => None,
522 }
523}
524
525#[cfg(feature = "json_condition")]
530fn extract_as_f64(actual: &dyn Any) -> Option<f64> {
531 if let Some(v) = actual.downcast_ref::<f64>() {
533 return Some(*v);
534 }
535 if let Some(v) = actual.downcast_ref::<f32>() {
536 return Some(*v as f64);
537 }
538 if let Some(v) = actual.downcast_ref::<i64>() {
539 return Some(*v as f64);
540 }
541 if let Some(v) = actual.downcast_ref::<i32>() {
542 return Some(*v as f64);
543 }
544 if let Some(v) = actual.downcast_ref::<i16>() {
545 return Some(*v as f64);
546 }
547 if let Some(v) = actual.downcast_ref::<i8>() {
548 return Some(*v as f64);
549 }
550 if let Some(v) = actual.downcast_ref::<u64>() {
551 return Some(*v as f64);
552 }
553 if let Some(v) = actual.downcast_ref::<u32>() {
554 return Some(*v as f64);
555 }
556 if let Some(v) = actual.downcast_ref::<u16>() {
557 return Some(*v as f64);
558 }
559 if let Some(v) = actual.downcast_ref::<u8>() {
560 return Some(*v as f64);
561 }
562 if let Some(v) = actual.downcast_ref::<isize>() {
563 return Some(*v as f64);
564 }
565 if let Some(v) = actual.downcast_ref::<usize>() {
566 return Some(*v as f64);
567 }
568 None
569}
570
571#[cfg(feature = "json_condition")]
572fn compare_json_to_any(
573 actual: &dyn Any,
574 expected: &serde_json::Value,
575 operator: &ConditionOperator,
576) -> (bool, Option<String>, Option<String>) {
577 if let Some(exp_f64) = expected.as_f64() {
579 if let Some(act_f64) = extract_as_f64(actual) {
580 let passed = match operator {
581 ConditionOperator::Equals => (act_f64 - exp_f64).abs() < f64::EPSILON,
582 ConditionOperator::NotEquals => (act_f64 - exp_f64).abs() >= f64::EPSILON,
583 ConditionOperator::GreaterThan => act_f64 > exp_f64,
584 ConditionOperator::LessThan => act_f64 < exp_f64,
585 ConditionOperator::GreaterThanOrEqual => act_f64 >= exp_f64,
586 ConditionOperator::LessThanOrEqual => act_f64 <= exp_f64,
587 _ => false,
588 };
589 return (passed, Some(act_f64.to_string()), Some(exp_f64.to_string()));
590 }
591 }
592
593 if let Some(exp_str) = expected.as_str() {
595 let act_str: Option<&str> = actual
596 .downcast_ref::<String>()
597 .map(|s| s.as_str())
598 .or_else(|| actual.downcast_ref::<&str>().copied());
599
600 if let Some(a) = act_str {
601 let passed = match operator {
602 ConditionOperator::Equals => a == exp_str,
603 ConditionOperator::NotEquals => a != exp_str,
604 ConditionOperator::Contains => a.contains(exp_str),
605 ConditionOperator::NotContains => !a.contains(exp_str),
606 ConditionOperator::StartsWith => a.starts_with(exp_str),
607 ConditionOperator::EndsWith => a.ends_with(exp_str),
608 ConditionOperator::GreaterThan => a > exp_str,
609 ConditionOperator::LessThan => a < exp_str,
610 ConditionOperator::GreaterThanOrEqual => a >= exp_str,
611 ConditionOperator::LessThanOrEqual => a <= exp_str,
612 ConditionOperator::IsEmpty => a.is_empty(),
613 ConditionOperator::IsNotEmpty => !a.is_empty(),
614 #[cfg(feature = "regex")]
615 ConditionOperator::Regex => regex::Regex::new(exp_str)
616 .map(|re| re.is_match(a))
617 .unwrap_or(false),
618 #[cfg(not(feature = "regex"))]
619 ConditionOperator::Regex => false,
620 _ => false,
621 };
622 return (passed, Some(a.to_string()), Some(exp_str.to_string()));
623 }
624 }
625
626 if let Some(exp_bool) = expected.as_bool() {
628 if let Some(act_bool) = actual.downcast_ref::<bool>() {
629 let passed = match operator {
630 ConditionOperator::Equals => *act_bool == exp_bool,
631 ConditionOperator::NotEquals => *act_bool != exp_bool,
632 _ => false,
633 };
634 return (
635 passed,
636 Some(act_bool.to_string()),
637 Some(exp_bool.to_string()),
638 );
639 }
640 }
641
642 (false, None, None)
643}
644
645#[cfg(feature = "json_condition")]
650use crate::condition::{JsonCondition, JsonNestedCondition};
651#[cfg(feature = "json_condition")]
652use crate::result::{JsonConditionResult, JsonEvalResult};
653
654#[cfg(feature = "json_condition")]
669pub fn evaluate_json_condition<M: Matchable>(
670 context: &M,
671 group: &JsonNestedCondition,
672) -> JsonEvalResult {
673 let mut details = Vec::new();
674 eval_json_nested_recursive(context, group, &mut details)
675}
676
677#[cfg(feature = "json_condition")]
678fn eval_json_nested_recursive<M: Matchable>(
679 context: &M,
680 group: &JsonNestedCondition,
681 details: &mut Vec<JsonConditionResult>,
682) -> JsonEvalResult {
683 let mut flags = Vec::new();
684
685 for rule in &group.rules {
687 let result = eval_json_rule(context, rule);
688 flags.push(result.passed);
689 details.push(result);
690 }
691
692 for nested_group in &group.nested {
694 let nested_result = eval_json_nested_recursive(context, nested_group, details);
695 flags.push(nested_result.matched);
696 }
697
698 let matched = match group.mode {
699 ConditionMode::AND => flags.iter().all(|&f| f),
700 ConditionMode::OR => flags.iter().any(|&f| f),
701 ConditionMode::XOR => flags.iter().filter(|&&f| f).count() == 1,
702 };
703
704 JsonEvalResult {
705 matched,
706 details: details.clone(),
707 }
708}
709
710#[cfg(feature = "json_condition")]
711fn eval_json_rule<M: Matchable>(context: &M, rule: &JsonCondition) -> JsonConditionResult {
712 let field = &rule.field;
713
714 let path_segments: Vec<&str> = field.split('.').collect();
716
717 let actual_value = if path_segments.len() == 1 {
719 context.get_field(field)
720 } else {
721 context.get_field_path(&path_segments)
722 };
723
724 match actual_value {
725 Some(actual) => {
726 let (passed, actual_str, _expected_str) =
727 compare_json_to_any(actual, &rule.value, &rule.operator);
728 JsonConditionResult {
729 passed,
730 field: field.clone(),
731 operator: rule.operator,
732 expected: rule.value.clone(),
733 actual: actual_str
734 .clone()
735 .and_then(|s| serde_json::from_str(&format!("\"{}\"", s)).ok())
736 .or_else(|| {
737 actual_str.and_then(|s| s.parse::<f64>().ok().map(serde_json::Value::from))
738 }),
739 error: None,
740 }
741 }
742 None => JsonConditionResult {
743 passed: false,
744 field: field.clone(),
745 operator: rule.operator,
746 expected: rule.value.clone(),
747 actual: None,
748 error: Some(format!("Field '{}' not found", field)),
749 },
750 }
751}