use std::{env, fmt};
use clap::{Arg, Command};
use log::LevelFilter;
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum LogLevel {
None = 0,
Error,
Warn,
Info,
Debug,
Trace,
}
impl fmt::Display for LogLevel {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
LogLevel::None => "none",
LogLevel::Error => "error",
LogLevel::Warn => "warn",
LogLevel::Info => "info",
LogLevel::Debug => "debug",
LogLevel::Trace => "trace",
};
write!(f, "{}", s)
}
}
impl From<u8> for LogLevel {
fn from(val: u8) -> Self { Self::from_verbosity_flag_count(val) }
}
impl From<LogLevel> for u8 {
fn from(log_level: LogLevel) -> Self { log_level.verbosity_flag_count() }
}
impl From<LogLevel> for LevelFilter {
fn from(log_level: LogLevel) -> Self {
match log_level {
LogLevel::None => LevelFilter::Off,
LogLevel::Error => LevelFilter::Error,
LogLevel::Warn => LevelFilter::Warn,
LogLevel::Info => LevelFilter::Info,
LogLevel::Debug => LevelFilter::Debug,
LogLevel::Trace => LevelFilter::Trace,
}
}
}
impl LogLevel {
pub fn verbosity_flag_count(&self) -> u8 { *self as u8 }
pub fn from_verbosity_flag_count(level: u8) -> Self {
if level > 5 {
log::warn!("Verbosity level {} exceeds maximum; using Trace", level);
}
match level {
0 => LogLevel::None,
1 => LogLevel::Error,
2 => LogLevel::Warn,
3 => LogLevel::Info,
4 => LogLevel::Debug,
_ => LogLevel::Trace,
}
}
pub fn from_args() -> Self {
let matches = Command::new(env!("CARGO_PKG_NAME"))
.arg(
Arg::new("verbose")
.short('v')
.action(clap::ArgAction::Count)
.help("Increase verbosity level (e.g., -vvv for Info)"),
)
.get_matches();
let verbosity = matches.get_count("verbose");
Self::from_verbosity_flag_count(verbosity)
}
pub fn apply_custom(
&self,
custom_log: Option<String>,
override_existing: bool,
) {
static INIT: std::sync::Once = std::sync::Once::new();
let filter = LevelFilter::from(*self);
INIT.call_once(|| {
if override_existing || env::var("RUST_LOG").is_err() {
let log_value = custom_log.unwrap_or_else(|| self.to_string());
env::set_var("RUST_LOG", log_value);
}
env_logger::Builder::from_env(
env_logger::Env::default().default_filter_or(self.to_string()),
)
.filter_level(filter)
.try_init()
.expect("Logger instantiation failed");
});
}
pub fn apply(self) { self.apply_custom(None, false) }
}