assertables/assert_in/
assert_in.rs

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