1use colored::*;
2use serde::{Deserialize, Serialize};
3use std::cmp::Ordering;
4use std::fmt;
5use std::str::FromStr;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
10pub enum LogLevel {
11 Trace,
13 Debug,
15 Info,
17 Success,
19 Warning,
21 Error,
23 Critical,
25}
26
27impl LogLevel {
28 pub fn as_str(&self) -> &'static str {
30 match self {
31 LogLevel::Trace => "TRACE",
32 LogLevel::Debug => "DEBUG",
33 LogLevel::Info => "INFO",
34 LogLevel::Success => "SUCCESS",
35 LogLevel::Warning => "WARNING",
36 LogLevel::Error => "ERROR",
37 LogLevel::Critical => "CRITICAL",
38 }
39 }
40
41 pub fn as_u8(&self) -> u8 {
44 match self {
45 LogLevel::Trace => 5,
46 LogLevel::Debug => 10,
47 LogLevel::Info => 20,
48 LogLevel::Success => 25,
49 LogLevel::Warning => 30,
50 LogLevel::Error => 40,
51 LogLevel::Critical => 50,
52 }
53 }
54
55 pub fn color(&self) -> &'static str {
57 match self {
58 LogLevel::Trace => "\x1b[37m", LogLevel::Debug => "\x1b[34m", LogLevel::Info => "\x1b[32m", LogLevel::Success => "\x1b[36m", LogLevel::Warning => "\x1b[33m", LogLevel::Error => "\x1b[31m", LogLevel::Critical => "\x1b[35m", }
66 }
67
68 pub fn emoji(&self) -> &'static str {
70 match self {
71 LogLevel::Trace => "đ",
72 LogLevel::Debug => "đ",
73 LogLevel::Info => "âšī¸",
74 LogLevel::Success => "â
",
75 LogLevel::Warning => "â ī¸",
76 LogLevel::Error => "â",
77 LogLevel::Critical => "đĨ",
78 }
79 }
80
81 pub fn reset_color() -> &'static str {
83 "\x1b[0m"
84 }
85
86 pub fn matches_module_pattern(&self, module_path: &str, pattern: &str) -> bool {
89 if pattern.contains('*') {
90 let pattern = pattern.replace('*', ".*");
92 match regex::Regex::new(&format!("^{}$", pattern)) {
93 Ok(regex) => regex.is_match(module_path),
94 Err(_) => {
95 eprintln!("Invalid regex pattern: {}", pattern);
96 false
97 }
98 }
99 } else {
100 module_path.contains(pattern)
102 }
103 }
104
105 pub fn to_string_colored(&self) -> String {
106 let level_str = self.to_string();
107 match self {
108 LogLevel::Error => level_str.red().to_string(),
109 LogLevel::Warning => level_str.yellow().to_string(),
110 LogLevel::Info => level_str.green().to_string(),
111 LogLevel::Debug => level_str.blue().to_string(),
112 LogLevel::Trace => level_str.cyan().to_string(),
113 LogLevel::Success => level_str.green().to_string(),
114 LogLevel::Critical => level_str.red().to_string(),
115 }
116 }
117}
118
119impl PartialOrd for LogLevel {
120 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
121 Some(self.as_u8().cmp(&other.as_u8()))
122 }
123}
124
125impl Ord for LogLevel {
126 fn cmp(&self, other: &Self) -> Ordering {
127 self.as_u8().cmp(&other.as_u8())
128 }
129}
130
131impl fmt::Display for LogLevel {
132 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
133 match self {
134 LogLevel::Trace => write!(f, "TRACE"),
135 LogLevel::Debug => write!(f, "DEBUG"),
136 LogLevel::Info => write!(f, "INFO"),
137 LogLevel::Warning => write!(f, "WARN"),
138 LogLevel::Error => write!(f, "ERROR"),
139 LogLevel::Success => write!(f, "SUCCESS"),
140 LogLevel::Critical => write!(f, "CRITICAL"),
141 }
142 }
143}
144
145impl FromStr for LogLevel {
146 type Err = String;
147
148 fn from_str(s: &str) -> Result<Self, Self::Err> {
149 match s.to_uppercase().as_str() {
150 "TRACE" => Ok(LogLevel::Trace),
151 "DEBUG" => Ok(LogLevel::Debug),
152 "INFO" => Ok(LogLevel::Info),
153 "SUCCESS" => Ok(LogLevel::Success),
154 "WARNING" => Ok(LogLevel::Warning),
155 "ERROR" => Ok(LogLevel::Error),
156 "CRITICAL" => Ok(LogLevel::Critical),
157 _ => Err(format!("Invalid log level: {}", s)),
158 }
159 }
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 #[test]
167 fn test_level_ordering() {
168 assert!(LogLevel::Trace < LogLevel::Debug);
169 assert!(LogLevel::Debug < LogLevel::Info);
170 assert!(LogLevel::Info < LogLevel::Success);
171 assert!(LogLevel::Success < LogLevel::Warning);
172 assert!(LogLevel::Warning < LogLevel::Error);
173 assert!(LogLevel::Error < LogLevel::Critical);
174 }
175
176 #[test]
177 fn test_level_string_representation() {
178 assert_eq!(LogLevel::Trace.as_str(), "TRACE");
179 assert_eq!(LogLevel::Debug.as_str(), "DEBUG");
180 assert_eq!(LogLevel::Info.as_str(), "INFO");
181 assert_eq!(LogLevel::Success.as_str(), "SUCCESS");
182 assert_eq!(LogLevel::Warning.as_str(), "WARNING");
183 assert_eq!(LogLevel::Error.as_str(), "ERROR");
184 assert_eq!(LogLevel::Critical.as_str(), "CRITICAL");
185 }
186
187 #[test]
188 fn test_level_display() {
189 assert_eq!(format!("{}", LogLevel::Info), "INFO");
190 assert_eq!(format!("{}", LogLevel::Error), "ERROR");
191 }
192
193 #[test]
194 fn test_level_numeric_values() {
195 assert_eq!(LogLevel::Trace.as_u8(), 5);
196 assert_eq!(LogLevel::Debug.as_u8(), 10);
197 assert_eq!(LogLevel::Info.as_u8(), 20);
198 assert_eq!(LogLevel::Success.as_u8(), 25);
199 assert_eq!(LogLevel::Warning.as_u8(), 30);
200 assert_eq!(LogLevel::Error.as_u8(), 40);
201 assert_eq!(LogLevel::Critical.as_u8(), 50);
202 }
203
204 #[test]
205 fn test_level_colors() {
206 assert_eq!(LogLevel::Trace.color(), "\x1b[37m");
207 assert_eq!(LogLevel::Debug.color(), "\x1b[34m");
208 assert_eq!(LogLevel::Info.color(), "\x1b[32m");
209 assert_eq!(LogLevel::Success.color(), "\x1b[36m");
210 assert_eq!(LogLevel::Warning.color(), "\x1b[33m");
211 assert_eq!(LogLevel::Error.color(), "\x1b[31m");
212 assert_eq!(LogLevel::Critical.color(), "\x1b[35m");
213 }
214
215 #[test]
216 fn test_level_from_str() {
217 assert_eq!("trace".parse::<LogLevel>(), Ok(LogLevel::Trace));
218 assert_eq!("DEBUG".parse::<LogLevel>(), Ok(LogLevel::Debug));
219 assert_eq!("info".parse::<LogLevel>(), Ok(LogLevel::Info));
220 assert_eq!("SUCCESS".parse::<LogLevel>(), Ok(LogLevel::Success));
221 assert_eq!("warning".parse::<LogLevel>(), Ok(LogLevel::Warning));
222 assert_eq!("ERROR".parse::<LogLevel>(), Ok(LogLevel::Error));
223 assert_eq!("critical".parse::<LogLevel>(), Ok(LogLevel::Critical));
224 assert!("invalid".parse::<LogLevel>().is_err());
225 }
226
227 #[test]
228 fn test_level_from_str_edge_cases() {
229 assert_eq!("TrAcE".parse::<LogLevel>(), Ok(LogLevel::Trace));
231 assert_eq!("DeBuG".parse::<LogLevel>(), Ok(LogLevel::Debug));
232
233 assert!(" trace ".parse::<LogLevel>().is_err());
235 assert!("debug ".parse::<LogLevel>().is_err());
236
237 assert!("".parse::<LogLevel>().is_err());
239 }
240
241 #[test]
242 fn test_level_comparisons() {
243 assert_eq!(LogLevel::Info, LogLevel::Info);
245 assert_ne!(LogLevel::Info, LogLevel::Error);
246
247 assert!(LogLevel::Trace < LogLevel::Critical);
249 assert!(LogLevel::Critical > LogLevel::Trace);
250
251 assert!(LogLevel::Trace <= LogLevel::Debug);
253 assert!(LogLevel::Debug >= LogLevel::Trace);
254 assert!(LogLevel::Info <= LogLevel::Info);
255 assert!(LogLevel::Info >= LogLevel::Info);
256 }
257
258 #[test]
259 fn test_color_code_format() {
260 assert!(LogLevel::Trace.color().starts_with("\x1b["));
262 assert!(LogLevel::Debug.color().starts_with("\x1b["));
263 assert!(LogLevel::Info.color().starts_with("\x1b["));
264 assert!(LogLevel::Success.color().starts_with("\x1b["));
265 assert!(LogLevel::Warning.color().starts_with("\x1b["));
266 assert!(LogLevel::Error.color().starts_with("\x1b["));
267 assert!(LogLevel::Critical.color().starts_with("\x1b["));
268
269 assert_eq!(LogLevel::reset_color(), "\x1b[0m");
271 }
272
273 #[test]
274 fn test_level_emoji() {
275 assert_eq!(LogLevel::Trace.emoji(), "đ");
276 assert_eq!(LogLevel::Debug.emoji(), "đ");
277 assert_eq!(LogLevel::Info.emoji(), "âšī¸");
278 assert_eq!(LogLevel::Success.emoji(), "â
");
279 assert_eq!(LogLevel::Warning.emoji(), "â ī¸");
280 assert_eq!(LogLevel::Error.emoji(), "â");
281 assert_eq!(LogLevel::Critical.emoji(), "đĨ");
282 }
283
284 #[test]
285 fn test_module_pattern_matching() {
286 let level = LogLevel::Info;
287
288 assert!(level.matches_module_pattern("my_app::module", "my_app"));
290 assert!(level.matches_module_pattern("my_app::module", "module"));
291 assert!(!level.matches_module_pattern("my_app::module", "other"));
292
293 assert!(level.matches_module_pattern("my_app::module", "my_app::*"));
295 assert!(level.matches_module_pattern("my_app::module", "*::module"));
296 assert!(!level.matches_module_pattern("my_app::module", "other::*"));
297 }
298
299 #[test]
300 fn test_serialization() {
301 let level = LogLevel::Info;
302 let serialized = serde_json::to_string(&level).unwrap();
303 assert_eq!(serialized, "\"Info\"");
304
305 let deserialized: LogLevel = serde_json::from_str(&serialized).unwrap();
306 assert_eq!(deserialized, level);
307 }
308}