bc_envelope_pattern/pattern/
pattern_impl.rs

1//! # Pattern
2//!
3//! ## Types of patterns
4//!
5//! The patterns in this crate are divided into three main categories:
6//!
7//! - **Leaf Patterns**: These patterns match specific CBOR values, such as
8//!   booleans, numbers, text, byte strings, dates, and more. They are the most
9//!   basic building blocks of the pattern system.
10//! - **Structure Patterns**: These patterns are used to match the structure of
11//!   envelopes. They can match specific structures, such as assertions,
12//!   subjects, predicates, objects, and more.
13//! - **Meta Patterns**: These patterns are used to combine and modify other
14//!   patterns. They allow you to create complex matching logic by combining
15//!   simpler patterns.
16//!
17//! ## On the difference between *regular* and *binary* regexes
18//!
19//! The text-based patterns in this crate are designed to work with the standard
20//! Rust `str` type, which is a UTF-8 encoded string. However, there are some
21//! patterns that need to work with raw bytes, such as when dealing with CBOR
22//! byte strings or envelope digests. These patterns take "binary regexes".
23//! There are some operational differences between the two types of regexes,
24//! which are summarized in the table below.
25//!
26//! | concern                           | Text Regex                                                                                                                                                                           | Binary Regex                                                                                                                                                                             |
27//! | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
28//! | **Haystack & captures**           | Works on `&str` / `String`; captures are `&str`.                                                                                                                                     | Works on `&[u8]` / `Vec<u8>`; captures are `&[u8]`. That means you can safely search data that is **not valid UTF-8**. ([docs.rs][1])                                                    |
29//! | **Fundamental matching unit**     | By default the engine iterates over **Unicode scalar values** (code-points). `.` matches an entire code-point, even if it takes multiple bytes in UTF-8. ([docs.rs][2])              | When the `u` flag is disabled the engine iterates **byte-by-byte**; `.` then matches exactly one byte. (With `u` on, it behaves like the text engine.) ([docs.rs][2])                    |
30//! | **Turning Unicode off (`(?-u)`)** | Allowed **only** when the resulting pattern still can’t match invalid UTF-8—e.g. `(?-u:\w)` is OK, `(?-u:\xFF)` is *rejected*. This preserves Rust’s `str` invariant. ([docs.rs][2]) | You can disable `u` anywhere, even if that lets the regex match arbitrary byte values such as `\x00` or `\xFF`. This is the big operational freedom “binary” regexes add. ([docs.rs][1]) |
31//! | **Empty-string matches**          | Guaranteed **not** to split a UTF-8 code-point; you’ll see at most one empty match between code-points.                                                                              | May report an empty match **between every byte** (because bytes are the atom). ([docs.rs][2])                                                                                            |
32//! | **Typical use-cases**             | Validating/processing normal text, log files, config files…                                                                                                                          | Packet inspection, parsing binary protocols, scanning blobs that may embed non-UTF-8 data, digging NUL-terminated C strings in memory dumps, etc.                                        |
33//!
34//! ### Example
35//!
36//! A binary regex matching any byte string ending with h'010203':
37//!
38//! ```text
39//! (?s-u).*\x01\x02\x03$
40//! ```
41//!
42//! Note:
43//!
44//! - The `(?s-u)` enables the "dot matches newline" mode, allowing `.` to match
45//!   across newlines, and disables Unicode mode, allowing `.` to match any byte
46//!   value.
47//! - The hexadecimal bytes values must each be prefixed with `\x`.
48//!
49//! ### References
50//!
51//! - <https://docs.rs/regex/latest/regex/bytes/index.html> "regex::bytes -
52//!   Rust"
53//! - <https://docs.rs/regex/latest/regex/> "regex - Rust"
54
55use std::{
56    cell::RefCell,
57    collections::HashMap,
58    ops::{RangeBounds, RangeInclusive},
59};
60
61use bc_envelope::prelude::*;
62use known_values::KnownValue;
63
64use super::{
65    Matcher, Path,
66    leaf::{
67        ArrayPattern, BoolPattern, ByteStringPattern, DatePattern,
68        KnownValuePattern, LeafPattern, MapPattern, NullPattern, NumberPattern,
69        TextPattern,
70    },
71    meta::{
72        AndPattern, AnyPattern, CapturePattern, GroupPattern, MetaPattern, NotPattern,
73        OrPattern, SearchPattern, TraversePattern,
74    },
75    structure::{
76        AssertionsPattern, DigestPattern, LeafStructurePattern, NodePattern,
77        ObjectPattern, ObscuredPattern, PredicatePattern, StructurePattern,
78        SubjectPattern, WrappedPattern,
79    },
80    vm,
81};
82use crate::{
83    DCBORPattern, Quantifier, Reluctance,
84    pattern::{
85        leaf::CBORPattern,
86        vm::Instr,
87    },
88};
89
90/// The main pattern type used for matching envelopes.
91#[derive(Debug, Clone, Hash, PartialEq, Eq)]
92pub enum Pattern {
93    /// Leaf patterns for matching CBOR values.
94    Leaf(LeafPattern),
95
96    /// Structure patterns for matching envelope elements.
97    Structure(StructurePattern),
98
99    /// Meta-patterns for combining and modifying other patterns.
100    Meta(MetaPattern),
101}
102
103impl Matcher for Pattern {
104    fn paths_with_captures(
105        &self,
106        haystack: &Envelope,
107    ) -> (Vec<Path>, HashMap<String, Vec<Path>>) {
108        let results = self.vm_run(haystack);
109        let mut paths = Vec::new();
110        let mut captures: HashMap<String, Vec<Path>> = HashMap::new();
111        for (p, caps) in results {
112            paths.push(p);
113            for (name, mut vals) in caps {
114                captures.entry(name).or_default().append(&mut vals);
115            }
116        }
117        (paths, captures)
118    }
119
120    fn is_complex(&self) -> bool {
121        match self {
122            Pattern::Leaf(leaf) => leaf.is_complex(),
123            Pattern::Structure(structure) => structure.is_complex(),
124            Pattern::Meta(meta) => meta.is_complex(),
125        }
126    }
127}
128
129// region: Leaf Patterns
130//
131//
132
133impl Pattern {
134    /// Creates a new `Pattern` that matches any CBOR value.
135    pub fn any_cbor() -> Self {
136        Pattern::Leaf(LeafPattern::Cbor(CBORPattern::any()))
137    }
138
139    /// Creates a new `Pattern` that matches a specific CBOR value.
140    pub fn cbor(cbor: impl CBOREncodable) -> Self {
141        Pattern::Leaf(LeafPattern::Cbor(CBORPattern::value(cbor)))
142    }
143
144    /// Creates a new `Pattern` that matches CBOR values using dcbor-pattern
145    /// expressions.
146    pub fn cbor_pattern(pattern: DCBORPattern) -> Self {
147        Pattern::Leaf(LeafPattern::Cbor(CBORPattern::pattern(pattern)))
148    }
149}
150
151impl Pattern {
152    /// Creates a new `Pattern` that matches any boolean value.
153    pub fn any_bool() -> Self {
154        Pattern::Leaf(LeafPattern::Bool(BoolPattern::any()))
155    }
156
157    /// Creates a new `Pattern` that matches a specific boolean value.
158    pub fn bool(b: bool) -> Self {
159        Pattern::Leaf(LeafPattern::Bool(BoolPattern::value(b)))
160    }
161}
162
163impl Pattern {
164    /// Creates a new `Pattern` that matches any text value.
165    pub fn any_text() -> Self {
166        Pattern::Leaf(LeafPattern::Text(TextPattern::any()))
167    }
168
169    /// Creates a new `Pattern` that matches a specific text value.
170    pub fn text<T: Into<String>>(value: T) -> Self {
171        Pattern::Leaf(LeafPattern::Text(TextPattern::value(value)))
172    }
173
174    /// Creates a new `Pattern` that matches text values that match the given
175    /// regular expression.
176    pub fn text_regex(regex: regex::Regex) -> Self {
177        Pattern::Leaf(LeafPattern::Text(TextPattern::regex(regex)))
178    }
179}
180
181impl Pattern {
182    /// Creates a new `Pattern` that matches any Date (CBOR tag 1) value.
183    pub fn any_date() -> Self {
184        Pattern::Leaf(LeafPattern::Date(DatePattern::any()))
185    }
186
187    /// Creates a new `Pattern` that matches a specific Date (CBOR tag 1) value.
188    pub fn date(date: Date) -> Self {
189        Pattern::Leaf(LeafPattern::Date(DatePattern::value(date)))
190    }
191
192    /// Creates a new `Pattern` that matches Date (CBOR tag 1) values within a
193    /// specified range (inclusive).
194    pub fn date_range(range: RangeInclusive<Date>) -> Self {
195        Pattern::Leaf(LeafPattern::Date(DatePattern::range(range)))
196    }
197
198    /// Creates a new `Pattern` that matches Date (CBOR tag 1) values that are
199    /// on or after the specified date.
200    pub fn date_earliest(date: Date) -> Self {
201        Pattern::Leaf(LeafPattern::Date(DatePattern::earliest(date)))
202    }
203
204    /// Creates a new `Pattern` that matches Date (CBOR tag 1) values that are
205    /// on or before the specified date.
206    pub fn date_latest(date: Date) -> Self {
207        Pattern::Leaf(LeafPattern::Date(DatePattern::latest(date)))
208    }
209
210    /// Creates a new `Pattern` that matches Date (CBOR tag 1) values by their
211    /// ISO-8601 string representation.
212    pub fn date_iso8601(iso_string: impl Into<String>) -> Self {
213        Pattern::Leaf(LeafPattern::Date(DatePattern::string(iso_string)))
214    }
215
216    /// Creates a new `Pattern` that matches Date (CBOR tag 1) values whose
217    /// ISO-8601 string representation matches the given regular expression.
218    pub fn date_regex(regex: regex::Regex) -> Self {
219        Pattern::Leaf(LeafPattern::Date(DatePattern::regex(regex)))
220    }
221}
222
223impl Pattern {
224    /// Creates a new `Pattern` that matches any number value.
225    pub fn any_number() -> Self {
226        Pattern::Leaf(LeafPattern::Number(NumberPattern::any()))
227    }
228
229    /// Creates a new `Pattern` that matches a specific number value.
230    pub fn number<T: Into<f64>>(value: T) -> Self {
231        Pattern::Leaf(LeafPattern::Number(NumberPattern::exact(value)))
232    }
233
234    /// Creates a new `Pattern` that matches number values within a specified
235    /// range (inclusive).
236    pub fn number_range<A: Into<f64> + Copy>(range: RangeInclusive<A>) -> Self {
237        Pattern::Leaf(LeafPattern::Number(NumberPattern::range(range)))
238    }
239
240    /// Creates a new `Pattern` that matches number values that are greater than
241    /// the specified value.
242    pub fn number_greater_than<T: Into<f64>>(value: T) -> Self {
243        Pattern::Leaf(LeafPattern::Number(NumberPattern::greater_than(value)))
244    }
245
246    /// Creates a new `Pattern` that matches number values that are greater than
247    /// or equal to the specified value.
248    pub fn number_greater_than_or_equal<T: Into<f64>>(value: T) -> Self {
249        Pattern::Leaf(LeafPattern::Number(
250            NumberPattern::greater_than_or_equal(value),
251        ))
252    }
253
254    /// Creates a new `Pattern` that matches number values that are less than
255    /// the specified value.
256    pub fn number_less_than<T: Into<f64>>(value: T) -> Self {
257        Pattern::Leaf(LeafPattern::Number(NumberPattern::less_than(value)))
258    }
259
260    /// Creates a new `Pattern` that matches number values that are less than or
261    /// equal to the specified value.
262    pub fn number_less_than_or_equal<T: Into<f64>>(value: T) -> Self {
263        Pattern::Leaf(LeafPattern::Number(NumberPattern::less_than_or_equal(
264            value,
265        )))
266    }
267
268    /// Creates a new `Pattern` that matches number values that are NaN (Not a
269    /// Number).
270    pub fn number_nan() -> Self {
271        Pattern::Leaf(LeafPattern::Number(NumberPattern::nan()))
272    }
273}
274
275impl Pattern {
276    /// Creates a new `Pattern` that matches any byte string value.
277    pub fn any_byte_string() -> Self {
278        Pattern::Leaf(LeafPattern::ByteString(ByteStringPattern::any()))
279    }
280
281    /// Creates a new `Pattern` that matches a specific byte string value.
282    pub fn byte_string(value: impl AsRef<[u8]>) -> Self {
283        Pattern::Leaf(LeafPattern::ByteString(ByteStringPattern::value(value)))
284    }
285
286    /// Creates a new `Pattern` that matches byte string values that match the
287    /// given binary regular expression.
288    pub fn byte_string_binary_regex(regex: regex::bytes::Regex) -> Self {
289        Pattern::Leaf(LeafPattern::ByteString(ByteStringPattern::regex(regex)))
290    }
291}
292
293impl Pattern {
294    pub fn any_known_value() -> Self {
295        Pattern::Leaf(LeafPattern::KnownValue(KnownValuePattern::any()))
296    }
297
298    pub fn known_value(value: KnownValue) -> Self {
299        Pattern::Leaf(LeafPattern::KnownValue(KnownValuePattern::value(value)))
300    }
301
302    pub fn known_value_named<T: Into<String>>(name: T) -> Self {
303        Pattern::Leaf(LeafPattern::KnownValue(KnownValuePattern::named(name)))
304    }
305
306    pub fn known_value_regex(regex: regex::Regex) -> Self {
307        Pattern::Leaf(LeafPattern::KnownValue(KnownValuePattern::regex(regex)))
308    }
309
310    pub fn unit() -> Self {
311        Self::known_value(known_values::UNIT)
312    }
313}
314
315impl Pattern {
316    pub fn any_array() -> Self {
317        Pattern::Leaf(LeafPattern::Array(ArrayPattern::any()))
318    }
319
320    pub fn array_with_range(interval: impl RangeBounds<usize>) -> Self {
321        Pattern::Leaf(LeafPattern::Array(ArrayPattern::interval(interval)))
322    }
323
324    pub fn array_with_count(count: usize) -> Self {
325        Pattern::Leaf(LeafPattern::Array(ArrayPattern::count(count)))
326    }
327
328    /// Creates an array pattern from a dcbor-pattern.
329    /// This is used internally by the parser to delegate to dcbor-pattern.
330    pub fn array_from_dcbor_pattern(pattern: DCBORPattern) -> Self {
331        Pattern::Leaf(LeafPattern::Array(ArrayPattern::from_dcbor_pattern(
332            pattern,
333        )))
334    }
335}
336
337impl Pattern {
338    pub fn any_map() -> Self {
339        Pattern::Leaf(LeafPattern::Map(MapPattern::any()))
340    }
341
342    pub fn map_with_range(interval: impl RangeBounds<usize>) -> Self {
343        Pattern::Leaf(LeafPattern::Map(MapPattern::interval(interval)))
344    }
345
346    pub fn map_with_count(count: usize) -> Self {
347        Pattern::Leaf(LeafPattern::Map(MapPattern::interval(count..=count)))
348    }
349}
350
351impl Pattern {
352    pub fn null() -> Self {
353        Pattern::Leaf(LeafPattern::Null(NullPattern))
354    }
355}
356
357impl Pattern {
358    /// Creates a new `Pattern` that matches any tagged value.
359    /// This is a proxy to dcbor-pattern's tagged functionality.
360    pub fn any_tag() -> Self {
361        Pattern::Leaf(crate::pattern::leaf::LeafPattern::Tag(
362            crate::pattern::leaf::TaggedPattern::any(),
363        ))
364    }
365
366    /// Creates a new `Pattern` that matches a specific tagged value with any
367    /// content. This is a proxy to dcbor-pattern's tagged functionality.
368    pub fn tagged(tag: impl Into<Tag>, pattern: DCBORPattern) -> Self {
369        Pattern::Leaf(crate::pattern::leaf::LeafPattern::Tag(
370            crate::pattern::leaf::TaggedPattern::with_tag(tag, pattern),
371        ))
372    }
373
374    /// Creates a new `Pattern` that matches a tagged value with specific tag
375    /// name and any content. This is a proxy to dcbor-pattern's tagged
376    /// functionality.
377    pub fn tagged_name(name: impl Into<String>, pattern: DCBORPattern) -> Self {
378        Pattern::Leaf(crate::pattern::leaf::LeafPattern::Tag(
379            crate::pattern::leaf::TaggedPattern::with_name(
380                name.into(),
381                pattern,
382            ),
383        ))
384    }
385
386    /// Creates a new `Pattern` that matches a tagged value with tag name
387    /// matching regex and any content. This is a proxy to dcbor-pattern's
388    /// tagged functionality.
389    pub fn tagged_regex(regex: regex::Regex, pattern: DCBORPattern) -> Self {
390        Pattern::Leaf(crate::pattern::leaf::LeafPattern::Tag(
391            crate::pattern::leaf::TaggedPattern::with_regex(regex, pattern),
392        ))
393    }
394
395    /// Creates a new `Pattern` that matches a tagged value from a
396    /// dcbor_pattern::TaggedPattern. This is an internal helper for the
397    /// parser.
398    pub(crate) fn tagged_from_dcbor_pattern(
399        tagged_pattern: dcbor_pattern::TaggedPattern,
400    ) -> Self {
401        Pattern::Leaf(crate::pattern::leaf::LeafPattern::Tag(
402            crate::pattern::leaf::TaggedPattern::from_dcbor_pattern(
403                tagged_pattern,
404            ),
405        ))
406    }
407}
408
409//
410//
411// endregion
412
413// region: Structure Patterns
414//
415//
416
417impl Pattern {
418    pub fn leaf() -> Self {
419        Pattern::Structure(StructurePattern::Leaf(LeafStructurePattern::new()))
420    }
421
422    pub fn any_assertion() -> Self {
423        Pattern::Structure(StructurePattern::Assertions(
424            AssertionsPattern::any(),
425        ))
426    }
427
428    pub fn assertion_with_predicate(pattern: Pattern) -> Self {
429        Pattern::Structure(StructurePattern::Assertions(
430            AssertionsPattern::with_predicate(pattern),
431        ))
432    }
433
434    pub fn assertion_with_object(pattern: Pattern) -> Self {
435        Pattern::Structure(StructurePattern::Assertions(
436            AssertionsPattern::with_object(pattern),
437        ))
438    }
439}
440
441impl Pattern {
442    pub fn any_subject() -> Self {
443        Pattern::Structure(StructurePattern::Subject(SubjectPattern::any()))
444    }
445
446    pub fn subject(pattern: Pattern) -> Self {
447        Pattern::Structure(StructurePattern::Subject(SubjectPattern::pattern(
448            pattern,
449        )))
450    }
451}
452
453impl Pattern {
454    pub fn any_predicate() -> Self {
455        Pattern::Structure(StructurePattern::Predicate(PredicatePattern::any()))
456    }
457
458    pub fn predicate(pattern: Pattern) -> Self {
459        Pattern::Structure(StructurePattern::Predicate(
460            PredicatePattern::pattern(pattern),
461        ))
462    }
463
464    pub fn any_object() -> Self {
465        Pattern::Structure(StructurePattern::Object(ObjectPattern::any()))
466    }
467
468    pub fn object(pattern: Pattern) -> Self {
469        Pattern::Structure(StructurePattern::Object(ObjectPattern::pattern(
470            pattern,
471        )))
472    }
473}
474
475impl Pattern {
476    pub fn digest(digest: bc_components::Digest) -> Self {
477        Pattern::Structure(StructurePattern::Digest(DigestPattern::digest(
478            digest,
479        )))
480    }
481
482    pub fn digest_prefix(prefix: impl AsRef<[u8]>) -> Self {
483        Pattern::Structure(StructurePattern::Digest(DigestPattern::prefix(
484            prefix,
485        )))
486    }
487
488    pub fn digest_binary_regex(regex: regex::bytes::Regex) -> Self {
489        Pattern::Structure(StructurePattern::Digest(
490            DigestPattern::binary_regex(regex),
491        ))
492    }
493
494    pub fn any_node() -> Self {
495        Pattern::Structure(StructurePattern::Node(NodePattern::any()))
496    }
497
498    pub fn node_with_assertions_range(range: impl RangeBounds<usize>) -> Self {
499        Pattern::Structure(StructurePattern::Node(NodePattern::interval(range)))
500    }
501
502    pub fn node_with_assertions_count(count: usize) -> Self {
503        Pattern::Structure(StructurePattern::Node(NodePattern::interval(
504            count..=count,
505        )))
506    }
507
508    pub fn obscured() -> Self {
509        Pattern::Structure(StructurePattern::Obscured(ObscuredPattern::any()))
510    }
511
512    pub fn elided() -> Self {
513        Pattern::Structure(
514            StructurePattern::Obscured(ObscuredPattern::elided()),
515        )
516    }
517
518    pub fn encrypted() -> Self {
519        Pattern::Structure(StructurePattern::Obscured(
520            ObscuredPattern::encrypted(),
521        ))
522    }
523
524    pub fn compressed() -> Self {
525        Pattern::Structure(StructurePattern::Obscured(
526            ObscuredPattern::compressed(),
527        ))
528    }
529}
530
531//
532//
533// endregion
534
535// region: Meta Patterns
536//
537//
538
539impl Pattern {
540    /// Creates a new `Pattern` that matches any element.
541    pub fn any() -> Self {
542        Pattern::Meta(MetaPattern::Any(AnyPattern::new()))
543    }
544}
545
546impl Pattern {
547    /// Creates a new `Pattern` that only matches if all specified patterns
548    /// match.
549    pub fn and(patterns: Vec<Pattern>) -> Self {
550        Pattern::Meta(MetaPattern::And(AndPattern::new(patterns)))
551    }
552
553    /// Creates a new `Pattern` that matches if at least one of the specified
554    /// patterns matches.
555    pub fn or(patterns: Vec<Pattern>) -> Self {
556        Pattern::Meta(MetaPattern::Or(OrPattern::new(patterns)))
557    }
558}
559
560impl Pattern {
561    /// Creates a new `Pattern` that matches a traversal order of patterns.
562    pub fn traverse(patterns: Vec<Pattern>) -> Self {
563        Pattern::Meta(MetaPattern::Traverse(TraversePattern::new(patterns)))
564    }
565}
566
567impl Pattern {
568    /// Creates a new `Pattern` that searches for a specific pattern within the
569    /// envelope. Useful for finding patterns that may not be at the root
570    /// of the envelope.
571    pub fn search(pattern: Pattern) -> Self {
572        Pattern::Meta(MetaPattern::Search(SearchPattern::new(pattern)))
573    }
574}
575
576impl Pattern {
577    /// Creates a new `Pattern` that negates another pattern; matches if the
578    /// specified pattern does not match.
579    pub fn not_matching(pattern: Pattern) -> Self {
580        Pattern::Meta(MetaPattern::Not(NotPattern::new(pattern)))
581    }
582}
583
584impl Pattern {
585    /// Compile self to byte-code (recursive).
586    pub(crate) fn compile(
587        &self,
588        code: &mut Vec<Instr>,
589        lits: &mut Vec<Pattern>,
590        captures: &mut Vec<String>,
591    ) {
592        use Pattern::*;
593        match self {
594            Leaf(leaf_pattern) => leaf_pattern.compile(code, lits, captures),
595            Structure(struct_pattern) => {
596                struct_pattern.compile(code, lits, captures)
597            }
598            Meta(meta_pattern) => meta_pattern.compile(code, lits, captures),
599        }
600    }
601}
602
603impl Pattern {
604    /// Creates a new `Pattern` that will match a pattern repeated a number of
605    /// times according to the specified range and greediness.
606    ///
607    /// In regex terms:
608    ///
609    /// | Range         | Quantifier   |
610    /// | :------------ | :----------- |
611    /// | `..`          | `*`          |
612    /// | `1..`         | `+`          |
613    /// | `0..=1`       | `?`          |
614    /// | `min..=max`   | `{min,max}`  |
615    /// | `min..`       | `{min,}`     |
616    /// | `..=max`      | `{0,max}`    |
617    /// | `n..=n`       | `{n}`        |
618    pub fn repeat(
619        pattern: Pattern,
620        interval: impl RangeBounds<usize>,
621        reluctance: Reluctance,
622    ) -> Self {
623        Pattern::Meta(MetaPattern::Group(GroupPattern::repeat(
624            pattern,
625            Quantifier::new(interval, reluctance),
626        )))
627    }
628
629    pub fn group(pattern: Pattern) -> Self {
630        Pattern::Meta(MetaPattern::Group(GroupPattern::new(pattern)))
631    }
632}
633
634impl Pattern {
635    /// Creates a new `Pattern` that will capture a pattern match with a name.
636    pub fn capture(name: impl AsRef<str>, pattern: Pattern) -> Self {
637        Pattern::Meta(MetaPattern::Capture(CapturePattern::new(name, pattern)))
638    }
639}
640
641//
642//
643// endregion
644
645impl std::fmt::Display for Pattern {
646    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
647        match self {
648            Pattern::Leaf(leaf) => write!(f, "{}", leaf),
649            Pattern::Structure(structure) => write!(f, "{}", structure),
650            Pattern::Meta(meta) => write!(f, "{}", meta),
651        }
652    }
653}
654
655impl Pattern {
656    /// Internal helper that runs the pattern through the VM and returns the
657    /// matching paths.
658    fn vm_run(
659        &self,
660        env: &Envelope,
661    ) -> Vec<(Path, HashMap<String, Vec<Path>>)> {
662        thread_local! {
663            static PROG: RefCell<HashMap<u64, vm::Program>> = RefCell::new(HashMap::new());
664        }
665
666        // cheap structural hash
667        use std::{
668            collections::hash_map::DefaultHasher,
669            hash::{Hash, Hasher},
670        };
671        let mut h = DefaultHasher::new();
672        self.hash(&mut h);
673        let key = h.finish();
674
675        let prog = PROG
676            .with(|cell| cell.borrow().get(&key).cloned())
677            .unwrap_or_else(|| {
678                let mut p = vm::Program {
679                    code: Vec::new(),
680                    literals: Vec::new(),
681                    capture_names: Vec::new(),
682                };
683                self.compile(
684                    &mut p.code,
685                    &mut p.literals,
686                    &mut p.capture_names,
687                );
688                p.code.push(Instr::Accept);
689                PROG.with(|cell| {
690                    cell.borrow_mut().insert(key, p.clone());
691                });
692                p
693            });
694
695        vm::run(&prog, env)
696    }
697
698    #[allow(dead_code)]
699    fn vm_paths(&self, env: &Envelope) -> Vec<Path> {
700        self.vm_run(env).into_iter().map(|(p, _)| p).collect()
701    }
702
703    pub(crate) fn collect_capture_names(&self, out: &mut Vec<String>) {
704        if let Pattern::Meta(meta) = self {
705            meta.collect_capture_names(out)
706        }
707    }
708}
709
710impl Pattern {
711    /// Creates a new `Pattern` that matches any wrapped envelope without
712    /// descending. Renamed from `wrapped()` to break tests so they can be
713    /// fixed.
714    pub fn wrapped() -> Self {
715        Pattern::Structure(StructurePattern::Wrapped(WrappedPattern::new()))
716    }
717
718    /// Creates a new `Pattern` that matches a wrapped envelope and also matches
719    /// on its unwrapped content.
720    pub fn unwrap_matching(pattern: Pattern) -> Self {
721        Pattern::Structure(StructurePattern::Wrapped(
722            WrappedPattern::unwrap_matching(pattern),
723        ))
724    }
725
726    /// Creates a new `Pattern` that matches any wrapped envelope and descends
727    /// into it.
728    pub fn unwrap() -> Self {
729        Pattern::Structure(StructurePattern::Wrapped(WrappedPattern::unwrap()))
730    }
731}