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.5/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.5/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.5/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.5/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.5/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.5/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.5/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.5/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}