1use core::fmt;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
23#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24#[repr(u8)]
25pub enum Severity {
26 Error = b'E',
28 Warning = b'W',
30 Critical = b'C',
32 Blocked = b'B',
34 Help = b'H',
36 Success = b'S',
38 Completed = b'K',
40 Info = b'I',
42 Trace = b'T',
44}
45
46impl Severity {
47 pub const fn as_char(self) -> char {
49 self as u8 as char
50 }
51
52 pub const fn as_str(self) -> &'static str {
54 match self {
55 Severity::Error => "Error",
56 Severity::Warning => "Warning",
57 Severity::Critical => "Critical",
58 Severity::Blocked => "Blocked",
59 Severity::Help => "Help",
60 Severity::Success => "Success",
61 Severity::Completed => "Completed",
62 Severity::Info => "Info",
63 Severity::Trace => "Trace",
64 }
65 }
66
67 pub const fn description(self) -> &'static str {
69 match self {
70 Severity::Error => "Operation failed",
71 Severity::Warning => "Potential issue or caveat",
72 Severity::Critical => "Severe issue requiring attention",
73 Severity::Blocked => "Execution blocked or waiting",
74 Severity::Help => "Helpful suggestion or recommendation",
75 Severity::Success => "Operation succeeded",
76 Severity::Completed => "Task or phase completed",
77 Severity::Info => "General informational events",
78 Severity::Trace => "Execution traces and instrumentation",
79 }
80 }
81
82 pub const fn priority(self) -> u8 {
91 match self {
92 Severity::Trace => 0,
93 Severity::Info => 1,
94 Severity::Completed => 2,
95 Severity::Success => 3,
96 Severity::Help => 4,
97 Severity::Warning => 5,
98 Severity::Critical => 6,
99 Severity::Blocked => 7,
100 Severity::Error => 8,
101 }
102 }
103
104 pub const fn is_blocking(self) -> bool {
109 matches!(self, Severity::Error | Severity::Blocked)
110 }
111
112 pub const fn is_positive(self) -> bool {
114 matches!(self, Severity::Success | Severity::Completed)
115 }
116
117 pub const fn is_negative(self) -> bool {
119 matches!(
120 self,
121 Severity::Error | Severity::Warning | Severity::Critical | Severity::Blocked
122 )
123 }
124
125 pub const fn is_neutral(self) -> bool {
127 matches!(self, Severity::Info | Severity::Trace)
128 }
129
130 #[cfg(feature = "emoji")]
157 pub const fn emoji(self) -> &'static str {
158 match self {
159 Severity::Error => "â",
160 Severity::Blocked => "đĢ",
161 Severity::Critical => "đĨ",
162 Severity::Warning => "â ī¸",
163 Severity::Help => "đĄ",
164 Severity::Success => "â
",
165 Severity::Completed => "âī¸",
166 Severity::Info => "âšī¸",
167 Severity::Trace => "đ",
168 }
169 }
170
171 #[cfg(feature = "ansi-colors")]
187 pub const fn ansi_color(self) -> &'static str {
188 match self {
189 Severity::Error => "\x1b[1;31m", Severity::Blocked => "\x1b[31m", Severity::Critical => "\x1b[1;33m", Severity::Warning => "\x1b[33m", Severity::Help => "\x1b[32m", Severity::Success => "\x1b[1;32m", Severity::Completed => "\x1b[32m", Severity::Info => "\x1b[36m", Severity::Trace => "\x1b[2;34m", }
199 }
200
201 #[cfg(feature = "ansi-colors")]
203 pub const ANSI_RESET: &'static str = "\x1b[0m";
204
205 pub const fn hex_color(self) -> &'static str {
222 match self {
223 Severity::Error => "#ce2c31",
224 Severity::Blocked => "#dc3e42",
225 Severity::Critical => "#cc4e00",
226 Severity::Warning => "#ab6400",
227 Severity::Help => "#218358",
228 Severity::Success => "#30a46c",
229 Severity::Completed => "#008573",
230 Severity::Info => "#0d74ce",
231 Severity::Trace => "#60646c",
232 }
233 }
234
235 pub const fn hex_color_dark(self) -> &'static str {
252 match self {
253 Severity::Error => "#ff9592",
254 Severity::Blocked => "#ec5d5e",
255 Severity::Critical => "#ffa057",
256 Severity::Warning => "#ffca16",
257 Severity::Help => "#3dd68c",
258 Severity::Success => "#33b074",
259 Severity::Completed => "#0bd8b6",
260 Severity::Info => "#70b8ff",
261 Severity::Trace => "#b0b4ba",
262 }
263 }
264
265 pub const fn hex_bg_color(self) -> &'static str {
270 match self {
271 Severity::Error => "#feebec",
272 Severity::Blocked => "#ffdbdc",
273 Severity::Critical => "#ffefd6",
274 Severity::Warning => "#fff7c2",
275 Severity::Help => "#e6f6eb",
276 Severity::Success => "#d6f1df",
277 Severity::Completed => "#e0f8f3",
278 Severity::Info => "#e6f4fe",
279 Severity::Trace => "#f0f0f3",
280 }
281 }
282
283 pub const fn hex_bg_color_dark(self) -> &'static str {
288 match self {
289 Severity::Error => "#3b1219",
290 Severity::Blocked => "#500f1c",
291 Severity::Critical => "#331e0b",
292 Severity::Warning => "#302008",
293 Severity::Help => "#132d21",
294 Severity::Success => "#113b29",
295 Severity::Completed => "#0d2d2a",
296 Severity::Info => "#0d2847",
297 Severity::Trace => "#212225",
298 }
299 }
300}
301
302impl fmt::Display for Severity {
303 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
304 write!(f, "{}", self.as_char())
305 }
306}
307
308impl PartialOrd for Severity {
309 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
310 Some(self.cmp(other))
311 }
312}
313
314impl Ord for Severity {
315 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
316 self.priority().cmp(&other.priority())
317 }
318}
319
320#[cfg(test)]
321mod tests {
322 use super::*;
323
324 #[test]
325 fn test_severity_ordering() {
326 assert!(Severity::Trace.priority() < Severity::Error.priority());
327 assert!(Severity::Warning.priority() < Severity::Critical.priority());
328 }
329
330 #[test]
331 fn test_severity_comparison() {
332 assert!(Severity::Trace < Severity::Error);
333 assert!(Severity::Info < Severity::Warning);
334 assert!(Severity::Warning < Severity::Critical);
335 assert!(Severity::Critical < Severity::Blocked);
336 assert!(Severity::Blocked < Severity::Error);
337 }
338
339 #[test]
340 fn test_severity_blocking() {
341 assert!(Severity::Error.is_blocking());
343 assert!(Severity::Blocked.is_blocking());
344
345 assert!(!Severity::Critical.is_blocking());
347 assert!(!Severity::Warning.is_blocking());
348 assert!(!Severity::Success.is_blocking());
349 assert!(!Severity::Completed.is_blocking());
350 assert!(!Severity::Info.is_blocking());
351 assert!(!Severity::Trace.is_blocking());
352 }
353
354 #[test]
355 fn test_severity_categorization() {
356 assert!(Severity::Success.is_positive());
358 assert!(Severity::Completed.is_positive());
359
360 assert!(Severity::Error.is_negative());
362 assert!(Severity::Warning.is_negative());
363 assert!(Severity::Critical.is_negative());
364 assert!(Severity::Blocked.is_negative());
365
366 assert!(Severity::Info.is_neutral());
368 assert!(Severity::Trace.is_neutral());
369
370 assert!(!Severity::Error.is_positive());
372 assert!(!Severity::Success.is_negative());
373 assert!(!Severity::Info.is_positive());
374 assert!(!Severity::Info.is_negative());
375 }
376
377 #[test]
378 fn test_severity_metadata() {
379 assert_eq!(Severity::Error.as_str(), "Error");
380 assert_eq!(Severity::Warning.as_str(), "Warning");
381 assert_eq!(Severity::Critical.as_str(), "Critical");
382
383 assert_eq!(Severity::Error.description(), "Operation failed");
384 assert_eq!(Severity::Warning.description(), "Potential issue or caveat");
385
386 assert_eq!(Severity::Error.as_char(), 'E');
387 assert_eq!(Severity::Warning.as_char(), 'W');
388 }
389
390 #[test]
391 fn test_severity_positive() {
392 assert!(Severity::Success.is_positive());
393 assert!(Severity::Completed.is_positive());
394 assert!(!Severity::Error.is_positive());
395 assert!(!Severity::Warning.is_positive());
396 assert!(!Severity::Critical.is_positive());
397 assert!(!Severity::Blocked.is_positive());
398 assert!(!Severity::Info.is_positive());
399 assert!(!Severity::Trace.is_positive());
400 }
401
402 #[cfg(feature = "emoji")]
403 #[test]
404 fn test_emoji() {
405 assert_eq!(Severity::Error.emoji(), "â");
406 assert_eq!(Severity::Warning.emoji(), "â ī¸");
407 assert_eq!(Severity::Critical.emoji(), "đĨ");
408 assert_eq!(Severity::Success.emoji(), "â
");
409 assert_eq!(Severity::Completed.emoji(), "âī¸");
410 assert_eq!(Severity::Info.emoji(), "âšī¸");
411 assert_eq!(Severity::Trace.emoji(), "đ");
412 assert_eq!(Severity::Blocked.emoji(), "đĢ");
413 }
414
415 #[cfg(feature = "ansi-colors")]
416 #[test]
417 fn test_ansi_colors() {
418 assert_eq!(Severity::Error.ansi_color(), "\x1b[1;31m");
419 assert_eq!(Severity::Warning.ansi_color(), "\x1b[33m");
420 assert_eq!(Severity::Success.ansi_color(), "\x1b[1;32m");
421 assert_eq!(Severity::ANSI_RESET, "\x1b[0m");
422 }
423
424 #[cfg(feature = "serde")]
425 #[test]
426 fn test_serde() {
427 use serde_json;
428
429 let severity = Severity::Error;
430 let json = serde_json::to_string(&severity).unwrap();
431 let deserialized: Severity = serde_json::from_str(&json).unwrap();
432 assert_eq!(severity, deserialized);
433 }
434}