use crate::terminal::{ColorLevel, detect_color_level};
use std::sync::atomic::{AtomicU8, Ordering};
static CACHED_LEVEL: AtomicU8 = AtomicU8::new(0);
#[cfg(test)]
pub(crate) fn reset_cached_level() {
CACHED_LEVEL.store(0, Ordering::Release);
}
fn u8_to_color_level(level: u8) -> ColorLevel {
match level {
0 => ColorLevel::__Uninitialized,
1 => ColorLevel::None,
2 => ColorLevel::Basic,
3 => ColorLevel::Ansi256,
4 => ColorLevel::TrueColor,
_ => unreachable!(),
}
}
fn color_level_to_u8(level: ColorLevel) -> u8 {
match level {
ColorLevel::__Uninitialized => 0,
ColorLevel::None => 1,
ColorLevel::Basic => 2,
ColorLevel::Ansi256 => 3,
ColorLevel::TrueColor => 4,
}
}
pub(crate) fn get_cached_level() -> ColorLevel {
let raw = CACHED_LEVEL.load(Ordering::Acquire);
if raw != 0 {
return u8_to_color_level(raw);
}
let detected = detect_color_level();
let new_raw = color_level_to_u8(detected);
match CACHED_LEVEL.compare_exchange(0, new_raw, Ordering::AcqRel, Ordering::Acquire) {
Ok(_) => detected,
Err(actual_raw) => u8_to_color_level(actual_raw),
}
}
pub fn set_override(level: ColorLevel) {
assert!(
level != ColorLevel::__Uninitialized,
"ColorLevel::__Uninitialized cannot be used as a color override; use clear_override() instead"
);
let state = color_level_to_u8(level);
CACHED_LEVEL.store(state, Ordering::Release);
}
pub fn clear_override() {
CACHED_LEVEL.store(0, Ordering::Release);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::MockTerminalGuard;
#[test]
fn test_set_override_persists_globally() {
let _guard = MockTerminalGuard::acquire(None);
set_override(ColorLevel::Ansi256);
assert_eq!(get_cached_level(), ColorLevel::Ansi256);
set_override(ColorLevel::None);
assert_eq!(get_cached_level(), ColorLevel::None);
clear_override();
}
#[test]
fn test_clear_override_restores_fallback_detection() {
let _guard = MockTerminalGuard::acquire(None);
set_override(ColorLevel::TrueColor);
assert_eq!(get_cached_level(), ColorLevel::TrueColor);
clear_override();
let current_atomic = u8_to_color_level(CACHED_LEVEL.load(Ordering::Relaxed));
assert_eq!(current_atomic, ColorLevel::__Uninitialized);
}
#[test]
fn test_u8_and_color_level_bijective_mapping() {
let levels = [
ColorLevel::__Uninitialized,
ColorLevel::None,
ColorLevel::Basic,
ColorLevel::Ansi256,
ColorLevel::TrueColor,
];
for level in levels {
let serialized = color_level_to_u8(level);
let deserialized = u8_to_color_level(serialized);
assert_eq!(level, deserialized);
}
}
#[test]
fn test_set_override_rejects_uninitialized() {
let _guard = MockTerminalGuard::acquire(None);
let result = std::panic::catch_unwind(|| {
set_override(ColorLevel::__Uninitialized);
});
assert!(result.is_err());
let current_atomic = u8_to_color_level(CACHED_LEVEL.load(Ordering::Acquire));
assert_eq!(current_atomic, ColorLevel::__Uninitialized);
}
#[test]
fn test_set_override_uninitialized_does_not_clear_existing_override() {
let _guard = MockTerminalGuard::acquire(None);
set_override(ColorLevel::TrueColor);
assert_eq!(get_cached_level(), ColorLevel::TrueColor);
let result = std::panic::catch_unwind(|| {
set_override(ColorLevel::__Uninitialized);
});
assert!(result.is_err());
assert_eq!(get_cached_level(), ColorLevel::TrueColor);
}
}