garbage_code_hunter/common/
severity.rs1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
7pub enum Severity {
8 Critical,
9 High,
10 Medium,
11 Low,
12 Info,
13}
14
15impl Severity {
16 pub fn emoji(&self) -> &'static str {
18 match self {
19 Severity::Critical => "\u{1f480}",
20 Severity::High => "\u{1f621}",
21 Severity::Medium => "\u{26a0}\u{fe0f}",
22 Severity::Low => "\u{1f4a7}",
23 Severity::Info => "\u{2139}\u{fe0f}",
24 }
25 }
26
27 pub fn penalty(&self) -> f64 {
29 match self {
30 Severity::Critical => 10.0,
31 Severity::High => 5.0,
32 Severity::Medium => 2.0,
33 Severity::Low => 0.5,
34 Severity::Info => 0.0,
35 }
36 }
37
38 pub fn weight(&self) -> f64 {
40 self.penalty()
41 }
42
43 pub fn label(&self) -> &'static str {
45 match self {
46 Severity::Critical => "Critical",
47 Severity::High => "High",
48 Severity::Medium => "Medium",
49 Severity::Low => "Low",
50 Severity::Info => "Info",
51 }
52 }
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58
59 #[test]
63 fn test_severity_ordering_adjacent() {
64 assert!(Severity::Critical < Severity::High, "Critical < High");
65 assert!(Severity::High < Severity::Medium, "High < Medium");
66 assert!(Severity::Medium < Severity::Low, "Medium < Low");
67 assert!(Severity::Low < Severity::Info, "Low < Info");
68 }
69
70 #[test]
72 fn test_severity_ordering_transitive() {
73 assert!(Severity::Critical < Severity::Medium, "Critical < Medium");
74 assert!(Severity::Critical < Severity::Low, "Critical < Low");
75 assert!(Severity::Critical < Severity::Info, "Critical < Info");
76 assert!(Severity::High < Severity::Low, "High < Low");
77 assert!(Severity::High < Severity::Info, "High < Info");
78 assert!(Severity::Medium < Severity::Info, "Medium < Info");
79 }
80
81 #[test]
83 fn test_severity_ordering_equal() {
84 assert_eq!(Severity::Critical, Severity::Critical);
85 assert_eq!(Severity::High, Severity::High);
86 assert_eq!(Severity::Medium, Severity::Medium);
87 assert_eq!(Severity::Low, Severity::Low);
88 assert_eq!(Severity::Info, Severity::Info);
89 }
90
91 #[test]
95 fn test_emoji_specific_values() {
96 assert_eq!(Severity::Critical.emoji(), "\u{1f480}", "Critical → skull");
97 assert_eq!(Severity::High.emoji(), "\u{1f621}", "High → angry");
98 assert_eq!(
99 Severity::Medium.emoji(),
100 "\u{26a0}\u{fe0f}",
101 "Medium → warning"
102 );
103 assert_eq!(Severity::Low.emoji(), "\u{1f4a7}", "Low → droplet");
104 assert_eq!(Severity::Info.emoji(), "\u{2139}\u{fe0f}", "Info → info");
105 }
106
107 #[test]
111 fn test_penalty_values() {
112 assert_eq!(Severity::Critical.penalty(), 10.0);
113 assert_eq!(Severity::High.penalty(), 5.0);
114 assert_eq!(Severity::Medium.penalty(), 2.0);
115 assert_eq!(Severity::Low.penalty(), 0.5);
116 assert_eq!(Severity::Info.penalty(), 0.0);
117 }
118
119 #[test]
121 fn test_penalty_ordering_matches_severity() {
122 assert!(Severity::Critical.penalty() > Severity::High.penalty());
123 assert!(Severity::High.penalty() > Severity::Medium.penalty());
124 assert!(Severity::Medium.penalty() > Severity::Low.penalty());
125 assert!(Severity::Low.penalty() > Severity::Info.penalty());
126 }
127
128 #[test]
132 fn test_weight_is_alias_for_penalty() {
133 for sev in [
134 Severity::Critical,
135 Severity::High,
136 Severity::Medium,
137 Severity::Low,
138 Severity::Info,
139 ] {
140 assert_eq!(
141 sev.weight(),
142 sev.penalty(),
143 "{:?}.weight() should equal .penalty()",
144 sev
145 );
146 }
147 }
148
149 #[test]
153 fn test_label_all_variants() {
154 assert_eq!(Severity::Critical.label(), "Critical");
155 assert_eq!(Severity::High.label(), "High");
156 assert_eq!(Severity::Medium.label(), "Medium");
157 assert_eq!(Severity::Low.label(), "Low");
158 assert_eq!(Severity::Info.label(), "Info");
159 }
160
161 #[test]
163 fn test_label_unique() {
164 let labels: Vec<&str> = [
165 Severity::Critical,
166 Severity::High,
167 Severity::Medium,
168 Severity::Low,
169 Severity::Info,
170 ]
171 .iter()
172 .map(|s| s.label())
173 .collect();
174 let unique: std::collections::HashSet<&&str> = labels.iter().collect();
175 assert_eq!(unique.len(), labels.len(), "all labels must be unique");
176 }
177
178 #[test]
182 fn test_severity_serde_roundtrip() {
183 for sev in [
184 Severity::Critical,
185 Severity::High,
186 Severity::Medium,
187 Severity::Low,
188 Severity::Info,
189 ] {
190 let json = serde_json::to_string(&sev).unwrap();
191 let parsed: Severity = serde_json::from_str(&json).unwrap();
192 assert_eq!(parsed, sev, "{sev:?} round-trips through JSON");
193 }
194 }
195
196 #[test]
199 fn test_severity_serde_from_string() {
200 let parsed: Severity = serde_json::from_str("\"Critical\"").unwrap();
202 assert_eq!(parsed, Severity::Critical);
203 let parsed: Severity = serde_json::from_str("\"Info\"").unwrap();
204 assert_eq!(parsed, Severity::Info);
205 }
206}