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