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