assertables/assert_matches/assert_matches.rs
1//! Assert expression matches a case.
2//!
3//! # Example
4//!
5//! ```rust
6//! use assertables::*;
7//!
8//! let a = 'a';
9//! assert_matches!(a, 'a'..='z');
10//! ```
11//!
12//! Note: this implementation of `assert_matches` is relatively basic.
13//!
14//! * If you want more capabilities, consider the crate `assert_matches`.
15//!
16//! * If you're using Rust nightly, use the std lib macro `assert_matches`.
17//!
18//! # Module macros
19//!
20//! * [`assert_matches`](macro@crate::assert_matches)
21//! * [`assert_matches_as_result`](macro@crate::assert_matches_as_result)
22//! * [`debug_assert_matches`](macro@crate::debug_assert_matches)
23
24/// Assert expression matches a case.
25///
26/// * If true, return Result `Ok(())`.
27///
28/// * Otherwise, return Result `Err(message)`.
29///
30/// This macro is useful for runtime checks, such as checking parameters,
31/// or sanitizing inputs, or handling different results in different ways.
32///
33/// # Module macros
34///
35/// * [`assert_matches`](macro@crate::assert_matches)
36/// * [`assert_matches_as_result`](macro@crate::assert_matches_as_result)
37/// * [`debug_assert_matches`](macro@crate::debug_assert_matches)
38///
39#[macro_export]
40macro_rules! assert_matches_as_result {
41 ($($arg:tt)*) => {
42 if matches!($($arg)*) {
43 Ok(())
44 } else {
45 Err(
46 format!(
47 concat!(
48 "assertion failed: `assert_matches!(a)`\n",
49 "https://docs.rs/assertables/", env!("CARGO_PKG_VERSION"), "/assertables/macro.assert_matches.html\n",
50 " args: `{}`",
51 ),
52 stringify!($($arg)*)
53 )
54 )
55 }
56 };
57}
58
59#[cfg(test)]
60mod test_assert_matches_as_result {
61 // use std::sync::Once;
62
63 //// Use char as per https://doc.rust-lang.org/std/macro.matches.html
64 mod use_char {
65
66 #[test]
67 fn success() {
68 let a = 'a';
69 let actual = assert_matches_as_result!(a, 'a'..='z');
70 assert_eq!(actual.unwrap(), ());
71 }
72
73 #[test]
74 fn failure() {
75 let a = 'a';
76 let actual = assert_matches_as_result!(a, 'b'..='z');
77 let message = concat!(
78 "assertion failed: `assert_matches!(a)`\n",
79 "https://docs.rs/assertables/",
80 env!("CARGO_PKG_VERSION"),
81 "/assertables/macro.assert_matches.html\n",
82 " args: `a, 'b'..='z'`",
83 );
84 assert_eq!(actual.unwrap_err(), message);
85 }
86 }
87
88 //// Use Some as per https://doc.rust-lang.org/std/macro.matches.html
89 mod use_some {
90
91 #[test]
92 fn success() {
93 let a = Some(1);
94 let actual = assert_matches_as_result!(a, Some(x) if x < 2);
95 assert_eq!(actual.unwrap(), ());
96 }
97
98 #[test]
99 fn failure() {
100 let a = Some(2);
101 let actual = assert_matches_as_result!(a, Some(x) if x < 2);
102 let message = concat!(
103 "assertion failed: `assert_matches!(a)`\n",
104 "https://docs.rs/assertables/",
105 env!("CARGO_PKG_VERSION"),
106 "/assertables/macro.assert_matches.html\n",
107 " args: `a, Some(x) if x < 2`",
108 );
109 assert_eq!(actual.unwrap_err(), message);
110 }
111 }
112}
113
114/// Assert expression is Some.
115///
116/// * If true, return `()`.
117///
118/// * Otherwise, call [`panic!`] with a message and the values of the
119/// expressions with their debug representations.
120///
121/// # Examples
122///
123/// ```rust
124/// use assertables::*;
125/// # use std::panic;
126///
127/// # fn main() {
128/// let a = 'a';
129/// assert_matches!(a, 'a'..='z');
130///
131/// # let result = panic::catch_unwind(|| {
132/// // This will panic
133/// let a = 'a';
134/// assert_matches!(a, 'b'..='z');
135/// # });
136/// // assertion failed: `assert_matches!(a)`
137/// // https://docs.rs/assertables/9.7.0/assertables/macro.assert_matches.html
138/// // args: `a, 'b'..='z'`
139/// # let actual = result.unwrap_err().downcast::<String>().unwrap().to_string();
140/// # let message = concat!(
141/// # "assertion failed: `assert_matches!(a)`\n",
142/// # "https://docs.rs/assertables/", env!("CARGO_PKG_VERSION"), "/assertables/macro.assert_matches.html\n",
143/// # " args: `a, 'b'..='z'`",
144/// # );
145/// # assert_eq!(actual, message);
146/// # }
147/// ```
148///
149/// # Module macros
150///
151/// * [`assert_matches`](macro@crate::assert_matches)
152/// * [`assert_matches_as_result`](macro@crate::assert_matches_as_result)
153/// * [`debug_assert_matches`](macro@crate::debug_assert_matches)
154///
155#[macro_export]
156macro_rules! assert_matches {
157 ($expression:expr, $pattern:pat if $guard:expr $(,)?) => {
158 match $crate::assert_matches_as_result!($expression, $pattern if $guard) {
159 Ok(()) => (),
160 Err(err) => panic!("{}", err),
161 }
162 };
163 ($expression:expr, $pattern:pat) => {
164 match $crate::assert_matches_as_result!($expression, $pattern) {
165 Ok(()) => (),
166 Err(err) => panic!("{}", err),
167 }
168 };
169 ($expression:expr, $pattern:pat if $guard:expr, $($message:tt)+) => {
170 match $crate::assert_matches_as_result!($expression, $pattern if $guard) {
171 Ok(()) => (),
172 Err(err) => panic!("{}\n{}", format_args!($($message)+), err),
173 }
174 };
175 ($expression:expr, $pattern:pat, $($message:tt)+) => {
176 match $crate::assert_matches_as_result!($expression, $pattern if $guard) {
177 Ok(()) => (),
178 Err(err) => panic!("{}\n{}", format_args!($($message)+), err),
179 }
180 };
181}
182
183#[cfg(test)]
184mod test_assert_matches {
185
186 //// Use char as per https://doc.rust-lang.org/std/macro.matches.html
187 mod use_char {
188 use std::panic;
189
190 #[test]
191 fn success() {
192 let a = 'a';
193 let actual = assert_matches!(a, 'a'..='z');
194 assert_eq!(actual, ());
195 }
196
197 #[test]
198 fn failure() {
199 let a = 'a';
200 let result = panic::catch_unwind(|| {
201 let _actual = assert_matches!(a, 'b'..='z');
202 });
203 let message = concat!(
204 "assertion failed: `assert_matches!(a)`\n",
205 "https://docs.rs/assertables/",
206 env!("CARGO_PKG_VERSION"),
207 "/assertables/macro.assert_matches.html\n",
208 " args: `a, 'b'..='z'`",
209 );
210 assert_eq!(
211 result
212 .unwrap_err()
213 .downcast::<String>()
214 .unwrap()
215 .to_string(),
216 message
217 );
218 }
219 }
220
221 //// Use Some as per https://doc.rust-lang.org/std/macro.matches.html
222 mod use_some {
223 use std::panic;
224
225 #[test]
226 fn success() {
227 let a = Some(1);
228 let actual = assert_matches!(a, Some(x) if x < 2);
229 assert_eq!(actual, ());
230 }
231
232 #[test]
233 fn failure() {
234 let a = Some(2);
235 let result = panic::catch_unwind(|| {
236 let _actual = assert_matches!(a, Some(x) if x < 2);
237 });
238 let message = concat!(
239 "assertion failed: `assert_matches!(a)`\n",
240 "https://docs.rs/assertables/",
241 env!("CARGO_PKG_VERSION"),
242 "/assertables/macro.assert_matches.html\n",
243 " args: `a, Some(x) if x < 2`",
244 );
245 assert_eq!(
246 result
247 .unwrap_err()
248 .downcast::<String>()
249 .unwrap()
250 .to_string(),
251 message
252 );
253 }
254 }
255}
256
257/// Assert expression is Some.
258///
259/// This macro provides the same statements as [`assert_matches`](macro.assert_matches.html),
260/// except this macro's statements are only enabled in non-optimized
261/// builds by default. An optimized build will not execute this macro's
262/// statements unless `-C debug-assertions` is passed to the compiler.
263///
264/// This macro is useful for checks that are too expensive to be present
265/// in a release build but may be helpful during development.
266///
267/// The result of expanding this macro is always type checked.
268///
269/// An unchecked assertion allows a program in an inconsistent state to
270/// keep running, which might have unexpected consequences but does not
271/// introduce unsafety as long as this only happens in safe code. The
272/// performance cost of assertions, however, is not measurable in general.
273/// Replacing `assert*!` with `debug_assert*!` is thus only encouraged
274/// after thorough profiling, and more importantly, only in safe code!
275///
276/// This macro is intended to work in a similar way to
277/// [`::std::debug_assert`](https://doc.rust-lang.org/std/macro.debug_assert.html).
278///
279/// # Module macros
280///
281/// * [`assert_matches`](macro@crate::assert_matches)
282/// * [`assert_matches`](macro@crate::assert_matches)
283/// * [`debug_assert_matches`](macro@crate::debug_assert_matches)
284///
285#[macro_export]
286macro_rules! debug_assert_matches {
287 ($($arg:tt)*) => {
288 if $crate::cfg!(debug_assertions) {
289 $crate::assert_matches!($($arg)*);
290 }
291 };
292}