use std::collections::HashMap;
use std::fmt;
use std::str::FromStr;
pub type Record<'a> = HashMap<&'a str, &'a str>;
pub trait RecordExt {
fn get_or_default(&self, key: &str) -> &str;
}
impl RecordExt for Record<'_> {
fn get_or_default(&self, key: &str) -> &str {
self.get(key).map_or("", |v| v)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Level {
Debug = 0,
Info = 1,
Warning = 2,
Error = 3,
Critical = 4,
}
impl Level {
pub fn to_str(&self) -> &str {
match self {
Level::Debug => "DEBUG",
Level::Info => "INFO",
Level::Warning => "WARNING",
Level::Error => "ERROR",
Level::Critical => "CRITICAL",
}
}
pub fn to_u8(self) -> u8 {
self as u8
}
pub fn from_u8(u8: u8) -> Level {
match u8 {
0 => Level::Debug,
1 => Level::Info,
2 => Level::Warning,
3 => Level::Error,
4 => Level::Critical,
_ => panic!("Invalid level"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseLevelError {
pub input: String,
}
impl fmt::Display for ParseLevelError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "invalid level: {}", self.input)
}
}
impl std::error::Error for ParseLevelError {}
impl FromStr for Level {
type Err = ParseLevelError;
fn from_str(s: &str) -> Result<Level, Self::Err> {
match s {
"DEBUG" => Ok(Level::Debug),
"INFO" => Ok(Level::Info),
"WARNING" => Ok(Level::Warning),
"ERROR" => Ok(Level::Error),
"CRITICAL" => Ok(Level::Critical),
_ => Err(ParseLevelError {
input: s.to_string(),
}),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
#[test]
fn record_get_or_default() {
let mut record: Record = HashMap::new();
record.insert("a", "1");
assert_eq!(record.get_or_default("a"), "1");
assert_eq!(record.get_or_default("missing"), "");
}
#[test]
fn level_ordering_is_by_severity() {
assert!(Level::Debug < Level::Info);
assert!(Level::Info < Level::Warning);
assert!(Level::Warning < Level::Error);
assert!(Level::Error < Level::Critical);
}
#[test]
fn level_u8_round_trips() {
for level in [
Level::Debug,
Level::Info,
Level::Warning,
Level::Error,
Level::Critical,
] {
assert_eq!(Level::from_u8(level.to_u8()), level);
}
}
#[test]
fn level_str_round_trips() {
for level in [
Level::Debug,
Level::Info,
Level::Warning,
Level::Error,
Level::Critical,
] {
assert_eq!(level.to_str().parse::<Level>().unwrap(), level);
}
}
#[test]
fn level_from_str_rejects_unknown() {
assert!("NOPE".parse::<Level>().is_err());
}
#[test]
#[should_panic]
fn level_from_u8_panics_on_out_of_range() {
Level::from_u8(42);
}
}