dcbor_pattern/pattern/meta/
sequence_pattern.rs

1use dcbor::prelude::*;
2
3use crate::pattern::{Matcher, Path, Pattern, vm::Instr};
4
5/// A pattern that matches a sequence of patterns in order.
6///
7/// This pattern is used to match multiple patterns consecutively,
8/// which is particularly useful for array elements or other sequential
9/// data structures.
10///
11/// # Examples
12///
13/// ```
14/// use dcbor_pattern::Pattern;
15///
16/// // Match a sequence of three specific text values
17/// let pattern = Pattern::sequence(vec![
18///     Pattern::text("first"),
19///     Pattern::text("second"),
20///     Pattern::text("third"),
21/// ]);
22/// ```
23#[derive(Debug, Clone, PartialEq, Eq)]
24pub struct SequencePattern(Vec<Pattern>);
25
26impl SequencePattern {
27    /// Creates a new sequence pattern with the given patterns.
28    pub fn new(patterns: Vec<Pattern>) -> Self { Self(patterns) }
29
30    /// Returns a reference to the patterns in this sequence.
31    pub fn patterns(&self) -> &[Pattern] { &self.0 }
32
33    /// Returns true if this sequence is empty.
34    pub fn is_empty(&self) -> bool { self.patterns().is_empty() }
35
36    /// Returns the number of patterns in this sequence.
37    pub fn len(&self) -> usize { self.patterns().len() }
38}
39
40impl Matcher for SequencePattern {
41    fn paths(&self, _haystack: &CBOR) -> Vec<Path> {
42        // For a sequence pattern, we need to find paths where all patterns
43        // match consecutively. This is primarily used within array patterns
44        // or other structural contexts.
45        //
46        // Since sequence patterns are typically used as part of other patterns
47        // (like arrays), and not as standalone matchers against arbitrary CBOR,
48        // we return empty paths when used directly.
49        //
50        // The actual sequence matching logic is handled by the VM through
51        // SequenceStart and SequenceNext instructions.
52        vec![]
53    }
54
55    fn compile(
56        &self,
57        code: &mut Vec<Instr>,
58        literals: &mut Vec<Pattern>,
59        captures: &mut Vec<String>,
60    ) {
61        if self.patterns().is_empty() {
62            // Empty sequence always matches
63            return;
64        }
65
66        if self.patterns().len() == 1 {
67            // Single pattern, just compile it directly
68            self.patterns()[0].compile(code, literals, captures);
69            return;
70        }
71
72        // Multiple patterns in sequence - use ExtendSequence and
73        // CombineSequence to implement proper sequence semantics in the
74        // VM
75        for (i, pattern) in self.patterns().iter().enumerate() {
76            if i > 0 {
77                // For patterns after the first, extend the sequence to move to
78                // next element
79                code.push(Instr::ExtendSequence);
80            }
81
82            // Compile the pattern to match current element
83            pattern.compile(code, literals, captures);
84
85            if i > 0 {
86                // Combine the sequence after matching (except for the first
87                // pattern)
88                code.push(Instr::CombineSequence);
89            }
90        }
91    }
92
93    fn collect_capture_names(&self, names: &mut Vec<String>) {
94        for pattern in self.patterns() {
95            pattern.collect_capture_names(names);
96        }
97    }
98
99    fn is_complex(&self) -> bool {
100        // A sequence is complex if it contains multiple patterns or
101        // if any of its patterns are complex
102        self.patterns().len() > 1
103            || self.patterns().iter().any(|p| p.is_complex())
104    }
105
106    fn paths_with_captures(
107        &self,
108        cbor: &CBOR,
109    ) -> (Vec<Path>, std::collections::HashMap<String, Vec<Path>>) {
110        // For sequence patterns, the capture logic is handled by the
111        // VM when compiled by the main Pattern. When called directly,
112        // we use the basic implementation.
113        (self.paths(cbor), std::collections::HashMap::new())
114    }
115}
116
117impl std::fmt::Display for SequencePattern {
118    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119        if self.patterns().is_empty() {
120            write!(f, "()")
121        } else {
122            let patterns_str: Vec<String> =
123                self.patterns().iter().map(|p| p.to_string()).collect();
124            write!(f, "{}", patterns_str.join(", "))
125        }
126    }
127}
128
129#[cfg(test)]
130mod tests {
131    use dcbor_parse::parse_dcbor_item;
132
133    use super::*;
134
135    #[test]
136    fn test_sequence_pattern_new() {
137        let patterns = vec![Pattern::text("first"), Pattern::text("second")];
138        let sequence = SequencePattern::new(patterns.clone());
139        assert_eq!(sequence.patterns(), &patterns);
140    }
141
142    #[test]
143    fn test_sequence_pattern_empty() {
144        let sequence = SequencePattern::new(vec![]);
145        assert!(sequence.is_empty());
146        assert_eq!(sequence.len(), 0);
147    }
148
149    #[test]
150    fn test_sequence_pattern_len() {
151        let patterns =
152            vec![Pattern::text("a"), Pattern::text("b"), Pattern::text("c")];
153        let sequence = SequencePattern::new(patterns);
154        assert!(!sequence.is_empty());
155        assert_eq!(sequence.len(), 3);
156    }
157
158    #[test]
159    fn test_sequence_pattern_display() {
160        let patterns = vec![
161            Pattern::text("first"),
162            Pattern::text("second"),
163            Pattern::text("third"),
164        ];
165        let sequence = SequencePattern::new(patterns);
166        let display = sequence.to_string();
167        assert!(display.contains("first"));
168        assert!(display.contains("second"));
169        assert!(display.contains("third"));
170        assert!(display.contains(", "));
171    }
172
173    #[test]
174    fn test_sequence_pattern_display_empty() {
175        let sequence = SequencePattern::new(vec![]);
176        assert_eq!(sequence.to_string(), "()");
177    }
178
179    #[test]
180    fn test_sequence_pattern_is_complex() {
181        // Empty sequence is not complex
182        let empty_sequence = SequencePattern::new(vec![]);
183        assert!(!empty_sequence.is_complex());
184
185        // Single simple pattern is not complex
186        let single_sequence = SequencePattern::new(vec![Pattern::text("test")]);
187        assert!(!single_sequence.is_complex());
188
189        // Multiple patterns are complex
190        let multi_sequence = SequencePattern::new(vec![
191            Pattern::text("first"),
192            Pattern::text("second"),
193        ]);
194        assert!(multi_sequence.is_complex());
195    }
196
197    #[test]
198    fn test_sequence_pattern_compile() {
199        let patterns = vec![Pattern::text("first"), Pattern::text("second")];
200        let sequence = SequencePattern::new(patterns);
201
202        let mut code = Vec::new();
203        let mut literals = Vec::new();
204        let mut captures = Vec::new();
205
206        sequence.compile(&mut code, &mut literals, &mut captures);
207
208        // Should compile both patterns sequentially
209        assert!(!code.is_empty());
210        // Should have two patterns in literals (one for each text pattern)
211        assert_eq!(literals.len(), 2);
212    }
213
214    #[test]
215    fn test_sequence_pattern_compile_empty() {
216        let sequence = SequencePattern::new(vec![]);
217
218        let mut code = Vec::new();
219        let mut literals = Vec::new();
220        let mut captures = Vec::new();
221
222        sequence.compile(&mut code, &mut literals, &mut captures);
223
224        // Empty sequence should not add any instructions
225        assert!(code.is_empty());
226    }
227
228    #[test]
229    fn test_sequence_pattern_collect_capture_names() {
230        let patterns = vec![
231            Pattern::capture("first", Pattern::text("a")),
232            Pattern::text("b"),
233            Pattern::capture("third", Pattern::text("c")),
234        ];
235        let sequence = SequencePattern::new(patterns);
236
237        let mut names = Vec::new();
238        sequence.collect_capture_names(&mut names);
239
240        assert_eq!(names.len(), 2);
241        assert!(names.contains(&"first".to_string()));
242        assert!(names.contains(&"third".to_string()));
243    }
244
245    #[test]
246    fn test_sequence_pattern_paths() {
247        let patterns = vec![Pattern::text("a"), Pattern::text("b")];
248        let sequence = SequencePattern::new(patterns);
249
250        let cbor = "test".to_cbor();
251        let paths = sequence.paths(&cbor);
252
253        // Sequence patterns return empty paths when used directly
254        assert!(paths.is_empty());
255    }
256
257    #[test]
258    fn test_sequence_pattern_with_array() {
259        // Test sequence pattern within an array context using parse_dcbor_item
260        let _array_cbor =
261            parse_dcbor_item(r#"["first", "second", "third"]"#).unwrap();
262
263        // Create a sequence pattern that matches the array elements
264        let sequence = SequencePattern::new(vec![
265            Pattern::text("first"),
266            Pattern::text("second"),
267            Pattern::text("third"),
268        ]);
269
270        // Verify the sequence pattern structure
271        assert_eq!(sequence.len(), 3);
272        assert!(!sequence.is_empty());
273        assert!(sequence.is_complex()); // Multiple patterns make it complex
274
275        // Test display format
276        let display = sequence.to_string();
277        assert!(display.contains("first"));
278        assert!(display.contains("second"));
279        assert!(display.contains("third"));
280        assert!(display.contains(", "));
281    }
282
283    #[test]
284    fn test_sequence_pattern_with_mixed_types() {
285        // Test sequence with different CBOR types
286        let _mixed_array =
287            parse_dcbor_item(r#"[42, "hello", true, null]"#).unwrap();
288
289        let sequence = SequencePattern::new(vec![
290            Pattern::number(42),
291            Pattern::text("hello"),
292            Pattern::bool(true),
293            Pattern::null(),
294        ]);
295
296        // Verify the sequence pattern properties
297        assert_eq!(sequence.len(), 4);
298        assert!(sequence.is_complex());
299
300        // Test compilation
301        let mut code = Vec::new();
302        let mut literals = Vec::new();
303        let mut captures = Vec::new();
304        sequence.compile(&mut code, &mut literals, &mut captures);
305
306        assert!(!code.is_empty());
307        assert_eq!(literals.len(), 4); // One literal for each pattern
308    }
309
310    #[test]
311    fn test_sequence_pattern_partial_match() {
312        // Test sequence that should match part of a larger array
313        let _large_array =
314            parse_dcbor_item(r#"["start", "middle1", "middle2", "end"]"#)
315                .unwrap();
316
317        // Create a sequence pattern for the middle elements
318        let sequence = SequencePattern::new(vec![
319            Pattern::text("middle1"),
320            Pattern::text("middle2"),
321        ]);
322
323        // Verify the sequence properties
324        assert_eq!(sequence.len(), 2);
325        assert!(!sequence.is_empty());
326        assert!(sequence.is_complex());
327
328        let display = sequence.to_string();
329        assert!(display.contains("middle1"));
330        assert!(display.contains("middle2"));
331        assert!(display.contains(", "));
332    }
333
334    #[test]
335    fn test_sequence_pattern_with_captures() {
336        // Test sequence pattern with capture groups
337        let sequence = SequencePattern::new(vec![
338            Pattern::capture("first_value", Pattern::text("hello")),
339            Pattern::capture("second_value", Pattern::number(42)),
340            Pattern::text("world"),
341        ]);
342
343        let mut names = Vec::new();
344        sequence.collect_capture_names(&mut names);
345
346        assert_eq!(names.len(), 2);
347        assert!(names.contains(&"first_value".to_string()));
348        assert!(names.contains(&"second_value".to_string()));
349
350        // Test the display format includes captures
351        let display = sequence.to_string();
352        assert!(display.contains("@first_value"));
353        assert!(display.contains("@second_value"));
354        assert!(display.contains(", "));
355    }
356
357    #[test]
358    fn test_sequence_pattern_with_simple_types() {
359        // Test sequence with various simple CBOR types
360        let _simple_array = parse_dcbor_item(r#"["text", 123, true]"#).unwrap();
361
362        let sequence = SequencePattern::new(vec![
363            Pattern::text("text"),
364            Pattern::number(123),
365            Pattern::bool(true),
366        ]);
367
368        // Verify sequence structure
369        assert_eq!(sequence.len(), 3);
370        assert!(sequence.is_complex());
371
372        // Test compilation
373        let mut code = Vec::new();
374        let mut literals = Vec::new();
375        let mut captures = Vec::new();
376        sequence.compile(&mut code, &mut literals, &mut captures);
377
378        assert!(!code.is_empty());
379        assert_eq!(literals.len(), 3); // One literal for each pattern
380    }
381
382    #[test]
383    fn test_sequence_pattern_with_byte_strings() {
384        // Test sequence with byte strings
385        let _bytes_array =
386            parse_dcbor_item(r#"[h'deadbeef', h'cafebabe', "text"]"#).unwrap();
387
388        let sequence = SequencePattern::new(vec![
389            Pattern::byte_string(hex::decode("deadbeef").unwrap()),
390            Pattern::byte_string(hex::decode("cafebabe").unwrap()),
391            Pattern::text("text"),
392        ]);
393
394        // Verify the sequence structure
395        assert_eq!(sequence.len(), 3);
396        assert!(sequence.is_complex());
397
398        // Check that each pattern in the sequence is correctly represented
399        let patterns = sequence.patterns();
400        assert_eq!(patterns.len(), 3);
401
402        // Test display formatting
403        let display = sequence.to_string();
404        assert!(display.contains(", "));
405    }
406}