Skip to main content

use_diagnostic_level/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::{fmt, str::FromStr};
5
6/// The severity level of a diagnostic.
7#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
8pub enum DiagnosticLevel {
9    /// Informational diagnostic.
10    Info,
11    /// Warning diagnostic.
12    Warning,
13    /// Error diagnostic.
14    Error,
15    /// Fatal diagnostic.
16    Fatal,
17}
18
19impl DiagnosticLevel {
20    /// Returns the canonical lowercase level name.
21    #[must_use]
22    pub const fn as_str(self) -> &'static str {
23        match self {
24            Self::Info => "info",
25            Self::Warning => "warning",
26            Self::Error => "error",
27            Self::Fatal => "fatal",
28        }
29    }
30
31    /// Returns `true` when the level is an error or more severe.
32    #[must_use]
33    pub const fn is_error(self) -> bool {
34        matches!(self, Self::Error | Self::Fatal)
35    }
36
37    /// Returns `true` when the level is fatal.
38    #[must_use]
39    pub const fn is_fatal(self) -> bool {
40        matches!(self, Self::Fatal)
41    }
42}
43
44impl fmt::Display for DiagnosticLevel {
45    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
46        formatter.write_str(self.as_str())
47    }
48}
49
50impl FromStr for DiagnosticLevel {
51    type Err = DiagnosticLevelParseError;
52
53    fn from_str(value: &str) -> Result<Self, Self::Err> {
54        match value.trim().to_ascii_lowercase().as_str() {
55            "info" => Ok(Self::Info),
56            "warn" | "warning" => Ok(Self::Warning),
57            "error" => Ok(Self::Error),
58            "fatal" => Ok(Self::Fatal),
59            _ => Err(DiagnosticLevelParseError),
60        }
61    }
62}
63
64/// Error returned when parsing a diagnostic level fails.
65#[derive(Clone, Copy, Debug, Eq, PartialEq)]
66pub struct DiagnosticLevelParseError;
67
68impl fmt::Display for DiagnosticLevelParseError {
69    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
70        formatter.write_str("unknown diagnostic level")
71    }
72}
73
74impl std::error::Error for DiagnosticLevelParseError {}
75
76#[cfg(test)]
77mod tests {
78    use super::{DiagnosticLevel, DiagnosticLevelParseError};
79
80    #[test]
81    fn parses_known_levels() -> Result<(), DiagnosticLevelParseError> {
82        assert_eq!("info".parse::<DiagnosticLevel>()?, DiagnosticLevel::Info);
83        assert_eq!(
84            "warning".parse::<DiagnosticLevel>()?,
85            DiagnosticLevel::Warning
86        );
87        assert_eq!("error".parse::<DiagnosticLevel>()?, DiagnosticLevel::Error);
88        assert_eq!("fatal".parse::<DiagnosticLevel>()?, DiagnosticLevel::Fatal);
89        Ok(())
90    }
91
92    #[test]
93    fn parses_aliases() -> Result<(), DiagnosticLevelParseError> {
94        assert_eq!("warn".parse::<DiagnosticLevel>()?, DiagnosticLevel::Warning);
95        assert_eq!(
96            " WARNING ".parse::<DiagnosticLevel>()?,
97            DiagnosticLevel::Warning
98        );
99        Ok(())
100    }
101
102    #[test]
103    fn displays_known_levels() {
104        assert_eq!(DiagnosticLevel::Info.to_string(), "info");
105        assert_eq!(DiagnosticLevel::Warning.to_string(), "warning");
106        assert_eq!(DiagnosticLevel::Error.to_string(), "error");
107        assert_eq!(DiagnosticLevel::Fatal.to_string(), "fatal");
108    }
109
110    #[test]
111    fn severity_ordering_places_more_severe_levels_greater() {
112        assert!(DiagnosticLevel::Warning > DiagnosticLevel::Info);
113        assert!(DiagnosticLevel::Error > DiagnosticLevel::Warning);
114        assert!(DiagnosticLevel::Fatal > DiagnosticLevel::Error);
115    }
116}