assertables/assert_is_match/
assert_is_match.rs

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