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