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}