assertables/assert_contains/
assert_not_contains.rs

1//! Assert an expression (such as a string) does not contain an expression (such as a substring).
2//!
3//! Pseudocode:<br>
4//! ¬ a.contains(b)
5//!
6//! These macros work with many kinds of Rust types, such as String, Vec, Range, HashSet.
7//! The specifics depend on each type's implementation of a method `contains`, and some types
8//! require the second argument to be borrowable, so be sure to check the Rust documentation.
9//!
10//! # Example
11//!
12//! ```rust
13//! use assertables::*;
14//! use std::collections::HashSet;
15//!
16//! // String does not contain substring.
17//! let a = "alfa";
18//! let b = "xx";
19//! assert_not_contains!(a, b);
20//!
21//! // Range does not contain value.
22//! // Notice the &b because the macro calls Range.contains(&self, &value).
23//! let a = 1..3;
24//! let b = 4;
25//! assert_not_contains!(a, &b);
26//!
27//! // Vector does not contain element.
28//! // Notice the &b because the macro calls Vec.contains(&self, &value).
29//! let a = vec![1, 2, 3];
30//! let b = 4;
31//! assert_not_contains!(a, &b);
32//!
33//! // HashSet does not contain element.
34//! // Notice the &b because the macro calls HashSet.contains(&self, &value).
35//! let a: HashSet<String> = [String::from("1")].into();
36//! let b: String = String::from("2");
37//! assert_not_contains!(a, &b);
38//!
39//! // HashSet does not contain element with automatic borrow optimization.
40//! // Notice the b because the value type &str is already a reference,
41//! // which HashSet.contains knows how to borrow to compare to String.
42//! let a: HashSet<String> = [String::from("1")].into();
43//! let b = "2";
44//! assert_not_contains!(a, b);
45//! ```
46//!
47//! # Module macros
48//!
49//! * [`assert_not_contains`](macro@crate::assert_not_contains)
50//! * [`assert_not_contains_as_result`](macro@crate::assert_not_contains_as_result)
51//! * [`debug_assert_not_contains`](macro@crate::debug_assert_not_contains)
52
53/// Assert an expression (such as a string) does not contain an expression (such as a substring).
54///
55/// Pseudocode:<br>
56/// ¬ a.contains(b)
57///
58/// * If true, return Result `Ok(())`.
59///
60/// * Otherwise, return Result `Err(message)`.
61///
62/// This macro is useful for runtime checks, such as checking parameters,
63/// or sanitizing inputs, or handling different results in different ways.
64///
65/// # Module macros
66///
67/// * [`assert_not_contains`](macro@crate::assert_not_contains)
68/// * [`assert_not_contains_as_result`](macro@crate::assert_not_contains_as_result)
69/// * [`debug_assert_not_contains`](macro@crate::debug_assert_not_contains)
70///
71#[macro_export]
72macro_rules! assert_not_contains_as_result {
73    ($container:expr, $containee:expr $(,)?) => {
74        match (&$container, &$containee) {
75            (container, containee) => {
76                if !(container.contains(*containee)) {
77                    Ok(())
78                } else {
79                    Err(format!(
80                        concat!(
81                            "assertion failed: `assert_not_contains!(container, containee)`\n",
82                            "https://docs.rs/assertables/9.8.1/assertables/macro.assert_not_contains.html\n",
83                            " container label: `{}`,\n",
84                            " container debug: `{:?}`,\n",
85                            " containee label: `{}`,\n",
86                            " containee debug: `{:?}`",
87                        ),
88                        stringify!($container),
89                        container,
90                        stringify!($containee),
91                        containee,
92                    ))
93                }
94            }
95        }
96    };
97}
98
99#[cfg(test)]
100mod test_assert_not_contains_as_result {
101    use std::panic;
102    use std::sync::Once;
103
104    mod str {
105        use super::*;
106
107        #[test]
108        fn success() {
109            let a = "alfa";
110            let b = "xx";
111            for _ in 0..1 {
112                let actual = assert_not_contains_as_result!(a, b);
113                assert_eq!(actual.unwrap(), ());
114            }
115        }
116
117        #[test]
118        fn success_once() {
119            static A: Once = Once::new();
120            fn a() -> &'static str {
121                if A.is_completed() {
122                    panic!("A.is_completed()")
123                } else {
124                    A.call_once(|| {})
125                }
126                "alfa"
127            }
128
129            static B: Once = Once::new();
130            fn b() -> &'static str {
131                if B.is_completed() {
132                    panic!("B.is_completed()")
133                } else {
134                    B.call_once(|| {})
135                }
136                "xx"
137            }
138
139            assert_eq!(A.is_completed(), false);
140            assert_eq!(B.is_completed(), false);
141            let result = assert_not_contains_as_result!(a(), b());
142            assert!(result.is_ok());
143            assert_eq!(A.is_completed(), true);
144            assert_eq!(B.is_completed(), true);
145        }
146        #[test]
147        fn failure() {
148            let a = "alfa";
149            let b = "lf";
150            let actual = assert_not_contains_as_result!(a, b);
151            let message = concat!(
152                "assertion failed: `assert_not_contains!(container, containee)`\n",
153                "https://docs.rs/assertables/9.8.1/assertables/macro.assert_not_contains.html\n",
154                " container label: `a`,\n",
155                " container debug: `\"alfa\"`,\n",
156                " containee label: `b`,\n",
157                " containee debug: `\"lf\"`"
158            );
159            assert_eq!(actual.unwrap_err(), message);
160        }
161    }
162
163    mod range_i32 {
164        use super::*;
165
166        #[test]
167        fn success() {
168            let a: std::ops::Range<i32> = 1..3;
169            let b: i32 = 4;
170            for _ in 0..1 {
171                let actual = assert_not_contains_as_result!(a, &b);
172                assert_eq!(actual.unwrap(), ());
173            }
174        }
175
176        #[test]
177        fn success_once() {
178            static A: Once = Once::new();
179            fn a() -> std::ops::Range<i32> {
180                if A.is_completed() {
181                    panic!("A.is_completed()")
182                } else {
183                    A.call_once(|| {})
184                }
185                1..3
186            }
187
188            static B: Once = Once::new();
189            fn b() -> i32 {
190                if B.is_completed() {
191                    panic!("B.is_completed()")
192                } else {
193                    B.call_once(|| {})
194                }
195                4
196            }
197
198            assert_eq!(A.is_completed(), false);
199            assert_eq!(B.is_completed(), false);
200            let result = assert_not_contains_as_result!(a(), &b());
201            assert!(result.is_ok());
202            assert_eq!(A.is_completed(), true);
203            assert_eq!(B.is_completed(), true);
204        }
205
206        #[test]
207        fn failure() {
208            let a: std::ops::Range<i32> = 1..3;
209            let b: i32 = 2;
210            let actual = assert_not_contains_as_result!(a, &b);
211            let message = concat!(
212                "assertion failed: `assert_not_contains!(container, containee)`\n",
213                "https://docs.rs/assertables/9.8.1/assertables/macro.assert_not_contains.html\n",
214                " container label: `a`,\n",
215                " container debug: `1..3`,\n",
216                " containee label: `&b`,\n",
217                " containee debug: `2`"
218            );
219            assert_eq!(actual.unwrap_err(), message);
220        }
221    }
222
223    mod range_string {
224        use super::*;
225
226        #[test]
227        fn success() {
228            let a: std::ops::Range<String> = String::from("1")..String::from("3");
229            let b: String = String::from("4");
230            for _ in 0..1 {
231                let actual = assert_not_contains_as_result!(a, &b);
232                assert_eq!(actual.unwrap(), ());
233            }
234        }
235
236        #[test]
237        fn success_once() {
238            static A: Once = Once::new();
239            fn a() -> std::ops::Range<String> {
240                if A.is_completed() {
241                    panic!("A.is_completed()")
242                } else {
243                    A.call_once(|| {})
244                }
245                String::from("1")..String::from("3")
246            }
247
248            static B: Once = Once::new();
249            fn b() -> String {
250                if B.is_completed() {
251                    panic!("B.is_completed()")
252                } else {
253                    B.call_once(|| {})
254                }
255                String::from("4")
256            }
257
258            assert_eq!(A.is_completed(), false);
259            assert_eq!(B.is_completed(), false);
260            let result = assert_not_contains_as_result!(a(), &b());
261            assert!(result.is_ok());
262            assert_eq!(A.is_completed(), true);
263            assert_eq!(B.is_completed(), true);
264        }
265
266        #[test]
267        fn failure() {
268            let a: std::ops::Range<String> = String::from("1")..String::from("3");
269            let b: String = String::from("2");
270            let actual = assert_not_contains_as_result!(a, &b);
271            let message = concat!(
272                "assertion failed: `assert_not_contains!(container, containee)`\n",
273                "https://docs.rs/assertables/9.8.1/assertables/macro.assert_not_contains.html\n",
274                " container label: `a`,\n",
275                " container debug: `\"1\"..\"3\"`,\n",
276                " containee label: `&b`,\n",
277                " containee debug: `\"2\"`"
278            );
279            assert_eq!(actual.unwrap_err(), message);
280        }
281    }
282
283    mod vec_i32 {
284        use super::*;
285
286        #[test]
287        fn success() {
288            let a: Vec<i32> = vec![1, 2, 3];
289            let b: i32 = 4;
290            for _ in 0..1 {
291                let actual = assert_not_contains_as_result!(a, &b);
292                assert_eq!(actual.unwrap(), ());
293            }
294        }
295
296        #[test]
297        fn success_once() {
298            static A: Once = Once::new();
299            fn a() -> Vec<i32> {
300                if A.is_completed() {
301                    panic!("A.is_completed()")
302                } else {
303                    A.call_once(|| {})
304                }
305                vec![1, 2, 3]
306            }
307
308            static B: Once = Once::new();
309            fn b() -> i32 {
310                if B.is_completed() {
311                    panic!("B.is_completed()")
312                } else {
313                    B.call_once(|| {})
314                }
315                4
316            }
317
318            assert_eq!(A.is_completed(), false);
319            assert_eq!(B.is_completed(), false);
320            let result = assert_not_contains_as_result!(a(), &b());
321            assert!(result.is_ok());
322            assert_eq!(A.is_completed(), true);
323            assert_eq!(B.is_completed(), true);
324        }
325
326        #[test]
327        fn failure() {
328            let a: Vec<i32> = vec![1, 2, 3];
329            let b: i32 = 2;
330            let actual = assert_not_contains_as_result!(a, &b);
331            let message = concat!(
332                "assertion failed: `assert_not_contains!(container, containee)`\n",
333                "https://docs.rs/assertables/9.8.1/assertables/macro.assert_not_contains.html\n",
334                " container label: `a`,\n",
335                " container debug: `[1, 2, 3]`,\n",
336                " containee label: `&b`,\n",
337                " containee debug: `2`"
338            );
339            assert_eq!(actual.unwrap_err(), message);
340        }
341    }
342
343    mod vec_string {
344        use super::*;
345
346        #[test]
347        fn success() {
348            let a: Vec<String> = vec![String::from("1"), String::from("2"), String::from("3")];
349            let b: String = String::from("4");
350            for _ in 0..1 {
351                let actual = assert_not_contains_as_result!(a, &b);
352                assert_eq!(actual.unwrap(), ());
353            }
354        }
355
356        #[test]
357        fn success_once() {
358            static A: Once = Once::new();
359            fn a() -> Vec<String> {
360                if A.is_completed() {
361                    panic!("A.is_completed()")
362                } else {
363                    A.call_once(|| {})
364                }
365                vec![String::from("1"), String::from("2"), String::from("3")]
366            }
367
368            static B: Once = Once::new();
369            fn b() -> String {
370                if B.is_completed() {
371                    panic!("B.is_completed()")
372                } else {
373                    B.call_once(|| {})
374                }
375                String::from("4")
376            }
377
378            assert_eq!(A.is_completed(), false);
379            assert_eq!(B.is_completed(), false);
380            let result = assert_not_contains_as_result!(a(), &b());
381            assert!(result.is_ok());
382            assert_eq!(A.is_completed(), true);
383            assert_eq!(B.is_completed(), true);
384        }
385
386        #[test]
387        fn failure() {
388            let a: Vec<String> = vec![String::from("1"), String::from("2"), String::from("3")];
389            let b: String = String::from("2");
390            let actual = assert_not_contains_as_result!(a, &b);
391            let message = concat!(
392                "assertion failed: `assert_not_contains!(container, containee)`\n",
393                "https://docs.rs/assertables/9.8.1/assertables/macro.assert_not_contains.html\n",
394                " container label: `a`,\n",
395                " container debug: `[\"1\", \"2\", \"3\"]`,\n",
396                " containee label: `&b`,\n",
397                " containee debug: `\"2\"`"
398            );
399            assert_eq!(actual.unwrap_err(), message);
400        }
401    }
402    mod hashset_string {
403        use super::*;
404        use std::collections::HashSet;
405
406        #[test]
407        fn success() {
408            let a: HashSet<String> = [String::from("1")].into();
409            let b: String = String::from("2");
410            for _ in 0..1 {
411                let actual = assert_not_contains_as_result!(a, &b);
412                assert_eq!(actual.unwrap(), ());
413            }
414        }
415
416        #[test]
417        fn success_once() {
418            static A: Once = Once::new();
419            fn a() -> HashSet<String> {
420                if A.is_completed() {
421                    panic!("A.is_completed()")
422                } else {
423                    A.call_once(|| {})
424                }
425                [String::from("1")].into()
426            }
427
428            static B: Once = Once::new();
429            fn b() -> String {
430                if B.is_completed() {
431                    panic!("B.is_completed()")
432                } else {
433                    B.call_once(|| {})
434                }
435                String::from("2")
436            }
437
438            assert_eq!(A.is_completed(), false);
439            assert_eq!(B.is_completed(), false);
440            let result = assert_not_contains_as_result!(a(), &b());
441            assert!(result.is_ok());
442            assert_eq!(A.is_completed(), true);
443            assert_eq!(B.is_completed(), true);
444        }
445
446        #[test]
447        fn failure() {
448            let a: HashSet<String> = [String::from("1")].into();
449            let b: String = String::from("1");
450            let actual = assert_not_contains_as_result!(a, &b);
451            let message = concat!(
452                "assertion failed: `assert_not_contains!(container, containee)`\n",
453                "https://docs.rs/assertables/9.8.1/assertables/macro.assert_not_contains.html\n",
454                " container label: `a`,\n",
455                " container debug: `{\"1\"}`,\n",
456                " containee label: `&b`,\n",
457                " containee debug: `\"1\"`"
458            );
459            assert_eq!(actual.unwrap_err(), message);
460        }
461    }
462
463    mod hashset_string_with_str_automatic_borrow_optimization {
464        use super::*;
465        use std::collections::HashSet;
466
467        #[test]
468        fn success() {
469            let a: HashSet<String> = [String::from("1")].into();
470            let b: &'static str = "2";
471            for _ in 0..1 {
472                let actual = assert_not_contains_as_result!(a, b);
473                assert_eq!(actual.unwrap(), ());
474            }
475        }
476
477        #[test]
478        fn success_once() {
479            static A: Once = Once::new();
480            fn a() -> HashSet<String> {
481                if A.is_completed() {
482                    panic!("A.is_completed()")
483                } else {
484                    A.call_once(|| {})
485                }
486                [String::from("1")].into()
487            }
488
489            static B: Once = Once::new();
490            fn b() -> &'static str {
491                if B.is_completed() {
492                    panic!("B.is_completed()")
493                } else {
494                    B.call_once(|| {})
495                }
496                "2"
497            }
498
499            assert_eq!(A.is_completed(), false);
500            assert_eq!(B.is_completed(), false);
501            let result = assert_not_contains_as_result!(a(), b());
502            assert!(result.is_ok());
503            assert_eq!(A.is_completed(), true);
504            assert_eq!(B.is_completed(), true);
505        }
506
507        #[test]
508        fn failure() {
509            let a: HashSet<String> = [String::from("1")].into();
510            let b: &'static str = "1";
511            let actual = assert_not_contains_as_result!(a, b);
512            let message = concat!(
513                "assertion failed: `assert_not_contains!(container, containee)`\n",
514                "https://docs.rs/assertables/9.8.1/assertables/macro.assert_not_contains.html\n",
515                " container label: `a`,\n",
516                " container debug: `{\"1\"}`,\n",
517                " containee label: `b`,\n",
518                " containee debug: `\"1\"`"
519            );
520            assert_eq!(actual.unwrap_err(), message);
521        }
522    }
523}
524
525/// Assert an expression (such as a string) does not contain an expression (such as a substring).
526///
527/// Pseudocode:<br>
528/// ¬ a.contains(b)
529///
530/// * If true, return `()`.
531///
532/// * Otherwise, call [`panic!`] with a message and the values of the
533///   expressions with their debug representations.
534///
535/// # Examples
536///
537/// ```rust
538/// use assertables::*;
539/// use std::collections::HashSet;
540/// # use std::panic;
541///
542/// # fn main() {
543/// // String contains substring
544/// let a = "alfa";
545/// let b = "zz";
546/// assert_not_contains!(a, b);
547///
548/// // Range contains value
549/// let a = 1..3;
550/// let b = 4;
551/// assert_not_contains!(a, &b);
552///
553/// // Vector contains element
554/// let a = vec![1, 2, 3];
555/// let b = 4;
556/// assert_not_contains!(a, &b);
557///
558/// // HashSet does not contain element.
559/// // Notice the &b because the macro calls HashSet.contains(&self, &value).
560/// let a: HashSet<String> = [String::from("1")].into();
561/// let b: String = String::from("2");
562/// assert_not_contains!(a, &b);
563///
564/// // HashSet does not contain element with automatic borrow optimization.
565/// // Notice the b because the value type &str is already a reference,
566/// // which HashSet.contains knows how to borrow to compare to String.
567/// let a: HashSet<String> = [String::from("1")].into();
568/// let b = "2";
569/// assert_not_contains!(a, b);
570///
571/// # let result = panic::catch_unwind(|| {
572/// // This will panic
573/// let a = "alfa";
574/// let b = "lf";
575/// assert_not_contains!(a, b);
576/// # });
577/// // assertion failed: `assert_not_contains!(container, containee)`
578/// // https://docs.rs/assertables/…/assertables/macro.assert_not_contains.html
579/// //  container label: `a`,
580/// //  container debug: `\"alfa\"`,
581/// //  containee label: `b`,
582/// //  containee debug: `\"lf\"`
583/// # let actual = result.unwrap_err().downcast::<String>().unwrap().to_string();
584/// # let message = concat!(
585/// #     "assertion failed: `assert_not_contains!(container, containee)`\n",
586/// #     "https://docs.rs/assertables/9.8.1/assertables/macro.assert_not_contains.html\n",
587/// #     " container label: `a`,\n",
588/// #     " container debug: `\"alfa\"`,\n",
589/// #     " containee label: `b`,\n",
590/// #     " containee debug: `\"lf\"`"
591/// # );
592/// # assert_eq!(actual, message);
593/// # }
594/// ```
595///
596/// # Module macros
597///
598/// * [`assert_not_contains`](macro@crate::assert_not_contains)
599/// * [`assert_not_contains_as_result`](macro@crate::assert_not_contains_as_result)
600/// * [`debug_assert_not_contains`](macro@crate::debug_assert_not_contains)
601///
602#[macro_export]
603macro_rules! assert_not_contains {
604    ($container:expr, $containee:expr $(,)?) => {
605        match $crate::assert_not_contains_as_result!($container, $containee) {
606            Ok(()) => (),
607            Err(err) => panic!("{}", err),
608        }
609    };
610    ($container:expr, $containee:expr, $($message:tt)+) => {
611        match $crate::assert_not_contains_as_result!($container, $containee) {
612            Ok(()) => (),
613            Err(err) => panic!("{}\n{}", format_args!($($message)+), err),
614        }
615    };
616}
617
618#[cfg(test)]
619mod test_assert_not_contains {
620    use std::panic;
621
622    mod str {
623        use super::*;
624
625        #[test]
626        fn success() {
627            let a = "alfa";
628            let b = "zz";
629            for _ in 0..1 {
630                let actual = assert_not_contains!(a, b);
631                assert_eq!(actual, ());
632            }
633        }
634
635        #[test]
636        fn failure() {
637            let result = panic::catch_unwind(|| {
638                let a = "alfa";
639                let b = "lf";
640                let _actual = assert_not_contains!(a, b);
641            });
642            let message = concat!(
643                "assertion failed: `assert_not_contains!(container, containee)`\n",
644                "https://docs.rs/assertables/9.8.1/assertables/macro.assert_not_contains.html\n",
645                " container label: `a`,\n",
646                " container debug: `\"alfa\"`,\n",
647                " containee label: `b`,\n",
648                " containee debug: `\"lf\"`"
649            );
650            assert_eq!(
651                result
652                    .unwrap_err()
653                    .downcast::<String>()
654                    .unwrap()
655                    .to_string(),
656                message
657            );
658        }
659    }
660
661    mod range_i32 {
662        use super::*;
663        use std::ops::Range;
664
665        #[test]
666        fn success() {
667            let a: Range<i32> = 1..3;
668            let b: i32 = 4;
669            for _ in 0..1 {
670                let actual = assert_not_contains!(a, &b);
671                assert_eq!(actual, ());
672            }
673        }
674
675        #[test]
676        fn failure() {
677            let result = panic::catch_unwind(|| {
678                let a: Range<i32> = 1..3;
679                let b: i32 = 2;
680                let _actual = assert_not_contains!(a, &b);
681            });
682            let message = concat!(
683                "assertion failed: `assert_not_contains!(container, containee)`\n",
684                "https://docs.rs/assertables/9.8.1/assertables/macro.assert_not_contains.html\n",
685                " container label: `a`,\n",
686                " container debug: `1..3`,\n",
687                " containee label: `&b`,\n",
688                " containee debug: `2`"
689            );
690            assert_eq!(
691                result
692                    .unwrap_err()
693                    .downcast::<String>()
694                    .unwrap()
695                    .to_string(),
696                message
697            );
698        }
699    }
700
701    mod range_string {
702        use super::*;
703        use std::ops::Range;
704
705        #[test]
706        fn success() {
707            let a: Range<String> = String::from("1")..String::from("3");
708            let b: String = String::from("4");
709            for _ in 0..1 {
710                let actual = assert_not_contains!(a, &b);
711                assert_eq!(actual, ());
712            }
713        }
714
715        #[test]
716        fn failure() {
717            let result = panic::catch_unwind(|| {
718                let a: Range<String> = String::from("1")..String::from("3");
719                let b: String = String::from("2");
720                let _actual = assert_not_contains!(a, &b);
721            });
722            let message = concat!(
723                "assertion failed: `assert_not_contains!(container, containee)`\n",
724                "https://docs.rs/assertables/9.8.1/assertables/macro.assert_not_contains.html\n",
725                " container label: `a`,\n",
726                " container debug: `\"1\"..\"3\"`,\n",
727                " containee label: `&b`,\n",
728                " containee debug: `\"2\"`"
729            );
730            assert_eq!(
731                result
732                    .unwrap_err()
733                    .downcast::<String>()
734                    .unwrap()
735                    .to_string(),
736                message
737            );
738        }
739    }
740
741    mod vec_i32 {
742        use super::*;
743
744        #[test]
745        fn success() {
746            let a: Vec<i32> = vec![1, 2, 3];
747            let b: i32 = 4;
748            for _ in 0..1 {
749                let actual = assert_not_contains!(a, &b);
750                assert_eq!(actual, ());
751            }
752        }
753
754        #[test]
755        fn failure() {
756            let result = panic::catch_unwind(|| {
757                let a: Vec<i32> = vec![1, 2, 3];
758                let b: i32 = 2;
759                let _actual = assert_not_contains!(a, &b);
760            });
761            let message = concat!(
762                "assertion failed: `assert_not_contains!(container, containee)`\n",
763                "https://docs.rs/assertables/9.8.1/assertables/macro.assert_not_contains.html\n",
764                " container label: `a`,\n",
765                " container debug: `[1, 2, 3]`,\n",
766                " containee label: `&b`,\n",
767                " containee debug: `2`"
768            );
769            assert_eq!(
770                result
771                    .unwrap_err()
772                    .downcast::<String>()
773                    .unwrap()
774                    .to_string(),
775                message
776            );
777        }
778    }
779
780    mod vec_string {
781        use super::*;
782
783        #[test]
784        fn success() {
785            let a: Vec<String> = vec![String::from("1"), String::from("2"), String::from("3")];
786            let b: String = String::from("4");
787            for _ in 0..1 {
788                let actual = assert_not_contains!(a, &b);
789                assert_eq!(actual, ());
790            }
791        }
792
793        #[test]
794        fn failure() {
795            let result = panic::catch_unwind(|| {
796                let a: Vec<String> = vec![String::from("1"), String::from("2"), String::from("3")];
797                let b: String = String::from("2");
798                let _actual = assert_not_contains!(a, &b);
799            });
800            let message = concat!(
801                "assertion failed: `assert_not_contains!(container, containee)`\n",
802                "https://docs.rs/assertables/9.8.1/assertables/macro.assert_not_contains.html\n",
803                " container label: `a`,\n",
804                " container debug: `[\"1\", \"2\", \"3\"]`,\n",
805                " containee label: `&b`,\n",
806                " containee debug: `\"2\"`"
807            );
808            assert_eq!(
809                result
810                    .unwrap_err()
811                    .downcast::<String>()
812                    .unwrap()
813                    .to_string(),
814                message
815            );
816        }
817    }
818
819    mod hashset_string {
820        use super::*;
821        use std::collections::HashSet;
822
823        #[test]
824        fn success() {
825            let a: HashSet<String> = [String::from("1")].into();
826            let b: String = String::from("2");
827            for _ in 0..1 {
828                let actual = assert_not_contains!(a, &b);
829                assert_eq!(actual, ());
830            }
831        }
832
833        #[test]
834        fn failure() {
835            let result = panic::catch_unwind(|| {
836                let a: HashSet<String> = [String::from("1")].into();
837                let b: String = String::from("1");
838                let _actual = assert_not_contains!(a, &b);
839            });
840            let message = concat!(
841                "assertion failed: `assert_not_contains!(container, containee)`\n",
842                "https://docs.rs/assertables/9.8.1/assertables/macro.assert_not_contains.html\n",
843                " container label: `a`,\n",
844                " container debug: `{\"1\"}`,\n",
845                " containee label: `&b`,\n",
846                " containee debug: `\"1\"`"
847            );
848            assert_eq!(
849                result
850                    .unwrap_err()
851                    .downcast::<String>()
852                    .unwrap()
853                    .to_string(),
854                message
855            );
856        }
857    }
858
859    mod hashset_string_with_str_automatic_borrow_optimization {
860        use super::*;
861        use std::collections::HashSet;
862
863        #[test]
864        fn success() {
865            let a: HashSet<String> = [String::from("1")].into();
866            let b: &'static str = "2";
867            for _ in 0..1 {
868                let actual = assert_not_contains!(a, b);
869                assert_eq!(actual, ());
870            }
871        }
872
873        #[test]
874        fn failure() {
875            let result = panic::catch_unwind(|| {
876                let a: HashSet<String> = [String::from("1")].into();
877                let b: &'static str = "1";
878                let _actual = assert_not_contains!(a, b);
879            });
880            let message = concat!(
881                "assertion failed: `assert_not_contains!(container, containee)`\n",
882                "https://docs.rs/assertables/9.8.1/assertables/macro.assert_not_contains.html\n",
883                " container label: `a`,\n",
884                " container debug: `{\"1\"}`,\n",
885                " containee label: `b`,\n",
886                " containee debug: `\"1\"`"
887            );
888            assert_eq!(
889                result
890                    .unwrap_err()
891                    .downcast::<String>()
892                    .unwrap()
893                    .to_string(),
894                message
895            );
896        }
897    }
898}
899
900/// Assert an expression (such as a string) does not contain an expression (such as a substring).
901///
902/// Pseudocode:<br>
903/// ¬ a.contains(b)
904///
905/// This macro provides the same statements as [`assert_not_contains`](macro.assert_not_contains.html),
906/// except this macro's statements are only enabled in non-optimized
907/// builds by default. An optimized build will not execute this macro's
908/// statements unless `-C debug-assertions` is passed to the compiler.
909///
910/// This macro is useful for checks that are too expensive to be present
911/// in a release build but may be helpful during development.
912///
913/// The result of expanding this macro is always type checked.
914///
915/// An unchecked assertion allows a program in an inconsistent state to
916/// keep running, which might have unexpected consequences but does not
917/// introduce unsafety as long as this only happens in safe code. The
918/// performance cost of assertions, however, is not measurable in general.
919/// Replacing `assert*!` with `debug_assert*!` is thus only encouraged
920/// after thorough profiling, and more importantly, only in safe code!
921///
922/// This macro is intended to work in a similar way to
923/// [`::std::debug_assert`](https://doc.rust-lang.org/std/macro.debug_assert.html).
924///
925/// # Module macros
926///
927/// * [`assert_not_contains`](macro@crate::assert_not_contains)
928/// * [`assert_not_contains`](macro@crate::assert_not_contains)
929/// * [`debug_assert_not_contains`](macro@crate::debug_assert_not_contains)
930///
931#[macro_export]
932macro_rules! debug_assert_not_contains {
933    ($($arg:tt)*) => {
934        if $crate::cfg!(debug_assertions) {
935            $crate::assert_not_contains!($($arg)*);
936        }
937    };
938}