use std::env;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ColorEnvOverride {
NoColor,
ForceColor,
ForceColorTruecolor,
None,
}
pub fn detect_color_env() -> ColorEnvOverride {
if env::var_os("NO_COLOR").is_some() {
return ColorEnvOverride::NoColor;
}
if let Ok(val) = env::var("FORCE_COLOR") {
return match val.as_str() {
"0" => ColorEnvOverride::NoColor,
"1" | "2" => ColorEnvOverride::ForceColor,
"3" => ColorEnvOverride::ForceColorTruecolor,
_ => ColorEnvOverride::ForceColor,
};
}
if let Ok(val) = env::var("CLICOLOR_FORCE") {
if val != "0" {
return ColorEnvOverride::ForceColor;
}
}
if let Ok(val) = env::var("CLICOLOR") {
if val == "0" {
return ColorEnvOverride::NoColor;
}
}
ColorEnvOverride::None
}
pub fn detect_reduce_motion() -> bool {
match env::var("REDUCE_MOTION") {
Ok(val) => val == "1" || val.eq_ignore_ascii_case("true"),
Err(_) => false,
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::env;
use std::sync::Mutex;
static ENV_LOCK: Mutex<()> = Mutex::new(());
fn with_env<F: FnOnce() -> ColorEnvOverride>(
vars: &[(&str, Option<&str>)],
f: F,
) -> ColorEnvOverride {
let _guard = ENV_LOCK.lock().unwrap();
let all_keys = ["NO_COLOR", "FORCE_COLOR", "CLICOLOR_FORCE", "CLICOLOR"];
let saved: Vec<(&str, Option<String>)> =
all_keys.iter().map(|k| (*k, env::var(k).ok())).collect();
for key in &all_keys {
env::remove_var(key);
}
for &(key, val) in vars {
match val {
Some(v) => env::set_var(key, v),
None => env::remove_var(key),
}
}
let result = f();
for (key, val) in saved {
match val {
Some(v) => env::set_var(key, v),
None => env::remove_var(key),
}
}
result
}
#[test]
fn test_no_color_set_disables_color() {
let r = with_env(&[("NO_COLOR", Some(""))], detect_color_env);
assert_eq!(r, ColorEnvOverride::NoColor);
}
#[test]
fn test_no_color_any_value() {
let r = with_env(&[("NO_COLOR", Some("1"))], detect_color_env);
assert_eq!(r, ColorEnvOverride::NoColor);
}
#[test]
fn test_force_color_3_truecolor() {
let r = with_env(&[("FORCE_COLOR", Some("3"))], detect_color_env);
assert_eq!(r, ColorEnvOverride::ForceColorTruecolor);
}
#[test]
fn test_force_color_0_disables() {
let r = with_env(&[("FORCE_COLOR", Some("0"))], detect_color_env);
assert_eq!(r, ColorEnvOverride::NoColor);
}
#[test]
fn test_force_color_1_forces() {
let r = with_env(&[("FORCE_COLOR", Some("1"))], detect_color_env);
assert_eq!(r, ColorEnvOverride::ForceColor);
}
#[test]
fn test_force_color_unknown_value_forces() {
let r = with_env(&[("FORCE_COLOR", Some("yes"))], detect_color_env);
assert_eq!(r, ColorEnvOverride::ForceColor);
}
#[test]
fn test_clicolor_force_1() {
let r = with_env(&[("CLICOLOR_FORCE", Some("1"))], detect_color_env);
assert_eq!(r, ColorEnvOverride::ForceColor);
}
#[test]
fn test_clicolor_force_0_does_not_force() {
let r = with_env(&[("CLICOLOR_FORCE", Some("0"))], detect_color_env);
assert_eq!(r, ColorEnvOverride::None);
}
#[test]
fn test_clicolor_0_disables() {
let r = with_env(&[("CLICOLOR", Some("0"))], detect_color_env);
assert_eq!(r, ColorEnvOverride::NoColor);
}
#[test]
fn test_clicolor_1_no_override() {
let r = with_env(&[("CLICOLOR", Some("1"))], detect_color_env);
assert_eq!(r, ColorEnvOverride::None);
}
#[test]
fn test_no_vars_set_returns_none() {
let r = with_env(&[], detect_color_env);
assert_eq!(r, ColorEnvOverride::None);
}
#[test]
fn test_no_color_wins_over_force_color() {
let r = with_env(
&[("NO_COLOR", Some("")), ("FORCE_COLOR", Some("3"))],
detect_color_env,
);
assert_eq!(r, ColorEnvOverride::NoColor);
}
#[test]
fn test_force_color_wins_over_clicolor_force() {
let r = with_env(
&[("FORCE_COLOR", Some("0")), ("CLICOLOR_FORCE", Some("1"))],
detect_color_env,
);
assert_eq!(r, ColorEnvOverride::NoColor);
}
fn with_reduce_motion<F: FnOnce() -> bool>(val: Option<&str>, f: F) -> bool {
let _guard = ENV_LOCK.lock().unwrap();
let saved = env::var("REDUCE_MOTION").ok();
env::remove_var("REDUCE_MOTION");
if let Some(v) = val {
env::set_var("REDUCE_MOTION", v);
}
let result = f();
match saved {
Some(v) => env::set_var("REDUCE_MOTION", v),
None => env::remove_var("REDUCE_MOTION"),
}
result
}
#[test]
fn test_reduce_motion_unset() {
let r = with_reduce_motion(None, super::detect_reduce_motion);
assert!(!r, "should be false when REDUCE_MOTION is not set");
}
#[test]
fn test_reduce_motion_1() {
let r = with_reduce_motion(Some("1"), super::detect_reduce_motion);
assert!(r, "should be true when REDUCE_MOTION=1");
}
#[test]
fn test_reduce_motion_true_lowercase() {
let r = with_reduce_motion(Some("true"), super::detect_reduce_motion);
assert!(r, "should be true when REDUCE_MOTION=true");
}
#[test]
fn test_reduce_motion_true_uppercase() {
let r = with_reduce_motion(Some("TRUE"), super::detect_reduce_motion);
assert!(r, "should be true when REDUCE_MOTION=TRUE");
}
#[test]
fn test_reduce_motion_true_mixed_case() {
let r = with_reduce_motion(Some("True"), super::detect_reduce_motion);
assert!(r, "should be true when REDUCE_MOTION=True");
}
#[test]
fn test_reduce_motion_0() {
let r = with_reduce_motion(Some("0"), super::detect_reduce_motion);
assert!(!r, "should be false when REDUCE_MOTION=0");
}
#[test]
fn test_reduce_motion_empty() {
let r = with_reduce_motion(Some(""), super::detect_reduce_motion);
assert!(!r, "should be false when REDUCE_MOTION is empty");
}
#[test]
fn test_reduce_motion_arbitrary_value() {
let r = with_reduce_motion(Some("yes"), super::detect_reduce_motion);
assert!(!r, "should be false for arbitrary values like 'yes'");
}
}