assertables/assert_abs_diff/assert_abs_diff_gt_x.rs
1//! Assert an absolute difference is greater than 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 = 2;
14//! assert_abs_diff_gt_x!(a, b, x);
15//! ```
16//!
17//! # Module macros
18//!
19//! * [`assert_abs_diff_gt_x`](macro@crate::assert_abs_diff_gt_x)
20//! * [`assert_abs_diff_gt_x_as_result`](macro@crate::assert_abs_diff_gt_x_as_result)
21//! * [`debug_assert_abs_diff_gt_x`](macro@crate::debug_assert_abs_diff_gt_x)
22
23/// Assert an absolute difference is greater than 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_gt_x`](macro@crate::assert_abs_diff_gt_x)
39/// * [`assert_abs_diff_gt_x_as_result`](macro@crate::assert_abs_diff_gt_x_as_result)
40/// * [`debug_assert_abs_diff_gt_x`](macro@crate::debug_assert_abs_diff_gt_x)
41///
42#[macro_export]
43macro_rules! assert_abs_diff_gt_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_gt_x!(a, b, x)`\n",
56 "https://docs.rs/assertables/9.6.1/assertables/macro.assert_abs_diff_gt_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_gt_x!(a, b, x)`\n",
83 "https://docs.rs/assertables/9.6.1/assertables/macro.assert_abs_diff_gt_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_gt_x_as_result {
109 use std::sync::Once;
110
111 #[test]
112 fn gt() {
113 let a: i8 = 10;
114 let b: i8 = 13;
115 let x: i8 = 2;
116 for _ in 0..1 {
117 let actual = assert_abs_diff_gt_x_as_result!(a, b, x);
118 assert_eq!(actual.unwrap(), (3, 2));
119 }
120 }
121
122 #[test]
123 fn gt_once() {
124 static A: Once = Once::new();
125 fn a() -> i8 {
126 if A.is_completed() {
127 panic!("A.is_completed()")
128 } else {
129 A.call_once(|| {})
130 }
131 10
132 }
133
134 static B: Once = Once::new();
135 fn b() -> i8 {
136 if B.is_completed() {
137 panic!("B.is_completed()")
138 } else {
139 B.call_once(|| {})
140 }
141 13
142 }
143
144 static X: Once = Once::new();
145 fn x() -> i8 {
146 if X.is_completed() {
147 panic!("X.is_completed()")
148 } else {
149 X.call_once(|| {})
150 }
151 2
152 }
153
154 assert_eq!(A.is_completed(), false);
155 assert_eq!(B.is_completed(), false);
156 assert_eq!(X.is_completed(), false);
157 let result = assert_abs_diff_gt_x_as_result!(a(), b(), x());
158 assert!(result.is_ok());
159 assert_eq!(A.is_completed(), true);
160 assert_eq!(B.is_completed(), true);
161 assert_eq!(X.is_completed(), true);
162 }
163
164 #[test]
165 fn eq() {
166 let a: i8 = 10;
167 let b: i8 = 13;
168 let x: i8 = 3;
169 let actual = assert_abs_diff_gt_x_as_result!(a, b, x);
170 let message = concat!(
171 "assertion failed: `assert_abs_diff_gt_x!(a, b, x)`\n",
172 "https://docs.rs/assertables/9.6.1/assertables/macro.assert_abs_diff_gt_x.html\n",
173 " a label: `a`,\n",
174 " a debug: `10`,\n",
175 " b label: `b`,\n",
176 " b debug: `13`,\n",
177 " x label: `x`,\n",
178 " x debug: `3`,\n",
179 " |Δ|: `3`,\n",
180 " |Δ| > x: false"
181 );
182 assert_eq!(actual.unwrap_err(), message);
183 }
184
185 #[test]
186 fn lt() {
187 let a: i8 = 10;
188 let b: i8 = 13;
189 let x: i8 = 4;
190 let actual = assert_abs_diff_gt_x_as_result!(a, b, x);
191 let message = concat!(
192 "assertion failed: `assert_abs_diff_gt_x!(a, b, x)`\n",
193 "https://docs.rs/assertables/9.6.1/assertables/macro.assert_abs_diff_gt_x.html\n",
194 " a label: `a`,\n",
195 " a debug: `10`,\n",
196 " b label: `b`,\n",
197 " b debug: `13`,\n",
198 " x label: `x`,\n",
199 " x debug: `4`,\n",
200 " |Δ|: `3`,\n",
201 " |Δ| > x: false"
202 );
203 assert_eq!(actual.unwrap_err(), message);
204 }
205
206 #[test]
207 fn overflow() {
208 let a: i8 = i8::MAX;
209 let b: i8 = i8::MIN;
210 let x: i8 = 0;
211 let actual = assert_abs_diff_gt_x_as_result!(a, b, x);
212 let message = format!(
213 concat!(
214 "assertion failed: `assert_abs_diff_gt_x!(a, b, x)`\n",
215 "https://docs.rs/assertables/9.6.1/assertables/macro.assert_abs_diff_gt_x.html\n",
216 " a label: `a`,\n",
217 " a debug: `{}`,\n",
218 " b label: `b`,\n",
219 " b debug: `{}`,\n",
220 " x label: `x`,\n",
221 " x debug: `{}`,\n",
222 " |Δ|: panic"
223 ),
224 a, b, x
225 );
226 assert_eq!(actual.unwrap_err(), message);
227 }
228}
229
230/// Assert an absolute difference is greater than an expression.
231///
232/// Pseudocode:<br>
233/// |Δ| > x
234///
235/// * If true, return `(lhs, rhs)`.
236///
237/// * Otherwise, call [`panic!`] with a message and the values of the
238/// expressions with their debug representations.
239///
240/// # Examples
241///
242/// ```rust
243/// use assertables::*;
244/// # use std::panic;
245///
246/// # fn main() {
247/// let a = 10;
248/// let b = 13;
249/// let x = 2;
250/// assert_abs_diff_gt_x!(a, b, x);
251///
252/// # let result = panic::catch_unwind(|| {
253/// // This will panic
254/// let a = 10;
255/// let b = 13;
256/// let x = 4;
257/// assert_abs_diff_gt_x!(a, b, x);
258/// # });
259/// // assertion failed: `assert_abs_diff_gt_x!(a, b)`
260/// // https://docs.rs/assertables/9.6.1/assertables/macro.assert_abs_diff_gt_x.html
261/// // a label: `a`,
262/// // a debug: `10`,
263/// // b label: `b`,
264/// // b debug: `13`,
265/// // x label: `x`,
266/// // x debug: `4`,
267/// // |Δ|: `3`,
268/// // |Δ| > x: false
269/// # let actual = result.unwrap_err().downcast::<String>().unwrap().to_string();
270/// # let message = concat!(
271/// # "assertion failed: `assert_abs_diff_gt_x!(a, b, x)`\n",
272/// # "https://docs.rs/assertables/9.6.1/assertables/macro.assert_abs_diff_gt_x.html\n",
273/// # " a label: `a`,\n",
274/// # " a debug: `10`,\n",
275/// # " b label: `b`,\n",
276/// # " b debug: `13`,\n",
277/// # " x label: `x`,\n",
278/// # " x debug: `4`,\n",
279/// # " |Δ|: `3`,\n",
280/// # " |Δ| > x: false"
281/// # );
282/// # assert_eq!(actual, message);
283/// # }
284/// ```
285///
286/// # Module macros
287///
288/// * [`assert_abs_diff_gt_x`](macro@crate::assert_abs_diff_gt_x)
289/// * [`assert_abs_diff_gt_x_as_result`](macro@crate::assert_abs_diff_gt_x_as_result)
290/// * [`debug_assert_abs_diff_gt_x`](macro@crate::debug_assert_abs_diff_gt_x)
291///
292#[macro_export]
293macro_rules! assert_abs_diff_gt_x {
294 ($a:expr, $b:expr, $x:expr $(,)?) => {
295 match $crate::assert_abs_diff_gt_x_as_result!($a, $b, $x) {
296 Ok(x) => x,
297 Err(err) => panic!("{}", err),
298 }
299 };
300 ($a:expr, $b:expr, $x:expr, $($message:tt)+) => {
301 match $crate::assert_abs_diff_gt_x_as_result!($a, $b, $x) {
302 Ok(x) => x,
303 Err(err) => panic!("{}\n{}", format_args!($($message)+), err),
304 }
305 };
306}
307
308#[cfg(test)]
309mod assert_abs_diff_gt_x {
310 use std::panic;
311
312 #[test]
313 fn gt() {
314 let a = 10;
315 let b = 13;
316 let x = 2;
317 for _ in 0..1 {
318 let actual = assert_abs_diff_gt_x!(a, b, x);
319 assert_eq!(actual, (3, 2));
320 }
321 }
322
323 #[test]
324 fn eq() {
325 let result = panic::catch_unwind(|| {
326 let a = 10;
327 let b = 13;
328 let x = 3;
329 let _actual = assert_abs_diff_gt_x!(a, b, x);
330 });
331 let message = concat!(
332 "assertion failed: `assert_abs_diff_gt_x!(a, b, x)`\n",
333 "https://docs.rs/assertables/9.6.1/assertables/macro.assert_abs_diff_gt_x.html\n",
334 " a label: `a`,\n",
335 " a debug: `10`,\n",
336 " b label: `b`,\n",
337 " b debug: `13`,\n",
338 " x label: `x`,\n",
339 " x debug: `3`,\n",
340 " |Δ|: `3`,\n",
341 " |Δ| > x: false"
342 );
343 assert_eq!(
344 result
345 .unwrap_err()
346 .downcast::<String>()
347 .unwrap()
348 .to_string(),
349 message
350 );
351 }
352
353 #[test]
354 fn lt() {
355 let result = panic::catch_unwind(|| {
356 let a = 10;
357 let b = 13;
358 let x = 4;
359 let _actual = assert_abs_diff_gt_x!(a, b, x);
360 });
361 let message = concat!(
362 "assertion failed: `assert_abs_diff_gt_x!(a, b, x)`\n",
363 "https://docs.rs/assertables/9.6.1/assertables/macro.assert_abs_diff_gt_x.html\n",
364 " a label: `a`,\n",
365 " a debug: `10`,\n",
366 " b label: `b`,\n",
367 " b debug: `13`,\n",
368 " x label: `x`,\n",
369 " x debug: `4`,\n",
370 " |Δ|: `3`,\n",
371 " |Δ| > x: false"
372 );
373 assert_eq!(
374 result
375 .unwrap_err()
376 .downcast::<String>()
377 .unwrap()
378 .to_string(),
379 message
380 );
381 }
382
383 #[test]
384 fn overflow() {
385 let a: i8 = i8::MAX;
386 let b: i8 = i8::MIN;
387 let x: i8 = 0;
388 let result = panic::catch_unwind(|| {
389 let _actual = assert_abs_diff_gt_x!(a, b, x);
390 });
391 let message = format!(
392 concat!(
393 "assertion failed: `assert_abs_diff_gt_x!(a, b, x)`\n",
394 "https://docs.rs/assertables/9.6.1/assertables/macro.assert_abs_diff_gt_x.html\n",
395 " a label: `a`,\n",
396 " a debug: `{}`,\n",
397 " b label: `b`,\n",
398 " b debug: `{}`,\n",
399 " x label: `x`,\n",
400 " x debug: `{}`,\n",
401 " |Δ|: panic"
402 ),
403 a, b, x
404 );
405 assert_eq!(
406 result
407 .unwrap_err()
408 .downcast::<String>()
409 .unwrap()
410 .to_string(),
411 message
412 );
413 }
414}
415
416/// Assert an absolute difference is greater than an expression.
417///
418/// Pseudocode:<br>
419/// |Δ| > c
420///
421/// This macro provides the same statements as [`assert_abs_diff_gt_x`](macro.assert_abs_diff_gt_x.html),
422/// except this macro's statements are only enabled in non-optimized
423/// builds by default. An optimized build will not execute this macro's
424/// statements unless `-x debug-assertions` is passed to the compiler.
425///
426/// This macro is useful for checks that are too expensive to be present
427/// in a release build but may be helpful during development.
428///
429/// The result of expanding this macro is always type checked.
430///
431/// An unchecked assertion allows a program in an inconsistent state to
432/// keep running, which might have unexpected consequences but does not
433/// introduce unsafety as long as this only happens in safe code. The
434/// performance cost of assertions, however, is not measurable in general.
435/// Replacing `assert*!` with `debug_assert*!` is thus only encouraged
436/// after thorough profiling, and more importantly, only in safe code!
437///
438/// This macro is intended to work in a similar way to
439/// [`::std::debug_assert`](https://doc.rust-lang.org/std/macro.debug_assert.html).
440///
441/// # Module macros
442///
443/// * [`assert_abs_diff_gt_x`](macro@crate::assert_abs_diff_gt_x)
444/// * [`assert_abs_diff_gt_x`](macro@crate::assert_abs_diff_gt_x)
445/// * [`debug_assert_abs_diff_gt_x`](macro@crate::debug_assert_abs_diff_gt_x)
446///
447#[macro_export]
448macro_rules! debug_assert_abs_diff_gt_x {
449 ($($arg:tt)*) => {
450 if $crate::cfg!(debug_assertions) {
451 $crate::assert_abs_diff_gt_x!($($arg)*);
452 }
453 };
454}