use core::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u8)]
pub enum Severity {
Error = b'E',
Warning = b'W',
Critical = b'C',
Blocked = b'B',
Help = b'H',
Success = b'S',
Completed = b'K',
Info = b'I',
Trace = b'T',
}
impl Severity {
pub const fn as_char(self) -> char {
self as u8 as char
}
pub const fn as_str(self) -> &'static str {
match self {
Severity::Error => "Error",
Severity::Warning => "Warning",
Severity::Critical => "Critical",
Severity::Blocked => "Blocked",
Severity::Help => "Help",
Severity::Success => "Success",
Severity::Completed => "Completed",
Severity::Info => "Info",
Severity::Trace => "Trace",
}
}
pub const fn description(self) -> &'static str {
match self {
Severity::Error => "Operation failed",
Severity::Warning => "Potential issue or caveat",
Severity::Critical => "Severe issue requiring attention",
Severity::Blocked => "Execution blocked or waiting",
Severity::Help => "Helpful suggestion or recommendation",
Severity::Success => "Operation succeeded",
Severity::Completed => "Task or phase completed",
Severity::Info => "General informational events",
Severity::Trace => "Execution traces and instrumentation",
}
}
pub const fn priority(self) -> u8 {
match self {
Severity::Trace => 0,
Severity::Info => 1,
Severity::Completed => 2,
Severity::Success => 3,
Severity::Help => 4,
Severity::Warning => 5,
Severity::Critical => 6,
Severity::Blocked => 7,
Severity::Error => 8,
}
}
pub const fn is_blocking(self) -> bool {
matches!(self, Severity::Error | Severity::Blocked)
}
pub const fn is_positive(self) -> bool {
matches!(self, Severity::Success | Severity::Completed)
}
pub const fn is_negative(self) -> bool {
matches!(
self,
Severity::Error | Severity::Warning | Severity::Critical | Severity::Blocked
)
}
pub const fn is_neutral(self) -> bool {
matches!(self, Severity::Info | Severity::Trace)
}
#[cfg(feature = "emoji")]
pub const fn emoji(self) -> &'static str {
match self {
Severity::Error => "â",
Severity::Blocked => "đĢ",
Severity::Critical => "đĨ",
Severity::Warning => "â ī¸",
Severity::Help => "đĄ",
Severity::Success => "â
",
Severity::Completed => "âī¸",
Severity::Info => "âšī¸",
Severity::Trace => "đ",
}
}
#[cfg(feature = "ansi-colors")]
pub const fn ansi_color(self) -> &'static str {
match self {
Severity::Error => "\x1b[1;31m", Severity::Blocked => "\x1b[31m", Severity::Critical => "\x1b[1;33m", Severity::Warning => "\x1b[33m", Severity::Help => "\x1b[32m", Severity::Success => "\x1b[1;32m", Severity::Completed => "\x1b[32m", Severity::Info => "\x1b[36m", Severity::Trace => "\x1b[2;34m", }
}
#[cfg(feature = "ansi-colors")]
pub const ANSI_RESET: &'static str = "\x1b[0m";
pub const fn hex_color(self) -> &'static str {
match self {
Severity::Error => "#ce2c31",
Severity::Blocked => "#dc3e42",
Severity::Critical => "#cc4e00",
Severity::Warning => "#ab6400",
Severity::Help => "#218358",
Severity::Success => "#30a46c",
Severity::Completed => "#008573",
Severity::Info => "#0d74ce",
Severity::Trace => "#60646c",
}
}
pub const fn hex_color_dark(self) -> &'static str {
match self {
Severity::Error => "#ff9592",
Severity::Blocked => "#ec5d5e",
Severity::Critical => "#ffa057",
Severity::Warning => "#ffca16",
Severity::Help => "#3dd68c",
Severity::Success => "#33b074",
Severity::Completed => "#0bd8b6",
Severity::Info => "#70b8ff",
Severity::Trace => "#b0b4ba",
}
}
pub const fn hex_bg_color(self) -> &'static str {
match self {
Severity::Error => "#feebec",
Severity::Blocked => "#ffdbdc",
Severity::Critical => "#ffefd6",
Severity::Warning => "#fff7c2",
Severity::Help => "#e6f6eb",
Severity::Success => "#d6f1df",
Severity::Completed => "#e0f8f3",
Severity::Info => "#e6f4fe",
Severity::Trace => "#f0f0f3",
}
}
pub const fn hex_bg_color_dark(self) -> &'static str {
match self {
Severity::Error => "#3b1219",
Severity::Blocked => "#500f1c",
Severity::Critical => "#331e0b",
Severity::Warning => "#302008",
Severity::Help => "#132d21",
Severity::Success => "#113b29",
Severity::Completed => "#0d2d2a",
Severity::Info => "#0d2847",
Severity::Trace => "#212225",
}
}
}
impl fmt::Display for Severity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_char())
}
}
impl PartialOrd for Severity {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Severity {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.priority().cmp(&other.priority())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_severity_ordering() {
assert!(Severity::Trace.priority() < Severity::Error.priority());
assert!(Severity::Warning.priority() < Severity::Critical.priority());
}
#[test]
fn test_severity_comparison() {
assert!(Severity::Trace < Severity::Error);
assert!(Severity::Info < Severity::Warning);
assert!(Severity::Warning < Severity::Critical);
assert!(Severity::Critical < Severity::Blocked);
assert!(Severity::Blocked < Severity::Error);
}
#[test]
fn test_severity_blocking() {
assert!(Severity::Error.is_blocking());
assert!(Severity::Blocked.is_blocking());
assert!(!Severity::Critical.is_blocking());
assert!(!Severity::Warning.is_blocking());
assert!(!Severity::Success.is_blocking());
assert!(!Severity::Completed.is_blocking());
assert!(!Severity::Info.is_blocking());
assert!(!Severity::Trace.is_blocking());
}
#[test]
fn test_severity_categorization() {
assert!(Severity::Success.is_positive());
assert!(Severity::Completed.is_positive());
assert!(Severity::Error.is_negative());
assert!(Severity::Warning.is_negative());
assert!(Severity::Critical.is_negative());
assert!(Severity::Blocked.is_negative());
assert!(Severity::Info.is_neutral());
assert!(Severity::Trace.is_neutral());
assert!(!Severity::Error.is_positive());
assert!(!Severity::Success.is_negative());
assert!(!Severity::Info.is_positive());
assert!(!Severity::Info.is_negative());
}
#[test]
fn test_severity_metadata() {
assert_eq!(Severity::Error.as_str(), "Error");
assert_eq!(Severity::Warning.as_str(), "Warning");
assert_eq!(Severity::Critical.as_str(), "Critical");
assert_eq!(Severity::Error.description(), "Operation failed");
assert_eq!(Severity::Warning.description(), "Potential issue or caveat");
assert_eq!(Severity::Error.as_char(), 'E');
assert_eq!(Severity::Warning.as_char(), 'W');
}
#[test]
fn test_severity_positive() {
assert!(Severity::Success.is_positive());
assert!(Severity::Completed.is_positive());
assert!(!Severity::Error.is_positive());
assert!(!Severity::Warning.is_positive());
assert!(!Severity::Critical.is_positive());
assert!(!Severity::Blocked.is_positive());
assert!(!Severity::Info.is_positive());
assert!(!Severity::Trace.is_positive());
}
#[cfg(feature = "emoji")]
#[test]
fn test_emoji() {
assert_eq!(Severity::Error.emoji(), "â");
assert_eq!(Severity::Warning.emoji(), "â ī¸");
assert_eq!(Severity::Critical.emoji(), "đĨ");
assert_eq!(Severity::Success.emoji(), "â
");
assert_eq!(Severity::Completed.emoji(), "âī¸");
assert_eq!(Severity::Info.emoji(), "âšī¸");
assert_eq!(Severity::Trace.emoji(), "đ");
assert_eq!(Severity::Blocked.emoji(), "đĢ");
}
#[cfg(feature = "ansi-colors")]
#[test]
fn test_ansi_colors() {
assert_eq!(Severity::Error.ansi_color(), "\x1b[1;31m");
assert_eq!(Severity::Warning.ansi_color(), "\x1b[33m");
assert_eq!(Severity::Success.ansi_color(), "\x1b[1;32m");
assert_eq!(Severity::ANSI_RESET, "\x1b[0m");
}
#[cfg(feature = "serde")]
#[test]
fn test_serde() {
use serde_json;
let severity = Severity::Error;
let json = serde_json::to_string(&severity).unwrap();
let deserialized: Severity = serde_json::from_str(&json).unwrap();
assert_eq!(severity, deserialized);
}
}