dcbor_pattern/pattern/meta/
and_pattern.rs

1use dcbor::prelude::*;
2
3use crate::pattern::{Matcher, Path, Pattern, vm::Instr};
4
5/// A pattern that matches if all contained patterns match.
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct AndPattern(Vec<Pattern>);
8
9impl AndPattern {
10    /// Creates a new `AndPattern` with the given patterns.
11    pub fn new(patterns: Vec<Pattern>) -> Self { AndPattern(patterns) }
12
13    /// Returns the patterns contained in this AND pattern.
14    pub fn patterns(&self) -> &[Pattern] { &self.0 }
15}
16
17impl Matcher for AndPattern {
18    fn paths(&self, haystack: &CBOR) -> Vec<Path> {
19        if self
20            .patterns()
21            .iter()
22            .all(|pattern| pattern.matches(haystack))
23        {
24            vec![vec![haystack.clone()]]
25        } else {
26            vec![]
27        }
28    }
29
30    fn paths_with_captures(
31        &self,
32        haystack: &CBOR,
33    ) -> (Vec<Path>, std::collections::HashMap<String, Vec<Path>>) {
34        // For AND patterns, all patterns must match, and we merge captures
35        let mut all_captures = std::collections::HashMap::new();
36
37        for pattern in self.patterns() {
38            let (paths, captures) = pattern.paths_with_captures(haystack);
39            if paths.is_empty() {
40                // If any pattern fails to match, AND fails
41                return (vec![], std::collections::HashMap::new());
42            }
43
44            // Merge captures
45            for (name, capture_paths) in captures {
46                all_captures
47                    .entry(name)
48                    .or_insert_with(Vec::new)
49                    .extend(capture_paths);
50            }
51        }
52
53        // If all patterns matched, return the basic path and merged captures
54        (vec![vec![haystack.clone()]], all_captures)
55    }
56
57    /// Compile into byte-code (AND = all must match).
58    fn compile(
59        &self,
60        code: &mut Vec<Instr>,
61        lits: &mut Vec<Pattern>,
62        captures: &mut Vec<String>,
63    ) {
64        // Each pattern must match at this position
65        for pattern in self.patterns() {
66            pattern.compile(code, lits, captures);
67        }
68    }
69
70    fn collect_capture_names(&self, names: &mut Vec<String>) {
71        // Collect captures from all patterns
72        for pattern in self.patterns() {
73            pattern.collect_capture_names(names);
74        }
75    }
76
77    fn is_complex(&self) -> bool {
78        // The pattern is complex if it contains more than one pattern, or if
79        // the one pattern is complex itself.
80        self.patterns().len() > 1
81            || self.patterns().iter().any(|p| p.is_complex())
82    }
83}
84
85impl std::fmt::Display for AndPattern {
86    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87        write!(
88            f,
89            "{}",
90            self.patterns()
91                .iter()
92                .map(|p| p.to_string())
93                .collect::<Vec<_>>()
94                .join(" & ")
95        )
96    }
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102
103    #[test]
104    fn test_and_pattern_display() {
105        let pattern1 = Pattern::number_greater_than(5);
106        let pattern2 = Pattern::number_less_than(10);
107        let and_pattern = AndPattern::new(vec![pattern1, pattern2]);
108        assert_eq!(and_pattern.to_string(), ">5 & <10");
109    }
110
111    #[test]
112    fn test_and_pattern_matches_when_all_patterns_match() {
113        let pattern = AndPattern::new(vec![
114            Pattern::number_greater_than(5),
115            Pattern::number_less_than(10),
116        ]);
117
118        let cbor_7 = CBOR::from(7);
119        assert!(pattern.matches(&cbor_7));
120    }
121
122    #[test]
123    fn test_and_pattern_fails_when_any_pattern_fails() {
124        let pattern = AndPattern::new(vec![
125            Pattern::number_greater_than(5),
126            Pattern::number_less_than(10),
127        ]);
128
129        let cbor_12 = CBOR::from(12); // > 10, so second pattern fails
130        assert!(!pattern.matches(&cbor_12));
131
132        let cbor_3 = CBOR::from(3); // < 5, so first pattern fails
133        assert!(!pattern.matches(&cbor_3));
134    }
135
136    #[test]
137    fn test_and_pattern_empty_returns_true() {
138        let pattern = AndPattern::new(vec![]);
139        let cbor = CBOR::from("any value");
140        assert!(pattern.matches(&cbor));
141    }
142}