1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
//! ## `Tolerance` and `DeltaEq` traits
//!
//! This module deals with comparing two colors by [`DeltaE`] within a certain [`Tolerance`].
//!
//! See also: [`assert_delta_eq`]
//!
//! ### Implementing `Tolerance` and `DeltaEq`
//!
//! ```
//! use deltae::*;
//!
//! struct MyTolerance(f32);
//!
//! impl Tolerance for MyTolerance {
//!     fn tolerance(self) -> f32 {
//!         self.0
//!     }
//! }
//!
//! #[derive(Copy, Clone)]
//! struct MyLab(f32, f32, f32);
//!
//! // Types that implement Into<Lab> also implement the Delta trait
//! impl From<MyLab> for LabValue {
//!     fn from(mylab: MyLab) -> LabValue {
//!         LabValue {
//!             l: mylab.0,
//!             a: mylab.1,
//!             b: mylab.2,
//!         }
//!     }
//! }
//!
//! impl<D: Delta + Copy> DeltaEq<D> for MyLab {}
//!
//! let mylab = MyLab(89.73, 1.88, -6.96);
//! let lab = LabValue::new(89.73, 1.88, -6.96).unwrap();
//! let de2000 = mylab.delta(lab, DEMethod::DE2000);
//! assert!(mylab.delta_eq(&lab, DE1976, 0.0));
//! ```
use crate::*;

/// Trait to determine whether two values are within a certain tolerance of [`DeltaE`]. Types that
/// implement Into<[`LabValue`]> implicitly implement [`Delta`]. Types that implement [`Delta`] and
/// [`Copy`] may also implement DeltaEq for other types that also implement [`Delta`] and [`Copy`].
/// ```
/// use deltae::*;
///
/// #[derive(Copy, Clone)]
/// struct MyLab(f32, f32, f32);
///
/// // Types that implement Into<Lab> implicitly implement the Delta trait
/// impl From<MyLab> for LabValue {
///     fn from(mylab: MyLab) -> LabValue {
///         LabValue {
///             l: mylab.0,
///             a: mylab.1,
///             b: mylab.2,
///         }
///     }
/// }
///
/// // Types that implement Delta and Copy may also implement DeltaEq for other types that also
/// // implement Delta and Copy
/// impl<D: Delta + Copy> DeltaEq<D> for MyLab {}
///
/// let mylab = MyLab(89.73, 1.88, -6.96);
/// let lab = LabValue::new(89.73, 1.88, -6.96).unwrap();
/// let de2000 = mylab.delta(lab, DEMethod::DE2000);
/// assert!(mylab.delta_eq(&lab, DE1976, 0.0));
/// ```
pub trait DeltaEq<D: Delta + Copy>: Delta + Copy {
    /// Return true if the value is less than or equal to the [`Tolerance`]
    fn delta_eq<T: Tolerance>(&self, other: D, method: DEMethod, tolerance: T) -> bool {
        self.delta(other, method).value() <= &tolerance.tolerance()
    }
}

/// Convenience macro for asserting two values are equivalent within a tolerance
/// ```
/// use deltae::*;
///
/// let lab0 = LabValue::new(50.0, 0.0, 0.0).unwrap();
/// let lab1 = LabValue::new(50.1, 0.1, 0.1).unwrap();
///
/// // Assert that the difference between lab0 and lab1 is less than 1.0 DE2000
/// assert_delta_eq!(lab0, lab1, DE2000, 1.0);
/// ```
#[macro_export]
macro_rules! assert_delta_eq {
    ($reference:expr, $sample:expr, $method:expr, $tolerance:expr) => {
        assert!($reference.delta_eq($sample, $method, $tolerance))
    };
    ($reference:expr, $sample:expr, $method:expr, $tolerance:expr, $($message:tt)*) => {
        assert!($reference.delta_eq($sample, $method, $tolerance), $($message)*)
    };
}

/// Convenience macro for asserting two values are equivalent within a tolerance
/// ```
/// use deltae::*;
///
/// let lab0 = LabValue::new(50.0, 0.0, 0.0).unwrap();
/// let lab1 = LabValue::new(50.1, 1.0, 1.0).unwrap();
///
/// // Assert that the difference between lab0 and lab1 is greater than 1.0 DE2000
/// assert_delta_ne!(lab0, lab1, DE2000, 1.0);
/// ```
#[macro_export]
macro_rules! assert_delta_ne {
    ($reference:expr, $sample:expr, $method:expr, $tolerance:expr) => {
        assert!(!$reference.delta_eq($sample, $method, $tolerance))
    };
    ($reference:expr, $sample:expr, $method:expr, $tolerance:expr, $($message:tt)*) => {
        assert!(!$reference.delta_eq($sample, $method, $tolerance), $($message)*)
    };
}

/// Trait to define a tolerance value for the [`DeltaEq`] trait
pub trait Tolerance {
    /// Return a tolerance value
    fn tolerance(self) -> f32;
}

impl Tolerance for f32 {
    fn tolerance(self) -> f32 {
        self
    }
}

impl Tolerance for f64 {
    fn tolerance(self) -> f32 {
        self as f32
    }
}

impl Tolerance for DeltaE {
    fn tolerance(self) -> f32 {
        self.value
    }
}

macro_rules! impl_delta_eq {
    ($t:ty) => {
        impl<D: Delta + Copy> DeltaEq<D> for $t {}
    }
}

impl_delta_eq!(LabValue);
impl_delta_eq!(LchValue);
impl_delta_eq!(XyzValue);