use std::time::Duration;
use ratatui::style::Color;
use super::state::RequestRecord;
pub fn truncate(s: &str, n: usize) -> &str {
if s.len() <= n {
return s;
}
let mut idx = n;
while !s.is_char_boundary(idx) && idx > 0 {
idx -= 1;
}
&s[..idx]
}
pub fn chrono_now() -> String {
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
let secs = now % 86400;
let h = secs / 3600;
let m = (secs % 3600) / 60;
let s = secs % 60;
format!("{h:02}:{m:02}:{s:02}")
}
pub fn humanize_uptime(d: Duration) -> String {
let secs = d.as_secs();
if secs < 60 {
format!("{secs}s")
} else if secs < 3600 {
format!("{}m{:02}s", secs / 60, secs % 60)
} else if secs < 86400 {
format!("{}h{:02}m", secs / 3600, (secs % 3600) / 60)
} else {
format!("{}d{:02}h", secs / 86400, (secs % 86400) / 3600)
}
}
pub fn status_color(status: u16) -> Color {
match status {
200..=299 => Color::Green,
300..=399 => Color::Cyan,
400..=499 => Color::Yellow,
500..=599 => Color::Red,
_ => Color::DarkGray,
}
}
pub fn outcome_color(rec: &RequestRecord) -> Color {
if rec.bypassed {
Color::LightGreen
} else if rec.blocked {
Color::LightRed
} else {
Color::White
}
}
pub fn status_bucket_index(status: u16) -> usize {
match status {
100..=199 => 0,
200..=299 => 1,
300..=399 => 2,
400..=499 => 3,
500..=599 => 4,
_ => 5,
}
}
pub const STATUS_BUCKET_LABELS: [&str; 6] = ["1xx", "2xx", "3xx", "4xx", "5xx", "?"];
pub fn status_bucket_color(idx: usize) -> Color {
match idx {
0 => Color::Magenta,
1 => Color::Green,
2 => Color::Cyan,
3 => Color::Yellow,
4 => Color::Red,
_ => Color::DarkGray,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn truncate_handles_multibyte() {
assert_eq!(truncate("hello", 10), "hello");
assert_eq!(truncate("hello world", 5), "hello");
assert_eq!(truncate("héllo", 3), "hé");
assert_eq!(truncate("★cat", 2), "");
assert_eq!(truncate("★cat", 3), "★");
assert_eq!(truncate("★cat", 4), "★c");
}
#[test]
fn humanize_handles_seconds_minutes_hours_days() {
assert_eq!(humanize_uptime(Duration::from_secs(5)), "5s");
assert_eq!(humanize_uptime(Duration::from_secs(95)), "1m35s");
assert_eq!(humanize_uptime(Duration::from_secs(3725)), "1h02m");
assert_eq!(humanize_uptime(Duration::from_secs(90000)), "1d01h");
}
#[test]
fn status_color_classification() {
assert_eq!(status_color(200), Color::Green);
assert_eq!(status_color(301), Color::Cyan);
assert_eq!(status_color(403), Color::Yellow);
assert_eq!(status_color(503), Color::Red);
assert_eq!(status_color(0), Color::DarkGray);
}
#[test]
fn status_bucket_indexing_covers_every_class() {
assert_eq!(status_bucket_index(100), 0);
assert_eq!(status_bucket_index(200), 1);
assert_eq!(status_bucket_index(304), 2);
assert_eq!(status_bucket_index(404), 3);
assert_eq!(status_bucket_index(503), 4);
assert_eq!(status_bucket_index(0), 5);
assert_eq!(status_bucket_index(700), 5);
}
}