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}