use std::cell::RefCell;
use std::io::IsTerminal;
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub enum ColorMode {
#[default]
Auto,
Always,
Never,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum RenderTarget {
Stdout,
Stderr,
Terminal(bool),
}
#[derive(Clone, Debug)]
pub struct ColorizeConfig {
color_mode: ColorMode,
}
thread_local! {
static CONFIG: RefCell<ColorizeConfig> = RefCell::new(ColorizeConfig::default());
#[cfg(test)]
#[allow(clippy::missing_const_for_thread_local)]
static STDOUT_TERMINAL_OVERRIDE: RefCell<Option<bool>> = RefCell::new(None);
#[cfg(test)]
#[allow(clippy::missing_const_for_thread_local)]
static STDERR_TERMINAL_OVERRIDE: RefCell<Option<bool>> = RefCell::new(None);
}
impl Default for ColorizeConfig {
fn default() -> Self {
Self {
color_mode: ColorMode::Auto,
}
}
}
impl ColorizeConfig {
pub fn set_color_mode(mode: ColorMode) {
CONFIG.with(|config| config.borrow_mut().color_mode = mode);
}
pub fn color_mode() -> ColorMode {
CONFIG.with(|config| config.borrow().color_mode)
}
#[deprecated(note = "use ColorizeConfig::set_color_mode(ColorMode) instead")]
pub fn set_terminal_check(check: bool) {
let mode = if check {
ColorMode::Auto
} else {
ColorMode::Always
};
Self::set_color_mode(mode);
}
}
pub(crate) fn should_colorize() -> bool {
should_colorize_for(RenderTarget::Stdout)
}
pub(crate) fn should_colorize_for(target: RenderTarget) -> bool {
match ColorizeConfig::color_mode() {
ColorMode::Never => false,
ColorMode::Always => std::env::var_os("NO_COLOR").is_none(),
ColorMode::Auto => std::env::var_os("NO_COLOR").is_none() && target_is_terminal(target),
}
}
fn target_is_terminal(target: RenderTarget) -> bool {
match target {
RenderTarget::Stdout => stdout_is_terminal(),
RenderTarget::Stderr => stderr_is_terminal(),
RenderTarget::Terminal(value) => value,
}
}
fn stdout_is_terminal() -> bool {
#[cfg(test)]
if let Some(value) = STDOUT_TERMINAL_OVERRIDE.with(|override_value| *override_value.borrow()) {
return value;
}
std::io::stdout().is_terminal()
}
fn stderr_is_terminal() -> bool {
#[cfg(test)]
if let Some(value) = STDERR_TERMINAL_OVERRIDE.with(|override_value| *override_value.borrow()) {
return value;
}
std::io::stderr().is_terminal()
}
#[cfg(test)]
pub(crate) fn set_terminal_override_for_tests(value: Option<bool>) {
STDOUT_TERMINAL_OVERRIDE.with(|override_value| *override_value.borrow_mut() = value);
}
#[cfg(test)]
pub(crate) fn get_terminal_override_for_tests() -> Option<bool> {
STDOUT_TERMINAL_OVERRIDE.with(|override_value| *override_value.borrow())
}
#[cfg(test)]
pub(crate) fn set_stderr_terminal_override_for_tests(value: Option<bool>) {
STDERR_TERMINAL_OVERRIDE.with(|override_value| *override_value.borrow_mut() = value);
}
#[cfg(test)]
pub(crate) fn get_stderr_terminal_override_for_tests() -> Option<bool> {
STDERR_TERMINAL_OVERRIDE.with(|override_value| *override_value.borrow())
}