1use std::{fmt, str::FromStr};
2
3use cfg_if::cfg_if;
4
5use crate::Error;
6
7pub(crate) const LOG_LEVEL_NAMES: [&str; Level::count()] =
8 ["critical", "error", "warn", "info", "debug", "trace"];
9
10const LOG_LEVEL_SHORT_NAMES: [&str; Level::count()] = ["C", "E", "W", "I", "D", "T"];
11
12#[repr(u16)]
54#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
55pub enum Level {
56 Critical = 0,
58 Error,
60 Warn,
62 Info,
64 Debug,
66 Trace,
68}
69
70#[cfg(feature = "serde")]
71impl serde::Serialize for Level {
72 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
73 where
74 S: serde::Serializer,
75 {
76 serializer.serialize_str(self.as_str())
77 }
78}
79
80cfg_if! {
81 if #[cfg(test)] {
82 crate::utils::const_assert!(atomic::Atomic::<Level>::is_lock_free());
83 }
84}
85
86impl Level {
87 #[must_use]
88 fn from_usize(u: usize) -> Option<Level> {
89 match u {
90 0 => Some(Level::Critical),
91 1 => Some(Level::Error),
92 2 => Some(Level::Warn),
93 3 => Some(Level::Info),
94 4 => Some(Level::Debug),
95 5 => Some(Level::Trace),
96 _ => None,
97 }
98 }
99
100 #[must_use]
101 const fn min_usize() -> usize {
102 Self::most_severe() as usize
103 }
104
105 #[must_use]
106 const fn max_usize() -> usize {
107 Self::most_verbose() as usize
108 }
109
110 #[must_use]
111 pub(crate) const fn count() -> usize {
112 Self::max_usize() + 1
113 }
114
115 #[must_use]
117 pub const fn most_severe() -> Level {
118 Level::Critical
119 }
120
121 #[must_use]
123 pub const fn most_verbose() -> Level {
124 Level::Trace
125 }
126
127 #[must_use]
131 pub fn as_str(&self) -> &'static str {
132 LOG_LEVEL_NAMES[*self as usize]
133 }
134
135 #[must_use]
136 pub(crate) fn as_short_str(&self) -> &'static str {
137 LOG_LEVEL_SHORT_NAMES[*self as usize]
138 }
139
140 pub fn iter() -> impl Iterator<Item = Self> {
155 (Self::min_usize()..=Self::max_usize()).map(|i| Self::from_usize(i).unwrap())
156 }
157}
158
159#[cfg(feature = "log")]
160impl From<log::Level> for Level {
161 fn from(level: log::Level) -> Self {
162 match level {
163 log::Level::Error => Self::Error,
164 log::Level::Warn => Self::Warn,
165 log::Level::Info => Self::Info,
166 log::Level::Debug => Self::Debug,
167 log::Level::Trace => Self::Trace,
168 }
169 }
170}
171
172impl fmt::Display for Level {
173 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
174 f.write_str(self.as_str())
175 }
176}
177
178impl FromStr for Level {
179 type Err = Error;
180
181 fn from_str(level: &str) -> Result<Level, Self::Err> {
182 LOG_LEVEL_NAMES
183 .iter()
184 .position(|&name| name.eq_ignore_ascii_case(level))
185 .into_iter()
186 .map(|idx| Level::from_usize(idx).unwrap())
187 .next()
188 .ok_or_else(|| Error::ParseLevel(level.to_string()))
189 }
190}
191
192#[repr(align(4))]
197#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
198pub enum LevelFilter {
199 Off,
201 Equal(Level),
203 NotEqual(Level),
205 MoreSevere(Level),
207 MoreSevereEqual(Level),
210 MoreVerbose(Level),
212 MoreVerboseEqual(Level),
215 All,
217}
218
219cfg_if! {
220 if #[cfg(test)] {
221 use std::mem::{align_of, size_of};
222 use crate::utils::const_assert;
223
224 const_assert!(atomic::Atomic::<LevelFilter>::is_lock_free());
225 const_assert!(size_of::<Level>() * 2 == size_of::<LevelFilter>());
226 const_assert!(align_of::<Level>() * 2 == align_of::<LevelFilter>());
227 }
228}
229
230impl LevelFilter {
231 #[deprecated(
233 since = "0.4.0",
234 note = "it may be removed in the future, use method `test()` instead"
235 )]
236 #[must_use]
237 pub fn compare(&self, level: Level) -> bool {
238 self.__test_const(level)
239 }
240
241 #[must_use]
256 pub fn test(&self, level: Level) -> bool {
257 self.__test_const(level)
258 }
259
260 #[doc(hidden)]
262 #[must_use]
263 pub const fn __test_const(&self, level: Level) -> bool {
264 let level_num: u16 = level as u16;
265
266 match *self {
267 Self::Off => false,
268 Self::Equal(stored) => level_num == stored as u16,
269 Self::NotEqual(stored) => level_num != stored as u16,
270 Self::MoreSevere(stored) => level_num < stored as u16,
271 Self::MoreSevereEqual(stored) => level_num <= stored as u16,
272 Self::MoreVerbose(stored) => level_num > stored as u16,
273 Self::MoreVerboseEqual(stored) => level_num >= stored as u16,
274 Self::All => true,
275 }
276 }
277
278 #[must_use]
279 pub(crate) fn from_str_for_env(text: &str) -> Option<LevelFilter> {
280 if let Ok(level) = Level::from_str(text) {
281 Some(LevelFilter::MoreSevereEqual(level))
282 } else if text.eq_ignore_ascii_case("off") {
283 Some(LevelFilter::Off)
284 } else if text.eq_ignore_ascii_case("all") {
285 Some(LevelFilter::All)
286 } else {
287 None
288 }
289 }
290}
291
292#[cfg(feature = "log")]
293impl From<log::LevelFilter> for LevelFilter {
294 fn from(filter: log::LevelFilter) -> Self {
295 match filter {
296 log::LevelFilter::Off => Self::Off,
297 filter => Self::MoreSevereEqual(Level::from(filter.to_level().unwrap())),
298 }
299 }
300}
301
302#[cfg(test)]
303mod tests {
304 use super::*;
305
306 #[test]
307 fn from_usize() {
308 assert_eq!(
309 Level::most_severe(),
310 Level::from_usize(Level::min_usize()).unwrap()
311 );
312
313 assert_eq!(
314 Level::most_verbose(),
315 Level::from_usize(Level::max_usize()).unwrap()
316 );
317 }
318
319 #[test]
320 fn from_str() {
321 fn to_random_case(input: &str) -> String {
322 input
323 .char_indices()
324 .map(|(i, char)| {
325 if i % 2 != 0 {
326 char.to_ascii_uppercase()
327 } else {
328 char.to_ascii_lowercase()
329 }
330 })
331 .collect::<String>()
332 }
333
334 for (i, &name) in LOG_LEVEL_NAMES.iter().enumerate() {
335 let from_usize = Level::from_usize(i);
336 let from_str = (
337 Level::from_str(&name.to_lowercase()),
338 Level::from_str(&name.to_uppercase()),
339 Level::from_str(&to_random_case(name)),
340 );
341
342 assert_eq!(from_usize.unwrap(), from_str.0.unwrap());
343 assert_eq!(from_usize.unwrap(), from_str.1.unwrap());
344 assert_eq!(from_usize.unwrap(), from_str.2.unwrap());
345 }
346
347 assert!(Level::from_str("notexist").is_err());
348 }
349
350 #[test]
351 fn as_short_str() {
352 for (&name, &short_name) in LOG_LEVEL_NAMES.iter().zip(LOG_LEVEL_SHORT_NAMES.iter()) {
353 assert_eq!(
354 name.chars()
355 .next()
356 .unwrap()
357 .to_ascii_uppercase()
358 .to_string(),
359 short_name
360 );
361 }
362 }
363
364 #[test]
365 fn level_filter_from_str_for_env() {
366 assert_eq!(
367 LevelFilter::MoreSevereEqual(Level::Info),
368 LevelFilter::from_str_for_env("iNFo").unwrap()
369 );
370
371 assert_eq!(
372 LevelFilter::MoreSevereEqual(Level::Warn),
373 LevelFilter::from_str_for_env("wARn").unwrap()
374 );
375
376 assert_eq!(
377 LevelFilter::Off,
378 LevelFilter::from_str_for_env("oFf").unwrap()
379 );
380
381 assert_eq!(
382 LevelFilter::All,
383 LevelFilter::from_str_for_env("aLl").unwrap()
384 );
385 }
386
387 #[test]
388 fn iter() {
389 let mut iter = Level::iter();
390 assert_eq!(iter.next(), Some(Level::Critical));
391 assert_eq!(iter.next(), Some(Level::Error));
392 assert_eq!(iter.next(), Some(Level::Warn));
393 assert_eq!(iter.next(), Some(Level::Info));
394 assert_eq!(iter.next(), Some(Level::Debug));
395 assert_eq!(iter.next(), Some(Level::Trace));
396 assert_eq!(iter.next(), None);
397 }
398
399 #[test]
400 fn filter() {
401 assert!(!LevelFilter::Off.test(Level::Trace));
402 assert!(!LevelFilter::Off.test(Level::Critical));
403 assert!(!LevelFilter::Off.test(Level::Warn));
404
405 assert!(LevelFilter::Equal(Level::Error).test(Level::Error));
406 assert!(!LevelFilter::Equal(Level::Error).test(Level::Warn));
407 assert!(!LevelFilter::Equal(Level::Error).test(Level::Critical));
408
409 assert!(LevelFilter::NotEqual(Level::Error).test(Level::Trace));
410 assert!(LevelFilter::NotEqual(Level::Error).test(Level::Info));
411 assert!(!LevelFilter::NotEqual(Level::Error).test(Level::Error));
412
413 assert!(LevelFilter::MoreSevere(Level::Info).test(Level::Warn));
414 assert!(LevelFilter::MoreSevere(Level::Info).test(Level::Error));
415 assert!(!LevelFilter::MoreSevere(Level::Info).test(Level::Info));
416
417 assert!(LevelFilter::MoreSevereEqual(Level::Info).test(Level::Warn));
418 assert!(LevelFilter::MoreSevereEqual(Level::Info).test(Level::Info));
419 assert!(!LevelFilter::MoreSevereEqual(Level::Info).test(Level::Trace));
420
421 assert!(LevelFilter::MoreVerbose(Level::Error).test(Level::Warn));
422 assert!(LevelFilter::MoreVerbose(Level::Error).test(Level::Info));
423 assert!(!LevelFilter::MoreVerbose(Level::Error).test(Level::Error));
424
425 assert!(LevelFilter::MoreVerboseEqual(Level::Error).test(Level::Warn));
426 assert!(LevelFilter::MoreVerboseEqual(Level::Error).test(Level::Error));
427 assert!(!LevelFilter::MoreVerboseEqual(Level::Error).test(Level::Critical));
428
429 assert!(LevelFilter::All.test(Level::Trace));
430 assert!(LevelFilter::All.test(Level::Critical));
431 assert!(LevelFilter::All.test(Level::Error));
432 }
433
434 #[cfg(feature = "log")]
435 #[test]
436 fn filter_from_log() {
437 assert_eq!(LevelFilter::from(log::LevelFilter::Off), LevelFilter::Off);
438 assert_eq!(
439 LevelFilter::from(log::LevelFilter::Error),
440 LevelFilter::MoreSevereEqual(Level::Error)
441 );
442 assert_eq!(
443 LevelFilter::from(log::LevelFilter::Warn),
444 LevelFilter::MoreSevereEqual(Level::Warn)
445 );
446 assert_eq!(
447 LevelFilter::from(log::LevelFilter::Info),
448 LevelFilter::MoreSevereEqual(Level::Info)
449 );
450 assert_eq!(
451 LevelFilter::from(log::LevelFilter::Debug),
452 LevelFilter::MoreSevereEqual(Level::Debug)
453 );
454 assert_eq!(
455 LevelFilter::from(log::LevelFilter::Trace),
456 LevelFilter::MoreSevereEqual(Level::Trace)
457 );
458 }
459}