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