Skip to main content

rules/
severity.rs

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}