use std::{ fmt, }; use lazy_static::lazy_static; /// Logging mode #[derive(Debug,Clone,PartialEq,Eq,Hash)] pub enum Mode { /// Output nothing None, /// Only print errors Error, /// Only print warnings Warn, /// Verbose message Verbose, /// Debug messages Debug, } #[derive(Debug, Clone, Copy, PartialEq,Eq,Hash)] pub enum Level { Debug, Info, Warning, Error, Fatal, } const LEVELS_FOR: &[(Mode, &[Level]); 5] = &[ (Mode::None, &[]), (Mode::Error, &[Level::Fatal, Level::Error]), (Mode::Warn, &[Level::Fatal, Level::Error, Level::Warning]), (Mode::Verbose, &[Level::Fatal, Level::Error, Level::Warning, Level::Info]), (Mode::Debug, &[Level::Fatal, Level::Error, Level::Warning, Level::Info, Level::Debug]), ]; impl Mode { /// Can we print this level? #[inline] pub fn level(&self, lv: Level) -> Option { if lv.mode_ok(self) { Some(lv) } else { None } } } impl Level { /// Can we print for `mode`? pub fn mode_ok(&self, mode: &Mode) -> bool { use std::collections::{HashMap, HashSet}; lazy_static! { static ref MAP: HashMap<&'static Mode, HashSet<&'static Level>> = { let mut m = HashMap::new(); for (key, values) in LEVELS_FOR.iter() { let mut set = HashSet::new(); for value in values.iter() { set.insert(value); } m.insert(key, set); } m }; } if let Some(values) = MAP.get(mode) { if values.contains(self) { true } else { false } } else { unreachable!() } } } impl fmt::Display for Level { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Level::Debug => write!(f, "DEBUG"), Level::Info => write!(f, "INFO"), Level::Warning => write!(f, "WARN"), Level::Error => write!(f, "ERROR"), Level::Fatal => write!(f, "FATAL"), #[allow(unreachable_patterns)] _ => write!(f, "(unbound)"), } } } macro_rules! log { ($level:tt, $mode:expr => $format:expr) => { { if let Some(level) = $mode.level($crate::log::Level::$level) { println!("{} [{}]: {}", $crate::log::timestamp(), level, $format); true } else { false }; } }; ($level:tt, $mode:expr => $format:expr, $($rest:expr),*) => { { if let Some(level) = $mode.level($crate::log::Level::$level) { println!("{} [{}]: {}", $crate::log::timestamp(), level, format!($format, $($rest)*)); true } else { false }; } }; ($mode:expr => $format:expr, $($rest:expr),*) => { { log!(Info, $mode => $format, $($rest)*); } }; ($level:expr, $format:expr, $($rest:expr),*) => { if let Some(level) = $level { println!("{} [{}]: {}", $crate::log::timestamp(), level, format!($format, $($rest)*)); true } else { false }; }; } pub fn timestamp() -> String { use chrono::{ offset::Utc, DateTime, }; let time: DateTime = std::time::SystemTime::now().into(); time.format("%Y.%m.%d %h:%M:%S").to_string() } //log!(Level::Debug, cmode => "Hello {}", "hi");