xpct/matchers/
pattern.rs

1use std::fmt;
2
3use crate::core::Match;
4
5use super::Mismatch;
6
7/// A runtime representation of a `match` pattern.
8///
9/// You can get a value of this type using the [`pattern!`] macro.
10///
11/// Printing this value with [`Debug`] or [`Display`] prints the stringified pattern.
12///
13/// [`pattern!`]: crate::pattern
14/// [`match_pattern`]: crate::match_pattern
15/// [`Debug`]: std::fmt::Debug
16/// [`Display`]: std::fmt::Display
17pub struct Pattern<'a, T> {
18    pattern: &'static str,
19    matches: Box<dyn for<'b> Fn(&'b T) -> bool + 'a>,
20}
21
22impl<'a, T> fmt::Debug for Pattern<'a, T> {
23    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24        f.write_str(self.pattern)
25    }
26}
27
28impl<'a, T> fmt::Display for Pattern<'a, T> {
29    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30        f.write_str(self.pattern)
31    }
32}
33
34impl<'a, T> Pattern<'a, T> {
35    /// This method is an implementation detail of the [`pattern!`][crate::pattern] macro and IS NOT
36    /// part of the public API.
37    #[doc(hidden)]
38    pub fn __new(pattern: &'static str, matches: impl for<'b> Fn(&'b T) -> bool + 'a) -> Self {
39        Self {
40            pattern,
41            matches: Box::new(matches),
42        }
43    }
44
45    /// Returns whether the given value matches the pattern.
46    ///
47    /// # Examples
48    ///
49    /// ```
50    /// use xpct::pattern;
51    ///
52    /// let pat = pattern!(Some(value) if *value == "foo");
53    ///
54    /// assert!(pat.matches(&Some("foo")));
55    /// ```
56    pub fn matches(&self, value: &T) -> bool {
57        (self.matches)(value)
58    }
59}
60
61/// The matcher for [`match_pattern`].
62///
63/// [`match_pattern`]: crate::match_pattern
64#[derive(Debug)]
65pub struct PatternMatcher<'a, Actual> {
66    spec: Pattern<'a, Actual>,
67}
68
69impl<'a, Actual> PatternMatcher<'a, Actual> {
70    /// Create a new [`PatternMatcher`] from the given spec.
71    ///
72    /// This accepts a [`Pattern`], which you can generate using the [`pattern!`][crate::pattern]
73    /// macro.
74    pub fn new(spec: Pattern<'a, Actual>) -> Self {
75        Self { spec }
76    }
77}
78
79impl<'a, 'b: 'a, Actual> Match<Actual> for PatternMatcher<'a, Actual> {
80    type Fail = Mismatch<Pattern<'a, Actual>, Actual>;
81
82    fn matches(&mut self, actual: &Actual) -> crate::Result<bool> {
83        Ok((self.spec.matches)(actual))
84    }
85
86    fn fail(self, actual: Actual) -> Self::Fail {
87        Mismatch {
88            expected: self.spec,
89            actual,
90        }
91    }
92}
93
94/// Construct a new [`Pattern`] value from a `match` pattern.
95///
96/// This macro is commonly used with the [`match_pattern`] matcher to test if an expression matches
97/// a pattern.
98///
99/// This macro supports the `pattern_1 | pattern_2` syntax supported by `match` arms:
100///
101/// ```
102/// use xpct::pattern;
103/// use xpct::matchers::pattern::Pattern;
104///
105/// let pat: Pattern<Option<&str>> = pattern!(Some("foo") | Some("bar"));
106/// ```
107///
108/// Match guards are also supported:
109///
110/// ```
111/// use xpct::pattern;
112/// use xpct::matchers::pattern::Pattern;
113///
114/// let pat: Pattern<Option<&str>> = pattern!(Some(value) if *value == "foo");
115/// ```
116///
117/// [`match_pattern`]: crate::match_pattern
118#[macro_export]
119macro_rules! pattern {
120    ($pattern:pat $( if $guard:expr )? $(,)?) => {
121        $crate::matchers::pattern::Pattern::__new(
122            stringify!($pattern $( if $guard )?),
123            |ref actual| match actual {
124                $pattern $( if $guard )? => true,
125                _ => false,
126            },
127        )
128    };
129}