use std::sync::atomic::{AtomicU8, Ordering::SeqCst};
use std::{env, io};
static COLOR_MODE: AtomicU8 = AtomicU8::new(ColorMode::Auto as u8);
fn detect_env_color_support() -> bool {
if let Some(no_color) = env::var_os("NO_COLOR")
&& !no_color.is_empty()
{
return false;
}
if let Some(force) = env::var_os("FORCE_COLOR")
&& !force.is_empty()
{
return force != "0" && !force.eq_ignore_ascii_case("false");
}
io::IsTerminal::is_terminal(&io::stderr())
}
fn cached_env_supports_color() -> bool {
static ENV_SUPPORTS_COLOR: AtomicU8 = AtomicU8::new(u8::MAX);
match ENV_SUPPORTS_COLOR.load(SeqCst) {
u8::MAX => {
let detected = detect_env_color_support();
ENV_SUPPORTS_COLOR.store(u8::from(detected), SeqCst);
detected
}
v => v == u8::from(true),
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[repr(u8)]
pub enum ColorMode {
#[default]
Auto = 0,
Always = 1,
Never = 2,
}
impl ColorMode {
#[inline]
const fn from_u8(value: u8) -> Option<Self> {
const fn is(value: u8, variant: ColorMode) -> bool {
value == variant as u8
}
match value {
x if is(x, Self::Auto) => Some(Self::Auto),
x if is(x, Self::Always) => Some(Self::Always),
x if is(x, Self::Never) => Some(Self::Never),
_ => None,
}
}
#[must_use]
#[inline]
pub fn should_colorize(self) -> bool {
match self {
Self::Auto => match get_color_mode() {
Self::Always => true,
Self::Never => false,
Self::Auto => cached_env_supports_color(),
},
Self::Always => true,
Self::Never => false,
}
}
}
const _: () = {
const fn assert_roundtrip(value: ColorMode) {
assert!(ColorMode::from_u8(value as u8).unwrap() as u8 == value as u8);
}
assert_roundtrip(ColorMode::Auto);
assert_roundtrip(ColorMode::Always);
assert_roundtrip(ColorMode::Never);
};
#[inline]
pub fn set_color_mode(mode: ColorMode) {
COLOR_MODE.store(mode as u8, SeqCst);
}
#[must_use]
#[inline]
pub fn get_color_mode() -> ColorMode {
match ColorMode::from_u8(COLOR_MODE.load(SeqCst)) {
Some(mode) => mode,
None => ColorMode::Auto,
}
}
macro_rules! style {
($expr:expr, $style:expr, $colorize:expr) => {{
::owo_colors::OwoColorize::style(
&$expr,
(if $colorize {
$style
} else {
$crate::style::Style::new()
})
.into_owo(),
)
}};
}
pub(crate) use style;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_global_color_mode() {
let original = get_color_mode();
set_color_mode(ColorMode::Never);
assert_eq!(get_color_mode(), ColorMode::Never);
set_color_mode(ColorMode::Always);
assert_eq!(get_color_mode(), ColorMode::Always);
set_color_mode(original);
}
#[test]
fn test_always_should_colorize() {
assert!(ColorMode::Always.should_colorize());
}
#[test]
fn test_never_should_not_colorize() {
assert!(!ColorMode::Never.should_colorize());
}
#[test]
fn test_auto_roundtrip_through_global() {
let original = get_color_mode();
set_color_mode(ColorMode::Auto);
assert_eq!(get_color_mode(), ColorMode::Auto);
set_color_mode(original);
}
#[test]
fn global_never_disables_auto_colorize() {
let original = get_color_mode();
set_color_mode(ColorMode::Never);
assert!(!ColorMode::Auto.should_colorize());
set_color_mode(ColorMode::Always);
assert!(ColorMode::Auto.should_colorize());
set_color_mode(original);
}
}