use colored::Color;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Severity {
Low,
Medium,
High,
Critical,
}
impl Severity {
#[inline]
pub fn from_score(score: f64) -> Self {
if score >= 8.0 {
Self::Critical
} else if score >= 6.0 {
Self::High
} else if score >= 4.0 {
Self::Medium
} else {
Self::Low
}
}
#[inline]
pub fn from_score_100(score: f64) -> Self {
if score >= 70.0 {
Self::Critical
} else if score >= 50.0 {
Self::High
} else if score >= 30.0 {
Self::Medium
} else {
Self::Low
}
}
#[inline]
pub const fn as_str(self) -> &'static str {
match self {
Self::Critical => "CRITICAL",
Self::High => "HIGH",
Self::Medium => "MEDIUM",
Self::Low => "LOW",
}
}
#[inline]
pub const fn color(self) -> Color {
match self {
Self::Critical => Color::Red,
Self::High => Color::Yellow,
Self::Medium => Color::Blue,
Self::Low => Color::Green,
}
}
#[inline]
pub const fn rank(self) -> u8 {
match self {
Self::Low => 1,
Self::Medium => 2,
Self::High => 3,
Self::Critical => 4,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn severity_thresholds() {
assert_eq!(Severity::from_score(10.0), Severity::Critical);
assert_eq!(Severity::from_score(8.0), Severity::Critical);
assert_eq!(Severity::from_score(7.9), Severity::High);
assert_eq!(Severity::from_score(6.0), Severity::High);
assert_eq!(Severity::from_score(5.9), Severity::Medium);
assert_eq!(Severity::from_score(4.0), Severity::Medium);
assert_eq!(Severity::from_score(3.9), Severity::Low);
assert_eq!(Severity::from_score(0.0), Severity::Low);
}
#[test]
fn severity_labels() {
assert_eq!(Severity::Critical.as_str(), "CRITICAL");
assert_eq!(Severity::High.as_str(), "HIGH");
assert_eq!(Severity::Medium.as_str(), "MEDIUM");
assert_eq!(Severity::Low.as_str(), "LOW");
}
#[test]
fn severity_colors() {
assert_eq!(Severity::Critical.color(), Color::Red);
assert_eq!(Severity::High.color(), Color::Yellow);
assert_eq!(Severity::Medium.color(), Color::Blue);
assert_eq!(Severity::Low.color(), Color::Green);
}
#[test]
fn severity_ordering() {
assert!(Severity::Critical > Severity::High);
assert!(Severity::High > Severity::Medium);
assert!(Severity::Medium > Severity::Low);
}
#[test]
fn severity_is_monotonic() {
let test_cases = [(0.0, 3.9), (4.0, 5.9), (6.0, 7.9), (8.0, 10.0)];
for (lower, higher) in test_cases {
let sev_lower = Severity::from_score(lower);
let sev_higher = Severity::from_score(higher);
assert!(
sev_higher >= sev_lower,
"Higher score ({}) should have same or higher severity than lower score ({})",
higher,
lower
);
}
}
#[test]
fn severity_thresholds_0_100_scale() {
assert_eq!(Severity::from_score_100(100.0), Severity::Critical);
assert_eq!(Severity::from_score_100(70.0), Severity::Critical);
assert_eq!(Severity::from_score_100(69.9), Severity::High);
assert_eq!(Severity::from_score_100(50.0), Severity::High);
assert_eq!(Severity::from_score_100(49.9), Severity::Medium);
assert_eq!(Severity::from_score_100(30.0), Severity::Medium);
assert_eq!(Severity::from_score_100(29.9), Severity::Low);
assert_eq!(Severity::from_score_100(0.0), Severity::Low);
}
#[test]
fn severity_100_scale_is_monotonic() {
let test_cases = [(0.0, 29.9), (30.0, 49.9), (50.0, 69.9), (70.0, 100.0)];
for (lower, higher) in test_cases {
let sev_lower = Severity::from_score_100(lower);
let sev_higher = Severity::from_score_100(higher);
assert!(
sev_higher >= sev_lower,
"Higher score ({}) should have same or higher severity than lower ({})",
higher,
lower
);
}
}
#[test]
fn severity_100_scale_practical_examples() {
assert_eq!(Severity::from_score_100(53.2), Severity::High);
assert_eq!(Severity::from_score_100(30.1), Severity::Medium);
assert_eq!(Severity::from_score_100(13.2), Severity::Low);
assert_eq!(Severity::from_score_100(7.35), Severity::Low);
}
#[test]
fn severity_rank_values() {
assert_eq!(Severity::Critical.rank(), 4);
assert_eq!(Severity::High.rank(), 3);
assert_eq!(Severity::Medium.rank(), 2);
assert_eq!(Severity::Low.rank(), 1);
}
#[test]
fn severity_rank_ordering_matches_enum_ordering() {
assert!(Severity::Critical.rank() > Severity::High.rank());
assert!(Severity::High.rank() > Severity::Medium.rank());
assert!(Severity::Medium.rank() > Severity::Low.rank());
assert!(Severity::Critical > Severity::High);
assert!(Severity::High > Severity::Medium);
assert!(Severity::Medium > Severity::Low);
}
}