1use serde::{Deserialize, Serialize};
2use std::fmt;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Hash, Default)]
5#[cfg_attr(feature = "wasm-bindings", derive(tsify::Tsify))]
6#[cfg_attr(feature = "uniffi-bindings", derive(uniffi::Enum))]
7#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
8pub enum Severity {
9 Debug = 0,
10 #[default]
11 Info = 1,
12 Warn = 2,
13 Error = 3,
14 Fatal = 4,
15}
16
17impl Severity {
18 pub const fn as_str(&self) -> &'static str {
19 match self {
20 Severity::Fatal => "FATAL",
21 Severity::Error => "ERROR",
22 Severity::Warn => "WARN",
23 Severity::Info => "INFO",
24 Severity::Debug => "DEBUG",
25 }
26 }
27
28 pub fn from_prefix(c: char) -> Severity {
29 match c.to_ascii_uppercase() {
30 'F' => Severity::Fatal,
31 'E' => Severity::Error,
32 'W' => Severity::Warn,
33 'I' => Severity::Info,
34 'D' => Severity::Debug,
35 _ => panic!("Invalid severity prefix: {}", c),
36 }
37 }
38}
39
40impl std::str::FromStr for Severity {
41 type Err = String;
42
43 fn from_str(s: &str) -> Result<Self, Self::Err> {
44 match s.to_uppercase().as_str() {
45 "FATAL" => Ok(Severity::Fatal),
46 "ERROR" => Ok(Severity::Error),
47 "WARN" => Ok(Severity::Warn),
48 "INFO" => Ok(Severity::Info),
49 "DEBUG" => Ok(Severity::Debug),
50 _ => Err(format!("Invalid severity '{s}'; expected one of: fatal, error, warn, info, debug")),
51 }
52 }
53}
54
55impl fmt::Display for Severity {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 f.write_str(self.as_str())
58 }
59}
60
61#[cfg(test)]
62mod tests {
63 use crate::Severity;
64 use std::str::FromStr;
65
66 #[test]
67 fn as_str_returns_screaming_case_for_all_variants() {
68 assert_eq!(Severity::Fatal.as_str(), "FATAL");
69 assert_eq!(Severity::Error.as_str(), "ERROR");
70 assert_eq!(Severity::Warn.as_str(), "WARN");
71 assert_eq!(Severity::Info.as_str(), "INFO");
72 assert_eq!(Severity::Debug.as_str(), "DEBUG");
73 }
74
75 #[test]
76 fn from_str_parses_case_insensitively() {
77 assert_eq!(Severity::from_str("fatal").unwrap(), Severity::Fatal);
78 assert_eq!(Severity::from_str("FATAL").unwrap(), Severity::Fatal);
79
80 assert_eq!(Severity::from_str("error").unwrap(), Severity::Error);
81 assert_eq!(Severity::from_str("ERROR").unwrap(), Severity::Error);
82
83 assert_eq!(Severity::from_str("warn").unwrap(), Severity::Warn);
84 assert_eq!(Severity::from_str("WARN").unwrap(), Severity::Warn);
85
86 assert_eq!(Severity::from_str("info").unwrap(), Severity::Info);
87 assert_eq!(Severity::from_str("INFO").unwrap(), Severity::Info);
88
89 assert_eq!(Severity::from_str("debug").unwrap(), Severity::Debug);
90 assert_eq!(Severity::from_str("DEBUG").unwrap(), Severity::Debug);
91 }
92
93 #[test]
94 fn from_str_errors_on_unknown_value() {
95 let err = Severity::from_str("test").unwrap_err();
96 assert_eq!(err, "Invalid severity 'test'; expected one of: fatal, error, warn, info, debug");
97 }
98
99 #[test]
100 fn from_prefix_maps_all_valid_chars_case_insensitively() {
101 assert_eq!(Severity::from_prefix('f'), Severity::Fatal);
102 assert_eq!(Severity::from_prefix('F'), Severity::Fatal);
103
104 assert_eq!(Severity::from_prefix('e'), Severity::Error);
105 assert_eq!(Severity::from_prefix('E'), Severity::Error);
106
107 assert_eq!(Severity::from_prefix('w'), Severity::Warn);
108 assert_eq!(Severity::from_prefix('W'), Severity::Warn);
109
110 assert_eq!(Severity::from_prefix('i'), Severity::Info);
111 assert_eq!(Severity::from_prefix('I'), Severity::Info);
112
113 assert_eq!(Severity::from_prefix('d'), Severity::Debug);
114 assert_eq!(Severity::from_prefix('D'), Severity::Debug);
115 }
116
117 #[test]
118 #[should_panic(expected = "Invalid severity prefix: t")]
119 fn from_prefix_panics_on_invalid_char() {
120 Severity::from_prefix('t');
121 }
122
123 #[test]
124 fn default() {
125 assert_eq!(Severity::default(), Severity::Info);
126 }
127
128 #[test]
129 fn ordering_ranks_debug_lowest_and_fatal_highest() {
130 assert!(Severity::Debug < Severity::Info, "Debug should rank below Info");
131 assert!(Severity::Info < Severity::Warn, "Info should rank below Warn");
132 assert!(Severity::Warn < Severity::Error, "Warn should rank below Error");
133 assert!(Severity::Error < Severity::Fatal, "Error should rank below Fatal");
134 }
135}