1use std::{
3 cmp, fmt,
4 hash::Hash,
5 str::FromStr,
6 sync::atomic::{self, AtomicU32},
7};
8
9#[repr(u32)]
11#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
12pub enum Level {
13 Fatal = 1,
17 Error,
21 Warn,
25 Info,
29 Debug,
33 Trace,
37}
38
39impl Level {
40 pub fn from_value(value: u32) -> Option<Self> {
41 match value {
42 1 => Some(Self::Fatal),
43 2 => Some(Self::Error),
44 3 => Some(Self::Warn),
45 4 => Some(Self::Info),
46 5 => Some(Self::Debug),
47 6 => Some(Self::Trace),
48 _ => None,
49 }
50 }
51
52 pub fn parse(value: &str) -> Option<Self> {
53 match value {
54 "fatal" => Some(Self::Fatal),
55 "error" => Some(Self::Error),
56 "warning" => Some(Self::Warn),
57 "info" => Some(Self::Info),
58 "debug" => Some(Self::Debug),
59 "trace" => Some(Self::Trace),
60 _ => None,
61 }
62 }
63}
64
65#[repr(u32)]
67#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
68pub enum LevelFilter {
69 Off,
71 Fatal,
73 Error,
75 Warn,
77 Info,
79 Debug,
81 Trace,
83}
84
85impl PartialEq<LevelFilter> for Level {
86 #[inline(always)]
87 fn eq(&self, other: &LevelFilter) -> bool {
88 *self as u32 == *other as u32
89 }
90}
91
92impl PartialOrd for Level {
93 #[inline(always)]
94 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
95 Some(self.cmp(other))
96 }
97
98 #[inline(always)]
99 fn lt(&self, other: &Self) -> bool {
100 (*self as u32) < *other as u32
101 }
102
103 #[inline(always)]
104 fn le(&self, other: &Self) -> bool {
105 *self as u32 <= *other as u32
106 }
107
108 #[inline(always)]
109 fn gt(&self, other: &Self) -> bool {
110 *self as u32 > *other as u32
111 }
112
113 #[inline(always)]
114 fn ge(&self, other: &Self) -> bool {
115 *self as u32 >= *other as u32
116 }
117}
118
119impl PartialOrd<LevelFilter> for Level {
120 #[inline(always)]
121 fn partial_cmp(&self, other: &LevelFilter) -> Option<cmp::Ordering> {
122 Some((*self as u32).cmp(&(*other as u32)))
123 }
124
125 #[inline(always)]
126 fn lt(&self, other: &LevelFilter) -> bool {
127 (*self as u32) < *other as u32
128 }
129
130 #[inline(always)]
131 fn le(&self, other: &LevelFilter) -> bool {
132 *self as u32 <= *other as u32
133 }
134
135 #[inline(always)]
136 fn gt(&self, other: &LevelFilter) -> bool {
137 *self as u32 > *other as u32
138 }
139
140 #[inline(always)]
141 fn ge(&self, other: &LevelFilter) -> bool {
142 *self as u32 >= *other as u32
143 }
144}
145
146impl Ord for Level {
147 #[inline(always)]
148 fn cmp(&self, other: &Self) -> cmp::Ordering {
149 (*self as u32).cmp(&(*other as u32))
150 }
151}
152
153fn ok_or<T, E>(t: Option<T>, e: E) -> Result<T, E> {
154 match t {
155 Some(t) => Ok(t),
156 None => Err(e),
157 }
158}
159
160pub struct ParseLevelError(());
161
162impl FromStr for Level {
163 type Err = ParseLevelError;
164 fn from_str(level: &str) -> Result<Self, Self::Err> {
165 ok_or(
166 LEVEL_NAMES
167 .iter()
168 .position(|&name| str::eq_ignore_ascii_case(name, level))
169 .into_iter()
170 .filter(|&idx| idx != 0)
171 .map(|idx| Self::from_u32(idx as u32).unwrap())
172 .next(),
173 ParseLevelError(()),
174 )
175 }
176}
177
178impl fmt::Display for Level {
179 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
180 fmt.pad(self.as_str())
181 }
182}
183
184impl Level {
185 pub(crate) fn from_u32(u: u32) -> Option<Self> {
186 match u {
187 1 => Some(Self::Fatal),
188 2 => Some(Self::Error),
189 3 => Some(Self::Warn),
190 4 => Some(Self::Info),
191 5 => Some(Self::Debug),
192 6 => Some(Self::Trace),
193 _ => None,
194 }
195 }
196 #[inline(always)]
198 pub fn max() -> Self {
199 Self::Trace
200 }
201
202 #[inline(always)]
204 pub fn to_level_filter(self) -> LevelFilter {
205 LevelFilter::from_u32(self as u32).unwrap()
206 }
207
208 pub fn as_str(self) -> &'static str {
212 LEVEL_NAMES[self as usize]
213 }
214
215 pub fn iter() -> impl Iterator<Item = Self> {
230 (1..7).map(|i| Self::from_u32(i).unwrap())
231 }
232}
233
234impl PartialEq<Level> for LevelFilter {
235 #[inline(always)]
236 fn eq(&self, other: &Level) -> bool {
237 other.eq(self)
238 }
239}
240
241impl PartialOrd for LevelFilter {
242 #[inline(always)]
243 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
244 Some(self.cmp(other))
245 }
246
247 #[inline(always)]
248 fn lt(&self, other: &Self) -> bool {
249 (*self as u32) < *other as u32
250 }
251
252 #[inline(always)]
253 fn le(&self, other: &Self) -> bool {
254 *self as u32 <= *other as u32
255 }
256
257 #[inline(always)]
258 fn gt(&self, other: &Self) -> bool {
259 *self as u32 > *other as u32
260 }
261
262 #[inline(always)]
263 fn ge(&self, other: &Self) -> bool {
264 *self as u32 >= *other as u32
265 }
266}
267
268impl PartialOrd<Level> for LevelFilter {
269 #[inline(always)]
270 fn partial_cmp(&self, other: &Level) -> Option<cmp::Ordering> {
271 Some((*self as u32).cmp(&(*other as u32)))
272 }
273
274 #[inline(always)]
275 fn lt(&self, other: &Level) -> bool {
276 (*self as u32) < *other as u32
277 }
278
279 #[inline(always)]
280 fn le(&self, other: &Level) -> bool {
281 *self as u32 <= *other as u32
282 }
283
284 #[inline(always)]
285 fn gt(&self, other: &Level) -> bool {
286 *self as u32 > *other as u32
287 }
288
289 #[inline(always)]
290 fn ge(&self, other: &Level) -> bool {
291 *self as u32 >= *other as u32
292 }
293}
294
295impl Ord for LevelFilter {
296 #[inline(always)]
297 fn cmp(&self, other: &Self) -> cmp::Ordering {
298 (*self as u32).cmp(&(*other as u32))
299 }
300}
301
302impl FromStr for LevelFilter {
303 type Err = ParseLevelError;
304 fn from_str(level: &str) -> Result<Self, Self::Err> {
305 ok_or(
306 LEVEL_NAMES
307 .iter()
308 .position(|&name| str::eq_ignore_ascii_case(name, level))
309 .map(|p| Self::from_u32(p as u32).unwrap()),
310 ParseLevelError(()),
311 )
312 }
313}
314
315impl fmt::Display for LevelFilter {
316 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
317 fmt.pad(self.as_str())
318 }
319}
320
321impl LevelFilter {
322 pub(crate) fn from_u32(u: u32) -> Option<Self> {
323 match u {
324 0 => Some(Self::Off),
325 1 => Some(Self::Fatal),
326 2 => Some(Self::Error),
327 3 => Some(Self::Warn),
328 4 => Some(Self::Info),
329 5 => Some(Self::Debug),
330 6 => Some(Self::Trace),
331 _ => None,
332 }
333 }
334
335 #[inline(always)]
337 pub fn max() -> Self {
338 Self::Trace
339 }
340
341 #[inline(always)]
345 pub fn to_level(self) -> Option<Level> {
346 Level::from_u32(self as u32)
347 }
348
349 pub fn as_str(self) -> &'static str {
353 LEVEL_NAMES[self as usize]
354 }
355
356 pub fn iter() -> impl Iterator<Item = Self> {
371 (0..7).map(|i| Self::from_u32(i).unwrap())
372 }
373}
374
375#[repr(u32)]
376#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
377pub enum Verbosity {
379 Min = 1,
383 Med,
387 Max,
391}
392
393#[repr(u32)]
395#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
396pub enum LodFilter {
397 Off,
399 Min,
401 Med,
403 Max,
405}
406
407impl PartialEq<LodFilter> for Verbosity {
408 #[inline(always)]
409 fn eq(&self, other: &LodFilter) -> bool {
410 *self as u32 == *other as u32
411 }
412}
413
414impl PartialOrd for Verbosity {
415 #[inline(always)]
416 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
417 Some(self.cmp(other))
418 }
419
420 #[inline(always)]
421 fn lt(&self, other: &Self) -> bool {
422 (*self as u32) < *other as u32
423 }
424
425 #[inline(always)]
426 fn le(&self, other: &Self) -> bool {
427 *self as u32 <= *other as u32
428 }
429
430 #[inline(always)]
431 fn gt(&self, other: &Self) -> bool {
432 *self as u32 > *other as u32
433 }
434
435 #[inline(always)]
436 fn ge(&self, other: &Self) -> bool {
437 *self as u32 >= *other as u32
438 }
439}
440
441impl PartialOrd<LodFilter> for Verbosity {
442 #[inline(always)]
443 fn partial_cmp(&self, other: &LodFilter) -> Option<cmp::Ordering> {
444 Some((*self as u32).cmp(&(*other as u32)))
445 }
446
447 #[inline(always)]
448 fn lt(&self, other: &LodFilter) -> bool {
449 (*self as u32) < *other as u32
450 }
451
452 #[inline(always)]
453 fn le(&self, other: &LodFilter) -> bool {
454 *self as u32 <= *other as u32
455 }
456
457 #[inline(always)]
458 fn gt(&self, other: &LodFilter) -> bool {
459 *self as u32 > *other as u32
460 }
461
462 #[inline(always)]
463 fn ge(&self, other: &LodFilter) -> bool {
464 *self as u32 >= *other as u32
465 }
466}
467
468impl Ord for Verbosity {
469 #[inline(always)]
470 fn cmp(&self, other: &Self) -> cmp::Ordering {
471 (*self as u32).cmp(&(*other as u32))
472 }
473}
474
475impl FromStr for Verbosity {
476 type Err = ParseLevelError;
477 fn from_str(level: &str) -> Result<Self, Self::Err> {
478 ok_or(
479 LOD_NAMES
480 .iter()
481 .position(|&name| str::eq_ignore_ascii_case(name, level))
482 .into_iter()
483 .filter(|&idx| idx != 0)
484 .map(|idx| Self::from_usize(idx).unwrap())
485 .next(),
486 ParseLevelError(()),
487 )
488 }
489}
490
491impl fmt::Display for Verbosity {
492 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
493 fmt.pad(self.as_str())
494 }
495}
496
497impl Verbosity {
498 fn from_usize(u: usize) -> Option<Self> {
499 match u {
500 1 => Some(Self::Min),
501 2 => Some(Self::Med),
502 3 => Some(Self::Max),
503 _ => None,
504 }
505 }
506
507 fn from_u32(u: u32) -> Option<Self> {
508 match u {
509 1 => Some(Self::Min),
510 2 => Some(Self::Med),
511 3 => Some(Self::Max),
512 _ => None,
513 }
514 }
515
516 #[inline(always)]
518 pub fn max() -> Self {
519 Self::Max
520 }
521
522 #[inline(always)]
524 pub fn to_level_filter(self) -> LodFilter {
525 LodFilter::from_u32(self as u32).unwrap()
526 }
527
528 pub fn as_str(self) -> &'static str {
532 LOD_NAMES[self as usize]
533 }
534
535 pub fn iter() -> impl Iterator<Item = Self> {
550 (1..4).map(|i| Self::from_usize(i).unwrap())
551 }
552}
553
554impl PartialEq<Verbosity> for LodFilter {
555 #[inline(always)]
556 fn eq(&self, other: &Verbosity) -> bool {
557 other.eq(self)
558 }
559}
560
561impl PartialOrd for LodFilter {
562 #[inline(always)]
563 fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
564 Some(self.cmp(other))
565 }
566
567 #[inline(always)]
568 fn lt(&self, other: &Self) -> bool {
569 (*self as u32) < *other as u32
570 }
571
572 #[inline(always)]
573 fn le(&self, other: &Self) -> bool {
574 *self as u32 <= *other as u32
575 }
576
577 #[inline(always)]
578 fn gt(&self, other: &Self) -> bool {
579 *self as u32 > *other as u32
580 }
581
582 #[inline(always)]
583 fn ge(&self, other: &Self) -> bool {
584 *self as u32 >= *other as u32
585 }
586}
587
588impl PartialOrd<Verbosity> for LodFilter {
589 #[inline(always)]
590 fn partial_cmp(&self, other: &Verbosity) -> Option<cmp::Ordering> {
591 Some((*self as u32).cmp(&(*other as u32)))
592 }
593
594 #[inline(always)]
595 fn lt(&self, other: &Verbosity) -> bool {
596 (*self as u32) < *other as u32
597 }
598
599 #[inline(always)]
600 fn le(&self, other: &Verbosity) -> bool {
601 *self as u32 <= *other as u32
602 }
603
604 #[inline(always)]
605 fn gt(&self, other: &Verbosity) -> bool {
606 *self as u32 > *other as u32
607 }
608
609 #[inline(always)]
610 fn ge(&self, other: &Verbosity) -> bool {
611 *self as u32 >= *other as u32
612 }
613}
614
615impl Ord for LodFilter {
616 #[inline(always)]
617 fn cmp(&self, other: &Self) -> cmp::Ordering {
618 (*self as u32).cmp(&(*other as u32))
619 }
620}
621
622impl FromStr for LodFilter {
623 type Err = ParseLevelError;
624 fn from_str(level: &str) -> Result<Self, Self::Err> {
625 ok_or(
626 LOD_NAMES
627 .iter()
628 .position(|&name| str::eq_ignore_ascii_case(name, level))
629 .map(|p| Self::from_usize(p).unwrap()),
630 ParseLevelError(()),
631 )
632 }
633}
634
635impl fmt::Display for LodFilter {
636 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
637 fmt.pad(self.as_str())
638 }
639}
640
641impl LodFilter {
642 fn from_usize(u: usize) -> Option<Self> {
643 match u {
644 0 => Some(Self::Off),
645 1 => Some(Self::Min),
646 2 => Some(Self::Med),
647 3 => Some(Self::Max),
648 _ => None,
649 }
650 }
651
652 fn from_u32(u: u32) -> Option<Self> {
653 match u {
654 0 => Some(Self::Off),
655 1 => Some(Self::Min),
656 2 => Some(Self::Med),
657 3 => Some(Self::Max),
658 _ => None,
659 }
660 }
661
662 #[inline(always)]
664 pub fn max() -> Self {
665 Self::Max
666 }
667
668 #[inline(always)]
672 pub fn to_level(self) -> Option<Verbosity> {
673 Verbosity::from_u32(self as u32)
674 }
675
676 pub fn as_str(self) -> &'static str {
680 LOD_NAMES[self as usize]
681 }
682
683 pub fn iter() -> impl Iterator<Item = Self> {
698 (0..4).map(|i| Self::from_usize(i).unwrap())
699 }
700}
701
702static MAX_LEVEL_FILTER: AtomicU32 = AtomicU32::new(0);
703static MAX_LOD_FILTER: AtomicU32 = AtomicU32::new(0);
704
705static LEVEL_NAMES: [&str; 7] = ["OFF", "FATAL", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"];
706static LOD_NAMES: [&str; 4] = ["OFF", "LOW", "MED", "HIGH"];
707
708#[inline(always)]
714pub fn set_max_level(level: LevelFilter) {
715 MAX_LEVEL_FILTER.store(level as u32, atomic::Ordering::Relaxed);
716}
717
718#[inline(always)]
720pub fn max_level() -> LevelFilter {
721 unsafe { std::mem::transmute(MAX_LEVEL_FILTER.load(atomic::Ordering::Relaxed)) }
728}
729
730#[inline(always)]
732pub fn set_max_lod(level: LodFilter) {
733 MAX_LOD_FILTER.store(level as u32, atomic::Ordering::Relaxed);
734}
735
736#[inline(always)]
738pub fn max_lod() -> LodFilter {
739 unsafe { std::mem::transmute(MAX_LOD_FILTER.load(atomic::Ordering::Relaxed)) }
741}
742
743pub const STATIC_MAX_LEVEL: LevelFilter = MAX_LEVEL_INNER;
753
754cfg_if::cfg_if! {
755 if #[cfg(all(not(debug_assertions), feature = "release_max_level_off"))] {
756 const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Off;
757 } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_error"))] {
758 const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Error;
759 } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_warn"))] {
760 const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Warn;
761 } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_info"))] {
762 const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Info;
763 } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_debug"))] {
764 const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Debug;
765 } else if #[cfg(all(not(debug_assertions), feature = "release_max_level_trace"))] {
766 const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Trace;
767 } else if #[cfg(feature = "max_level_off")] {
768 const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Off;
769 } else if #[cfg(feature = "max_level_error")] {
770 const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Error;
771 } else if #[cfg(feature = "max_level_warn")] {
772 const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Warn;
773 } else if #[cfg(feature = "max_level_info")] {
774 const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Info;
775 } else if #[cfg(feature = "max_level_debug")] {
776 const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Debug;
777 } else {
778 const MAX_LEVEL_INNER: LevelFilter = LevelFilter::Trace;
779 }
780}
781
782pub const STATIC_MAX_LOD: LodFilter = MAX_LOD_INNER;
792
793cfg_if::cfg_if! {
794 if #[cfg(all(not(debug_assertions), feature = "release_max_lod_off"))] {
795 const MAX_LOD_INNER: LodFilter = LodFilter::Off;
796 } else if #[cfg(all(not(debug_assertions), feature = "release_max_lod_min"))] {
797 const MAX_LOD_INNER: LodFilter = LodFilter::Min;
798 } else if #[cfg(all(not(debug_assertions), feature = "release_max_lod_med"))] {
799 const MAX_LOD_INNER: LodFilter = LodFilter::Med;
800 } else if #[cfg(all(not(debug_assertions), feature = "release_max_lod_max"))] {
801 const MAX_LOD_INNER: LodFilter = LodFilter::Max;
802 } else if #[cfg(feature = "max_lod_off")] {
803 const MAX_LOD_INNER: LodFilter = LodFilter::Off;
804 } else if #[cfg(feature = "max_lod_min")] {
805 const MAX_LOD_INNER: LodFilter = LodFilter::Min;
806 } else if #[cfg(feature = "max_lod_med")] {
807 const MAX_LOD_INNER: LodFilter = LodFilter::Med;
808 } else {
809 const MAX_LOD_INNER: LodFilter = LodFilter::Max;
810 }
811}