use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub enum Severity {
Critical,
High,
Medium,
Low,
Info,
}
impl Severity {
pub fn emoji(&self) -> &'static str {
match self {
Severity::Critical => "\u{1f480}",
Severity::High => "\u{1f621}",
Severity::Medium => "\u{26a0}\u{fe0f}",
Severity::Low => "\u{1f4a7}",
Severity::Info => "\u{2139}\u{fe0f}",
}
}
pub fn penalty(&self) -> f64 {
match self {
Severity::Critical => 10.0,
Severity::High => 5.0,
Severity::Medium => 2.0,
Severity::Low => 0.5,
Severity::Info => 0.0,
}
}
pub fn weight(&self) -> f64 {
self.penalty()
}
pub fn label(&self) -> &'static str {
match self {
Severity::Critical => "Critical",
Severity::High => "High",
Severity::Medium => "Medium",
Severity::Low => "Low",
Severity::Info => "Info",
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_severity_ordering_adjacent() {
assert!(Severity::Critical < Severity::High, "Critical < High");
assert!(Severity::High < Severity::Medium, "High < Medium");
assert!(Severity::Medium < Severity::Low, "Medium < Low");
assert!(Severity::Low < Severity::Info, "Low < Info");
}
#[test]
fn test_severity_ordering_transitive() {
assert!(Severity::Critical < Severity::Medium, "Critical < Medium");
assert!(Severity::Critical < Severity::Low, "Critical < Low");
assert!(Severity::Critical < Severity::Info, "Critical < Info");
assert!(Severity::High < Severity::Low, "High < Low");
assert!(Severity::High < Severity::Info, "High < Info");
assert!(Severity::Medium < Severity::Info, "Medium < Info");
}
#[test]
fn test_severity_ordering_equal() {
assert_eq!(Severity::Critical, Severity::Critical);
assert_eq!(Severity::High, Severity::High);
assert_eq!(Severity::Medium, Severity::Medium);
assert_eq!(Severity::Low, Severity::Low);
assert_eq!(Severity::Info, Severity::Info);
}
#[test]
fn test_emoji_specific_values() {
assert_eq!(Severity::Critical.emoji(), "\u{1f480}", "Critical → skull");
assert_eq!(Severity::High.emoji(), "\u{1f621}", "High → angry");
assert_eq!(
Severity::Medium.emoji(),
"\u{26a0}\u{fe0f}",
"Medium → warning"
);
assert_eq!(Severity::Low.emoji(), "\u{1f4a7}", "Low → droplet");
assert_eq!(Severity::Info.emoji(), "\u{2139}\u{fe0f}", "Info → info");
}
#[test]
fn test_penalty_values() {
assert_eq!(Severity::Critical.penalty(), 10.0);
assert_eq!(Severity::High.penalty(), 5.0);
assert_eq!(Severity::Medium.penalty(), 2.0);
assert_eq!(Severity::Low.penalty(), 0.5);
assert_eq!(Severity::Info.penalty(), 0.0);
}
#[test]
fn test_penalty_ordering_matches_severity() {
assert!(Severity::Critical.penalty() > Severity::High.penalty());
assert!(Severity::High.penalty() > Severity::Medium.penalty());
assert!(Severity::Medium.penalty() > Severity::Low.penalty());
assert!(Severity::Low.penalty() > Severity::Info.penalty());
}
#[test]
fn test_weight_is_alias_for_penalty() {
for sev in [
Severity::Critical,
Severity::High,
Severity::Medium,
Severity::Low,
Severity::Info,
] {
assert_eq!(
sev.weight(),
sev.penalty(),
"{:?}.weight() should equal .penalty()",
sev
);
}
}
#[test]
fn test_label_all_variants() {
assert_eq!(Severity::Critical.label(), "Critical");
assert_eq!(Severity::High.label(), "High");
assert_eq!(Severity::Medium.label(), "Medium");
assert_eq!(Severity::Low.label(), "Low");
assert_eq!(Severity::Info.label(), "Info");
}
#[test]
fn test_label_unique() {
let labels: Vec<&str> = [
Severity::Critical,
Severity::High,
Severity::Medium,
Severity::Low,
Severity::Info,
]
.iter()
.map(|s| s.label())
.collect();
let unique: std::collections::HashSet<&&str> = labels.iter().collect();
assert_eq!(unique.len(), labels.len(), "all labels must be unique");
}
#[test]
fn test_severity_serde_roundtrip() {
for sev in [
Severity::Critical,
Severity::High,
Severity::Medium,
Severity::Low,
Severity::Info,
] {
let json = serde_json::to_string(&sev).unwrap();
let parsed: Severity = serde_json::from_str(&json).unwrap();
assert_eq!(parsed, sev, "{sev:?} round-trips through JSON");
}
}
#[test]
fn test_severity_serde_from_string() {
let parsed: Severity = serde_json::from_str("\"Critical\"").unwrap();
assert_eq!(parsed, Severity::Critical);
let parsed: Severity = serde_json::from_str("\"Info\"").unwrap();
assert_eq!(parsed, Severity::Info);
}
}