1use std::collections::HashMap;
2use std::fmt;
3use std::str::FromStr;
4
5pub type Record<'a> = HashMap<&'a str, &'a str>;
8
9pub trait RecordExt {
11 fn get_or_default(&self, key: &str) -> &str;
13}
14
15impl RecordExt for Record<'_> {
16 fn get_or_default(&self, key: &str) -> &str {
17 self.get(key).map_or("", |v| v)
18 }
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
26pub enum Level {
27 Debug = 0,
29 Info = 1,
31 Warning = 2,
33 Error = 3,
35 Critical = 4,
37}
38
39impl Level {
40 pub fn to_str(&self) -> &str {
42 match self {
43 Level::Debug => "DEBUG",
44 Level::Info => "INFO",
45 Level::Warning => "WARNING",
46 Level::Error => "ERROR",
47 Level::Critical => "CRITICAL",
48 }
49 }
50 pub fn to_u8(self) -> u8 {
52 self as u8
53 }
54 pub fn from_u8(u8: u8) -> Level {
56 match u8 {
57 0 => Level::Debug,
58 1 => Level::Info,
59 2 => Level::Warning,
60 3 => Level::Error,
61 4 => Level::Critical,
62 _ => panic!("Invalid level"),
63 }
64 }
65}
66
67#[derive(Debug, Clone, PartialEq, Eq)]
69pub struct ParseLevelError {
70 pub input: String,
72}
73
74impl fmt::Display for ParseLevelError {
75 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76 write!(f, "invalid level: {}", self.input)
77 }
78}
79
80impl std::error::Error for ParseLevelError {}
81
82impl FromStr for Level {
83 type Err = ParseLevelError;
84
85 fn from_str(s: &str) -> Result<Level, Self::Err> {
88 match s {
89 "DEBUG" => Ok(Level::Debug),
90 "INFO" => Ok(Level::Info),
91 "WARNING" => Ok(Level::Warning),
92 "ERROR" => Ok(Level::Error),
93 "CRITICAL" => Ok(Level::Critical),
94 _ => Err(ParseLevelError {
95 input: s.to_string(),
96 }),
97 }
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104 use std::collections::HashMap;
105
106 #[test]
107 fn record_get_or_default() {
108 let mut record: Record = HashMap::new();
109 record.insert("a", "1");
110 assert_eq!(record.get_or_default("a"), "1");
111 assert_eq!(record.get_or_default("missing"), "");
112 }
113
114 #[test]
115 fn level_ordering_is_by_severity() {
116 assert!(Level::Debug < Level::Info);
117 assert!(Level::Info < Level::Warning);
118 assert!(Level::Warning < Level::Error);
119 assert!(Level::Error < Level::Critical);
120 }
121
122 #[test]
123 fn level_u8_round_trips() {
124 for level in [
125 Level::Debug,
126 Level::Info,
127 Level::Warning,
128 Level::Error,
129 Level::Critical,
130 ] {
131 assert_eq!(Level::from_u8(level.to_u8()), level);
132 }
133 }
134
135 #[test]
136 fn level_str_round_trips() {
137 for level in [
138 Level::Debug,
139 Level::Info,
140 Level::Warning,
141 Level::Error,
142 Level::Critical,
143 ] {
144 assert_eq!(level.to_str().parse::<Level>().unwrap(), level);
146 }
147 }
148
149 #[test]
150 fn level_from_str_rejects_unknown() {
151 assert!("NOPE".parse::<Level>().is_err());
152 }
153
154 #[test]
155 #[should_panic]
156 fn level_from_u8_panics_on_out_of_range() {
157 Level::from_u8(42);
158 }
159}