assertables/assert_diff/
assert_diff_ne_x.rs

1//! Assert a difference is not equal to an expression.
2//!
3//! Pseudocode:<br>
4//! Δ ≠ x
5//!
6//! # Example
7//!
8//! ```rust
9//! use assertables::*;
10//!
11//! let a: i8 = 10;
12//! let b: i8 = 13;
13//! let x: i8 = 4;
14//! assert_diff_ne_x!(a, b, x);
15//! ```
16//!
17//! # Module macros
18//!
19//! * [`assert_diff_ne_x`](macro@crate::assert_diff_ne_x)
20//! * [`assert_diff_ne_x_as_result`](macro@crate::assert_diff_ne_x_as_result)
21//! * [`debug_assert_diff_ne_x`](macro@crate::debug_assert_diff_ne_x)
22
23/// Assert a difference is not equal to an expression.
24///
25/// Pseudocode:<br>
26/// Δ ≠ x
27///
28/// * If true, return Result `Ok((lhs, rhs))`.
29///
30/// * When false, return [`Err`] with a message and the values of the
31///   expressions with their debug representations.
32///
33/// This macro provides the same statements as [`assert_`](macro.assert_.html), except this macro
34/// returns a Result, rather than doing a panic.
35///
36/// This macro is useful for runtime checks, such as checking parameters, or
37/// sanitizing inputs, or handling different results in different ways.
38///
39/// # Module macros
40///
41/// * [`assert_diff_ne_x`](macro@crate::assert_diff_ne_x)
42/// * [`assert_diff_ne_x_as_result`](macro@crate::assert_diff_ne_x_as_result)
43/// * [`debug_assert_diff_ne_x`](macro@crate::debug_assert_diff_ne_x)
44///
45#[macro_export]
46macro_rules! assert_diff_ne_x_as_result {
47    ($a:expr, $b:expr, $x:expr $(,)?) => {
48        match (&$a, &$b, &$x) {
49            (a, b, x) => {
50                match ::std::panic::catch_unwind(|| b - a) {
51                    Ok(delta) => {
52                        if delta != *x {
53                            Ok((delta, *x))
54                        } else {
55                            Err(format!(
56                                concat!(
57                                    "assertion failed: `assert_diff_ne_x!(a, b, x)`\n",
58                                    "https://docs.rs/assertables/",
59                                    env!("CARGO_PKG_VERSION"),
60                                    "/assertables/macro.assert_diff_ne_x.html\n",
61                                    " a label: `{}`,\n",
62                                    " a debug: `{:?}`,\n",
63                                    " b label: `{}`,\n",
64                                    " b debug: `{:?}`,\n",
65                                    " x label: `{}`,\n",
66                                    " x debug: `{:?}`,\n",
67                                    "       Δ: `{:?}`,\n",
68                                    "   Δ ≠ x: {}"
69                                ),
70                                stringify!($a),
71                                a,
72                                stringify!($b),
73                                b,
74                                stringify!($x),
75                                x,
76                                delta,
77                                false
78                            ))
79                        }
80                    }
81                    Err(_err) => {
82                        Err(format!(
83                            concat!(
84                                "assertion failed: `assert_diff_ne_x!(a, b, x)`\n",
85                                "https://docs.rs/assertables/",
86                                env!("CARGO_PKG_VERSION"),
87                                "/assertables/macro.assert_diff_ne_x.html\n",
88                                " a label: `{}`,\n",
89                                " a debug: `{:?}`,\n",
90                                " b label: `{}`,\n",
91                                " b debug: `{:?}`,\n",
92                                " x label: `{}`,\n",
93                                " x debug: `{:?}`,\n",
94                                "       Δ: panic", //TODO add the panic message
95                            ),
96                            stringify!($a),
97                            a,
98                            stringify!($b),
99                            b,
100                            stringify!($x),
101                            x
102                        ))
103                    }
104                }
105            }
106        }
107    };
108}
109
110#[cfg(test)]
111mod test_assert_diff_ne_x_as_result {
112    use std::sync::Once;
113
114    #[test]
115    fn lt() {
116        let a: i8 = 10;
117        let b: i8 = 13;
118        let x: i8 = 2;
119        for _ in 0..1 {
120            let actual = assert_diff_ne_x_as_result!(a, b, x);
121            assert_eq!(actual.unwrap(), (3 as i8, 2 as i8));
122        }
123    }
124
125    #[test]
126    fn lt_once() {
127        static A: Once = Once::new();
128        fn a() -> i32 {
129            if A.is_completed() {
130                panic!("A.is_completed()")
131            } else {
132                A.call_once(|| {})
133            }
134            10
135        }
136
137        static B: Once = Once::new();
138        fn b() -> i32 {
139            if B.is_completed() {
140                panic!("B.is_completed()")
141            } else {
142                B.call_once(|| {})
143            }
144            13
145        }
146
147        static X: Once = Once::new();
148        fn x() -> i32 {
149            if X.is_completed() {
150                panic!("X.is_completed()")
151            } else {
152                X.call_once(|| {})
153            }
154            4
155        }
156
157        assert_eq!(A.is_completed(), false);
158        assert_eq!(B.is_completed(), false);
159        assert_eq!(X.is_completed(), false);
160        let result = assert_diff_ne_x_as_result!(a(), b(), x());
161        assert!(result.is_ok());
162        assert_eq!(A.is_completed(), true);
163        assert_eq!(B.is_completed(), true);
164        assert_eq!(X.is_completed(), true);
165    }
166
167    #[test]
168    fn gt() {
169        let a: i8 = 10;
170        let b: i8 = 13;
171        let x: i8 = 4;
172        for _ in 0..1 {
173            let actual = assert_diff_ne_x_as_result!(a, b, x);
174            assert_eq!(actual.unwrap(), (3 as i8, 4 as i8));
175        }
176    }
177
178    #[test]
179    fn gt_once() {
180        static A: Once = Once::new();
181        fn a() -> i32 {
182            if A.is_completed() {
183                panic!("A.is_completed()")
184            } else {
185                A.call_once(|| {})
186            }
187            10
188        }
189
190        static B: Once = Once::new();
191        fn b() -> i32 {
192            if B.is_completed() {
193                panic!("B.is_completed()")
194            } else {
195                B.call_once(|| {})
196            }
197            13
198        }
199
200        static X: Once = Once::new();
201        fn x() -> i32 {
202            if X.is_completed() {
203                panic!("X.is_completed()")
204            } else {
205                X.call_once(|| {})
206            }
207            2
208        }
209
210        assert_eq!(A.is_completed(), false);
211        assert_eq!(B.is_completed(), false);
212        assert_eq!(X.is_completed(), false);
213        let result = assert_diff_ne_x_as_result!(a(), b(), x());
214        assert!(result.is_ok());
215        assert_eq!(A.is_completed(), true);
216        assert_eq!(B.is_completed(), true);
217        assert_eq!(X.is_completed(), true);
218    }
219
220    #[test]
221    fn eq() {
222        let a: i8 = 10;
223        let b: i8 = 13;
224        let x: i8 = 3;
225        let actual = assert_diff_ne_x_as_result!(a, b, x);
226        let message = concat!(
227            "assertion failed: `assert_diff_ne_x!(a, b, x)`\n",
228            "https://docs.rs/assertables/",
229            env!("CARGO_PKG_VERSION"),
230            "/assertables/macro.assert_diff_ne_x.html\n",
231            " a label: `a`,\n",
232            " a debug: `10`,\n",
233            " b label: `b`,\n",
234            " b debug: `13`,\n",
235            " x label: `x`,\n",
236            " x debug: `3`,\n",
237            "       Δ: `3`,\n",
238            "   Δ ≠ x: false"
239        );
240        assert_eq!(actual.unwrap_err(), message);
241    }
242
243    #[test]
244    fn overflow() {
245        let a: i8 = i8::MAX;
246        let b: i8 = i8::MIN;
247        let x: i8 = 0;
248        let actual = assert_diff_ne_x_as_result!(a, b, x);
249        let message = format!(
250            concat!(
251                "assertion failed: `assert_diff_ne_x!(a, b, x)`\n",
252                "https://docs.rs/assertables/",
253                env!("CARGO_PKG_VERSION"),
254                "/assertables/macro.assert_diff_ne_x.html\n",
255                " a label: `a`,\n",
256                " a debug: `{}`,\n",
257                " b label: `b`,\n",
258                " b debug: `{}`,\n",
259                " x label: `x`,\n",
260                " x debug: `{}`,\n",
261                "       Δ: panic"
262            ),
263            a, b, x
264        );
265        assert_eq!(actual.unwrap_err(), message);
266    }
267}
268
269/// Assert a difference is not equal to an expression.
270///
271/// Pseudocode:<br>
272/// Δ ≠ x
273///
274/// * If true, return `(lhs, rhs)`.
275///
276/// * Otherwise, call [`panic!`] with a message and the values of the
277///   expressions with their debug representations.
278///
279/// # Examples
280///
281/// ```rust
282/// use assertables::*;
283/// # use std::panic;
284///
285/// # fn main() {
286/// let a: i8 = 10;
287/// let b: i8 = 13;
288/// let x: i8 = 4;
289/// assert_diff_ne_x!(a, b, x);
290///
291/// # let result = panic::catch_unwind(|| {
292/// // This will panic
293/// let a: i8 = 10;
294/// let b: i8 = 13;
295/// let x: i8 = 3;
296/// assert_diff_ne_x!(a, b, x);
297/// # });
298/// // assertion failed: `assert_diff_ne_x!(a, b, x)`
299/// // https://docs.rs/assertables/9.7.0/assertables/macro.assert_diff_ne_x.html
300/// //        a label: `a`,
301/// //        a debug: `10`,
302/// //        b label: `b`,
303/// //        b debug: `13`,
304/// //        x label: `x`,
305/// //        x debug: `3`,
306/// //              Δ: `3`,
307/// //          Δ ≠ x: false
308/// # let actual = result.unwrap_err().downcast::<String>().unwrap().to_string();
309/// # let message = concat!(
310/// #     "assertion failed: `assert_diff_ne_x!(a, b, x)`\n",
311/// #     "https://docs.rs/assertables/", env!("CARGO_PKG_VERSION"), "/assertables/macro.assert_diff_ne_x.html\n",
312/// #     " a label: `a`,\n",
313/// #     " a debug: `10`,\n",
314/// #     " b label: `b`,\n",
315/// #     " b debug: `13`,\n",
316/// #     " x label: `x`,\n",
317/// #     " x debug: `3`,\n",
318/// #     "       Δ: `3`,\n",
319/// #     "   Δ ≠ x: false",
320/// # );
321/// # assert_eq!(actual, message);
322/// # }
323/// ```
324///
325/// # Module macros
326///
327/// * [`assert_diff_ne_x`](macro@crate::assert_diff_ne_x)
328/// * [`assert_diff_ne_x_as_result`](macro@crate::assert_diff_ne_x_as_result)
329/// * [`debug_assert_diff_ne_x`](macro@crate::debug_assert_diff_ne_x)
330///
331#[macro_export]
332macro_rules! assert_diff_ne_x {
333    ($a:expr, $b:expr, $x:expr $(,)?) => {
334        match $crate::assert_diff_ne_x_as_result!($a, $b, $x) {
335            Ok(x) => x,
336            Err(err) => panic!("{}", err),
337        }
338    };
339    ($a:expr, $b:expr, $x:expr, $($message:tt)+) => {
340        match $crate::assert_diff_ne_x_as_result!($a, $b, $x) {
341            Ok(x) => x,
342            Err(err) => panic!("{}\n{}", format_args!($($message)+), err),
343        }
344    };
345}
346
347#[cfg(test)]
348mod test_assert_diff_ne_x {
349    use std::panic;
350
351    #[test]
352    fn lt() {
353        let a: i8 = 10;
354        let b: i8 = 13;
355        let x: i8 = 4;
356        for _ in 0..1 {
357            let actual = assert_diff_ne_x!(a, b, x);
358            assert_eq!(actual, (3 as i8, 4 as i8));
359        }
360    }
361
362    #[test]
363    fn gt() {
364        let a: i8 = 10;
365        let b: i8 = 13;
366        let x: i8 = 2;
367        for _ in 0..1 {
368            let actual = assert_diff_ne_x!(a, b, x);
369            assert_eq!(actual, (3 as i8, 2 as i8));
370        }
371    }
372
373    #[test]
374    fn eq() {
375        let a: i8 = 10;
376        let b: i8 = 13;
377        let x: i8 = 3;
378        let result = panic::catch_unwind(|| {
379            let _actual = assert_diff_ne_x!(a, b, x);
380        });
381        let message = concat!(
382            "assertion failed: `assert_diff_ne_x!(a, b, x)`\n",
383            "https://docs.rs/assertables/",
384            env!("CARGO_PKG_VERSION"),
385            "/assertables/macro.assert_diff_ne_x.html\n",
386            " a label: `a`,\n",
387            " a debug: `10`,\n",
388            " b label: `b`,\n",
389            " b debug: `13`,\n",
390            " x label: `x`,\n",
391            " x debug: `3`,\n",
392            "       Δ: `3`,\n",
393            "   Δ ≠ x: false"
394        );
395        assert_eq!(
396            result
397                .unwrap_err()
398                .downcast::<String>()
399                .unwrap()
400                .to_string(),
401            message
402        );
403    }
404
405    #[test]
406    fn overflow() {
407        let a: i8 = i8::MAX;
408        let b: i8 = i8::MIN;
409        let x: i8 = 0;
410        let result = panic::catch_unwind(|| {
411            let _actual = assert_diff_ne_x!(a, b, x);
412        });
413        let message = format!(
414            concat!(
415                "assertion failed: `assert_diff_ne_x!(a, b, x)`\n",
416                "https://docs.rs/assertables/",
417                env!("CARGO_PKG_VERSION"),
418                "/assertables/macro.assert_diff_ne_x.html\n",
419                " a label: `a`,\n",
420                " a debug: `{}`,\n",
421                " b label: `b`,\n",
422                " b debug: `{}`,\n",
423                " x label: `x`,\n",
424                " x debug: `{}`,\n",
425                "       Δ: panic"
426            ),
427            a, b, x
428        );
429        assert_eq!(
430            result
431                .unwrap_err()
432                .downcast::<String>()
433                .unwrap()
434                .to_string(),
435            message
436        );
437    }
438}
439
440/// Assert a difference is not equal to an expression.
441///
442/// Pseudocode:<br>
443/// Δ = x
444///
445/// This macro provides the same statements as [`assert_diff_ne_x`](macro.assert_diff_ne_x.html),
446/// except this macro's statements are only enabled in non-optimized
447/// builds by default. An optimized build will not execute this macro's
448/// statements unless `-C debug-assertions` is passed to the compiler.
449///
450/// This macro is useful for checks that are too expensive to be present
451/// in a release build but may be helpful during development.
452///
453/// The result of expanding this macro is always type checked.
454///
455/// An unchecked assertion allows a program in an inconsistent state to
456/// keep running, which might have unexpected consequences but does not
457/// introduce unsafety as long as this only happens in safe code. The
458/// performance cost of assertions, however, is not measurable in general.
459/// Replacing `assert*!` with `debug_assert*!` is thus only encouraged
460/// after thorough profiling, and more importantly, only in safe code!
461///
462/// This macro is intended to work in a similar way to
463/// [`::std::debug_assert`](https://doc.rust-lang.org/std/macro.debug_assert.html).
464///
465/// # Module macros
466///
467/// * [`assert_diff_ne_x`](macro@crate::assert_diff_ne_x)
468/// * [`assert_diff_ne_x_as_result`](macro@crate::assert_diff_ne_x_as_result)
469/// * [`debug_assert_diff_ne_x`](macro@crate::debug_assert_diff_ne_x)
470///
471#[macro_export]
472macro_rules! debug_assert_diff_ne_x {
473    ($($arg:tt)*) => {
474        if $crate::cfg!(debug_assertions) {
475            $crate::assert_diff_ne_x!($($arg)*);
476        }
477    };
478}