use std::convert::TryFrom;
use std::error::Error;
use std::fmt::{Arguments, Display};
use std::ops::{BitOr, BitOrAssign};
use std::str::FromStr;
pub trait LogVisitor {
fn kv_u64(&mut self, key: Option<&str>, val: u64);
fn kv_i64(&mut self, key: Option<&str>, val: i64);
fn kv_f64(&mut self, key: Option<&str>, val: f64);
fn kv_bool(&mut self, key: Option<&str>, val: bool);
fn kv_null(&mut self, key: Option<&str>);
fn kv_str(&mut self, key: Option<&str>, val: &str);
fn kv_fmt(&mut self, key: Option<&str>, val: &Arguments<'_>);
fn kv_map(&mut self, key: Option<&str>);
fn kv_mapend(&mut self, key: Option<&str>);
fn kv_arr(&mut self, key: Option<&str>);
fn kv_arrend(&mut self, key: Option<&str>);
}
pub struct LogRecord<'a> {
pub id: LogID,
pub level: LogLevel,
pub target: &'a str,
pub fmt: Arguments<'a>,
pub kvscan: &'a dyn Fn(&mut dyn LogVisitor),
}
pub type LogID = u64;
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
#[repr(u32)]
#[non_exhaustive]
pub enum LogLevel {
Trace = 0,
Debug = 1,
Info = 2,
Warn = 3,
Error = 4,
Off = 8,
Audit = 5,
Open = 6,
Close = 7,
}
impl LogLevel {
pub fn name(self) -> &'static str {
match self {
Self::Trace => "TRACE",
Self::Debug => "DEBUG",
Self::Info => "INFO",
Self::Warn => "WARN",
Self::Error => "ERROR",
Self::Off => "OFF",
Self::Audit => "AUDIT",
Self::Open => "OPEN",
Self::Close => "CLOSE",
}
}
pub fn all_levels() -> &'static [LogLevel] {
&[
Self::Trace,
Self::Debug,
Self::Info,
Self::Warn,
Self::Error,
Self::Off,
Self::Audit,
Self::Open,
Self::Close,
]
}
}
impl Display for LogLevel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.name().fmt(f)
}
}
impl TryFrom<u8> for LogLevel {
type Error = LogLevelError;
fn try_from(value: u8) -> Result<Self, LogLevelError> {
const TRACE: u8 = LogLevel::Trace as u8;
const DEBUG: u8 = LogLevel::Debug as u8;
const INFO: u8 = LogLevel::Info as u8;
const WARN: u8 = LogLevel::Warn as u8;
const ERROR: u8 = LogLevel::Error as u8;
const OFF: u8 = LogLevel::Off as u8;
const AUDIT: u8 = LogLevel::Audit as u8;
const OPEN: u8 = LogLevel::Open as u8;
const CLOSE: u8 = LogLevel::Close as u8;
Ok(match value {
TRACE => Self::Trace,
DEBUG => Self::Debug,
INFO => Self::Info,
WARN => Self::Warn,
ERROR => Self::Error,
OFF => Self::Off,
AUDIT => Self::Audit,
OPEN => Self::Open,
CLOSE => Self::Close,
_ => return Err(LogLevelError),
})
}
}
impl FromStr for LogLevel {
type Err = LogLevelError;
fn from_str(s: &str) -> Result<LogLevel, LogLevelError> {
let s = s.trim();
macro_rules! ret_if_matches {
($name:literal, $val:expr) => {
if s.eq_ignore_ascii_case($name) {
return Ok($val);
}
};
}
if let Some(c) = s.as_bytes().first() {
match c {
b'T' | b't' => ret_if_matches!("TRACE", Self::Trace),
b'D' | b'd' => ret_if_matches!("DEBUG", Self::Debug),
b'I' | b'i' => ret_if_matches!("INFO", Self::Info),
b'W' | b'w' => ret_if_matches!("WARN", Self::Warn),
b'E' | b'e' => ret_if_matches!("ERROR", Self::Error),
b'O' | b'o' => {
ret_if_matches!("OFF", Self::Off);
ret_if_matches!("OPEN", Self::Open);
}
b'A' | b'a' => ret_if_matches!("AUDIT", Self::Audit),
b'C' | b'c' => ret_if_matches!("CLOSE", Self::Close),
_ => (),
}
}
Err(LogLevelError)
}
}
#[derive(Debug, Eq, PartialEq)]
pub struct LogLevelError;
impl Error for LogLevelError {}
impl Display for LogLevelError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
"invalid logging level".fmt(f)
}
}
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
pub struct LogFilter(u32);
impl LogFilter {
#[inline]
pub fn new() -> Self {
Self::default()
}
#[inline]
pub fn all(levels: &[LogLevel]) -> Self {
let mut rv = Self::new();
for level in levels {
rv |= Self::from(*level);
}
rv
}
#[inline]
pub fn allows(&self, level: LogLevel) -> bool {
0 != (self.0 & (1 << (level as u32)))
}
#[inline]
pub fn is_empty(&self) -> bool {
self.0 == 0
}
}
impl From<LogLevel> for LogFilter {
#[inline]
fn from(level: LogLevel) -> Self {
match level {
LogLevel::Trace
| LogLevel::Debug
| LogLevel::Info
| LogLevel::Warn
| LogLevel::Error
| LogLevel::Off => Self(0x1F & (0x1F << level as u32)),
LogLevel::Audit => Self(1 << LogLevel::Audit as u32),
LogLevel::Open | LogLevel::Close => {
Self((1 << LogLevel::Open as u32) | (1 << LogLevel::Close as u32))
}
}
}
}
impl BitOr for LogFilter {
type Output = Self;
#[inline]
fn bitor(self, rhs: Self) -> Self {
Self(self.0 | rhs.0)
}
}
impl BitOrAssign for LogFilter {
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
impl Display for LogFilter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
"LogFilter(".fmt(f)?;
let mut first = true;
for level in LogLevel::all_levels() {
if 0 != (self.0 & (1 << *level as u32)) {
if first {
first = false;
} else {
",".fmt(f)?;
}
level.name().fmt(f)?;
}
}
")".fmt(f)
}
}
impl FromStr for LogFilter {
type Err = LogLevelError;
fn from_str(s: &str) -> Result<LogFilter, LogLevelError> {
let mut rv = LogFilter::new();
for level in s.split(',') {
let level = LogLevel::from_str(level)?;
rv |= LogFilter::from(level);
}
Ok(rv)
}
}