use std::io::IsTerminal;
use anstyle::{AnsiColor, Color, Style};
use crate::cli::ColorChoice;
pub fn should_color(choice: ColorChoice) -> bool {
match choice {
ColorChoice::Always => true,
ColorChoice::Never => false,
ColorChoice::Auto => {
if std::env::var_os("NO_COLOR").is_some_and(|v| !v.is_empty()) {
return false;
}
std::io::stdout().is_terminal()
}
}
}
const NO_STYLE: Style = Style::new();
pub fn status_style(prefix: &str, enabled: bool) -> Style {
if !enabled {
return NO_STYLE;
}
match prefix.trim() {
"PASS" => Style::new().fg_color(Some(Color::Ansi(AnsiColor::Green))),
"WARN" => Style::new().fg_color(Some(Color::Ansi(AnsiColor::Yellow))),
"FAIL" => Style::new()
.fg_color(Some(Color::Ansi(AnsiColor::Red)))
.bold(),
"ERR" => Style::new()
.fg_color(Some(Color::Ansi(AnsiColor::Red)))
.bold(),
"SKIP" => Style::new().dimmed(),
_ => NO_STYLE,
}
}
pub fn paint(style: Style, text: &str) -> String {
format!("{style}{text}{style:#}")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn always_returns_true() {
assert!(should_color(ColorChoice::Always));
}
#[test]
fn never_returns_false() {
assert!(!should_color(ColorChoice::Never));
}
#[test]
fn auto_with_no_color_env_returns_false() {
let prior = std::env::var_os("NO_COLOR");
unsafe {
std::env::set_var("NO_COLOR", "1");
}
let result = should_color(ColorChoice::Auto);
unsafe {
match prior {
Some(v) => std::env::set_var("NO_COLOR", v),
None => std::env::remove_var("NO_COLOR"),
}
}
assert!(!result, "NO_COLOR=1 should suppress auto color");
}
#[test]
fn status_style_disabled_is_empty() {
let painted = paint(status_style("PASS", false), "PASS");
assert_eq!(painted, "PASS");
}
#[test]
fn status_style_enabled_wraps_with_ansi() {
let painted = paint(status_style("FAIL", true), "FAIL");
assert!(
painted.contains('\u{1b}'),
"enabled FAIL prefix should carry ANSI escapes: {painted:?}"
);
assert!(painted.contains("FAIL"));
}
#[test]
fn unknown_prefix_returns_no_style() {
let style = status_style("WHATEVER", true);
assert_eq!(paint(style, "WHATEVER"), "WHATEVER");
}
}