konnektoren_core/analytics/
trend.rs1use strum_macros::Display;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Hash, Display)]
4pub enum Trend {
5 Improving,
6 #[default]
7 Stable,
8 Declining,
9}
10
11impl Trend {
12 const EPSILON: f64 = 0.0001;
13
14 pub fn from_value(value: f64) -> Self {
15 if value > Self::EPSILON {
16 Trend::Improving
17 } else if value < -Self::EPSILON {
18 Trend::Declining
19 } else {
20 Trend::Stable
21 }
22 }
23}
24
25impl PartialOrd for Trend {
26 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
27 match (self, other) {
28 (Trend::Improving, Trend::Improving) => Some(std::cmp::Ordering::Equal),
30 (Trend::Improving, _) => Some(std::cmp::Ordering::Greater),
31
32 (Trend::Stable, Trend::Improving) => Some(std::cmp::Ordering::Less),
34 (Trend::Stable, Trend::Stable) => Some(std::cmp::Ordering::Equal),
35 (Trend::Stable, Trend::Declining) => Some(std::cmp::Ordering::Greater),
36
37 (Trend::Declining, Trend::Declining) => Some(std::cmp::Ordering::Equal),
39 (Trend::Declining, _) => Some(std::cmp::Ordering::Less),
40 }
41 }
42}
43
44impl Ord for Trend {
45 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
46 self.partial_cmp(other).unwrap()
47 }
48}
49
50#[cfg(test)]
51mod tests {
52 use super::*;
53
54 #[test]
55 fn test_trend_from_value() {
56 assert_eq!(Trend::from_value(1.5), Trend::Improving);
57 assert_eq!(Trend::from_value(-1.5), Trend::Declining);
58 assert_eq!(Trend::from_value(0.0), Trend::Stable);
59 assert_eq!(Trend::from_value(0.001), Trend::Improving);
60 assert_eq!(Trend::from_value(-0.001), Trend::Declining);
61 }
62
63 #[test]
64 fn test_trend_ordering() {
65 assert!(Trend::Improving > Trend::Stable);
67 assert!(Trend::Improving > Trend::Declining);
68 assert!(Trend::Improving == Trend::Improving);
69
70 assert!(Trend::Stable < Trend::Improving);
72 assert!(Trend::Stable == Trend::Stable);
73 assert!(Trend::Stable > Trend::Declining);
74
75 assert!(Trend::Declining < Trend::Improving);
77 assert!(Trend::Declining < Trend::Stable);
78 assert!(Trend::Declining == Trend::Declining);
79 }
80
81 #[test]
82 fn test_trend_default() {
83 assert_eq!(Trend::default(), Trend::Stable);
84 }
85
86 #[test]
87 fn test_trend_sorting() {
88 let mut trends = vec![
89 Trend::Declining,
90 Trend::Improving,
91 Trend::Stable,
92 Trend::Improving,
93 Trend::Declining,
94 ];
95 trends.sort();
96
97 assert_eq!(
98 trends,
99 vec![
100 Trend::Declining,
101 Trend::Declining,
102 Trend::Stable,
103 Trend::Improving,
104 Trend::Improving,
105 ]
106 );
107 }
108
109 #[test]
110 fn test_trend_clone_and_copy() {
111 let trend = Trend::Improving;
112 let cloned = trend;
113 assert_eq!(trend, cloned);
114
115 let copied = trend;
116 assert_eq!(trend, copied);
117 }
118
119 #[test]
120 fn test_trend_debug_format() {
121 assert_eq!(format!("{:?}", Trend::Improving), "Improving");
122 assert_eq!(format!("{:?}", Trend::Stable), "Stable");
123 assert_eq!(format!("{:?}", Trend::Declining), "Declining");
124 }
125
126 #[test]
127 fn test_trend_hash() {
128 use std::collections::HashSet;
129
130 let mut set = HashSet::new();
131 set.insert(Trend::Improving);
132 set.insert(Trend::Stable);
133 set.insert(Trend::Declining);
134 set.insert(Trend::Improving); assert_eq!(set.len(), 3);
137 assert!(set.contains(&Trend::Improving));
138 assert!(set.contains(&Trend::Stable));
139 assert!(set.contains(&Trend::Declining));
140 }
141
142 #[test]
143 fn test_trend_partial_ord() {
144 let trends = [Trend::Improving, Trend::Stable, Trend::Declining];
146
147 for &t1 in &trends {
148 for &t2 in &trends {
149 assert!(t1.partial_cmp(&t2).is_some());
151
152 if t1 == t2 {
154 assert_eq!(t1.partial_cmp(&t2), Some(std::cmp::Ordering::Equal));
155 }
156
157 if t1 > t2 {
159 assert_eq!(t1.partial_cmp(&t2), Some(std::cmp::Ordering::Greater));
160 } else if t1 < t2 {
161 assert_eq!(t1.partial_cmp(&t2), Some(std::cmp::Ordering::Less));
162 }
163 }
164 }
165 }
166}