use std::str::FromStr;
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[error("unrecognized log level: {0:?}")]
pub struct ParseLogLevelError(pub String);
#[derive(Debug, Clone, PartialEq, Eq, strum::Display)]
pub enum LogLevel {
#[strum(to_string = "I")]
Info,
#[strum(to_string = "D")]
Debug,
#[strum(to_string = "W")]
Warning,
#[strum(to_string = "N")]
NonFatal,
#[strum(to_string = "F")]
Fatal,
#[strum(default)]
Unknown(String),
}
impl LogLevel {
pub fn label(&self) -> &str {
match self {
Self::Info => "INFO",
Self::Debug => "DEBUG",
Self::Warning => "WARN",
Self::NonFatal => "NFATAL",
Self::Fatal => "FATAL",
Self::Unknown(s) => s,
}
}
}
impl FromStr for LogLevel {
type Err = ParseLogLevelError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
match input {
"I" => Ok(Self::Info),
"D" => Ok(Self::Debug),
"W" => Ok(Self::Warning),
"N" => Ok(Self::NonFatal),
"F" => Ok(Self::Fatal),
other => Err(ParseLogLevelError(other.to_string())),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use test_case::test_case;
#[test_case("I" => LogLevel::Info)]
#[test_case("D" => LogLevel::Debug)]
#[test_case("W" => LogLevel::Warning)]
#[test_case("N" => LogLevel::NonFatal)]
#[test_case("F" => LogLevel::Fatal)]
fn parse_roundtrip(flag: &str) -> LogLevel {
flag.parse().unwrap()
}
#[test]
fn parse_invalid() {
let err = "X".parse::<LogLevel>().unwrap_err();
assert_eq!(err.to_string(), r#"unrecognized log level: "X""#);
}
#[test]
fn label_known_variants() {
assert_eq!(LogLevel::Info.label(), "INFO");
assert_eq!(LogLevel::Debug.label(), "DEBUG");
assert_eq!(LogLevel::Warning.label(), "WARN");
assert_eq!(LogLevel::NonFatal.label(), "NFATAL");
assert_eq!(LogLevel::Fatal.label(), "FATAL");
}
#[test]
fn label_unknown_returns_inner() {
let level = LogLevel::Unknown("Z".to_string());
assert_eq!(level.label(), "Z");
}
#[test]
fn display_unknown_uses_strum_default() {
let level = LogLevel::Unknown("Q".to_string());
assert_eq!(level.to_string(), "Q");
}
}