use anyhow::Result;
use std::sync::OnceLock;
use termcolor::ColorChoice;
static COR_CACHE: OnceLock<ColorChoice> = OnceLock::new();
pub fn initialize(no_color: bool) -> Result<()> {
let choice = determine_color(no_color);
let _ = COR_CACHE.set(choice);
tracing::debug!("terminal color configuration: {:?}", choice);
Ok(())
}
#[must_use]
pub fn color_choice() -> ColorChoice {
*COR_CACHE.get().unwrap_or(&ColorChoice::Never)
}
#[must_use]
pub fn is_interactive() -> bool {
use std::io::IsTerminal;
if std::env::var("TERM").as_deref() == Ok("dumb") {
return false;
}
std::io::stdout().is_terminal()
}
fn determine_color(no_color_cli: bool) -> ColorChoice {
if no_color_cli {
return ColorChoice::Never;
}
if std::env::var("NO_COLOR").is_ok() {
return ColorChoice::Never;
}
if std::env::var("CLICOLOR_FORCE").as_deref() == Ok("1") {
return ColorChoice::Always;
}
if is_interactive() {
ColorChoice::Auto
} else {
ColorChoice::Never
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn no_color_cli_returns_never() {
let choice = determine_color(true);
assert!(matches!(choice, ColorChoice::Never));
}
#[test]
fn no_color_env_returns_never() {
let anterior = std::env::var("NO_COLOR").ok();
let anterior_force = std::env::var("CLICOLOR_FORCE").ok();
std::env::set_var("NO_COLOR", "1");
std::env::remove_var("CLICOLOR_FORCE");
let choice = determine_color(false);
assert!(matches!(choice, ColorChoice::Never));
match anterior {
Some(v) => std::env::set_var("NO_COLOR", v),
None => std::env::remove_var("NO_COLOR"),
}
match anterior_force {
Some(v) => std::env::set_var("CLICOLOR_FORCE", v),
None => std::env::remove_var("CLICOLOR_FORCE"),
}
}
#[test]
fn clicolor_force_returns_always() {
let anterior = std::env::var("NO_COLOR").ok();
let anterior_force = std::env::var("CLICOLOR_FORCE").ok();
std::env::remove_var("NO_COLOR");
std::env::set_var("CLICOLOR_FORCE", "1");
let choice = determine_color(false);
assert!(matches!(choice, ColorChoice::Always));
match anterior {
Some(v) => std::env::set_var("NO_COLOR", v),
None => std::env::remove_var("NO_COLOR"),
}
match anterior_force {
Some(v) => std::env::set_var("CLICOLOR_FORCE", v),
None => std::env::remove_var("CLICOLOR_FORCE"),
}
}
#[test]
fn color_choice_returns_never_without_init() {
let _ = color_choice();
}
#[test]
fn is_interactive_returns_bool() {
let _ = is_interactive();
}
}