fluent_asserter/
number_approx_asserter.rs

1use super::*;
2use num::traits::Pow;
3
4pub trait IsApproxEqual<T> {
5    fn is_approx_equal(&self, expected_value: T, delta: T);
6}
7
8macro_rules! abs_diff_unsigned_eq {
9    ($x:expr, $y:expr, $d:expr) => {
10        if (if $x > $y { $x - $y } else { $y - $x }) > $d {
11            panic!("AssertionError: not equal within delta"); //TODO: improve error message
12        }
13    };
14}
15
16macro_rules! abs_diff_signed_eq {
17    ($x:expr, $y:expr, $d:expr) => {
18        if (($x - $y).abs() > $d) {
19            panic!("AssertionError: not equal within delta"); //TODO: improve error message
20        }
21    };
22}
23
24macro_rules! abs_diff {
25    ($x:expr, $y:expr) => {
26        ($x - $y).abs()
27    };
28}
29
30macro_rules! generate_is_approx_equal_impl_for_signed {
31    ($TStructType:ident) => {
32        impl IsApproxEqual<$TStructType> for Asserter<$TStructType> {
33            fn is_approx_equal(&self, expected: $TStructType, delta: $TStructType) {
34                abs_diff_signed_eq!(self.value, expected, delta);
35            }
36        }
37    };
38}
39
40generate_is_approx_equal_impl_for_signed!(i8);
41generate_is_approx_equal_impl_for_signed!(i16);
42generate_is_approx_equal_impl_for_signed!(i32);
43generate_is_approx_equal_impl_for_signed!(i64);
44generate_is_approx_equal_impl_for_signed!(i128);
45
46macro_rules! generate_is_approx_equal_impl_for_unsigned {
47    ($TStructType:ident) => {
48        impl IsApproxEqual<$TStructType> for Asserter<$TStructType> {
49            fn is_approx_equal(&self, expected: $TStructType, delta: $TStructType) {
50                abs_diff_unsigned_eq!(self.value, expected, delta);
51            }
52        }
53    };
54}
55
56generate_is_approx_equal_impl_for_unsigned!(u8);
57generate_is_approx_equal_impl_for_unsigned!(u16);
58generate_is_approx_equal_impl_for_unsigned!(u32);
59generate_is_approx_equal_impl_for_unsigned!(u64);
60generate_is_approx_equal_impl_for_unsigned!(u128);
61
62macro_rules! get_length_of_rounder {
63    ($delta:expr) => {
64        $delta
65            .to_string()
66            .split('.')
67            .last()
68            .unwrap()
69            .len()
70            .to_string()
71            .parse()
72            .unwrap()
73    };
74}
75
76macro_rules! round {
77    ($TStructType:ident, $diff:expr,$rounder:expr) => {{
78        let number: $TStructType = format!("{}", $diff).parse().unwrap();
79        let number: $TStructType = (number * $rounder).round() / $rounder;
80
81        return number;
82    }};
83}
84
85impl IsApproxEqual<f64> for Asserter<f64> {
86    fn is_approx_equal(&self, expected_value: f64, delta: f64) {
87        let rounder = 10f64.pow(get_length_of_rounder_f64(delta));
88
89        let diff = abs_diff!(self.value, expected_value);
90
91        let diff_f64 = round_f64(diff, rounder);
92        let delta_f64 = round_f64(delta, rounder);
93
94        if diff_f64 > delta_f64 {
95            panic!(
96                "The number '{}' is not approximately equal to '{}' within delta '{}'",
97                self.name, expected_value, delta
98            )
99        }
100    }
101}
102
103impl IsApproxEqual<f32> for Asserter<f32> {
104    fn is_approx_equal(&self, expected_value: f32, delta: f32) {
105        let rounder = 10f32.pow(get_length_of_rounder_f32(delta));
106
107        let diff = abs_diff!(self.value, expected_value);
108
109        let diff_f32 = round_f32(diff, rounder);
110        let delta_f32 = round_f32(delta, rounder);
111
112        if diff_f32 > delta_f32 {
113            panic!(
114                "The number '{}' is not approximately equal to '{}' within delta '{}'",
115                self.name, expected_value, delta
116            )
117        }
118    }
119}
120
121fn get_length_of_rounder_f64<T>(delta: T) -> f64
122where
123    T: ToString,
124{
125    return get_length_of_rounder!(delta);
126}
127
128fn get_length_of_rounder_f32<T>(delta: T) -> f32
129where
130    T: ToString,
131{
132    return get_length_of_rounder!(delta);
133}
134
135fn round_f64<T>(diff: T, rounder: f64) -> f64
136where
137    T: std::fmt::Display,
138{
139    round!(f64, diff, rounder)
140}
141
142fn round_f32<T>(diff: T, rounder: f32) -> f32
143where
144    T: std::fmt::Display,
145{
146    round!(f32, diff, rounder)
147}