assertables/assert_contains/
assert_contains.rs

1//! Assert a container is a match for an expression.
2//!
3//! Pseudocode:<br>
4//! a.contains(b)
5//!
6//! # Example
7//!
8//! ```rust
9//! use assertables::*;
10//!
11//! // String contains substring
12//! let a: &str = "alfa";
13//! let b: &str = "lf";
14//! assert_contains!(a, b);
15//!
16//! // Range contains value
17//! let a = 1..3;
18//! let b = 2;
19//! assert_contains!(a, &b);
20//!
21//! // Vector contains element
22//! let a = vec![1, 2, 3];
23//! let b = 2;
24//! assert_contains!(a, &b);
25//! ```
26//!
27//! # Module macros
28//!
29//! * [`assert_contains`](macro@crate::assert_contains)
30//! * [`assert_contains_as_result`](macro@crate::assert_contains_as_result)
31//! * [`debug_assert_contains`](macro@crate::debug_assert_contains)
32
33/// Assert an expression (such as a string) contains an expression (such as a substring).
34///
35/// Pseudocode:<br>
36/// a.contains(b)
37///
38/// * If true, return Result `Ok(())`.
39///
40/// * Otherwise, return Result `Err(message)`.
41///
42/// This macro is useful for runtime checks, such as checking parameters,
43/// or sanitizing inputs, or handling different results in different ways.
44///
45/// # Module macros
46///
47/// * [`assert_contains`](macro@crate::assert_contains)
48/// * [`assert_contains_as_result`](macro@crate::assert_contains_as_result)
49/// * [`debug_assert_contains`](macro@crate::debug_assert_contains)
50///
51#[macro_export]
52macro_rules! assert_contains_as_result {
53    ($container:expr, $containee:expr $(,)?) => {{
54        match (&$container, &$containee) {
55            (container, containee) => {
56                if container.contains($containee) {
57                    Ok(())
58                } else {
59                    Err(
60                        format!(
61                            concat!(
62                                "assertion failed: `assert_contains!(container, containee)`\n",
63                                "https://docs.rs/assertables/9.5.3/assertables/macro.assert_contains.html\n",
64                                " container label: `{}`,\n",
65                                " container debug: `{:?}`,\n",
66                                " containee label: `{}`,\n",
67                                " containee debug: `{:?}`",
68                            ),
69                            stringify!($container),
70                            container,
71                            stringify!($containee),
72                            containee,
73                        )
74                    )
75                }
76            }
77        }
78    }};
79}
80
81#[cfg(test)]
82mod test_assert_contains_as_result {
83
84    mod str {
85
86        #[test]
87        fn success() {
88            let a = "alfa";
89            let b = "lf";
90            let actual = assert_contains_as_result!(a, b);
91            assert_eq!(actual.unwrap(), ());
92        }
93
94        #[test]
95        fn failure() {
96            let a: &str = "alfa";
97            let b = "zz";
98            let actual = assert_contains_as_result!(a, b);
99            let message = concat!(
100                "assertion failed: `assert_contains!(container, containee)`\n",
101                "https://docs.rs/assertables/9.5.3/assertables/macro.assert_contains.html\n",
102                " container label: `a`,\n",
103                " container debug: `\"alfa\"`,\n",
104                " containee label: `b`,\n",
105                " containee debug: `\"zz\"`"
106            );
107            assert_eq!(actual.unwrap_err(), message);
108        }
109    }
110
111    mod range {
112
113        #[test]
114        fn success() {
115            let a = 1..3;
116            let b = 2;
117            let actual = assert_contains_as_result!(a, &b);
118            assert_eq!(actual.unwrap(), ());
119        }
120
121        #[test]
122        fn failure() {
123            let a = 1..3;
124            let b = 4;
125            let actual = assert_contains_as_result!(a, &b);
126            let message = concat!(
127                "assertion failed: `assert_contains!(container, containee)`\n",
128                "https://docs.rs/assertables/9.5.3/assertables/macro.assert_contains.html\n",
129                " container label: `a`,\n",
130                " container debug: `1..3`,\n",
131                " containee label: `&b`,\n",
132                " containee debug: `4`"
133            );
134            assert_eq!(actual.unwrap_err(), message);
135        }
136    }
137
138    mod vec {
139
140        #[test]
141        fn success() {
142            let a = 1..3;
143            let b = 2;
144            let actual = assert_contains_as_result!(a, &b);
145            assert_eq!(actual.unwrap(), ());
146        }
147
148        #[test]
149        fn failure() {
150            let a = vec![1, 2, 3];
151            let b = 4;
152            let actual = assert_contains_as_result!(a, &b);
153            let message = concat!(
154                "assertion failed: `assert_contains!(container, containee)`\n",
155                "https://docs.rs/assertables/9.5.3/assertables/macro.assert_contains.html\n",
156                " container label: `a`,\n",
157                " container debug: `[1, 2, 3]`,\n",
158                " containee label: `&b`,\n",
159                " containee debug: `4`"
160            );
161            assert_eq!(actual.unwrap_err(), message);
162        }
163    }
164}
165
166/// Assert a container is a match for an expression.
167///
168/// Pseudocode:<br>
169/// a.contains(b)
170///
171/// * If true, return `()`.
172///
173/// * Otherwise, call [`panic!`] with a message and the values of the
174///   expressions with their debug representations.
175///
176/// # Examples
177///
178/// ```rust
179/// use assertables::*;
180/// # use std::panic;
181///
182/// # fn main() {
183/// // Return Ok when a string contains a substring
184/// let a = "alfa";
185/// let b = "lf";
186/// assert_contains!(a, b);
187///
188/// // Return Ok when a range contains a value
189/// let a = 1..3;
190/// let b = 2;
191/// assert_contains!(a, &b);
192///
193/// # let result = panic::catch_unwind(|| {
194/// // This will panic
195/// let a = "alfa";
196/// let b = "zz";
197/// assert_contains!(a, b);
198/// # });
199/// // assertion failed: `assert_contains!(container, containee)`
200/// // https://docs.rs/assertables/9.5.3/assertables/macro.assert_contains.html
201/// //  container label: `a`,
202/// //  container debug: `\"alfa\"`,
203/// //  containee label: `b`,
204/// //  containee debug: `\"zz\"`
205/// # let actual = result.unwrap_err().downcast::<String>().unwrap().to_string();
206/// # let message = concat!(
207/// #     "assertion failed: `assert_contains!(container, containee)`\n",
208/// #     "https://docs.rs/assertables/9.5.3/assertables/macro.assert_contains.html\n",
209/// #     " container label: `a`,\n",
210/// #     " container debug: `\"alfa\"`,\n",
211/// #     " containee label: `b`,\n",
212/// #     " containee debug: `\"zz\"`"
213/// # );
214/// # assert_eq!(actual, message);
215/// # }
216/// ```
217///
218/// # Module macros
219///
220/// * [`assert_contains`](macro@crate::assert_contains)
221/// * [`assert_contains_as_result`](macro@crate::assert_contains_as_result)
222/// * [`debug_assert_contains`](macro@crate::debug_assert_contains)
223///
224#[macro_export]
225macro_rules! assert_contains {
226    ($container:expr, $containee:expr $(,)?) => {{
227        match $crate::assert_contains_as_result!($container, $containee) {
228            Ok(()) => (),
229            Err(err) => panic!("{}", err),
230        }
231    }};
232    ($container:expr, $containee:expr, $($message:tt)+) => {{
233        match $crate::assert_contains_as_result!($container, $containee) {
234            Ok(()) => (),
235            Err(err) => panic!("{}\n{}", format_args!($($message)+), err),
236        }
237    }};
238}
239
240#[cfg(test)]
241mod test_assert_contains {
242
243    mod str {
244        use std::panic;
245
246        #[test]
247        fn success() {
248            let a = "alfa";
249            let b = "lf";
250            let actual = assert_contains!(a, b);
251            assert_eq!(actual, ());
252        }
253
254        #[test]
255        fn failure() {
256            let a: &str = "alfa";
257            let b = "zz";
258            let result = panic::catch_unwind(|| {
259                let _actual = assert_contains!(a, b);
260            });
261            let message = concat!(
262                "assertion failed: `assert_contains!(container, containee)`\n",
263                "https://docs.rs/assertables/9.5.3/assertables/macro.assert_contains.html\n",
264                " container label: `a`,\n",
265                " container debug: `\"alfa\"`,\n",
266                " containee label: `b`,\n",
267                " containee debug: `\"zz\"`"
268            );
269            assert_eq!(
270                result
271                    .unwrap_err()
272                    .downcast::<String>()
273                    .unwrap()
274                    .to_string(),
275                message
276            );
277        }
278    }
279
280    mod range {
281        use std::panic;
282
283        #[test]
284        fn success() {
285            let a = 1..3;
286            let b = 2;
287            let actual = assert_contains!(a, &b);
288            assert_eq!(actual, ());
289        }
290
291        #[test]
292        fn failure() {
293            let a = 1..3;
294            let b = 4;
295            let result = panic::catch_unwind(|| {
296                let _actual = assert_contains!(a, &b);
297            });
298            let message = concat!(
299                "assertion failed: `assert_contains!(container, containee)`\n",
300                "https://docs.rs/assertables/9.5.3/assertables/macro.assert_contains.html\n",
301                " container label: `a`,\n",
302                " container debug: `1..3`,\n",
303                " containee label: `&b`,\n",
304                " containee debug: `4`"
305            );
306            assert_eq!(
307                result
308                    .unwrap_err()
309                    .downcast::<String>()
310                    .unwrap()
311                    .to_string(),
312                message
313            );
314        }
315    }
316
317    mod vec {
318        use std::panic;
319
320        #[test]
321        fn success() {
322            let a = 1..3;
323            let b = 2;
324            let actual = assert_contains!(a, &b);
325            assert_eq!(actual, ());
326        }
327
328        #[test]
329        fn failure() {
330            let a = vec![1, 2, 3];
331            let b = 4;
332            let result = panic::catch_unwind(|| {
333                let _actual = assert_contains!(a, &b);
334            });
335            let message = concat!(
336                "assertion failed: `assert_contains!(container, containee)`\n",
337                "https://docs.rs/assertables/9.5.3/assertables/macro.assert_contains.html\n",
338                " container label: `a`,\n",
339                " container debug: `[1, 2, 3]`,\n",
340                " containee label: `&b`,\n",
341                " containee debug: `4`"
342            );
343            assert_eq!(
344                result
345                    .unwrap_err()
346                    .downcast::<String>()
347                    .unwrap()
348                    .to_string(),
349                message
350            );
351        }
352    }
353}
354
355/// Assert a container is a match for an expression.
356///
357/// Pseudocode:<br>
358/// a.contains(b)
359///
360/// This macro provides the same statements as [`assert_contains`](macro.assert_contains.html),
361/// except this macro's statements are only enabled in non-optimized
362/// builds by default. An optimized build will not execute this macro's
363/// statements unless `-C debug-assertions` is passed to the compiler.
364///
365/// This macro is useful for checks that are too expensive to be present
366/// in a release build but may be helpful during development.
367///
368/// The result of expanding this macro is always type checked.
369///
370/// An unchecked assertion allows a program in an inconsistent state to
371/// keep running, which might have unexpected consequences but does not
372/// introduce unsafety as long as this only happens in safe code. The
373/// performance cost of assertions, however, is not measurable in general.
374/// Replacing `assert*!` with `debug_assert*!` is thus only encouraged
375/// after thorough profiling, and more importantly, only in safe code!
376///
377/// This macro is intended to work in a similar way to
378/// [`::std::debug_assert`](https://doc.rust-lang.org/std/macro.debug_assert.html).
379///
380/// # Module macros
381///
382/// * [`assert_contains`](macro@crate::assert_contains)
383/// * [`assert_contains`](macro@crate::assert_contains)
384/// * [`debug_assert_contains`](macro@crate::debug_assert_contains)
385///
386#[macro_export]
387macro_rules! debug_assert_contains {
388    ($($arg:tt)*) => {
389        if $crate::cfg!(debug_assertions) {
390            $crate::assert_contains!($($arg)*);
391        }
392    };
393}