assertables/assert_abs_diff/
assert_abs_diff_le_x.rs

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