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,
            },
        )
    };
}