use std::io::{self, IsTerminal};
pub fn color_supported() -> bool {
color_supported_with(&|k| std::env::var(k).ok())
}
fn color_supported_with(get_env: &dyn Fn(&str) -> Option<String>) -> bool {
color_enabled_for(
resolve_color_mode(get_env, newt_core::ColorMode::Auto),
io::stdout().is_terminal(),
)
}
pub(crate) fn resolve_color_mode(
get_env: &dyn Fn(&str) -> Option<String>,
cfg_color: newt_core::ColorMode,
) -> newt_core::ColorMode {
if let Some(kw) = get_env("NEWT_COLOR") {
if let Some(mode) = newt_core::ColorMode::from_keyword(&kw) {
return mode;
}
}
if get_env("NO_COLOR").is_some() {
return newt_core::ColorMode::Never;
}
if get_env("TERM").as_deref() == Some("dumb") {
return newt_core::ColorMode::Never;
}
cfg_color
}
pub(crate) fn color_enabled_for(mode: newt_core::ColorMode, is_tty: bool) -> bool {
mode.forced().unwrap_or(is_tty)
}
#[cfg(test)]
mod tests {
use super::*;
use newt_core::ColorMode;
fn mock_env<'a>(pairs: &'a [(&'a str, &'a str)]) -> impl Fn(&str) -> Option<String> + 'a {
|k| {
pairs
.iter()
.find(|(key, _)| *key == k)
.map(|(_, v)| v.to_string())
}
}
#[test]
fn no_color_env_disables_color() {
assert!(!color_supported_with(&mock_env(&[("NO_COLOR", "1")])));
}
#[test]
fn no_color_empty_value_still_disables() {
assert!(!color_supported_with(&mock_env(&[("NO_COLOR", "")])));
}
#[test]
fn dumb_term_disables_color() {
assert!(!color_supported_with(&mock_env(&[("TERM", "dumb")])));
}
#[test]
fn non_dumb_term_passes_env_check() {
let get_env = mock_env(&[("TERM", "xterm-256color")]);
let _ = color_supported_with(&get_env);
}
#[test]
fn newt_color_env_wins_over_config() {
let env = mock_env(&[("NEWT_COLOR", "mono")]);
assert_eq!(resolve_color_mode(&env, ColorMode::Always), ColorMode::Mono);
}
#[test]
fn explicit_color_flag_overrides_no_color() {
let env = mock_env(&[("NEWT_COLOR", "always"), ("NO_COLOR", "1")]);
assert_eq!(resolve_color_mode(&env, ColorMode::Auto), ColorMode::Always);
assert!(color_enabled_for(ColorMode::Always, false));
}
#[test]
fn no_color_beats_persisted_config_but_not_the_flag() {
let env = mock_env(&[("NO_COLOR", "1")]);
assert_eq!(resolve_color_mode(&env, ColorMode::Dark), ColorMode::Never);
let env = mock_env(&[("TERM", "dumb")]);
assert_eq!(resolve_color_mode(&env, ColorMode::Dark), ColorMode::Never);
}
#[test]
fn config_color_used_when_no_env_signal() {
let env = mock_env(&[]);
assert_eq!(resolve_color_mode(&env, ColorMode::Light), ColorMode::Light);
assert_eq!(resolve_color_mode(&env, ColorMode::Auto), ColorMode::Auto);
}
#[test]
fn invalid_newt_color_is_ignored_and_falls_through() {
let env = mock_env(&[("NEWT_COLOR", "rainbow"), ("NO_COLOR", "1")]);
assert_eq!(resolve_color_mode(&env, ColorMode::Auto), ColorMode::Never);
}
#[test]
fn color_enabled_for_applies_mode_against_tty() {
assert!(color_enabled_for(ColorMode::Always, false));
assert!(!color_enabled_for(ColorMode::Never, true));
assert!(!color_enabled_for(ColorMode::Mono, true));
assert!(color_enabled_for(ColorMode::Dark, false));
assert!(color_enabled_for(ColorMode::Auto, true));
assert!(!color_enabled_for(ColorMode::Auto, false));
}
}