witchcraft_log/
level.rs

1// Copyright 2019 Palantir Technologies, Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14use serde::de;
15use serde::de::{DeserializeSeed, EnumAccess, VariantAccess, Visitor};
16use serde::{Deserialize, Deserializer, Serialize, Serializer};
17use std::cmp::Ordering;
18use std::error::Error;
19use std::fmt;
20use std::str::FromStr;
21
22static LOG_LEVEL_NAMES: [&str; 7] = ["OFF", "FATAL", "ERROR", "WARN", "INFO", "DEBUG", "TRACE"];
23
24/// The verbosity level of a log record.
25#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
26pub enum Level {
27    /// The "fatal" level.
28    ///
29    /// Designates an extremely serious error.
30    // make the discriminants match up with LevelFilter's
31    Fatal = 1,
32    /// The "error" level.
33    ///
34    /// Designates an error.
35    Error,
36    /// The "warn" level.
37    ///
38    /// Designates a potentially concerning event.
39    Warn,
40    /// The "info" level.
41    ///
42    /// Designates useful information.
43    Info,
44    /// The "debug" level.
45    ///
46    /// Designates lower priority information.
47    Debug,
48    /// The "trace" level.
49    ///
50    /// Designates very low priority, often extremely verbose, information.
51    Trace,
52}
53
54impl fmt::Display for Level {
55    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
56        fmt.pad(LOG_LEVEL_NAMES[*self as usize])
57    }
58}
59
60impl FromStr for Level {
61    type Err = FromStrError;
62
63    fn from_str(s: &str) -> Result<Level, FromStrError> {
64        LOG_LEVEL_NAMES
65            .iter()
66            .position(|level| s.eq_ignore_ascii_case(level))
67            .and_then(Level::from_usize)
68            .ok_or(FromStrError(()))
69    }
70}
71
72impl PartialEq<LevelFilter> for Level {
73    #[inline]
74    fn eq(&self, other: &LevelFilter) -> bool {
75        *self as usize == *other as usize
76    }
77}
78
79impl PartialOrd<LevelFilter> for Level {
80    #[inline]
81    fn partial_cmp(&self, other: &LevelFilter) -> Option<Ordering> {
82        (*self as usize).partial_cmp(&(*other as usize))
83    }
84
85    #[inline]
86    fn lt(&self, other: &LevelFilter) -> bool {
87        (*self as usize) < (*other as usize)
88    }
89
90    #[inline]
91    fn le(&self, other: &LevelFilter) -> bool {
92        (*self as usize) <= (*other as usize)
93    }
94
95    #[inline]
96    fn gt(&self, other: &LevelFilter) -> bool {
97        (*self as usize) > (*other as usize)
98    }
99
100    #[inline]
101    fn ge(&self, other: &LevelFilter) -> bool {
102        (*self as usize) >= (*other as usize)
103    }
104}
105
106impl Level {
107    fn from_usize(n: usize) -> Option<Level> {
108        match n {
109            1 => Some(Level::Fatal),
110            2 => Some(Level::Error),
111            3 => Some(Level::Warn),
112            4 => Some(Level::Info),
113            5 => Some(Level::Debug),
114            6 => Some(Level::Trace),
115            _ => None,
116        }
117    }
118
119    /// Returns the standard string name of the level.
120    pub fn as_str(self) -> &'static str {
121        LOG_LEVEL_NAMES[self as usize]
122    }
123}
124
125/// A filter for log record verbosity levels.
126///
127/// The variants match `Level`'s, with the addition of `Off`, which filters out all log messages.
128#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
129#[repr(usize)] // we need to store this as a usize in MAX_LOG_LEVEL_FILTER
130pub enum LevelFilter {
131    /// A level lower than all log levels.
132    Off,
133    /// Corresponds to the `Fatal` log level.
134    Fatal,
135    /// Corresponds to the `Error` log level.
136    Error,
137    /// Corresponds to the `Warn` log level.
138    Warn,
139    /// Corresponds to the `Info` log level.
140    Info,
141    /// Corresponds to the `Debug` log level.
142    Debug,
143    /// Corresponds to the `Trace` log level.
144    Trace,
145}
146
147impl fmt::Display for LevelFilter {
148    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
149        fmt.pad(LOG_LEVEL_NAMES[*self as usize])
150    }
151}
152
153impl FromStr for LevelFilter {
154    type Err = FromStrError;
155
156    fn from_str(s: &str) -> Result<LevelFilter, FromStrError> {
157        LOG_LEVEL_NAMES
158            .iter()
159            .position(|level| s.eq_ignore_ascii_case(level))
160            .and_then(LevelFilter::from_usize)
161            .ok_or(FromStrError(()))
162    }
163}
164
165impl PartialEq<Level> for LevelFilter {
166    #[inline]
167    fn eq(&self, other: &Level) -> bool {
168        *self as usize == *other as usize
169    }
170}
171
172impl PartialOrd<Level> for LevelFilter {
173    #[inline]
174    fn partial_cmp(&self, other: &Level) -> Option<Ordering> {
175        (*self as usize).partial_cmp(&(*other as usize))
176    }
177
178    #[inline]
179    fn lt(&self, other: &Level) -> bool {
180        (*self as usize) < (*other as usize)
181    }
182
183    #[inline]
184    fn le(&self, other: &Level) -> bool {
185        (*self as usize) <= (*other as usize)
186    }
187
188    #[inline]
189    fn gt(&self, other: &Level) -> bool {
190        (*self as usize) > (*other as usize)
191    }
192
193    #[inline]
194    fn ge(&self, other: &Level) -> bool {
195        (*self as usize) >= (*other as usize)
196    }
197}
198
199impl LevelFilter {
200    fn from_usize(n: usize) -> Option<LevelFilter> {
201        match n {
202            0 => Some(LevelFilter::Off),
203            1 => Some(LevelFilter::Fatal),
204            2 => Some(LevelFilter::Error),
205            3 => Some(LevelFilter::Warn),
206            4 => Some(LevelFilter::Info),
207            5 => Some(LevelFilter::Debug),
208            6 => Some(LevelFilter::Trace),
209            _ => None,
210        }
211    }
212}
213
214/// An error parsing a `Level` or `LevelFilter` from a string.
215#[derive(Debug)]
216pub struct FromStrError(());
217
218impl fmt::Display for FromStrError {
219    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
220        fmt.write_str("invalid log level")
221    }
222}
223
224impl Error for FromStrError {}
225
226// The Deserialize impls are handwritten to be case insensitive using FromStr.
227impl Serialize for Level {
228    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
229    where
230        S: Serializer,
231    {
232        match *self {
233            Level::Fatal => serializer.serialize_unit_variant("Level", 0, "FATAL"),
234            Level::Error => serializer.serialize_unit_variant("Level", 1, "ERROR"),
235            Level::Warn => serializer.serialize_unit_variant("Level", 2, "WARN"),
236            Level::Info => serializer.serialize_unit_variant("Level", 3, "INFO"),
237            Level::Debug => serializer.serialize_unit_variant("Level", 4, "DEBUG"),
238            Level::Trace => serializer.serialize_unit_variant("Level", 5, "TRACE"),
239        }
240    }
241}
242
243impl<'de> Deserialize<'de> for Level {
244    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
245    where
246        D: Deserializer<'de>,
247    {
248        struct LevelIdentifier;
249
250        impl Visitor<'_> for LevelIdentifier {
251            type Value = Level;
252
253            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
254                formatter.write_str("log level")
255            }
256
257            fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
258            where
259                E: de::Error,
260            {
261                // Case insensitive.
262                FromStr::from_str(s)
263                    .map_err(|_| de::Error::unknown_variant(s, &LOG_LEVEL_NAMES[1..]))
264            }
265        }
266
267        impl<'de> DeserializeSeed<'de> for LevelIdentifier {
268            type Value = Level;
269
270            fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
271            where
272                D: Deserializer<'de>,
273            {
274                deserializer.deserialize_identifier(LevelIdentifier)
275            }
276        }
277
278        struct LevelEnum;
279
280        impl<'de> Visitor<'de> for LevelEnum {
281            type Value = Level;
282
283            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
284                formatter.write_str("log level")
285            }
286
287            fn visit_enum<A>(self, value: A) -> Result<Self::Value, A::Error>
288            where
289                A: EnumAccess<'de>,
290            {
291                let (level, variant) = value.variant_seed(LevelIdentifier)?;
292                // Every variant is a unit variant.
293                variant.unit_variant()?;
294                Ok(level)
295            }
296        }
297
298        deserializer.deserialize_enum("Level", &LOG_LEVEL_NAMES[1..], LevelEnum)
299    }
300}
301
302impl Serialize for LevelFilter {
303    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
304    where
305        S: Serializer,
306    {
307        match *self {
308            LevelFilter::Off => serializer.serialize_unit_variant("LevelFilter", 0, "OFF"),
309            LevelFilter::Fatal => serializer.serialize_unit_variant("LevelFilter", 1, "FATAL"),
310            LevelFilter::Error => serializer.serialize_unit_variant("LevelFilter", 2, "ERROR"),
311            LevelFilter::Warn => serializer.serialize_unit_variant("LevelFilter", 3, "WARN"),
312            LevelFilter::Info => serializer.serialize_unit_variant("LevelFilter", 4, "INFO"),
313            LevelFilter::Debug => serializer.serialize_unit_variant("LevelFilter", 5, "DEBUG"),
314            LevelFilter::Trace => serializer.serialize_unit_variant("LevelFilter", 6, "TRACE"),
315        }
316    }
317}
318
319impl<'de> Deserialize<'de> for LevelFilter {
320    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
321    where
322        D: Deserializer<'de>,
323    {
324        struct LevelFilterIdentifier;
325
326        impl Visitor<'_> for LevelFilterIdentifier {
327            type Value = LevelFilter;
328
329            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
330                formatter.write_str("log level filter")
331            }
332
333            fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
334            where
335                E: de::Error,
336            {
337                // Case insensitive.
338                FromStr::from_str(s).map_err(|_| de::Error::unknown_variant(s, &LOG_LEVEL_NAMES))
339            }
340        }
341
342        impl<'de> DeserializeSeed<'de> for LevelFilterIdentifier {
343            type Value = LevelFilter;
344
345            fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
346            where
347                D: Deserializer<'de>,
348            {
349                deserializer.deserialize_identifier(LevelFilterIdentifier)
350            }
351        }
352
353        struct LevelFilterEnum;
354
355        impl<'de> Visitor<'de> for LevelFilterEnum {
356            type Value = LevelFilter;
357
358            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
359                formatter.write_str("log level filter")
360            }
361
362            fn visit_enum<A>(self, value: A) -> Result<Self::Value, A::Error>
363            where
364                A: EnumAccess<'de>,
365            {
366                let (level_filter, variant) = value.variant_seed(LevelFilterIdentifier)?;
367                // Every variant is a unit variant.
368                variant.unit_variant()?;
369                Ok(level_filter)
370            }
371        }
372
373        deserializer.deserialize_enum("LevelFilter", &LOG_LEVEL_NAMES, LevelFilterEnum)
374    }
375}
376
377#[cfg(test)]
378mod tests {
379    use serde_test::{assert_de_tokens, assert_de_tokens_error, assert_tokens, Token};
380
381    use crate::{Level, LevelFilter};
382
383    fn level_token(variant: &'static str) -> Token {
384        Token::UnitVariant {
385            name: "Level",
386            variant,
387        }
388    }
389
390    fn level_filter_token(variant: &'static str) -> Token {
391        Token::UnitVariant {
392            name: "LevelFilter",
393            variant,
394        }
395    }
396
397    #[test]
398    fn test_level_ser_de() {
399        let cases = [
400            (Level::Fatal, [level_token("FATAL")]),
401            (Level::Error, [level_token("ERROR")]),
402            (Level::Warn, [level_token("WARN")]),
403            (Level::Info, [level_token("INFO")]),
404            (Level::Debug, [level_token("DEBUG")]),
405            (Level::Trace, [level_token("TRACE")]),
406        ];
407
408        for &(s, expected) in &cases {
409            assert_tokens(&s, &expected);
410        }
411    }
412
413    #[test]
414    fn test_level_case_insensitive() {
415        let cases = [
416            (Level::Fatal, [level_token("fatal")]),
417            (Level::Error, [level_token("error")]),
418            (Level::Warn, [level_token("warn")]),
419            (Level::Info, [level_token("info")]),
420            (Level::Debug, [level_token("debug")]),
421            (Level::Trace, [level_token("trace")]),
422        ];
423
424        for &(s, expected) in &cases {
425            assert_de_tokens(&s, &expected);
426        }
427    }
428
429    #[test]
430    fn test_level_de_error() {
431        let msg = "unknown variant `errorx`, expected one of \
432                   `FATAL`, `ERROR`, `WARN`, `INFO`, `DEBUG`, `TRACE`";
433        assert_de_tokens_error::<Level>(&[level_token("errorx")], msg);
434    }
435
436    #[test]
437    fn test_level_filter_ser_de() {
438        let cases = [
439            (LevelFilter::Off, [level_filter_token("OFF")]),
440            (LevelFilter::Fatal, [level_filter_token("FATAL")]),
441            (LevelFilter::Error, [level_filter_token("ERROR")]),
442            (LevelFilter::Warn, [level_filter_token("WARN")]),
443            (LevelFilter::Info, [level_filter_token("INFO")]),
444            (LevelFilter::Debug, [level_filter_token("DEBUG")]),
445            (LevelFilter::Trace, [level_filter_token("TRACE")]),
446        ];
447
448        for &(s, expected) in &cases {
449            assert_tokens(&s, &expected);
450        }
451    }
452
453    #[test]
454    fn test_level_filter_case_insensitive() {
455        let cases = [
456            (LevelFilter::Off, [level_filter_token("off")]),
457            (LevelFilter::Fatal, [level_filter_token("fatal")]),
458            (LevelFilter::Error, [level_filter_token("error")]),
459            (LevelFilter::Warn, [level_filter_token("warn")]),
460            (LevelFilter::Info, [level_filter_token("info")]),
461            (LevelFilter::Debug, [level_filter_token("debug")]),
462            (LevelFilter::Trace, [level_filter_token("trace")]),
463        ];
464
465        for &(s, expected) in &cases {
466            assert_de_tokens(&s, &expected);
467        }
468    }
469
470    #[test]
471    fn test_level_filter_de_error() {
472        let msg = "unknown variant `errorx`, expected one of \
473                   `OFF`, `FATAL`, `ERROR`, `WARN`, `INFO`, `DEBUG`, `TRACE`";
474        assert_de_tokens_error::<LevelFilter>(&[level_filter_token("errorx")], msg);
475    }
476}