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