use dark_light::{detect as detect_os_theme, Mode as OsThemeMode};
use once_cell::sync::Lazy;
use std::sync::Mutex;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ColorMode {
Light,
Dark,
}
type ThemeDetector = fn() -> ColorMode;
static THEME_DETECTOR: Lazy<Mutex<ThemeDetector>> = Lazy::new(|| Mutex::new(os_theme_detector));
pub fn set_theme_detector(detector: ThemeDetector) {
let mut guard = THEME_DETECTOR.lock().unwrap();
*guard = detector;
}
pub fn detect_color_mode() -> ColorMode {
let detector = THEME_DETECTOR.lock().unwrap();
(*detector)()
}
fn os_theme_detector() -> ColorMode {
match detect_os_theme() {
OsThemeMode::Dark => ColorMode::Dark,
OsThemeMode::Light => ColorMode::Light,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{render_with_output, OutputMode, Theme};
use console::Style;
use serde::Serialize;
use serial_test::serial;
#[derive(Serialize)]
struct SimpleData {
message: String,
}
#[test]
#[serial]
fn test_adaptive_theme_uses_detector() {
console::set_colors_enabled(true);
let theme = Theme::new().add_adaptive(
"tone",
Style::new(), Some(Style::new().green().force_styling(true)), Some(Style::new().red().force_styling(true)), );
let data = SimpleData {
message: "hi".into(),
};
set_theme_detector(|| ColorMode::Dark);
let dark_output = render_with_output(
r#"[tone]{{ message }}[/tone]"#,
&data,
&theme,
OutputMode::Term,
)
.unwrap();
assert!(
dark_output.contains("\x1b[31"),
"Expected red color in dark mode, got: {}",
dark_output
);
set_theme_detector(|| ColorMode::Light);
let light_output = render_with_output(
r#"[tone]{{ message }}[/tone]"#,
&data,
&theme,
OutputMode::Term,
)
.unwrap();
assert!(
light_output.contains("\x1b[32"),
"Expected green color in light mode, got: {}",
light_output
);
set_theme_detector(|| ColorMode::Light);
}
#[test]
#[serial]
fn test_detect_color_mode_uses_override() {
set_theme_detector(|| ColorMode::Dark);
assert_eq!(detect_color_mode(), ColorMode::Dark);
set_theme_detector(|| ColorMode::Light);
assert_eq!(detect_color_mode(), ColorMode::Light);
}
}