miden_assembly_syntax/testing/
pattern.rs

1use alloc::string::String;
2use core::fmt;
3
4/// Represents a pattern for matching text abstractly
5/// for use in asserting contents of complex diagnostics
6#[derive(Debug)]
7pub enum Pattern {
8    /// Searches for an exact match of the given literal in the input string
9    Literal(alloc::borrow::Cow<'static, str>),
10    /// Searches for a match of the given regular expression in the input string
11    Regex(regex::Regex),
12}
13impl Pattern {
14    /// Construct a [Pattern] representing the given regular expression
15    #[track_caller]
16    pub fn regex(pattern: impl AsRef<str>) -> Self {
17        Self::Regex(regex::Regex::new(pattern.as_ref()).expect("invalid regex"))
18    }
19
20    /// Check if this pattern matches `input`
21    pub fn is_match(&self, input: impl AsRef<str>) -> bool {
22        match self {
23            Self::Literal(pattern) => input.as_ref().contains(pattern.as_ref()),
24            Self::Regex(regex) => regex.is_match(input.as_ref()),
25        }
26    }
27
28    /// Assert that this pattern matches `input`.
29    ///
30    /// This behaves like `assert_eq!` or `assert_matches!`, i.e. it
31    /// will produce a helpful panic message on failure that renders
32    /// the difference between what the pattern expected, and what
33    /// it actually was matched against.
34    #[track_caller]
35    pub fn assert_match(&self, input: impl AsRef<str>) {
36        let input = input.as_ref();
37        if !self.is_match(input) {
38            panic!(
39                r"expected string was not found in emitted diagnostics:
40expected input to {expected}
41matched against: `{actual}`
42",
43                expected = self,
44                actual = input
45            );
46        }
47    }
48
49    /// Like [Pattern::assert_match], but renders additional context
50    /// in the case of failure to aid in troubleshooting.
51    #[track_caller]
52    pub fn assert_match_with_context(&self, input: impl AsRef<str>, context: impl AsRef<str>) {
53        let input = input.as_ref();
54        let context = context.as_ref();
55        if !self.is_match(input) {
56            panic!(
57                r"expected string was not found in emitted diagnostics:
58expected input to {expected}
59matched against: `{actual}`
60full output: `{context}`
61",
62                expected = self,
63                actual = input
64            );
65        }
66    }
67}
68
69impl fmt::Display for Pattern {
70    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
71        match self {
72            Self::Literal(lit) => write!(f, "contain `{lit}`"),
73            Self::Regex(pat) => write!(f, "match regular expression `{}`", pat.as_str()),
74        }
75    }
76}
77
78impl From<&'static str> for Pattern {
79    fn from(s: &'static str) -> Self {
80        Self::Literal(alloc::borrow::Cow::Borrowed(s.trim()))
81    }
82}
83
84impl From<String> for Pattern {
85    fn from(s: String) -> Self {
86        Self::Literal(alloc::borrow::Cow::Owned(s))
87    }
88}
89
90impl From<regex::Regex> for Pattern {
91    fn from(pat: regex::Regex) -> Self {
92        Self::Regex(pat)
93    }
94}