Skip to main content

pattern_core/test_utils/
helpers.rs

1//! Test helper utilities for pattern comparison and validation
2
3use std::fmt::Debug;
4
5/// Error type for pattern comparison failures
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct PatternComparisonError {
8    pub message: String,
9    pub differences: Vec<Difference>,
10    pub path: Vec<String>,
11}
12
13/// A single difference found during pattern comparison
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct Difference {
16    pub field: String,
17    pub expected: String,
18    pub actual: String,
19}
20
21/// Options for pattern comparison
22#[derive(Debug, Clone)]
23pub struct PatternComparisonOptions {
24    pub deep: bool,
25    pub ignore_fields: Vec<String>,
26    pub approximate_equality: bool,
27}
28
29impl Default for PatternComparisonOptions {
30    fn default() -> Self {
31        Self {
32            deep: true,
33            ignore_fields: Vec::new(),
34            approximate_equality: false,
35        }
36    }
37}
38
39/// Rules for pattern structure validation
40#[derive(Debug, Clone, Default)]
41pub struct ValidationRules {
42    pub max_depth: Option<usize>,
43    pub max_elements: Option<usize>,
44    pub required_fields: Vec<String>,
45}
46
47/// Error type for pattern validation failures
48#[derive(Debug, Clone, PartialEq, Eq)]
49pub struct ValidationError {
50    pub message: String,
51    pub rule_violated: String,
52    pub location: Vec<String>,
53}
54
55/// Compare two patterns for equality with detailed error messages
56///
57/// # Arguments
58///
59/// * `actual` - Actual pattern value
60/// * `expected` - Expected pattern value
61/// * `msg` - Error message prefix if comparison fails
62///
63/// # Returns
64///
65/// `Result<(), PatternComparisonError>` - Ok if patterns are equal, Err with details if not
66pub fn assert_patterns_equal<V>(
67    _actual: &V,
68    _expected: &V,
69    _msg: &str,
70) -> Result<(), PatternComparisonError>
71where
72    V: PartialEq + Debug,
73{
74    // Placeholder implementation
75    // Will be fully implemented when Pattern types are defined in feature 004
76    Ok(())
77}
78
79/// Validate that a pattern has valid structure
80///
81/// # Arguments
82///
83/// * `pattern` - Pattern to validate
84/// * `rules` - Validation rules to apply
85///
86/// # Returns
87///
88/// `Result<(), ValidationError>` - Ok if pattern is valid, Err with details if not
89pub fn assert_pattern_structure_valid<V>(
90    _pattern: &V,
91    _rules: &ValidationRules,
92) -> Result<(), ValidationError>
93where
94    V: Debug,
95{
96    // Placeholder implementation
97    // Will be fully implemented when Pattern types are defined in feature 004
98    Ok(())
99}
100
101/// Compare patterns with equivalence checking options
102///
103/// # Arguments
104///
105/// * `pattern_a` - First pattern
106/// * `pattern_b` - Second pattern
107/// * `options` - Comparison options
108///
109/// # Returns
110///
111/// `Result<(), PatternComparisonError>` - Ok if patterns are equivalent, Err with details if not
112pub fn assert_patterns_equivalent<V>(
113    _pattern_a: &V,
114    _pattern_b: &V,
115    _options: &PatternComparisonOptions,
116) -> Result<(), PatternComparisonError>
117where
118    V: PartialEq + Debug,
119{
120    // Placeholder implementation
121    // Will be fully implemented when Pattern types are defined in feature 004
122    Ok(())
123}
124
125// ====================================================================================
126// Effect Counting Utilities (for traversable short-circuit verification)
127// ====================================================================================
128
129use std::sync::atomic::{AtomicUsize, Ordering};
130
131/// Counter for tracking side effects during traversal
132///
133/// Used to verify short-circuit behavior: if traversal short-circuits on error,
134/// the counter should show that not all values were processed.
135#[derive(Debug)]
136pub struct EffectCounter {
137    count: AtomicUsize,
138}
139
140impl EffectCounter {
141    /// Create a new effect counter starting at 0
142    pub fn new() -> Self {
143        Self {
144            count: AtomicUsize::new(0),
145        }
146    }
147
148    /// Increment the counter (called each time effectful function is invoked)
149    pub fn increment(&self) {
150        self.count.fetch_add(1, Ordering::SeqCst);
151    }
152
153    /// Get the current count
154    pub fn count(&self) -> usize {
155        self.count.load(Ordering::SeqCst)
156    }
157
158    /// Reset the counter to 0
159    pub fn reset(&self) {
160        self.count.store(0, Ordering::SeqCst);
161    }
162}
163
164impl Default for EffectCounter {
165    fn default() -> Self {
166        Self::new()
167    }
168}
169
170impl Clone for EffectCounter {
171    fn clone(&self) -> Self {
172        Self {
173            count: AtomicUsize::new(self.count()),
174        }
175    }
176}
177
178/// Helper function to create a counting effectful function for testing
179///
180/// Returns a closure that increments the counter each time it's called,
181/// then applies the provided function.
182///
183/// # Example
184///
185/// ```ignore
186/// let counter = EffectCounter::new();
187/// let counting_fn = counting_effect(&counter, |x: &i32| {
188///     if *x > 0 { Ok(*x) } else { Err("negative") }
189/// });
190///
191/// // Use counting_fn in traverse
192/// let result = pattern.traverse_result(counting_fn);
193///
194/// // Check how many times the function was called
195/// assert_eq!(counter.count(), expected_count);
196/// ```
197pub fn counting_effect<'a, V, W, E, F>(
198    counter: &'a EffectCounter,
199    f: F,
200) -> impl Fn(&V) -> Result<W, E> + 'a
201where
202    F: Fn(&V) -> Result<W, E> + 'a,
203    V: 'a,
204{
205    move |v| {
206        counter.increment();
207        f(v)
208    }
209}