#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd)]
pub struct Level(LevelInner);
impl Level {
pub const TRACE: Self = Self(LevelInner::Trace);
pub const DEBUG: Self = Self(LevelInner::Debug);
pub const INFO: Self = Self(LevelInner::Info);
pub const WARN: Self = Self(LevelInner::Warn);
pub const ERROR: Self = Self(LevelInner::Error);
}
impl std::convert::TryFrom<&str> for Level {
type Error = String;
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value.trim() {
"trace" | "TRACE" => Ok(Level::TRACE),
"debug" | "DEBUG" => Ok(Level::DEBUG),
"info" | "INFO" => Ok(Level::INFO),
"warn" | "WARN" => Ok(Level::WARN),
"error" | "ERROR" => Ok(Level::ERROR),
unknown => Err(format!("unknown log level: {} (expected one of 'trace', 'debug', 'info', 'warn', or 'error')", unknown)),
}
}
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
enum LevelInner {
Trace = 0,
Debug = 1,
Info = 2,
Warn = 3,
Error = 4,
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Metadata<'a> {
target: &'a str,
level: Level,
module_path: Option<&'a str>,
}
impl<'a> Metadata<'a> {
pub const fn new(target: &'a str, level: Level, module_path: Option<&'a str>) -> Self {
Self { target, level, module_path }
}
pub fn level(&self) -> &Level {
&self.level
}
pub fn target(&self) -> &'a str {
self.target
}
pub fn module_path(&self) -> Option<&'a str> {
self.module_path
}
}
#[cfg(test)]
mod tests {
use std::convert::TryFrom as _;
use super::*;
#[test]
fn level_try_from_valid() {
let cases = &[
("trace", Level::TRACE),
("TRACE", Level::TRACE),
("debug", Level::DEBUG),
("DEBUG", Level::DEBUG),
("info", Level::INFO),
("INFO", Level::INFO),
("warn", Level::WARN),
("WARN", Level::WARN),
("error", Level::ERROR),
("ERROR", Level::ERROR),
];
for (input, expected) in cases {
assert_eq!(Level::try_from(*input).unwrap(), *expected);
let input_whitespace = format!(" {} ", input);
assert_eq!(Level::try_from(&*input_whitespace).unwrap(), *expected);
}
}
#[test]
fn level_try_from_invalid() {
let cases = &["", "foo", "bar", "baz", "qux", "quux"];
for input in cases {
assert!(Level::try_from(*input).is_err());
}
}
#[test]
fn level_ordering() {
assert!(Level::TRACE < Level::DEBUG);
assert!(Level::DEBUG < Level::INFO);
assert!(Level::ERROR > Level::DEBUG);
assert!(Level::WARN == Level::WARN);
let levels = &[Level::TRACE, Level::DEBUG, Level::INFO, Level::WARN, Level::ERROR];
for i in 0..levels.len() {
let current_level = levels[i];
let lower_levels = &levels[..i];
let higher_levels = &levels[i + 1..];
for lower_level in lower_levels {
assert!(current_level > *lower_level);
assert!(*lower_level < current_level);
}
for higher_level in higher_levels {
assert!(current_level < *higher_level);
assert!(*higher_level > current_level);
}
}
}
}