Skip to main content

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                "expected string was not found in emitted diagnostics:\n\
40                 expected input to {self}\n\
41                 matched against: `{input}`"
42            );
43        }
44    }
45
46    /// Like [Pattern::assert_match], but renders additional context
47    /// in the case of failure to aid in troubleshooting.
48    #[track_caller]
49    pub fn assert_match_with_context(&self, input: impl AsRef<str>, context: impl AsRef<str>) {
50        let input = input.as_ref();
51        let context = context.as_ref();
52        if !self.is_match(input) {
53            panic!(
54                "expected string was not found in emitted diagnostics:\n\
55                 expected input to {self}\n\
56                 matched against: `{input}`\n\
57                 full output: `{context}`"
58            );
59        }
60    }
61}
62
63impl fmt::Display for Pattern {
64    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
65        match self {
66            Self::Literal(lit) => write!(f, "contain `{lit}`"),
67            Self::Regex(pat) => write!(f, "match regular expression `{}`", pat.as_str()),
68        }
69    }
70}
71
72impl From<&'static str> for Pattern {
73    fn from(s: &'static str) -> Self {
74        Self::Literal(alloc::borrow::Cow::Borrowed(s.trim()))
75    }
76}
77
78impl From<String> for Pattern {
79    fn from(s: String) -> Self {
80        Self::Literal(alloc::borrow::Cow::Owned(s))
81    }
82}
83
84impl From<regex::Regex> for Pattern {
85    fn from(pat: regex::Regex) -> Self {
86        Self::Regex(pat)
87    }
88}