use std::{cmp, error, fmt, str::FromStr};
use serde::{Deserialize, Serialize};
static LOG_LEVEL_NAMES: [&str; 6] = ["OFF", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"];
static LEVEL_PARSE_ERROR: &str =
"attempted to convert a string that doesn't match an existing log level";
#[repr(usize)]
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
pub enum Level {
Error = 1,
Warn,
Info,
Debug,
Trace,
}
impl PartialEq<LevelFilter> for Level {
#[inline]
fn eq(&self, other: &LevelFilter) -> bool {
*self as usize == *other as usize
}
}
impl PartialOrd for Level {
#[inline]
fn partial_cmp(&self, other: &Level) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
#[inline]
fn lt(&self, other: &Level) -> bool {
(*self as usize) < *other as usize
}
#[inline]
fn le(&self, other: &Level) -> bool {
*self as usize <= *other as usize
}
#[inline]
fn gt(&self, other: &Level) -> bool {
*self as usize > *other as usize
}
#[inline]
fn ge(&self, other: &Level) -> bool {
*self as usize >= *other as usize
}
}
impl PartialOrd<LevelFilter> for Level {
#[inline]
fn partial_cmp(&self, other: &LevelFilter) -> Option<cmp::Ordering> {
Some((*self as usize).cmp(&(*other as usize)))
}
#[inline]
fn lt(&self, other: &LevelFilter) -> bool {
(*self as usize) < *other as usize
}
#[inline]
fn le(&self, other: &LevelFilter) -> bool {
*self as usize <= *other as usize
}
#[inline]
fn gt(&self, other: &LevelFilter) -> bool {
*self as usize > *other as usize
}
#[inline]
fn ge(&self, other: &LevelFilter) -> bool {
*self as usize >= *other as usize
}
}
impl Ord for Level {
#[inline]
fn cmp(&self, other: &Level) -> cmp::Ordering {
(*self as usize).cmp(&(*other as usize))
}
}
fn eq_ignore_ascii_case(a: &str, b: &str) -> bool {
fn to_ascii_uppercase(c: u8) -> u8 {
if (b'a'..=b'z').contains(&c) {
c - b'a' + b'A'
} else {
c
}
}
if a.len() == b.len() {
a.bytes()
.zip(b.bytes())
.all(|(a, b)| to_ascii_uppercase(a) == to_ascii_uppercase(b))
} else {
false
}
}
impl FromStr for Level {
type Err = ParseLevelError;
fn from_str(level: &str) -> Result<Level, Self::Err> {
LOG_LEVEL_NAMES
.iter()
.position(|&name| eq_ignore_ascii_case(name, level))
.into_iter()
.filter(|&idx| idx != 0)
.map(|idx| Level::from_usize(idx).unwrap())
.next()
.ok_or(ParseLevelError(()))
}
}
impl fmt::Display for Level {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.pad(self.as_str())
}
}
impl Level {
fn from_usize(u: usize) -> Option<Level> {
match u {
1 => Some(Level::Error),
2 => Some(Level::Warn),
3 => Some(Level::Info),
4 => Some(Level::Debug),
5 => Some(Level::Trace),
_ => None,
}
}
#[inline]
pub fn max() -> Level {
Level::Trace
}
#[inline]
pub fn to_level_filter(&self) -> LevelFilter {
LevelFilter::from_usize(*self as usize).unwrap()
}
pub fn as_str(&self) -> &'static str {
LOG_LEVEL_NAMES[*self as usize]
}
pub fn iter() -> impl Iterator<Item = Self> {
(1..6).map(|i| Self::from_usize(i).unwrap())
}
}
#[repr(usize)]
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
pub enum LevelFilter {
Off,
Error,
Warn,
Info,
Debug,
Trace,
}
impl PartialEq<Level> for LevelFilter {
#[inline]
fn eq(&self, other: &Level) -> bool {
other.eq(self)
}
}
impl PartialOrd for LevelFilter {
#[inline]
fn partial_cmp(&self, other: &LevelFilter) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
#[inline]
fn lt(&self, other: &LevelFilter) -> bool {
(*self as usize) < *other as usize
}
#[inline]
fn le(&self, other: &LevelFilter) -> bool {
*self as usize <= *other as usize
}
#[inline]
fn gt(&self, other: &LevelFilter) -> bool {
*self as usize > *other as usize
}
#[inline]
fn ge(&self, other: &LevelFilter) -> bool {
*self as usize >= *other as usize
}
}
impl PartialOrd<Level> for LevelFilter {
#[inline]
fn partial_cmp(&self, other: &Level) -> Option<cmp::Ordering> {
Some((*self as usize).cmp(&(*other as usize)))
}
#[inline]
fn lt(&self, other: &Level) -> bool {
(*self as usize) < *other as usize
}
#[inline]
fn le(&self, other: &Level) -> bool {
*self as usize <= *other as usize
}
#[inline]
fn gt(&self, other: &Level) -> bool {
*self as usize > *other as usize
}
#[inline]
fn ge(&self, other: &Level) -> bool {
*self as usize >= *other as usize
}
}
impl Ord for LevelFilter {
#[inline]
fn cmp(&self, other: &LevelFilter) -> cmp::Ordering {
(*self as usize).cmp(&(*other as usize))
}
}
impl FromStr for LevelFilter {
type Err = ParseLevelError;
fn from_str(level: &str) -> Result<LevelFilter, Self::Err> {
LOG_LEVEL_NAMES
.iter()
.position(|&name| eq_ignore_ascii_case(name, level))
.map(|p| LevelFilter::from_usize(p).unwrap())
.ok_or(ParseLevelError(()))
}
}
impl fmt::Display for LevelFilter {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.pad(self.as_str())
}
}
impl LevelFilter {
fn from_usize(u: usize) -> Option<LevelFilter> {
match u {
0 => Some(LevelFilter::Off),
1 => Some(LevelFilter::Error),
2 => Some(LevelFilter::Warn),
3 => Some(LevelFilter::Info),
4 => Some(LevelFilter::Debug),
5 => Some(LevelFilter::Trace),
_ => None,
}
}
#[inline]
pub fn max() -> LevelFilter {
LevelFilter::Trace
}
#[inline]
pub fn to_level(&self) -> Option<Level> {
Level::from_usize(*self as usize)
}
pub fn as_str(&self) -> &'static str {
LOG_LEVEL_NAMES[*self as usize]
}
pub fn iter() -> impl Iterator<Item = Self> {
(0..6).map(|i| Self::from_usize(i).unwrap())
}
}
#[allow(missing_copy_implementations)]
#[derive(Debug, PartialEq)]
pub struct ParseLevelError(());
impl fmt::Display for ParseLevelError {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.write_str(LEVEL_PARSE_ERROR)
}
}
impl error::Error for ParseLevelError {}