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 || self.patterns().iter().any(|p| p.is_complex())
103    }
104
105    fn paths_with_captures(
106        &self,
107        cbor: &CBOR,
108    ) -> (Vec<Path>, std::collections::HashMap<String, Vec<Path>>) {
109        // For sequence patterns, the capture logic is handled by the
110        // VM when compiled by the main Pattern. When called directly,
111        // we use the basic implementation.
112        (self.paths(cbor), std::collections::HashMap::new())
113    }
114}
115
116impl std::fmt::Display for SequencePattern {
117    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
118        if self.patterns().is_empty() {
119            write!(f, "()")
120        } else {
121            let patterns_str: Vec<String> =
122                self.patterns().iter().map(|p| p.to_string()).collect();
123            write!(f, "{}", patterns_str.join(", "))
124        }
125    }
126}
127
128#[cfg(test)]
129mod tests {
130    use dcbor_parse::parse_dcbor_item;
131
132    use super::*;
133
134    #[test]
135    fn test_sequence_pattern_new() {
136        let patterns = vec![Pattern::text("first"), Pattern::text("second")];
137        let sequence = SequencePattern::new(patterns.clone());
138        assert_eq!(sequence.patterns(), &patterns);
139    }
140
141    #[test]
142    fn test_sequence_pattern_empty() {
143        let sequence = SequencePattern::new(vec![]);
144        assert!(sequence.is_empty());
145        assert_eq!(sequence.len(), 0);
146    }
147
148    #[test]
149    fn test_sequence_pattern_len() {
150        let patterns =
151            vec![Pattern::text("a"), Pattern::text("b"), Pattern::text("c")];
152        let sequence = SequencePattern::new(patterns);
153        assert!(!sequence.is_empty());
154        assert_eq!(sequence.len(), 3);
155    }
156
157    #[test]
158    fn test_sequence_pattern_display() {
159        let patterns = vec![
160            Pattern::text("first"),
161            Pattern::text("second"),
162            Pattern::text("third"),
163        ];
164        let sequence = SequencePattern::new(patterns);
165        let display = sequence.to_string();
166        assert!(display.contains("first"));
167        assert!(display.contains("second"));
168        assert!(display.contains("third"));
169        assert!(display.contains(", "));
170    }
171
172    #[test]
173    fn test_sequence_pattern_display_empty() {
174        let sequence = SequencePattern::new(vec![]);
175        assert_eq!(sequence.to_string(), "()");
176    }
177
178    #[test]
179    fn test_sequence_pattern_is_complex() {
180        // Empty sequence is not complex
181        let empty_sequence = SequencePattern::new(vec![]);
182        assert!(!empty_sequence.is_complex());
183
184        // Single simple pattern is not complex
185        let single_sequence = SequencePattern::new(vec![Pattern::text("test")]);
186        assert!(!single_sequence.is_complex());
187
188        // Multiple patterns are complex
189        let multi_sequence = SequencePattern::new(vec![
190            Pattern::text("first"),
191            Pattern::text("second"),
192        ]);
193        assert!(multi_sequence.is_complex());
194    }
195
196    #[test]
197    fn test_sequence_pattern_compile() {
198        let patterns = vec![Pattern::text("first"), Pattern::text("second")];
199        let sequence = SequencePattern::new(patterns);
200
201        let mut code = Vec::new();
202        let mut literals = Vec::new();
203        let mut captures = Vec::new();
204
205        sequence.compile(&mut code, &mut literals, &mut captures);
206
207        // Should compile both patterns sequentially
208        assert!(!code.is_empty());
209        // Should have two patterns in literals (one for each text pattern)
210        assert_eq!(literals.len(), 2);
211    }
212
213    #[test]
214    fn test_sequence_pattern_compile_empty() {
215        let sequence = SequencePattern::new(vec![]);
216
217        let mut code = Vec::new();
218        let mut literals = Vec::new();
219        let mut captures = Vec::new();
220
221        sequence.compile(&mut code, &mut literals, &mut captures);
222
223        // Empty sequence should not add any instructions
224        assert!(code.is_empty());
225    }
226
227    #[test]
228    fn test_sequence_pattern_collect_capture_names() {
229        let patterns = vec![
230            Pattern::capture("first", Pattern::text("a")),
231            Pattern::text("b"),
232            Pattern::capture("third", Pattern::text("c")),
233        ];
234        let sequence = SequencePattern::new(patterns);
235
236        let mut names = Vec::new();
237        sequence.collect_capture_names(&mut names);
238
239        assert_eq!(names.len(), 2);
240        assert!(names.contains(&"first".to_string()));
241        assert!(names.contains(&"third".to_string()));
242    }
243
244    #[test]
245    fn test_sequence_pattern_paths() {
246        let patterns = vec![Pattern::text("a"), Pattern::text("b")];
247        let sequence = SequencePattern::new(patterns);
248
249        let cbor = "test".to_cbor();
250        let paths = sequence.paths(&cbor);
251
252        // Sequence patterns return empty paths when used directly
253        assert!(paths.is_empty());
254    }
255
256    #[test]
257    fn test_sequence_pattern_with_array() {
258        // Test sequence pattern within an array context using parse_dcbor_item
259        let _array_cbor =
260            parse_dcbor_item(r#"["first", "second", "third"]"#).unwrap();
261
262        // Create a sequence pattern that matches the array elements
263        let sequence = SequencePattern::new(vec![
264            Pattern::text("first"),
265            Pattern::text("second"),
266            Pattern::text("third"),
267        ]);
268
269        // Verify the sequence pattern structure
270        assert_eq!(sequence.len(), 3);
271        assert!(!sequence.is_empty());
272        assert!(sequence.is_complex()); // Multiple patterns make it complex
273
274        // Test display format
275        let display = sequence.to_string();
276        assert!(display.contains("first"));
277        assert!(display.contains("second"));
278        assert!(display.contains("third"));
279        assert!(display.contains(", "));
280    }
281
282    #[test]
283    fn test_sequence_pattern_with_mixed_types() {
284        // Test sequence with different CBOR types
285        let _mixed_array =
286            parse_dcbor_item(r#"[42, "hello", true, null]"#).unwrap();
287
288        let sequence = SequencePattern::new(vec![
289            Pattern::number(42),
290            Pattern::text("hello"),
291            Pattern::bool(true),
292            Pattern::null(),
293        ]);
294
295        // Verify the sequence pattern properties
296        assert_eq!(sequence.len(), 4);
297        assert!(sequence.is_complex());
298
299        // Test compilation
300        let mut code = Vec::new();
301        let mut literals = Vec::new();
302        let mut captures = Vec::new();
303        sequence.compile(&mut code, &mut literals, &mut captures);
304
305        assert!(!code.is_empty());
306        assert_eq!(literals.len(), 4); // One literal for each pattern
307    }
308
309    #[test]
310    fn test_sequence_pattern_partial_match() {
311        // Test sequence that should match part of a larger array
312        let _large_array =
313            parse_dcbor_item(r#"["start", "middle1", "middle2", "end"]"#)
314                .unwrap();
315
316        // Create a sequence pattern for the middle elements
317        let sequence = SequencePattern::new(vec![
318            Pattern::text("middle1"),
319            Pattern::text("middle2"),
320        ]);
321
322        // Verify the sequence properties
323        assert_eq!(sequence.len(), 2);
324        assert!(!sequence.is_empty());
325        assert!(sequence.is_complex());
326
327        let display = sequence.to_string();
328        assert!(display.contains("middle1"));
329        assert!(display.contains("middle2"));
330        assert!(display.contains(", "));
331    }
332
333    #[test]
334    fn test_sequence_pattern_with_captures() {
335        // Test sequence pattern with capture groups
336        let sequence = SequencePattern::new(vec![
337            Pattern::capture("first_value", Pattern::text("hello")),
338            Pattern::capture("second_value", Pattern::number(42)),
339            Pattern::text("world"),
340        ]);
341
342        let mut names = Vec::new();
343        sequence.collect_capture_names(&mut names);
344
345        assert_eq!(names.len(), 2);
346        assert!(names.contains(&"first_value".to_string()));
347        assert!(names.contains(&"second_value".to_string()));
348
349        // Test the display format includes captures
350        let display = sequence.to_string();
351        assert!(display.contains("@first_value"));
352        assert!(display.contains("@second_value"));
353        assert!(display.contains(", "));
354    }
355
356    #[test]
357    fn test_sequence_pattern_with_simple_types() {
358        // Test sequence with various simple CBOR types
359        let _simple_array = parse_dcbor_item(r#"["text", 123, true]"#).unwrap();
360
361        let sequence = SequencePattern::new(vec![
362            Pattern::text("text"),
363            Pattern::number(123),
364            Pattern::bool(true),
365        ]);
366
367        // Verify sequence structure
368        assert_eq!(sequence.len(), 3);
369        assert!(sequence.is_complex());
370
371        // Test compilation
372        let mut code = Vec::new();
373        let mut literals = Vec::new();
374        let mut captures = Vec::new();
375        sequence.compile(&mut code, &mut literals, &mut captures);
376
377        assert!(!code.is_empty());
378        assert_eq!(literals.len(), 3); // One literal for each pattern
379    }
380
381    #[test]
382    fn test_sequence_pattern_with_byte_strings() {
383        // Test sequence with byte strings
384        let _bytes_array =
385            parse_dcbor_item(r#"[h'deadbeef', h'cafebabe', "text"]"#).unwrap();
386
387        let sequence = SequencePattern::new(vec![
388            Pattern::byte_string(hex::decode("deadbeef").unwrap()),
389            Pattern::byte_string(hex::decode("cafebabe").unwrap()),
390            Pattern::text("text"),
391        ]);
392
393        // Verify the sequence structure
394        assert_eq!(sequence.len(), 3);
395        assert!(sequence.is_complex());
396
397        // Check that each pattern in the sequence is correctly represented
398        let patterns = sequence.patterns();
399        assert_eq!(patterns.len(), 3);
400
401        // Test display formatting
402        let display = sequence.to_string();
403        assert!(display.contains(", "));
404    }
405}