use owo_colors::OwoColorize;
use std::io::IsTerminal;
use tabled::{Table, Tabled};
fn use_color() -> bool {
std::env::var("NO_COLOR").is_err() && std::io::stdout().is_terminal()
}
pub fn print_kv(pairs: &[(String, String)]) {
let max_key = pairs.iter().map(|(k, _)| k.len()).max().unwrap_or(0);
for (k, v) in pairs {
println!(" {:<width$} {}", k, v, width = max_key);
}
}
pub fn fmt_temp(temp_str: &str) -> String {
let Ok(t) = temp_str.trim().parse::<f64>() else {
return temp_str.to_string();
};
let display = format!("{}°C", t as i64);
if !use_color() {
return display;
}
if t >= 60.0 {
display.red().to_string()
} else if t >= 45.0 {
display.yellow().to_string()
} else {
display.green().to_string()
}
}
pub fn fmt_vol_status(status: &str) -> String {
if !use_color() {
return status.to_string();
}
match status {
"ready" => status.green().to_string(),
"degraded" | "error" | "not active" => status.red().to_string(),
"not ready" => status.yellow().to_string(),
_ => status.to_string(),
}
}
#[derive(Tabled)]
pub struct VolumeRow {
#[tabled(rename = "Label")]
pub label: String,
#[tabled(rename = "Status")]
pub status: String,
#[tabled(rename = "Pool")]
pub pool: String,
#[tabled(rename = "Type")]
pub vol_type: String,
}
#[derive(Tabled)]
pub struct DiskRow {
#[tabled(rename = "Slot")]
pub slot: String,
#[tabled(rename = "Model")]
pub model: String,
#[tabled(rename = "Kind")]
pub kind: String,
#[tabled(rename = "Temp")]
pub temp: String,
}
pub fn print_volumes(volumes: &[VolumeRow], disks: &[DiskRow]) {
println!("VOLUMES");
if volumes.is_empty() {
println!(" (none)");
} else {
let display_volumes: Vec<VolumeRow> = volumes
.iter()
.map(|v| VolumeRow {
label: v.label.clone(),
status: fmt_vol_status(&v.status),
pool: v.pool.clone(),
vol_type: v.vol_type.clone(),
})
.collect();
println!("{}", Table::new(display_volumes));
}
println!();
println!("DISKS");
if disks.is_empty() {
println!(" (none)");
} else {
println!("{}", Table::new(disks));
}
}
#[derive(Tabled)]
pub struct ShareRow {
#[tabled(rename = "Name")]
pub name: String,
#[tabled(rename = "Path")]
pub path: String,
#[tabled(rename = "Items")]
pub items: String,
}
pub fn print_shares(rows: &[ShareRow]) {
println!("{}", Table::new(rows));
}
#[derive(Tabled)]
pub struct FileRow {
#[tabled(rename = "Type")]
pub file_type: String,
#[tabled(rename = "Name")]
pub name: String,
#[tabled(rename = "Size")]
pub size: String,
#[tabled(rename = "Modified")]
pub modified: String,
}
pub fn print_files(rows: &[FileRow]) {
println!("{}", Table::new(rows));
println!(" {} item(s)", rows.len());
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fmt_temp_below_threshold() {
assert_eq!(fmt_temp("32"), "32°C");
assert_eq!(fmt_temp("0"), "0°C");
assert_eq!(fmt_temp("44"), "44°C");
}
#[test]
fn test_fmt_temp_at_yellow_threshold() {
assert_eq!(fmt_temp("45"), "45°C");
assert_eq!(fmt_temp("59"), "59°C");
}
#[test]
fn test_fmt_temp_at_red_threshold() {
assert_eq!(fmt_temp("60"), "60°C");
assert_eq!(fmt_temp("75"), "75°C");
}
#[test]
fn test_fmt_temp_invalid_input() {
assert_eq!(fmt_temp("N/A"), "N/A");
assert_eq!(fmt_temp(""), "");
}
#[test]
fn test_fmt_temp_decimal() {
assert_eq!(fmt_temp("38.5"), "38°C");
}
#[test]
fn test_fmt_vol_status_ready() {
assert_eq!(fmt_vol_status("ready"), "ready");
}
#[test]
fn test_fmt_vol_status_error_states() {
assert_eq!(fmt_vol_status("error"), "error");
assert_eq!(fmt_vol_status("degraded"), "degraded");
assert_eq!(fmt_vol_status("not active"), "not active");
}
#[test]
fn test_fmt_vol_status_not_ready() {
assert_eq!(fmt_vol_status("not ready"), "not ready");
}
#[test]
fn test_fmt_vol_status_unknown() {
assert_eq!(fmt_vol_status("something-else"), "something-else");
}
}