Skip to main content

diagnostics_log_types/
lib.rs

1// Copyright 2024 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.
3
4//! This crate provides the basic types that we rely on for logs.
5
6#![warn(missing_docs)]
7
8use fidl_fuchsia_diagnostics_types_common as fdiagnostics;
9use std::str::FromStr;
10use std::{cmp, fmt};
11
12#[cfg(feature = "serde")]
13#[doc(hidden)]
14pub mod serde_ext;
15
16/// Severities a log message can have, often called the log's "level".
17#[cfg_attr(feature = "serde", derive(schemars::JsonSchema))]
18#[cfg_attr(feature = "serde", serde(rename_all = "UPPERCASE"))]
19#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
20#[repr(u8)]
21pub enum Severity {
22    /// Trace severity level
23    Trace = 0x10,
24    /// Debug severity level
25    Debug = 0x20,
26    /// Info severity level
27    Info = 0x30,
28    /// Warn severity level
29    Warn = 0x40,
30    /// Error severity level
31    Error = 0x50,
32    /// Fatal severity level
33    Fatal = 0x60,
34}
35
36#[cfg(feature = "serde")]
37impl serde::Serialize for Severity {
38    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
39    where
40        S: serde::Serializer,
41    {
42        serde_ext::severity::serialize(self, serializer)
43    }
44}
45
46#[cfg(feature = "serde")]
47impl<'de> serde::Deserialize<'de> for Severity {
48    fn deserialize<D>(deserializer: D) -> Result<Severity, D::Error>
49    where
50        D: serde::Deserializer<'de>,
51    {
52        serde_ext::severity::deserialize(deserializer)
53    }
54}
55
56impl Severity {
57    /// Returns a severity and also the raw severity if it's  not an exact match of a severity value.
58    pub fn parse_exact(raw_severity: u8) -> (Option<u8>, Severity) {
59        if raw_severity == Severity::Trace as u8 {
60            (None, Severity::Trace)
61        } else if raw_severity == Severity::Debug as u8 {
62            (None, Severity::Debug)
63        } else if raw_severity == Severity::Info as u8 {
64            (None, Severity::Info)
65        } else if raw_severity == Severity::Warn as u8 {
66            (None, Severity::Warn)
67        } else if raw_severity == Severity::Error as u8 {
68            (None, Severity::Error)
69        } else if raw_severity == Severity::Fatal as u8 {
70            (None, Severity::Fatal)
71        } else {
72            (Some(raw_severity), Severity::from(raw_severity))
73        }
74    }
75
76    /// Returns the string representation of a severity.
77    pub fn as_str(&self) -> &'static str {
78        match self {
79            Severity::Trace => "TRACE",
80            Severity::Debug => "DEBUG",
81            Severity::Info => "INFO",
82            Severity::Warn => "WARN",
83            Severity::Error => "ERROR",
84            Severity::Fatal => "FATAL",
85        }
86    }
87}
88
89macro_rules! impl_from_signed {
90    ($($type:ty),*) => {
91        $(
92            impl From<$type> for Severity {
93                fn from(value: $type) -> Severity {
94                    match value {
95                        ..0x00 => Severity::Trace,
96                        0x00..=0x10 => Severity::Trace,
97                        0x11..=0x20 => Severity::Debug,
98                        0x21..=0x30 => Severity::Info,
99                        0x31..=0x40 => Severity::Warn,
100                        0x41..=0x50 => Severity::Error,
101                        0x51.. => Severity::Fatal,
102                    }
103                }
104            }
105        )*
106    }
107}
108
109macro_rules! impl_from_unsigned {
110    ($($type:ty),*) => {
111        $(
112            impl From<$type> for Severity {
113                fn from(value: $type) -> Severity {
114                    match value {
115                        0x00..=0x10 => Severity::Trace,
116                        0x11..=0x20 => Severity::Debug,
117                        0x21..=0x30 => Severity::Info,
118                        0x31..=0x40 => Severity::Warn,
119                        0x41..=0x50 => Severity::Error,
120                        0x51.. => Severity::Fatal,
121                    }
122                }
123            }
124        )*
125    }
126}
127
128impl_from_signed!(i8, i16, i32, i64, i128);
129impl_from_unsigned!(u8, u16, u32, u64, u128);
130
131impl From<log::Level> for Severity {
132    fn from(level: log::Level) -> Severity {
133        match level {
134            log::Level::Trace => Severity::Trace,
135            log::Level::Debug => Severity::Debug,
136            log::Level::Info => Severity::Info,
137            log::Level::Warn => Severity::Warn,
138            log::Level::Error => Severity::Error,
139        }
140    }
141}
142
143impl TryFrom<log::LevelFilter> for Severity {
144    type Error = ();
145    fn try_from(s: log::LevelFilter) -> Result<Severity, ()> {
146        match s {
147            log::LevelFilter::Off => Err(()),
148            log::LevelFilter::Trace => Ok(Severity::Trace),
149            log::LevelFilter::Debug => Ok(Severity::Debug),
150            log::LevelFilter::Info => Ok(Severity::Info),
151            log::LevelFilter::Warn => Ok(Severity::Warn),
152            log::LevelFilter::Error => Ok(Severity::Error),
153        }
154    }
155}
156
157impl From<Severity> for log::LevelFilter {
158    fn from(s: Severity) -> log::LevelFilter {
159        match s {
160            Severity::Trace => log::LevelFilter::Trace,
161            Severity::Debug => log::LevelFilter::Debug,
162            Severity::Info => log::LevelFilter::Info,
163            Severity::Warn => log::LevelFilter::Warn,
164            Severity::Fatal | Severity::Error => log::LevelFilter::Error,
165        }
166    }
167}
168
169impl From<Severity> for fdiagnostics::Severity {
170    fn from(s: Severity) -> fdiagnostics::Severity {
171        match s {
172            Severity::Trace => fdiagnostics::Severity::Trace,
173            Severity::Debug => fdiagnostics::Severity::Debug,
174            Severity::Info => fdiagnostics::Severity::Info,
175            Severity::Warn => fdiagnostics::Severity::Warn,
176            Severity::Error => fdiagnostics::Severity::Error,
177            Severity::Fatal => fdiagnostics::Severity::Fatal,
178        }
179    }
180}
181
182impl From<fdiagnostics::Severity> for Severity {
183    fn from(s: fdiagnostics::Severity) -> Severity {
184        match s {
185            fdiagnostics::Severity::Trace => Severity::Trace,
186            fdiagnostics::Severity::Debug => Severity::Debug,
187            fdiagnostics::Severity::Info => Severity::Info,
188            fdiagnostics::Severity::Warn => Severity::Warn,
189            fdiagnostics::Severity::Error => Severity::Error,
190            fdiagnostics::Severity::Fatal => Severity::Fatal,
191            fdiagnostics::Severity::__SourceBreaking { .. } => Severity::Info,
192        }
193    }
194}
195
196impl AsRef<str> for Severity {
197    fn as_ref(&self) -> &str {
198        self.as_str()
199    }
200}
201
202impl fmt::Display for Severity {
203    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204        write!(f, "{}", self.as_str())
205    }
206}
207
208/// Parsing error for severities.
209#[derive(thiserror::Error, Debug)]
210pub enum Error {
211    /// Attempted to parse a string that didn't map to a valid severity.
212    #[error("invalid severity: {0}")]
213    Invalid(String),
214}
215
216impl FromStr for Severity {
217    type Err = Error;
218    fn from_str(s: &str) -> Result<Self, Self::Err> {
219        let s = s.to_lowercase();
220        match s.as_str() {
221            "trace" => Ok(Severity::Trace),
222            "debug" => Ok(Severity::Debug),
223            "info" => Ok(Severity::Info),
224            "warn" | "warning" => Ok(Severity::Warn),
225            "error" => Ok(Severity::Error),
226            "fatal" => Ok(Severity::Fatal),
227            other => Err(Error::Invalid(other.to_string())),
228        }
229    }
230}
231
232impl PartialEq<fdiagnostics::Severity> for Severity {
233    fn eq(&self, other: &fdiagnostics::Severity) -> bool {
234        matches!(
235            (self, other),
236            (Severity::Trace, fdiagnostics::Severity::Trace)
237                | (Severity::Debug, fdiagnostics::Severity::Debug)
238                | (Severity::Info, fdiagnostics::Severity::Info)
239                | (Severity::Warn, fdiagnostics::Severity::Warn)
240                | (Severity::Error, fdiagnostics::Severity::Error)
241                | (Severity::Fatal, fdiagnostics::Severity::Fatal)
242        )
243    }
244}
245
246impl PartialOrd<fdiagnostics::Severity> for Severity {
247    fn partial_cmp(&self, other: &fdiagnostics::Severity) -> Option<cmp::Ordering> {
248        let other = match other {
249            fdiagnostics::Severity::Trace => Severity::Trace,
250            fdiagnostics::Severity::Debug => Severity::Debug,
251            fdiagnostics::Severity::Info => Severity::Info,
252            fdiagnostics::Severity::Warn => Severity::Warn,
253            fdiagnostics::Severity::Error => Severity::Error,
254            fdiagnostics::Severity::Fatal => Severity::Fatal,
255            fdiagnostics::Severity::__SourceBreaking { .. } => return None,
256        };
257        self.partial_cmp(&other)
258    }
259}
260
261#[cfg(test)]
262mod tests {
263    use super::*;
264
265    #[test]
266    fn from_fdiagnostics_severity() {
267        assert_eq!(Severity::from(fdiagnostics::Severity::Trace), Severity::Trace);
268        assert_eq!(Severity::from(fdiagnostics::Severity::Debug), Severity::Debug);
269        assert_eq!(Severity::from(fdiagnostics::Severity::Info), Severity::Info);
270        assert_eq!(Severity::from(fdiagnostics::Severity::Warn), Severity::Warn);
271        assert_eq!(Severity::from(fdiagnostics::Severity::Error), Severity::Error);
272        assert_eq!(Severity::from(fdiagnostics::Severity::Fatal), Severity::Fatal);
273        assert_eq!(
274            Severity::from(fdiagnostics::Severity::__SourceBreaking { unknown_ordinal: 123 }),
275            Severity::Info
276        );
277    }
278
279    #[test]
280    fn partial_cmp_fdiagnostics_severity() {
281        assert_eq!(
282            Severity::Info.partial_cmp(&fdiagnostics::Severity::Warn),
283            Some(cmp::Ordering::Less)
284        );
285        assert_eq!(
286            Severity::Warn.partial_cmp(&fdiagnostics::Severity::Info),
287            Some(cmp::Ordering::Greater)
288        );
289        assert_eq!(
290            Severity::Info.partial_cmp(&fdiagnostics::Severity::Info),
291            Some(cmp::Ordering::Equal)
292        );
293        assert_eq!(
294            Severity::Info
295                .partial_cmp(&fdiagnostics::Severity::__SourceBreaking { unknown_ordinal: 123 }),
296            None
297        );
298    }
299}