condition_matcher/
matcher.rs

1//! # Matcher Module
2//! 
3//! This module provides the core matching functionality for evaluating conditions
4//! against values.
5//! 
6//! ## Example
7//! 
8//! ```rust
9//! use condition_matcher::{Matcher, MatcherMode, Condition, ConditionSelector, ConditionOperator, Matchable, MatchableDerive};
10//! 
11//! #[derive(MatchableDerive, PartialEq, Debug)]
12//! struct User {
13//!     name: String,
14//!     age: u32,
15//! }
16//! 
17//! let user = User { name: "Alice".to_string(), age: 30 };
18//! 
19//! let mut matcher = Matcher::new(MatcherMode::AND);
20//! matcher.add_condition(Condition {
21//!     selector: ConditionSelector::FieldValue("age", &30u32),
22//!     operator: ConditionOperator::Equals,
23//! });
24//! 
25//! assert!(matcher.run(&user).unwrap());
26//! ```
27
28use std::{any::Any, collections::HashMap, fmt};
29
30use crate::condition::{Condition, ConditionOperator, ConditionSelector};
31
32// Re-export the derive macro
33pub use condition_matcher_derive::Matchable as MatchableDerive;
34
35/// Errors that can occur during condition matching
36#[derive(Debug, Clone, PartialEq)]
37pub enum MatchError {
38    /// The specified field was not found on the type
39    FieldNotFound {
40        field: String,
41        type_name: String,
42    },
43    /// Type mismatch between expected and actual values
44    TypeMismatch {
45        field: String,
46        expected: String,
47        actual: String,
48    },
49    /// The operator is not supported for this type/context
50    UnsupportedOperator {
51        operator: String,
52        context: String,
53    },
54    /// Length check is not supported for this type
55    LengthNotSupported {
56        type_name: String,
57    },
58    /// Regex compilation failed
59    #[cfg(feature = "regex")]
60    RegexError {
61        pattern: String,
62        message: String,
63    },
64    /// The field path is empty
65    EmptyFieldPath,
66    /// Nested field not found
67    NestedFieldNotFound {
68        path: Vec<String>,
69        failed_at: String,
70    },
71}
72
73impl fmt::Display for MatchError {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        match self {
76            MatchError::FieldNotFound { field, type_name } => {
77                write!(f, "Field '{}' not found on type '{}'", field, type_name)
78            }
79            MatchError::TypeMismatch { field, expected, actual } => {
80                write!(f, "Type mismatch for field '{}': expected '{}', got '{}'", field, expected, actual)
81            }
82            MatchError::UnsupportedOperator { operator, context } => {
83                write!(f, "Operator '{}' not supported for {}", operator, context)
84            }
85            MatchError::LengthNotSupported { type_name } => {
86                write!(f, "Length check not supported for type '{}'", type_name)
87            }
88            #[cfg(feature = "regex")]
89            MatchError::RegexError { pattern, message } => {
90                write!(f, "Invalid regex pattern '{}': {}", pattern, message)
91            }
92            MatchError::EmptyFieldPath => {
93                write!(f, "Field path cannot be empty")
94            }
95            MatchError::NestedFieldNotFound { path, failed_at } => {
96                write!(f, "Nested field not found at '{}' in path {:?}", failed_at, path)
97            }
98        }
99    }
100}
101
102impl std::error::Error for MatchError {}
103
104/// Result of a match operation with detailed information
105#[derive(Debug, Clone)]
106pub struct MatchResult {
107    /// Whether the overall match succeeded
108    pub matched: bool,
109    /// Individual results for each condition
110    pub condition_results: Vec<ConditionResult>,
111    /// The matching mode used
112    pub mode: MatcherMode,
113}
114
115impl MatchResult {
116    /// Returns true if the match succeeded
117    pub fn is_match(&self) -> bool {
118        self.matched
119    }
120    
121    /// Returns the conditions that passed
122    pub fn passed_conditions(&self) -> Vec<&ConditionResult> {
123        self.condition_results.iter().filter(|r| r.passed).collect()
124    }
125    
126    /// Returns the conditions that failed
127    pub fn failed_conditions(&self) -> Vec<&ConditionResult> {
128        self.condition_results.iter().filter(|r| !r.passed).collect()
129    }
130}
131
132/// Result of evaluating a single condition
133#[derive(Debug, Clone)]
134pub struct ConditionResult {
135    /// Whether this condition passed
136    pub passed: bool,
137    /// Description of what was checked
138    pub description: String,
139    /// The actual value that was compared (as string for display)
140    pub actual_value: Option<String>,
141    /// The expected value (as string for display)
142    pub expected_value: Option<String>,
143    /// Error if evaluation failed
144    pub error: Option<MatchError>,
145}
146
147/// Mode for combining multiple conditions
148#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
149#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
150pub enum MatcherMode {
151    /// All conditions must match
152    #[default]
153    AND,
154    /// At least one condition must match
155    OR,
156    /// Exactly one condition must match
157    XOR,
158}
159
160/// Trait for types that can be matched against conditions.
161/// 
162/// This trait allows different types to opt-in to specific matching capabilities.
163/// For structs, you can use `#[derive(Matchable)]` to automatically implement field access.
164/// 
165/// ## Example
166/// 
167/// ```rust
168/// use condition_matcher::{Matchable, MatchableDerive};
169/// use std::any::Any;
170/// 
171/// #[derive(MatchableDerive, PartialEq)]
172/// struct MyStruct {
173///     value: i32,
174///     name: String,
175/// }
176/// 
177/// // The derive macro automatically implements get_field for all fields
178/// ```
179pub trait Matchable: PartialEq + Sized {
180    /// Get the length of the value if supported (for strings, collections, etc.)
181    fn get_length(&self) -> Option<usize> {
182        None
183    }
184    
185    /// Get a field value by name as a type-erased reference.
186    /// Returns None if field access is not supported or field doesn't exist.
187    fn get_field(&self, _field: &str) -> Option<&dyn Any> {
188        None
189    }
190    
191    /// Get a nested field value by path.
192    /// Default implementation walks through get_field calls.
193    fn get_field_path(&self, _path: &[&str]) -> Option<&dyn Any> {
194        None
195    }
196    
197    /// Get the type name as a string
198    fn type_name(&self) -> &str {
199        std::any::type_name::<Self>()
200    }
201    
202    /// Check if the value is considered "empty" (for collections, strings, options)
203    fn is_empty(&self) -> Option<bool> {
204        self.get_length().map(|len| len == 0)
205    }
206    
207    /// Check if this is a None/null value (for Option types)
208    fn is_none(&self) -> bool {
209        false
210    }
211}
212
213/// The main matcher struct that holds conditions and evaluates them against values.
214/// 
215/// ## Example
216/// 
217/// ```rust
218/// use condition_matcher::{Matcher, MatcherMode, Condition, ConditionSelector, ConditionOperator};
219/// 
220/// let mut matcher: Matcher<&str> = Matcher::new(MatcherMode::AND);
221/// matcher
222///     .add_condition(Condition {
223///         selector: ConditionSelector::Length(5),
224///         operator: ConditionOperator::GreaterThanOrEqual,
225///     })
226///     .add_condition(Condition {
227///         selector: ConditionSelector::Value("test"),
228///         operator: ConditionOperator::NotEquals,
229///     });
230/// 
231/// assert!(matcher.run(&"hello").unwrap());
232/// ```
233#[derive(Debug)]
234pub struct Matcher<'a, T: Matchable> {
235    pub mode: MatcherMode,
236    pub conditions: Vec<Condition<'a, T>>,
237}
238
239impl<'a, T: Matchable + 'static> Matcher<'a, T> {
240    /// Create a new matcher with the specified mode
241    pub fn new(mode: MatcherMode) -> Self {
242        Self {
243            mode,
244            conditions: Vec::new(),
245        }
246    }
247    
248    /// Create a new matcher with AND mode
249    pub fn and() -> Self {
250        Self::new(MatcherMode::AND)
251    }
252    
253    /// Create a new matcher with OR mode
254    pub fn or() -> Self {
255        Self::new(MatcherMode::OR)
256    }
257    
258    /// Create a new matcher with XOR mode
259    pub fn xor() -> Self {
260        Self::new(MatcherMode::XOR)
261    }
262
263    /// Add a condition to this matcher
264    pub fn add_condition(&mut self, condition: Condition<'a, T>) -> &mut Self {
265        self.conditions.push(condition);
266        self
267    }
268    
269    /// Add multiple conditions at once
270    pub fn add_conditions(&mut self, conditions: impl IntoIterator<Item = Condition<'a, T>>) -> &mut Self {
271        self.conditions.extend(conditions);
272        self
273    }
274
275    /// Run the matcher and return a simple boolean result.
276    /// Returns Err if any condition evaluation fails critically.
277    pub fn run(&self, value: &T) -> Result<bool, MatchError> {
278        let result = self.run_detailed(value)?;
279        Ok(result.matched)
280    }
281    
282    /// Run the matcher and return detailed results for each condition
283    pub fn run_detailed(&self, value: &T) -> Result<MatchResult, MatchError> {
284        let mut condition_results = Vec::new();
285        
286        for condition in self.conditions.iter() {
287            let result = self.evaluate_condition(condition, value);
288            condition_results.push(result);
289        }
290
291        let matched = match self.mode {
292            MatcherMode::AND => condition_results.iter().all(|r| r.passed),
293            MatcherMode::OR => condition_results.iter().any(|r| r.passed),
294            MatcherMode::XOR => condition_results.iter().filter(|r| r.passed).count() == 1,
295        };
296        
297        Ok(MatchResult {
298            matched,
299            condition_results,
300            mode: self.mode,
301        })
302    }
303    
304    fn evaluate_condition(&self, condition: &Condition<'a, T>, value: &T) -> ConditionResult {
305        match &condition.selector {
306            ConditionSelector::Length(expected_length) => {
307                self.eval_length(value, *expected_length, &condition.operator)
308            }
309            ConditionSelector::Type(type_name) => {
310                self.eval_type(value, type_name, &condition.operator)
311            }
312            ConditionSelector::Value(value_to_check) => {
313                self.eval_value(value, value_to_check, &condition.operator)
314            }
315            ConditionSelector::FieldValue(field_name, expected_value) => {
316                self.eval_field_value(value, field_name, *expected_value, &condition.operator)
317            }
318            ConditionSelector::FieldPath(path, expected_value) => {
319                self.eval_field_path(value, path, *expected_value, &condition.operator)
320            }
321            ConditionSelector::Not(inner_condition) => {
322                let mut result = self.evaluate_condition(inner_condition, value);
323                result.passed = !result.passed;
324                result.description = format!("NOT({})", result.description);
325                result
326            }
327        }
328    }
329    
330    fn eval_length(&self, value: &T, expected: usize, operator: &ConditionOperator) -> ConditionResult {
331        match value.get_length() {
332            Some(actual) => {
333                let passed = compare_numeric(actual, expected, operator);
334                ConditionResult {
335                    passed,
336                    description: format!("length {:?} {}", operator, expected),
337                    actual_value: Some(actual.to_string()),
338                    expected_value: Some(expected.to_string()),
339                    error: None,
340                }
341            }
342            None => ConditionResult {
343                passed: false,
344                description: format!("length {:?} {}", operator, expected),
345                actual_value: None,
346                expected_value: Some(expected.to_string()),
347                error: Some(MatchError::LengthNotSupported {
348                    type_name: value.type_name().to_string(),
349                }),
350            },
351        }
352    }
353    
354    fn eval_type(&self, value: &T, expected_type: &str, operator: &ConditionOperator) -> ConditionResult {
355        let actual_type = value.type_name();
356        let passed = match operator {
357            ConditionOperator::Equals => actual_type == expected_type,
358            ConditionOperator::NotEquals => actual_type != expected_type,
359            ConditionOperator::Contains => actual_type.contains(expected_type),
360            _ => false,
361        };
362        
363        ConditionResult {
364            passed,
365            description: format!("type {:?} {}", operator, expected_type),
366            actual_value: Some(actual_type.to_string()),
367            expected_value: Some(expected_type.to_string()),
368            error: None,
369        }
370    }
371    
372    fn eval_value(&self, value: &T, expected: &T, operator: &ConditionOperator) -> ConditionResult {
373        let passed = match operator {
374            ConditionOperator::Equals => value == expected,
375            ConditionOperator::NotEquals => value != expected,
376            _ => false,
377        };
378        
379        ConditionResult {
380            passed,
381            description: format!("value {:?}", operator),
382            actual_value: None,
383            expected_value: None,
384            error: None,
385        }
386    }
387    
388    fn eval_field_value(
389        &self,
390        value: &T,
391        field: &str,
392        expected: &dyn Any,
393        operator: &ConditionOperator,
394    ) -> ConditionResult {
395        match value.get_field(field) {
396            Some(actual) => {
397                let (passed, actual_str, expected_str) = compare_any_values(actual, expected, operator);
398                ConditionResult {
399                    passed,
400                    description: format!("field '{}' {:?}", field, operator),
401                    actual_value: actual_str,
402                    expected_value: expected_str,
403                    error: None,
404                }
405            }
406            None => ConditionResult {
407                passed: false,
408                description: format!("field '{}' {:?}", field, operator),
409                actual_value: None,
410                expected_value: None,
411                error: Some(MatchError::FieldNotFound {
412                    field: field.to_string(),
413                    type_name: value.type_name().to_string(),
414                }),
415            },
416        }
417    }
418    
419    fn eval_field_path(
420        &self,
421        value: &T,
422        path: &[&str],
423        expected: &dyn Any,
424        operator: &ConditionOperator,
425    ) -> ConditionResult {
426        if path.is_empty() {
427            return ConditionResult {
428                passed: false,
429                description: "field path".to_string(),
430                actual_value: None,
431                expected_value: None,
432                error: Some(MatchError::EmptyFieldPath),
433            };
434        }
435        
436        // Try to use get_field_path first
437        if let Some(actual) = value.get_field_path(path) {
438            let (passed, actual_str, expected_str) = compare_any_values(actual, expected, operator);
439            return ConditionResult {
440                passed,
441                description: format!("field path '{:?}' {:?}", path, operator),
442                actual_value: actual_str,
443                expected_value: expected_str,
444                error: None,
445            };
446        }
447        
448        // Fallback: try first field only (basic implementation)
449        match value.get_field(path[0]) {
450            Some(actual) if path.len() == 1 => {
451                let (passed, actual_str, expected_str) = compare_any_values(actual, expected, operator);
452                ConditionResult {
453                    passed,
454                    description: format!("field path '{:?}' {:?}", path, operator),
455                    actual_value: actual_str,
456                    expected_value: expected_str,
457                    error: None,
458                }
459            }
460            _ => ConditionResult {
461                passed: false,
462                description: format!("field path '{:?}' {:?}", path, operator),
463                actual_value: None,
464                expected_value: None,
465                error: Some(MatchError::NestedFieldNotFound {
466                    path: path.iter().map(|s| s.to_string()).collect(),
467                    failed_at: path[0].to_string(),
468                }),
469            },
470        }
471    }
472}
473
474// ============================================================================
475// Comparison Functions
476// ============================================================================
477
478fn compare_numeric<N: PartialOrd>(actual: N, expected: N, operator: &ConditionOperator) -> bool {
479    match operator {
480        ConditionOperator::Equals => actual == expected,
481        ConditionOperator::NotEquals => actual != expected,
482        ConditionOperator::GreaterThan => actual > expected,
483        ConditionOperator::LessThan => actual < expected,
484        ConditionOperator::GreaterThanOrEqual => actual >= expected,
485        ConditionOperator::LessThanOrEqual => actual <= expected,
486        _ => false,
487    }
488}
489
490fn compare_any_values(
491    actual: &dyn Any,
492    expected: &dyn Any,
493    operator: &ConditionOperator,
494) -> (bool, Option<String>, Option<String>) {
495    // Integer types
496    if let Some(result) = try_compare::<i8>(actual, expected, operator) { return result; }
497    if let Some(result) = try_compare::<i16>(actual, expected, operator) { return result; }
498    if let Some(result) = try_compare::<i32>(actual, expected, operator) { return result; }
499    if let Some(result) = try_compare::<i64>(actual, expected, operator) { return result; }
500    if let Some(result) = try_compare::<i128>(actual, expected, operator) { return result; }
501    if let Some(result) = try_compare::<isize>(actual, expected, operator) { return result; }
502    
503    // Unsigned integer types
504    if let Some(result) = try_compare::<u8>(actual, expected, operator) { return result; }
505    if let Some(result) = try_compare::<u16>(actual, expected, operator) { return result; }
506    if let Some(result) = try_compare::<u32>(actual, expected, operator) { return result; }
507    if let Some(result) = try_compare::<u64>(actual, expected, operator) { return result; }
508    if let Some(result) = try_compare::<u128>(actual, expected, operator) { return result; }
509    if let Some(result) = try_compare::<usize>(actual, expected, operator) { return result; }
510    
511    // Float types
512    if let Some(result) = try_compare::<f32>(actual, expected, operator) { return result; }
513    if let Some(result) = try_compare::<f64>(actual, expected, operator) { return result; }
514    
515    // Boolean
516    if let Some(result) = try_compare::<bool>(actual, expected, operator) { return result; }
517    
518    // String types with string operations
519    if let Some(result) = try_compare_strings(actual, expected, operator) { return result; }
520    
521    // Char
522    if let Some(result) = try_compare::<char>(actual, expected, operator) { return result; }
523    
524    // No match found
525    (false, None, None)
526}
527
528fn try_compare<T: PartialOrd + PartialEq + fmt::Display + 'static>(
529    actual: &dyn Any,
530    expected: &dyn Any,
531    operator: &ConditionOperator,
532) -> Option<(bool, Option<String>, Option<String>)> {
533    if let (Some(a), Some(e)) = (actual.downcast_ref::<T>(), expected.downcast_ref::<T>()) {
534        let passed = match operator {
535            ConditionOperator::Equals => a == e,
536            ConditionOperator::NotEquals => a != e,
537            ConditionOperator::GreaterThan => a > e,
538            ConditionOperator::LessThan => a < e,
539            ConditionOperator::GreaterThanOrEqual => a >= e,
540            ConditionOperator::LessThanOrEqual => a <= e,
541            _ => return None,
542        };
543        Some((passed, Some(a.to_string()), Some(e.to_string())))
544    } else {
545        None
546    }
547}
548
549fn try_compare_strings(
550    actual: &dyn Any,
551    expected: &dyn Any,
552    operator: &ConditionOperator,
553) -> Option<(bool, Option<String>, Option<String>)> {
554    // Get the actual string
555    let actual_str: Option<&str> = actual.downcast_ref::<String>().map(|s| s.as_str())
556        .or_else(|| actual.downcast_ref::<&str>().copied());
557    
558    // Get the expected string
559    let expected_str: Option<&str> = expected.downcast_ref::<String>().map(|s| s.as_str())
560        .or_else(|| expected.downcast_ref::<&str>().copied());
561    
562    match (actual_str, expected_str) {
563        (Some(a), Some(e)) => {
564            let passed = match operator {
565                ConditionOperator::Equals => a == e,
566                ConditionOperator::NotEquals => a != e,
567                ConditionOperator::Contains => a.contains(e),
568                ConditionOperator::NotContains => !a.contains(e),
569                ConditionOperator::StartsWith => a.starts_with(e),
570                ConditionOperator::EndsWith => a.ends_with(e),
571                ConditionOperator::GreaterThan => a > e,
572                ConditionOperator::LessThan => a < e,
573                ConditionOperator::GreaterThanOrEqual => a >= e,
574                ConditionOperator::LessThanOrEqual => a <= e,
575                ConditionOperator::IsEmpty => a.is_empty(),
576                ConditionOperator::IsNotEmpty => !a.is_empty(),
577                #[cfg(feature = "regex")]
578                ConditionOperator::Regex => {
579                    regex::Regex::new(e).map(|re| re.is_match(a)).unwrap_or(false)
580                }
581                #[cfg(not(feature = "regex"))]
582                ConditionOperator::Regex => false,
583                _ => return None,
584            };
585            Some((passed, Some(a.to_string()), Some(e.to_string())))
586        }
587        _ => None,
588    }
589}
590
591// ============================================================================
592// Matchable Implementations for Common Types
593// ============================================================================
594
595impl Matchable for &str {
596    fn get_length(&self) -> Option<usize> {
597        Some(self.len())
598    }
599    
600    fn is_empty(&self) -> Option<bool> {
601        Some((*self).is_empty())
602    }
603}
604
605impl Matchable for String {
606    fn get_length(&self) -> Option<usize> {
607        Some(self.len())
608    }
609    
610    fn is_empty(&self) -> Option<bool> {
611        Some(self.is_empty())
612    }
613}
614
615impl<T: Matchable> Matchable for Vec<T> {
616    fn get_length(&self) -> Option<usize> {
617        Some(self.len())
618    }
619    
620    fn is_empty(&self) -> Option<bool> {
621        Some(self.is_empty())
622    }
623}
624
625impl<K, V> Matchable for HashMap<K, V>
626where
627    K: std::borrow::Borrow<str> + std::hash::Hash + Eq,
628    V: PartialEq + 'static,
629{
630    fn get_length(&self) -> Option<usize> {
631        Some(self.len())
632    }
633    
634    fn get_field(&self, field: &str) -> Option<&dyn Any> {
635        self.get(field).map(|v| v as &dyn Any)
636    }
637    
638    fn is_empty(&self) -> Option<bool> {
639        Some(self.is_empty())
640    }
641}
642
643impl<T: Matchable + 'static> Matchable for Option<T> {
644    fn get_length(&self) -> Option<usize> {
645        self.as_ref().and_then(|v| v.get_length())
646    }
647    
648    fn get_field(&self, field: &str) -> Option<&dyn Any> {
649        self.as_ref().and_then(|v| v.get_field(field))
650    }
651    
652    fn is_none(&self) -> bool {
653        self.is_none()
654    }
655    
656    fn is_empty(&self) -> Option<bool> {
657        Some(self.is_none())
658    }
659}
660
661// Implement for primitive types
662macro_rules! impl_matchable_primitive {
663    ($($t:ty),*) => {
664        $(
665            impl Matchable for $t {}
666        )*
667    };
668}
669
670impl_matchable_primitive!(
671    i8, i16, i32, i64, i128, isize,
672    u8, u16, u32, u64, u128, usize,
673    f32, f64,
674    bool, char
675);
676
677// ============================================================================
678// Builder API
679// ============================================================================
680
681/// A builder for creating matchers with a fluent API
682/// 
683/// ## Example
684/// 
685/// ```rust
686/// use condition_matcher::{MatcherBuilder, MatcherMode, ConditionOperator};
687/// 
688/// let matcher = MatcherBuilder::<i32>::new()
689///     .mode(MatcherMode::AND)
690///     .value_equals(42)
691///     .build();
692/// 
693/// assert!(matcher.run(&42).unwrap());
694/// ```
695pub struct MatcherBuilder<'a, T: Matchable> {
696    mode: MatcherMode,
697    conditions: Vec<Condition<'a, T>>,
698}
699
700impl<'a, T: Matchable + 'static> MatcherBuilder<'a, T> {
701    /// Create a new builder with default AND mode
702    pub fn new() -> Self {
703        Self {
704            mode: MatcherMode::AND,
705            conditions: Vec::new(),
706        }
707    }
708    
709    /// Set the matching mode
710    pub fn mode(mut self, mode: MatcherMode) -> Self {
711        self.mode = mode;
712        self
713    }
714    
715    /// Add a condition that the value equals the expected value
716    pub fn value_equals(mut self, expected: T) -> Self {
717        self.conditions.push(Condition {
718            selector: ConditionSelector::Value(expected),
719            operator: ConditionOperator::Equals,
720        });
721        self
722    }
723    
724    /// Add a condition that the value does not equal the expected value
725    pub fn value_not_equals(mut self, expected: T) -> Self {
726        self.conditions.push(Condition {
727            selector: ConditionSelector::Value(expected),
728            operator: ConditionOperator::NotEquals,
729        });
730        self
731    }
732    
733    /// Add a length condition
734    pub fn length(mut self, len: usize, operator: ConditionOperator) -> Self {
735        self.conditions.push(Condition {
736            selector: ConditionSelector::Length(len),
737            operator,
738        });
739        self
740    }
741    
742    /// Add a condition that length equals the expected value
743    pub fn length_equals(self, len: usize) -> Self {
744        self.length(len, ConditionOperator::Equals)
745    }
746    
747    /// Add a condition that length is greater than or equal to the expected value
748    pub fn length_gte(self, len: usize) -> Self {
749        self.length(len, ConditionOperator::GreaterThanOrEqual)
750    }
751    
752    /// Add a condition that length is less than or equal to the expected value
753    pub fn length_lte(self, len: usize) -> Self {
754        self.length(len, ConditionOperator::LessThanOrEqual)
755    }
756    
757    /// Add a raw condition
758    pub fn condition(mut self, condition: Condition<'a, T>) -> Self {
759        self.conditions.push(condition);
760        self
761    }
762    
763    /// Build the matcher
764    pub fn build(self) -> Matcher<'a, T> {
765        Matcher {
766            mode: self.mode,
767            conditions: self.conditions,
768        }
769    }
770}
771
772impl<'a, T: Matchable + 'static> Default for MatcherBuilder<'a, T> {
773    fn default() -> Self {
774        Self::new()
775    }
776}
777
778/// Builder for field-based conditions
779/// 
780/// ## Example
781/// 
782/// ```rust
783/// use condition_matcher::{FieldConditionBuilder, Matchable, MatchableDerive, Matcher, MatcherMode};
784/// 
785/// #[derive(MatchableDerive, PartialEq)]
786/// struct User {
787///     age: u32,
788/// }
789/// 
790/// let condition = FieldConditionBuilder::<User>::new("age").gte(&18u32);
791/// 
792/// let mut matcher = Matcher::new(MatcherMode::AND);
793/// matcher.add_condition(condition);
794/// 
795/// let user = User { age: 25 };
796/// assert!(matcher.run(&user).unwrap());
797/// ```
798pub struct FieldConditionBuilder<'a, T> {
799    field: &'a str,
800    _phantom: std::marker::PhantomData<T>,
801}
802
803impl<'a, T: Matchable> FieldConditionBuilder<'a, T> {
804    /// Create a new field condition builder for the given field
805    pub fn new(field: &'a str) -> Self {
806        Self {
807            field,
808            _phantom: std::marker::PhantomData,
809        }
810    }
811    
812    /// Field equals value
813    pub fn equals(self, value: &'a dyn Any) -> Condition<'a, T> {
814        Condition {
815            selector: ConditionSelector::FieldValue(self.field, value),
816            operator: ConditionOperator::Equals,
817        }
818    }
819    
820    /// Field not equals value
821    pub fn not_equals(self, value: &'a dyn Any) -> Condition<'a, T> {
822        Condition {
823            selector: ConditionSelector::FieldValue(self.field, value),
824            operator: ConditionOperator::NotEquals,
825        }
826    }
827    
828    /// Field greater than value
829    pub fn gt(self, value: &'a dyn Any) -> Condition<'a, T> {
830        Condition {
831            selector: ConditionSelector::FieldValue(self.field, value),
832            operator: ConditionOperator::GreaterThan,
833        }
834    }
835    
836    /// Field greater than or equal value
837    pub fn gte(self, value: &'a dyn Any) -> Condition<'a, T> {
838        Condition {
839            selector: ConditionSelector::FieldValue(self.field, value),
840            operator: ConditionOperator::GreaterThanOrEqual,
841        }
842    }
843    
844    /// Field less than value
845    pub fn lt(self, value: &'a dyn Any) -> Condition<'a, T> {
846        Condition {
847            selector: ConditionSelector::FieldValue(self.field, value),
848            operator: ConditionOperator::LessThan,
849        }
850    }
851    
852    /// Field less than or equal value
853    pub fn lte(self, value: &'a dyn Any) -> Condition<'a, T> {
854        Condition {
855            selector: ConditionSelector::FieldValue(self.field, value),
856            operator: ConditionOperator::LessThanOrEqual,
857        }
858    }
859    
860    /// Field contains substring (for string fields)
861    pub fn contains(self, value: &'a dyn Any) -> Condition<'a, T> {
862        Condition {
863            selector: ConditionSelector::FieldValue(self.field, value),
864            operator: ConditionOperator::Contains,
865        }
866    }
867    
868    /// Field starts with prefix (for string fields)
869    pub fn starts_with(self, value: &'a dyn Any) -> Condition<'a, T> {
870        Condition {
871            selector: ConditionSelector::FieldValue(self.field, value),
872            operator: ConditionOperator::StartsWith,
873        }
874    }
875    
876    /// Field ends with suffix (for string fields)
877    pub fn ends_with(self, value: &'a dyn Any) -> Condition<'a, T> {
878        Condition {
879            selector: ConditionSelector::FieldValue(self.field, value),
880            operator: ConditionOperator::EndsWith,
881        }
882    }
883}
884
885/// Convenience function to create a field condition builder
886pub fn field<'a, T: Matchable>(name: &'a str) -> FieldConditionBuilder<'a, T> {
887    FieldConditionBuilder::new(name)
888}
889
890// ============================================================================
891// Tests
892// ============================================================================
893
894#[cfg(test)]
895mod tests {
896    use super::*;
897    use crate::condition::{ConditionOperator, ConditionSelector};
898
899    #[test]
900    fn test_matcher_and_mode() {
901        let mut matcher: Matcher<&str> = Matcher::new(MatcherMode::AND);
902        matcher
903            .add_condition(Condition {
904                selector: ConditionSelector::Length(5),
905                operator: ConditionOperator::GreaterThanOrEqual,
906            })
907            .add_condition(Condition {
908                selector: ConditionSelector::Value("something"),
909                operator: ConditionOperator::NotEquals,
910            });
911        
912        assert_eq!(matcher.run(&"test").unwrap(), false);
913        assert_eq!(matcher.run(&"test12345").unwrap(), true);
914        assert_eq!(matcher.run(&"something").unwrap(), false);
915        assert_eq!(matcher.run(&"somethingelse").unwrap(), true);
916    }
917
918    #[test]
919    fn test_matcher_or_mode() {
920        let mut matcher: Matcher<&str> = Matcher::new(MatcherMode::OR);
921        matcher
922            .add_condition(Condition {
923                selector: ConditionSelector::Length(4),
924                operator: ConditionOperator::Equals,
925            })
926            .add_condition(Condition {
927                selector: ConditionSelector::Value("hello"),
928                operator: ConditionOperator::Equals,
929            });
930        
931        assert_eq!(matcher.run(&"test").unwrap(), true);
932        assert_eq!(matcher.run(&"hello").unwrap(), true);
933        assert_eq!(matcher.run(&"world").unwrap(), false);
934    }
935
936    #[test]
937    fn test_matcher_xor_mode() {
938        let mut matcher: Matcher<&str> = Matcher::new(MatcherMode::XOR);
939        matcher
940            .add_condition(Condition {
941                selector: ConditionSelector::Length(4),
942                operator: ConditionOperator::Equals,
943            })
944            .add_condition(Condition {
945                selector: ConditionSelector::Value("test"),
946                operator: ConditionOperator::Equals,
947            });
948        
949        assert_eq!(matcher.run(&"test").unwrap(), false);
950        assert_eq!(matcher.run(&"hello").unwrap(), false);
951        assert_eq!(matcher.run(&"abcd").unwrap(), true);
952    }
953
954    #[test]
955    fn test_type_checking() {
956        let mut matcher: Matcher<&str> = Matcher::new(MatcherMode::AND);
957        matcher.add_condition(Condition {
958            selector: ConditionSelector::Type("&str".to_string()),
959            operator: ConditionOperator::Equals,
960        });
961        
962        assert_eq!(matcher.run(&"test").unwrap(), true);
963    }
964
965    #[test]
966    fn test_field_checking() {
967        use crate::matcher::MatchableDerive;
968
969        #[derive(MatchableDerive, PartialEq, Debug)]
970        struct TestStruct {
971            a: i32,
972            b: String,
973        }
974
975        let test_value = TestStruct {
976            a: 1,
977            b: "test".to_string(),
978        };
979
980        // Test equals
981        let mut matcher: Matcher<TestStruct> = Matcher::new(MatcherMode::AND);
982        matcher.add_condition(Condition {
983            selector: ConditionSelector::FieldValue("a", &1i32),
984            operator: ConditionOperator::Equals,
985        });
986        assert_eq!(matcher.run(&test_value).unwrap(), true);
987
988        // Test not equals
989        let mut matcher2: Matcher<TestStruct> = Matcher::new(MatcherMode::AND);
990        matcher2.add_condition(Condition {
991            selector: ConditionSelector::FieldValue("a", &2i32),
992            operator: ConditionOperator::Equals,
993        });
994        assert_eq!(matcher2.run(&test_value).unwrap(), false);
995
996        // Test string field
997        let mut matcher3: Matcher<TestStruct> = Matcher::new(MatcherMode::AND);
998        matcher3.add_condition(Condition {
999            selector: ConditionSelector::FieldValue("b", &"test"),
1000            operator: ConditionOperator::Equals,
1001        });
1002        assert_eq!(matcher3.run(&test_value).unwrap(), true);
1003    }
1004
1005    #[test]
1006    fn test_numeric_comparisons_on_fields() {
1007        use crate::matcher::MatchableDerive;
1008
1009        #[derive(MatchableDerive, PartialEq, Debug)]
1010        struct Person {
1011            age: u32,
1012            score: f64,
1013        }
1014
1015        let person = Person { age: 25, score: 85.5 };
1016
1017        // Test greater than
1018        let mut matcher: Matcher<Person> = Matcher::new(MatcherMode::AND);
1019        matcher.add_condition(Condition {
1020            selector: ConditionSelector::FieldValue("age", &18u32),
1021            operator: ConditionOperator::GreaterThan,
1022        });
1023        assert!(matcher.run(&person).unwrap());
1024
1025        // Test less than or equal
1026        let mut matcher2: Matcher<Person> = Matcher::new(MatcherMode::AND);
1027        matcher2.add_condition(Condition {
1028            selector: ConditionSelector::FieldValue("age", &25u32),
1029            operator: ConditionOperator::LessThanOrEqual,
1030        });
1031        assert!(matcher2.run(&person).unwrap());
1032
1033        // Test float comparison
1034        let mut matcher3: Matcher<Person> = Matcher::new(MatcherMode::AND);
1035        matcher3.add_condition(Condition {
1036            selector: ConditionSelector::FieldValue("score", &80.0f64),
1037            operator: ConditionOperator::GreaterThan,
1038        });
1039        assert!(matcher3.run(&person).unwrap());
1040    }
1041
1042    #[test]
1043    fn test_string_operations() {
1044        use crate::matcher::MatchableDerive;
1045
1046        #[derive(MatchableDerive, PartialEq, Debug)]
1047        struct Email {
1048            address: String,
1049        }
1050
1051        let email = Email {
1052            address: "user@example.com".to_string(),
1053        };
1054
1055        // Test contains
1056        let mut matcher: Matcher<Email> = Matcher::new(MatcherMode::AND);
1057        matcher.add_condition(Condition {
1058            selector: ConditionSelector::FieldValue("address", &"@example"),
1059            operator: ConditionOperator::Contains,
1060        });
1061        assert!(matcher.run(&email).unwrap());
1062
1063        // Test starts with
1064        let mut matcher2: Matcher<Email> = Matcher::new(MatcherMode::AND);
1065        matcher2.add_condition(Condition {
1066            selector: ConditionSelector::FieldValue("address", &"user@"),
1067            operator: ConditionOperator::StartsWith,
1068        });
1069        assert!(matcher2.run(&email).unwrap());
1070
1071        // Test ends with
1072        let mut matcher3: Matcher<Email> = Matcher::new(MatcherMode::AND);
1073        matcher3.add_condition(Condition {
1074            selector: ConditionSelector::FieldValue("address", &".com"),
1075            operator: ConditionOperator::EndsWith,
1076        });
1077        assert!(matcher3.run(&email).unwrap());
1078
1079        // Test not contains
1080        let mut matcher4: Matcher<Email> = Matcher::new(MatcherMode::AND);
1081        matcher4.add_condition(Condition {
1082            selector: ConditionSelector::FieldValue("address", &"@gmail"),
1083            operator: ConditionOperator::NotContains,
1084        });
1085        assert!(matcher4.run(&email).unwrap());
1086    }
1087
1088    #[test]
1089    fn test_detailed_results() {
1090        let mut matcher: Matcher<&str> = Matcher::new(MatcherMode::AND);
1091        matcher
1092            .add_condition(Condition {
1093                selector: ConditionSelector::Length(4),
1094                operator: ConditionOperator::Equals,
1095            })
1096            .add_condition(Condition {
1097                selector: ConditionSelector::Value("test"),
1098                operator: ConditionOperator::Equals,
1099            });
1100        
1101        let result = matcher.run_detailed(&"test").unwrap();
1102        assert!(result.is_match());
1103        assert_eq!(result.passed_conditions().len(), 2);
1104        assert_eq!(result.failed_conditions().len(), 0);
1105        
1106        let result2 = matcher.run_detailed(&"hello").unwrap();
1107        assert!(!result2.is_match());
1108        assert_eq!(result2.passed_conditions().len(), 0);
1109        assert_eq!(result2.failed_conditions().len(), 2);
1110    }
1111
1112    #[test]
1113    fn test_builder_api() {
1114        let matcher = MatcherBuilder::<&str>::new()
1115            .mode(MatcherMode::AND)
1116            .length_gte(4)
1117            .value_not_equals("bad")
1118            .build();
1119        
1120        assert!(matcher.run(&"good").unwrap());
1121        assert!(!matcher.run(&"bad").unwrap());
1122        assert!(!matcher.run(&"hi").unwrap());
1123    }
1124
1125    #[test]
1126    fn test_field_builder() {
1127        use crate::matcher::MatchableDerive;
1128
1129        #[derive(MatchableDerive, PartialEq, Debug)]
1130        struct User {
1131            age: u32,
1132        }
1133
1134        let user = User { age: 25 };
1135
1136        let condition = field::<User>("age").gte(&18u32);
1137        let mut matcher = Matcher::new(MatcherMode::AND);
1138        matcher.add_condition(condition);
1139        
1140        assert!(matcher.run(&user).unwrap());
1141    }
1142
1143    #[test]
1144    fn test_convenience_constructors() {
1145        let and_matcher: Matcher<&str> = Matcher::and();
1146        assert_eq!(and_matcher.mode, MatcherMode::AND);
1147        
1148        let or_matcher: Matcher<&str> = Matcher::or();
1149        assert_eq!(or_matcher.mode, MatcherMode::OR);
1150        
1151        let xor_matcher: Matcher<&str> = Matcher::xor();
1152        assert_eq!(xor_matcher.mode, MatcherMode::XOR);
1153    }
1154
1155    #[test]
1156    fn test_error_on_missing_field() {
1157        use crate::matcher::MatchableDerive;
1158
1159        #[derive(MatchableDerive, PartialEq, Debug)]
1160        struct User {
1161            name: String,
1162        }
1163
1164        let user = User { name: "Alice".to_string() };
1165
1166        let mut matcher: Matcher<User> = Matcher::new(MatcherMode::AND);
1167        matcher.add_condition(Condition {
1168            selector: ConditionSelector::FieldValue("nonexistent", &"value"),
1169            operator: ConditionOperator::Equals,
1170        });
1171
1172        let result = matcher.run_detailed(&user).unwrap();
1173        assert!(!result.is_match());
1174        
1175        let failed = result.failed_conditions();
1176        assert_eq!(failed.len(), 1);
1177        assert!(failed[0].error.is_some());
1178    }
1179
1180    #[test]
1181    fn test_not_operator() {
1182        use crate::matcher::MatchableDerive;
1183
1184        #[derive(MatchableDerive, PartialEq, Debug)]
1185        struct Item {
1186            active: bool,
1187        }
1188
1189        let item = Item { active: false };
1190
1191        // Test NOT operator - should match because NOT(active=true) is true when active=false
1192        let inner_condition = Condition {
1193            selector: ConditionSelector::FieldValue("active", &true),
1194            operator: ConditionOperator::Equals,
1195        };
1196
1197        let mut matcher: Matcher<Item> = Matcher::new(MatcherMode::AND);
1198        matcher.add_condition(Condition {
1199            selector: ConditionSelector::Not(Box::new(inner_condition)),
1200            operator: ConditionOperator::Equals, // operator is ignored for NOT
1201        });
1202
1203        assert!(matcher.run(&item).unwrap());
1204    }
1205
1206    #[test]
1207    fn test_optional_fields() {
1208        use crate::matcher::MatchableDerive;
1209
1210        #[derive(MatchableDerive, PartialEq, Debug)]
1211        struct Profile {
1212            name: String,
1213            nickname: Option<String>,
1214        }
1215
1216        let profile_with_nick = Profile {
1217            name: "Alice".to_string(),
1218            nickname: Some("Ali".to_string()),
1219        };
1220
1221        let profile_without_nick = Profile {
1222            name: "Bob".to_string(),
1223            nickname: None,
1224        };
1225
1226        // Test matching optional field when present
1227        let mut matcher: Matcher<Profile> = Matcher::new(MatcherMode::AND);
1228        matcher.add_condition(Condition {
1229            selector: ConditionSelector::FieldValue("nickname", &"Ali"),
1230            operator: ConditionOperator::Equals,
1231        });
1232
1233        assert!(matcher.run(&profile_with_nick).unwrap());
1234        // When None, field access returns None, so the match fails
1235        assert!(!matcher.run(&profile_without_nick).unwrap());
1236    }
1237
1238    #[cfg(feature = "regex")]
1239    #[test]
1240    fn test_regex_matching() {
1241        use crate::matcher::MatchableDerive;
1242
1243        #[derive(MatchableDerive, PartialEq, Debug)]
1244        struct Email {
1245            address: String,
1246        }
1247
1248        let email = Email {
1249            address: "user@example.com".to_string(),
1250        };
1251
1252        let mut matcher: Matcher<Email> = Matcher::new(MatcherMode::AND);
1253        matcher.add_condition(Condition {
1254            selector: ConditionSelector::FieldValue("address", &r"^[a-z]+@[a-z]+\.[a-z]+$"),
1255            operator: ConditionOperator::Regex,
1256        });
1257
1258        assert!(matcher.run(&email).unwrap());
1259
1260        // Test non-matching regex
1261        let bad_email = Email {
1262            address: "not-an-email".to_string(),
1263        };
1264        assert!(!matcher.run(&bad_email).unwrap());
1265    }
1266}