1use std::fmt;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
11pub enum LogLevel {
12 Trace,
13 Debug,
14 Info,
15 Warn,
16 Error,
17}
18
19impl fmt::Display for LogLevel {
20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21 match self {
22 Self::Trace => write!(f, "TRACE"),
23 Self::Debug => write!(f, "DEBUG"),
24 Self::Info => write!(f, "INFO"),
25 Self::Warn => write!(f, "WARN"),
26 Self::Error => write!(f, "ERROR"),
27 }
28 }
29}
30
31pub struct LogEntry {
33 pub level: LogLevel,
34 pub target: &'static str,
36 pub message: String,
37 pub elapsed_us: Option<u64>,
39}
40
41pub trait Logger: Send + Sync {
46 fn log(&self, entry: LogEntry);
47 fn is_enabled(&self, level: LogLevel) -> bool;
48 fn flush(&self);
49}
50
51pub struct NoopLogger;
53
54impl Logger for NoopLogger {
55 fn log(&self, _entry: LogEntry) {}
56 fn is_enabled(&self, _level: LogLevel) -> bool {
57 false
58 }
59 fn flush(&self) {}
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65
66 #[test]
67 fn log_level_ordering() {
68 assert!(LogLevel::Trace < LogLevel::Debug);
69 assert!(LogLevel::Debug < LogLevel::Info);
70 assert!(LogLevel::Info < LogLevel::Warn);
71 assert!(LogLevel::Warn < LogLevel::Error);
72 }
73
74 #[test]
75 fn log_level_display() {
76 assert_eq!(LogLevel::Trace.to_string(), "TRACE");
77 assert_eq!(LogLevel::Info.to_string(), "INFO");
78 assert_eq!(LogLevel::Error.to_string(), "ERROR");
79 }
80
81 #[test]
82 fn noop_logger_accepts_entries() {
83 let logger = NoopLogger;
84 logger.log(LogEntry {
85 level: LogLevel::Info,
86 target: "test",
87 message: "hello".into(),
88 elapsed_us: None,
89 });
90 }
92
93 #[test]
94 fn noop_logger_is_never_enabled() {
95 let logger = NoopLogger;
96 assert!(!logger.is_enabled(LogLevel::Trace));
97 assert!(!logger.is_enabled(LogLevel::Error));
98 }
99
100 #[test]
101 fn noop_logger_flush_is_safe() {
102 let logger = NoopLogger;
103 logger.flush();
104 }
105
106 #[test]
107 fn log_entry_with_timing() {
108 let entry = LogEntry {
109 level: LogLevel::Debug,
110 target: "tui",
111 message: "frame render".into(),
112 elapsed_us: Some(1234),
113 };
114 assert_eq!(entry.elapsed_us, Some(1234));
115 assert_eq!(entry.target, "tui");
116 }
117}