1#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd)]
3pub struct Level(LevelInner);
4
5impl Level {
6 pub const TRACE: Self = Self(LevelInner::Trace);
8
9 pub const DEBUG: Self = Self(LevelInner::Debug);
11
12 pub const INFO: Self = Self(LevelInner::Info);
14
15 pub const WARN: Self = Self(LevelInner::Warn);
17
18 pub const ERROR: Self = Self(LevelInner::Error);
20}
21
22impl std::convert::TryFrom<&str> for Level {
23 type Error = String;
24
25 fn try_from(value: &str) -> Result<Self, Self::Error> {
26 match value.trim() {
27 "trace" | "TRACE" => Ok(Level::TRACE),
28 "debug" | "DEBUG" => Ok(Level::DEBUG),
29 "info" | "INFO" => Ok(Level::INFO),
30 "warn" | "WARN" => Ok(Level::WARN),
31 "error" | "ERROR" => Ok(Level::ERROR),
32 unknown => Err(format!("unknown log level: {} (expected one of 'trace', 'debug', 'info', 'warn', or 'error')", unknown)),
33 }
34 }
35}
36
37#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
38enum LevelInner {
39 Trace = 0,
40 Debug = 1,
41 Info = 2,
42 Warn = 3,
43 Error = 4,
44}
45
46#[derive(Clone, Debug, Eq, PartialEq)]
63pub struct Metadata<'a> {
64 target: &'a str,
65 level: Level,
66 module_path: Option<&'a str>,
67}
68
69impl<'a> Metadata<'a> {
70 pub const fn new(target: &'a str, level: Level, module_path: Option<&'a str>) -> Self {
72 Self { target, level, module_path }
73 }
74
75 pub fn level(&self) -> &Level {
77 &self.level
78 }
79
80 pub fn target(&self) -> &'a str {
85 self.target
86 }
87
88 pub fn module_path(&self) -> Option<&'a str> {
92 self.module_path
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use std::convert::TryFrom as _;
99
100 use super::*;
101
102 #[test]
103 fn level_try_from_valid() {
104 let cases = &[
105 ("trace", Level::TRACE),
106 ("TRACE", Level::TRACE),
107 ("debug", Level::DEBUG),
108 ("DEBUG", Level::DEBUG),
109 ("info", Level::INFO),
110 ("INFO", Level::INFO),
111 ("warn", Level::WARN),
112 ("WARN", Level::WARN),
113 ("error", Level::ERROR),
114 ("ERROR", Level::ERROR),
115 ];
116
117 for (input, expected) in cases {
118 assert_eq!(Level::try_from(*input).unwrap(), *expected);
119
120 let input_whitespace = format!(" {} ", input);
122 assert_eq!(Level::try_from(&*input_whitespace).unwrap(), *expected);
123 }
124 }
125
126 #[test]
127 fn level_try_from_invalid() {
128 let cases = &["", "foo", "bar", "baz", "qux", "quux"];
129
130 for input in cases {
131 assert!(Level::try_from(*input).is_err());
132 }
133 }
134
135 #[test]
136 fn level_ordering() {
137 assert!(Level::TRACE < Level::DEBUG);
139 assert!(Level::DEBUG < Level::INFO);
140 assert!(Level::ERROR > Level::DEBUG);
141 assert!(Level::WARN == Level::WARN);
142
143 let levels = &[Level::TRACE, Level::DEBUG, Level::INFO, Level::WARN, Level::ERROR];
145
146 for i in 0..levels.len() {
147 let current_level = levels[i];
148 let lower_levels = &levels[..i];
149 let higher_levels = &levels[i + 1..];
150
151 for lower_level in lower_levels {
152 assert!(current_level > *lower_level);
153 assert!(*lower_level < current_level);
154 }
155
156 for higher_level in higher_levels {
157 assert!(current_level < *higher_level);
158 assert!(*higher_level > current_level);
159 }
160 }
161 }
162}