use crate::tui::theme::colors;
use ratatui::{prelude::*, widgets::Widget};
#[derive(Debug, Clone)]
pub struct SeverityBadge {
severity: String,
compact: bool,
}
impl SeverityBadge {
pub(crate) fn style_for(severity: &str) -> Style {
let scheme = colors();
let bg_color = scheme.severity_color(severity);
let fg_color = scheme.severity_badge_fg(severity);
let style = Style::default().fg(fg_color).bg(bg_color);
match severity.to_lowercase().as_str() {
"critical" | "high" => style.bold(),
_ => style,
}
}
pub(crate) fn fg_color(severity: &str) -> Color {
colors().severity_color(severity)
}
pub(crate) fn indicator(severity: &str) -> &'static str {
match severity.to_lowercase().as_str() {
"critical" => "C",
"high" => "H",
"medium" | "moderate" => "M",
"low" => "L",
"info" | "informational" => "I",
"none" => "-",
_ => "U",
}
}
}
impl Widget for SeverityBadge {
fn render(self, area: Rect, buf: &mut Buffer) {
if area.width < 3 || area.height < 1 {
return;
}
let text = if self.compact {
format!(" {} ", Self::indicator(&self.severity))
} else {
let label = self.severity.to_uppercase();
if area.width as usize >= label.len() + 2 {
format!(" {label} ")
} else {
format!(" {} ", Self::indicator(&self.severity))
}
};
let style = Self::style_for(&self.severity);
let x = area.x;
let y = area.y;
for (i, ch) in text.chars().enumerate() {
if i < area.width as usize
&& let Some(cell) = buf.cell_mut((x + i as u16, y))
{
cell.set_char(ch).set_style(style);
}
}
}
}
pub struct SeverityBar {
pub critical: usize,
pub high: usize,
pub medium: usize,
pub low: usize,
}
impl SeverityBar {
pub(crate) const fn new(critical: usize, high: usize, medium: usize, low: usize) -> Self {
Self {
critical,
high,
medium,
low,
}
}
pub(crate) const fn total(&self) -> usize {
self.critical + self.high + self.medium + self.low
}
}
impl Widget for SeverityBar {
fn render(self, area: Rect, buf: &mut Buffer) {
if area.width < 4 || area.height < 1 {
return;
}
let total = self.total();
if total == 0 {
return;
}
let scheme = colors();
let width = area.width as usize;
let crit_w = (self.critical * width / total).max(usize::from(self.critical > 0));
let high_w = (self.high * width / total).max(usize::from(self.high > 0));
let med_w = (self.medium * width / total).max(usize::from(self.medium > 0));
let low_w = width.saturating_sub(crit_w + high_w + med_w);
let mut x = area.x;
let y = area.y;
for _ in 0..crit_w {
if x < area.x + area.width {
if let Some(cell) = buf.cell_mut((x, y)) {
cell.set_char('█')
.set_style(Style::default().fg(scheme.critical));
}
x += 1;
}
}
for _ in 0..high_w {
if x < area.x + area.width {
if let Some(cell) = buf.cell_mut((x, y)) {
cell.set_char('█')
.set_style(Style::default().fg(scheme.high));
}
x += 1;
}
}
for _ in 0..med_w {
if x < area.x + area.width {
if let Some(cell) = buf.cell_mut((x, y)) {
cell.set_char('█')
.set_style(Style::default().fg(scheme.medium));
}
x += 1;
}
}
for _ in 0..low_w {
if x < area.x + area.width {
if let Some(cell) = buf.cell_mut((x, y)) {
cell.set_char('█')
.set_style(Style::default().fg(scheme.low));
}
x += 1;
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_severity_indicators() {
assert_eq!(SeverityBadge::indicator("critical"), "C");
assert_eq!(SeverityBadge::indicator("HIGH"), "H");
assert_eq!(SeverityBadge::indicator("medium"), "M");
assert_eq!(SeverityBadge::indicator("low"), "L");
assert_eq!(SeverityBadge::indicator("info"), "I");
assert_eq!(SeverityBadge::indicator("none"), "-");
assert_eq!(SeverityBadge::indicator("unknown"), "U");
assert_eq!(SeverityBadge::indicator("other"), "U");
}
#[test]
fn test_severity_colors_use_theme() {
let scheme = colors();
assert_eq!(SeverityBadge::fg_color("critical"), scheme.critical);
assert_eq!(SeverityBadge::fg_color("high"), scheme.high);
assert_eq!(SeverityBadge::fg_color("medium"), scheme.medium);
assert_eq!(SeverityBadge::fg_color("low"), scheme.low);
}
}