use std::{
cmp, fmt,
hash::Hash,
str::FromStr,
sync::atomic::{self, AtomicU32},
};
#[repr(u32)]
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
pub enum Level {
Fatal = 1,
Error,
Warn,
Info,
Debug,
Trace,
}
impl Level {
pub fn from_value(value: u32) -> Option<Self> {
match value {
1 => Some(Self::Fatal),
2 => Some(Self::Error),
3 => Some(Self::Warn),
4 => Some(Self::Info),
5 => Some(Self::Debug),
6 => Some(Self::Trace),
_ => None,
}
}
}
#[repr(u32)]
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
pub enum LevelFilter {
Off,
Fatal,
Error,
Warn,
Info,
Debug,
Trace,
}
impl PartialEq<LevelFilter> for Level {
#[inline(always)]
fn eq(&self, other: &LevelFilter) -> bool {
*self as u32 == *other as u32
}
}
impl PartialOrd for Level {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
#[inline(always)]
fn lt(&self, other: &Self) -> bool {
(*self as u32) < *other as u32
}
#[inline(always)]
fn le(&self, other: &Self) -> bool {
*self as u32 <= *other as u32
}
#[inline(always)]
fn gt(&self, other: &Self) -> bool {
*self as u32 > *other as u32
}
#[inline(always)]
fn ge(&self, other: &Self) -> bool {
*self as u32 >= *other as u32
}
}
impl PartialOrd<LevelFilter> for Level {
#[inline(always)]
fn partial_cmp(&self, other: &LevelFilter) -> Option<cmp::Ordering> {
Some((*self as u32).cmp(&(*other as u32)))
}
#[inline(always)]
fn lt(&self, other: &LevelFilter) -> bool {
(*self as u32) < *other as u32
}
#[inline(always)]
fn le(&self, other: &LevelFilter) -> bool {
*self as u32 <= *other as u32
}
#[inline(always)]
fn gt(&self, other: &LevelFilter) -> bool {
*self as u32 > *other as u32
}
#[inline(always)]
fn ge(&self, other: &LevelFilter) -> bool {
*self as u32 >= *other as u32
}
}
impl Ord for Level {
#[inline(always)]
fn cmp(&self, other: &Self) -> cmp::Ordering {
(*self as u32).cmp(&(*other as u32))
}
}
fn ok_or<T, E>(t: Option<T>, e: E) -> Result<T, E> {
match t {
Some(t) => Ok(t),
None => Err(e),
}
}
pub struct ParseLevelError(());
impl FromStr for Level {
type Err = ParseLevelError;
fn from_str(level: &str) -> Result<Self, Self::Err> {
ok_or(
LEVEL_NAMES
.iter()
.position(|&name| str::eq_ignore_ascii_case(name, level))
.into_iter()
.filter(|&idx| idx != 0)
.map(|idx| Self::from_u32(idx as u32).unwrap())
.next(),
ParseLevelError(()),
)
}
}
impl fmt::Display for Level {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.pad(self.as_str())
}
}
impl Level {
pub(crate) fn from_u32(u: u32) -> Option<Self> {
match u {
1 => Some(Self::Fatal),
2 => Some(Self::Error),
3 => Some(Self::Warn),
4 => Some(Self::Info),
5 => Some(Self::Debug),
6 => Some(Self::Trace),
_ => None,
}
}
#[inline(always)]
pub fn max() -> Self {
Self::Trace
}
#[inline(always)]
pub fn to_level_filter(self) -> LevelFilter {
LevelFilter::from_u32(self as u32).unwrap()
}
pub fn as_str(self) -> &'static str {
LEVEL_NAMES[self as usize]
}
pub fn iter() -> impl Iterator<Item = Self> {
(1..7).map(|i| Self::from_u32(i).unwrap())
}
}
impl PartialEq<Level> for LevelFilter {
#[inline(always)]
fn eq(&self, other: &Level) -> bool {
other.eq(self)
}
}
impl PartialOrd for LevelFilter {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
#[inline(always)]
fn lt(&self, other: &Self) -> bool {
(*self as u32) < *other as u32
}
#[inline(always)]
fn le(&self, other: &Self) -> bool {
*self as u32 <= *other as u32
}
#[inline(always)]
fn gt(&self, other: &Self) -> bool {
*self as u32 > *other as u32
}
#[inline(always)]
fn ge(&self, other: &Self) -> bool {
*self as u32 >= *other as u32
}
}
impl PartialOrd<Level> for LevelFilter {
#[inline(always)]
fn partial_cmp(&self, other: &Level) -> Option<cmp::Ordering> {
Some((*self as u32).cmp(&(*other as u32)))
}
#[inline(always)]
fn lt(&self, other: &Level) -> bool {
(*self as u32) < *other as u32
}
#[inline(always)]
fn le(&self, other: &Level) -> bool {
*self as u32 <= *other as u32
}
#[inline(always)]
fn gt(&self, other: &Level) -> bool {
*self as u32 > *other as u32
}
#[inline(always)]
fn ge(&self, other: &Level) -> bool {
*self as u32 >= *other as u32
}
}
impl Ord for LevelFilter {
#[inline(always)]
fn cmp(&self, other: &Self) -> cmp::Ordering {
(*self as u32).cmp(&(*other as u32))
}
}
impl FromStr for LevelFilter {
type Err = ParseLevelError;
fn from_str(level: &str) -> Result<Self, Self::Err> {
ok_or(
LEVEL_NAMES
.iter()
.position(|&name| str::eq_ignore_ascii_case(name, level))
.map(|p| Self::from_u32(p as u32).unwrap()),
ParseLevelError(()),
)
}
}
impl fmt::Display for LevelFilter {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.pad(self.as_str())
}
}
impl LevelFilter {
pub(crate) fn from_u32(u: u32) -> Option<Self> {
match u {
0 => Some(Self::Off),
1 => Some(Self::Fatal),
2 => Some(Self::Error),
3 => Some(Self::Warn),
4 => Some(Self::Info),
5 => Some(Self::Debug),
6 => Some(Self::Trace),
_ => None,
}
}
#[inline(always)]
pub fn max() -> Self {
Self::Trace
}
#[inline(always)]
pub fn to_level(self) -> Option<Level> {
Level::from_u32(self as u32)
}
pub fn as_str(self) -> &'static str {
LEVEL_NAMES[self as usize]
}
pub fn iter() -> impl Iterator<Item = Self> {
(0..7).map(|i| Self::from_u32(i).unwrap())
}
}
#[repr(u32)]
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
pub enum Verbosity {
Min = 1,
Med,
Max,
}
#[repr(u32)]
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
pub enum LodFilter {
Off,
Min,
Med,
Max,
}
impl PartialEq<LodFilter> for Verbosity {
#[inline(always)]
fn eq(&self, other: &LodFilter) -> bool {
*self as u32 == *other as u32
}
}
impl PartialOrd for Verbosity {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
#[inline(always)]
fn lt(&self, other: &Self) -> bool {
(*self as u32) < *other as u32
}
#[inline(always)]
fn le(&self, other: &Self) -> bool {
*self as u32 <= *other as u32
}
#[inline(always)]
fn gt(&self, other: &Self) -> bool {
*self as u32 > *other as u32
}
#[inline(always)]
fn ge(&self, other: &Self) -> bool {
*self as u32 >= *other as u32
}
}
impl PartialOrd<LodFilter> for Verbosity {
#[inline(always)]
fn partial_cmp(&self, other: &LodFilter) -> Option<cmp::Ordering> {
Some((*self as u32).cmp(&(*other as u32)))
}
#[inline(always)]
fn lt(&self, other: &LodFilter) -> bool {
(*self as u32) < *other as u32
}
#[inline(always)]
fn le(&self, other: &LodFilter) -> bool {
*self as u32 <= *other as u32
}
#[inline(always)]
fn gt(&self, other: &LodFilter) -> bool {
*self as u32 > *other as u32
}
#[inline(always)]
fn ge(&self, other: &LodFilter) -> bool {
*self as u32 >= *other as u32
}
}
impl Ord for Verbosity {
#[inline(always)]
fn cmp(&self, other: &Self) -> cmp::Ordering {
(*self as u32).cmp(&(*other as u32))
}
}
impl FromStr for Verbosity {
type Err = ParseLevelError;
fn from_str(level: &str) -> Result<Self, Self::Err> {
ok_or(
LOD_NAMES
.iter()
.position(|&name| str::eq_ignore_ascii_case(name, level))
.into_iter()
.filter(|&idx| idx != 0)
.map(|idx| Self::from_usize(idx).unwrap())
.next(),
ParseLevelError(()),
)
}
}
impl fmt::Display for Verbosity {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.pad(self.as_str())
}
}
impl Verbosity {
fn from_usize(u: usize) -> Option<Self> {
match u {
1 => Some(Self::Min),
2 => Some(Self::Med),
3 => Some(Self::Max),
_ => None,
}
}
fn from_u32(u: u32) -> Option<Self> {
match u {
1 => Some(Self::Min),
2 => Some(Self::Med),
3 => Some(Self::Max),
_ => None,
}
}
#[inline(always)]
pub fn max() -> Self {
Self::Max
}
#[inline(always)]
pub fn to_level_filter(self) -> LodFilter {
LodFilter::from_u32(self as u32).unwrap()
}
pub fn as_str(self) -> &'static str {
LOD_NAMES[self as usize]
}
pub fn iter() -> impl Iterator<Item = Self> {
(1..4).map(|i| Self::from_usize(i).unwrap())
}
}
impl PartialEq<Verbosity> for LodFilter {
#[inline(always)]
fn eq(&self, other: &Verbosity) -> bool {
other.eq(self)
}
}
impl PartialOrd for LodFilter {
#[inline(always)]
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
#[inline(always)]
fn lt(&self, other: &Self) -> bool {
(*self as u32) < *other as u32
}
#[inline(always)]
fn le(&self, other: &Self) -> bool {
*self as u32 <= *other as u32
}
#[inline(always)]
fn gt(&self, other: &Self) -> bool {
*self as u32 > *other as u32
}
#[inline(always)]
fn ge(&self, other: &Self) -> bool {
*self as u32 >= *other as u32
}
}
impl PartialOrd<Verbosity> for LodFilter {
#[inline(always)]
fn partial_cmp(&self, other: &Verbosity) -> Option<cmp::Ordering> {
Some((*self as u32).cmp(&(*other as u32)))
}
#[inline(always)]
fn lt(&self, other: &Verbosity) -> bool {
(*self as u32) < *other as u32
}
#[inline(always)]
fn le(&self, other: &Verbosity) -> bool {
*self as u32 <= *other as u32
}
#[inline(always)]
fn gt(&self, other: &Verbosity) -> bool {
*self as u32 > *other as u32
}
#[inline(always)]
fn ge(&self, other: &Verbosity) -> bool {
*self as u32 >= *other as u32
}
}
impl Ord for LodFilter {
#[inline(always)]
fn cmp(&self, other: &Self) -> cmp::Ordering {
(*self as u32).cmp(&(*other as u32))
}
}
impl FromStr for LodFilter {
type Err = ParseLevelError;
fn from_str(level: &str) -> Result<Self, Self::Err> {
ok_or(
LOD_NAMES
.iter()
.position(|&name| str::eq_ignore_ascii_case(name, level))
.map(|p| Self::from_usize(p).unwrap()),
ParseLevelError(()),
)
}
}
impl fmt::Display for LodFilter {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.pad(self.as_str())
}
}
impl LodFilter {
fn from_usize(u: usize) -> Option<Self> {
match u {
0 => Some(Self::Off),
1 => Some(Self::Min),
2 => Some(Self::Med),
3 => Some(Self::Max),
_ => None,
}
}
fn from_u32(u: u32) -> Option<Self> {
match u {
0 => Some(Self::Off),
1 => Some(Self::Min),
2 => Some(Self::Med),
3 => Some(Self::Max),
_ => None,
}
}
#[inline(always)]
pub fn max() -> Self {
Self::Max
}
#[inline(always)]
pub fn to_level(self) -> Option<Verbosity> {
Verbosity::from_u32(self as u32)
}
pub fn as_str(self) -> &'static str {
LOD_NAMES[self as usize]
}
pub fn iter() -> impl Iterator<Item = Self> {
(0..4).map(|i| Self::from_usize(i).unwrap())
}
}
static MAX_LEVEL_FILTER: AtomicU32 = AtomicU32::new(0);
static MAX_LOD_FILTER: AtomicU32 = AtomicU32::new(0);
static LEVEL_NAMES: [&str; 7] = ["OFF", "FATAL", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"];
static LOD_NAMES: [&str; 4] = ["OFF", "LOW", "MED", "HIGH"];
#[inline(always)]
pub fn set_max_level(level: LevelFilter) {
MAX_LEVEL_FILTER.store(level as u32, atomic::Ordering::Relaxed);
}
#[inline(always)]
pub fn max_level() -> LevelFilter {
unsafe { std::mem::transmute(MAX_LEVEL_FILTER.load(atomic::Ordering::Relaxed)) }
}
#[inline(always)]
pub fn set_max_lod(level: LodFilter) {
MAX_LOD_FILTER.store(level as u32, atomic::Ordering::Relaxed);
}
#[inline(always)]
pub fn max_lod() -> LodFilter {
unsafe { std::mem::transmute(MAX_LOD_FILTER.load(atomic::Ordering::Relaxed)) }
}
pub const STATIC_MAX_LEVEL: LevelFilter = MAX_LEVEL_INNER;
cfg_if::cfg_if! {
if #[cfg(all(not(debug_assertions), feature = "release_max_level_off"))] {
const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Off;
} else if #[cfg(all(not(debug_assertions), feature = "release_max_level_error"))] {
const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Error;
} else if #[cfg(all(not(debug_assertions), feature = "release_max_level_warn"))] {
const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Warn;
} else if #[cfg(all(not(debug_assertions), feature = "release_max_level_info"))] {
const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Info;
} else if #[cfg(all(not(debug_assertions), feature = "release_max_level_debug"))] {
const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Debug;
} else if #[cfg(all(not(debug_assertions), feature = "release_max_level_trace"))] {
const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Trace;
} else if #[cfg(feature = "max_level_off")] {
const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Off;
} else if #[cfg(feature = "max_level_error")] {
const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Error;
} else if #[cfg(feature = "max_level_warn")] {
const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Warn;
} else if #[cfg(feature = "max_level_info")] {
const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Info;
} else if #[cfg(feature = "max_level_debug")] {
const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Debug;
} else {
const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Trace;
}
}
pub const STATIC_MAX_LOD: LodFilter = MAX_LOD_INNER;
cfg_if::cfg_if! {
if #[cfg(all(not(debug_assertions), feature = "release_max_lod_off"))] {
const MAX_LOD_INNER: LodFilter = LodFilter::Off;
} else if #[cfg(all(not(debug_assertions), feature = "release_max_lod_min"))] {
const MAX_LOD_INNER: LodFilter = LodFilter::Min;
} else if #[cfg(all(not(debug_assertions), feature = "release_max_lod_med"))] {
const MAX_LOD_INNER: LodFilter = LodFilter::Med;
} else if #[cfg(all(not(debug_assertions), feature = "release_max_lod_max"))] {
const MAX_LOD_INNER: LodFilter = LodFilter::Max;
} else if #[cfg(feature = "max_lod_off")] {
const MAX_LOD_INNER: LodFilter = LodFilter::Off;
} else if #[cfg(feature = "max_lod_min")] {
const MAX_LOD_INNER: LodFilter = LodFilter::Min;
} else if #[cfg(feature = "max_lod_med")] {
const MAX_LOD_INNER: LodFilter = LodFilter::Med;
} else {
const MAX_LOD_INNER: LodFilter = LodFilter::Max;
}
}