claims/
assert_matches.rs

1/// Asserts that the expression matches the provided pattern.
2///
3/// Works like the [`std::matches!`] macro, but panics if there is no match.
4///
5/// ## Uses
6///
7/// Assertions are always checked in both debug and release builds, and cannot be disabled.
8/// See [`debug_assert_matches!`] for assertions that are not enabled in release builds by default.
9///
10/// ## Custom messages
11///
12/// This macro has a second form, where a custom panic message can be provided with or without
13/// arguments for formatting. See [`std::fmt`] for syntax for this form.
14///
15/// ## Examples
16///
17/// ```rust
18/// # #[macro_use] extern crate claims;
19/// # fn main() {
20/// let foo = 'f';
21/// assert_matches!(foo, 'A'..='Z' | 'a'..='z');
22///
23/// // With a custom message
24/// assert_matches!(foo, 'A'..='Z' | 'a'..='z', "expecting it to be letter: {}", foo);
25/// # }
26/// ```
27///
28/// ```rust,should_panic
29/// # #[macro_use] extern crate claims;
30/// # fn main() {
31/// let bar: Option<i32> = None;
32/// assert_matches!(bar, Some(x) if x > 2);  // Will panic
33/// # }
34/// ```
35///
36/// [`std::matches!`]: https://doc.rust-lang.org/stable/std/macro.matches.html
37/// [`std::fmt`]: https://doc.rust-lang.org/std/fmt/index.html
38/// [`debug_assert_matches!`]: crate::debug_assert_matches!
39#[macro_export]
40macro_rules! assert_matches {
41    ($expression:expr, $($pattern:pat)|+ $(if $guard:expr)? $(,)?) => {
42        #[allow(unreachable_patterns)]
43        match $expression {
44            $($pattern)|+ $(if $guard)? => {},
45            other => {
46                ::core::panic!(r#"assertion failed, expression does not match the given pattern.
47    expression: {:?}
48    pattern: {}"#, other, ::core::stringify!($($pattern)|+ $(if $guard)?));
49            }
50        }
51    };
52    ($expression:expr, $($pattern:pat)|+ $(if $guard:expr)?, $($arg:tt)+) => {
53        #[allow(unreachable_patterns)]
54        match $expression {
55            $($pattern)|+ $(if $guard)? => {},
56            other => {
57                ::core::panic!(r#"assertion failed, expression does not match the given pattern.
58    expression: {:?}
59    pattern: {}: {}"#, other, ::core::stringify!($($pattern)|+ $(if $guard)?), ::core::format_args!($($arg)+));
60            }
61        }
62    };
63}
64
65/// Asserts that the expression matches the provided pattern on debug builds.
66///
67///
68/// This macro behaves the same as [`assert_matches!`] on debug builds. On release builds it is a
69/// no-op.
70#[macro_export]
71macro_rules! debug_assert_matches {
72    ($($arg:tt)*) => {
73        #[cfg(debug_assertions)]
74        $crate::assert_matches!($($arg)*);
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    #[allow(dead_code)]
81    #[derive(Debug)]
82    enum Foo {
83        Bar(usize),
84        Baz(usize),
85    }
86
87    #[test]
88    fn matches() {
89        assert_matches!(Foo::Bar(42), Foo::Bar(_));
90    }
91
92    #[test]
93    fn matches_multiple_variants() {
94        assert_matches!(Foo::Baz(42), Foo::Bar(_) | Foo::Baz(_));
95    }
96
97    #[test]
98    #[should_panic(
99        expected = "assertion failed, expression does not match the given pattern.\n    expression: Bar(42)\n    pattern: Foo::Baz(_)"
100    )]
101    fn not_matches() {
102        assert_matches!(Foo::Bar(42), Foo::Baz(_));
103    }
104
105    #[test]
106    #[should_panic(
107        expected = "assertion failed, expression does not match the given pattern.\n    expression: Bar(42)\n    pattern: Foo::Baz(_): foo"
108    )]
109    fn not_matches_custom_message() {
110        assert_matches!(Foo::Bar(42), Foo::Baz(_), "foo");
111    }
112
113    #[test]
114    fn matches_if_guard() {
115        assert_matches!(Foo::Bar(42), Foo::Bar(x) if x < 100);
116    }
117
118    #[test]
119    #[should_panic(
120        expected = "assertion failed, expression does not match the given pattern.\n    expression: Bar(42)\n    pattern: Foo::Bar(x) if x > 100"
121    )]
122    fn not_matches_if_guard() {
123        assert_matches!(Foo::Bar(42), Foo::Bar(x) if x > 100);
124    }
125
126    #[test]
127    #[should_panic(
128        expected = "assertion failed, expression does not match the given pattern.\n    expression: Bar(42)\n    pattern: Foo::Bar(x) if x > 100: foo"
129    )]
130    fn not_matches_if_guard_custom_message() {
131        assert_matches!(Foo::Bar(42), Foo::Bar(x) if x > 100, "foo");
132    }
133
134    #[rustversion::since(1.53)]
135    #[test]
136    fn matches_nested_pattern() {
137        assert_matches!(Some(Foo::Bar(42)), Some(Foo::Bar(_) | Foo::Baz(1 | 2)));
138    }
139
140    #[rustversion::since(1.53)]
141    #[test]
142    #[should_panic(
143        expected = "assertion failed, expression does not match the given pattern.\n    expression: None\n    pattern: Some(Foo::Bar(_) | Foo::Baz(1 | 2))"
144    )]
145    fn not_matches_nested_pattern() {
146        assert_matches!(None, Some(Foo::Bar(_) | Foo::Baz(1 | 2)));
147    }
148
149    #[test]
150    #[cfg_attr(not(debug_assertions), ignore = "only run in debug mode")]
151    fn debug_matches() {
152        debug_assert_matches!(Foo::Bar(42), Foo::Bar(_));
153    }
154
155    #[test]
156    #[cfg_attr(not(debug_assertions), ignore = "only run in debug mode")]
157    fn debug_matches_multiple_variants() {
158        debug_assert_matches!(Foo::Baz(42), Foo::Bar(_) | Foo::Baz(_));
159    }
160
161    #[test]
162    #[cfg_attr(not(debug_assertions), ignore = "only run in debug mode")]
163    #[should_panic(
164        expected = "assertion failed, expression does not match the given pattern.\n    expression: Bar(42)\n    pattern: Foo::Baz(_)"
165    )]
166    fn debug_not_matches() {
167        debug_assert_matches!(Foo::Bar(42), Foo::Baz(_));
168    }
169
170    #[test]
171    #[cfg_attr(not(debug_assertions), ignore = "only run in debug mode")]
172    #[should_panic(
173        expected = "assertion failed, expression does not match the given pattern.\n    expression: Bar(42)\n    pattern: Foo::Baz(_): foo"
174    )]
175    fn debug_not_matches_custom_message() {
176        debug_assert_matches!(Foo::Bar(42), Foo::Baz(_), "foo");
177    }
178
179    #[test]
180    #[cfg_attr(not(debug_assertions), ignore = "only run in debug mode")]
181    fn debug_matches_if_guard() {
182        debug_assert_matches!(Foo::Bar(42), Foo::Bar(x) if x < 100);
183    }
184
185    #[test]
186    #[cfg_attr(not(debug_assertions), ignore = "only run in debug mode")]
187    #[should_panic(
188        expected = "assertion failed, expression does not match the given pattern.\n    expression: Bar(42)\n    pattern: Foo::Bar(x) if x > 100"
189    )]
190    fn debug_not_matches_if_guard() {
191        debug_assert_matches!(Foo::Bar(42), Foo::Bar(x) if x > 100);
192    }
193
194    #[test]
195    #[cfg_attr(not(debug_assertions), ignore = "only run in debug mode")]
196    #[should_panic(
197        expected = "assertion failed, expression does not match the given pattern.\n    expression: Bar(42)\n    pattern: Foo::Bar(x) if x > 100: foo"
198    )]
199    fn debug_not_matches_if_guard_custom_message() {
200        debug_assert_matches!(Foo::Bar(42), Foo::Bar(x) if x > 100, "foo");
201    }
202
203    #[rustversion::since(1.53)]
204    #[test]
205    #[cfg_attr(not(debug_assertions), ignore = "only run in debug mode")]
206    fn debug_matches_nested_pattern() {
207        debug_assert_matches!(Some(Foo::Bar(42)), Some(Foo::Bar(_) | Foo::Baz(1 | 2)));
208    }
209
210    #[rustversion::since(1.53)]
211    #[test]
212    #[cfg_attr(not(debug_assertions), ignore = "only run in debug mode")]
213    #[should_panic(
214        expected = "assertion failed, expression does not match the given pattern.\n    expression: None\n    pattern: Some(Foo::Bar(_) | Foo::Baz(1 | 2))"
215    )]
216    fn debug_not_matches_nested_pattern() {
217        debug_assert_matches!(None, Some(Foo::Bar(_) | Foo::Baz(1 | 2)));
218    }
219
220    #[test]
221    #[cfg_attr(debug_assertions, ignore = "only run in release mode")]
222    fn debug_release_not_matches() {
223        debug_assert_matches!(Foo::Bar(42), Foo::Baz(_));
224    }
225
226    #[test]
227    #[cfg_attr(debug_assertions, ignore = "only run in release mode")]
228    fn debug_release_not_matches_if_guard() {
229        debug_assert_matches!(Foo::Bar(42), Foo::Bar(x) if x > 100);
230    }
231
232    #[rustversion::since(1.53)]
233    #[test]
234    #[cfg_attr(debug_assertions, ignore = "only run in release mode")]
235    fn debug_release_not_matches_nested_pattern() {
236        debug_assert_matches!(None, Some(Foo::Bar(_) | Foo::Baz(1 | 2)));
237    }
238}