use std::sync::OnceLock;
use ratatui::style::Color;
use crate::config::UiConfig;
#[derive(Debug, Clone, Copy)]
pub struct Theme {
pub accent: Color,
pub pass: Color,
pub warn: Color,
pub fail: Color,
pub info: Color,
pub dim: Color,
pub tab_active_bg: Color,
pub tab_active_fg: Color,
}
impl Theme {
pub const fn default_palette() -> Self {
Self {
accent: Color::Yellow,
pass: Color::Green,
warn: Color::Yellow,
fail: Color::Red,
info: Color::Cyan,
dim: Color::DarkGray,
tab_active_bg: Color::Yellow,
tab_active_fg: Color::Black,
}
}
pub const fn mono() -> Self {
Self {
accent: Color::White,
pass: Color::White,
warn: Color::Gray,
fail: Color::White,
info: Color::Gray,
dim: Color::DarkGray,
tab_active_bg: Color::White,
tab_active_fg: Color::Black,
}
}
}
impl Default for Theme {
fn default() -> Self {
Self::default_palette()
}
}
static ACTIVE: OnceLock<Theme> = OnceLock::new();
pub fn from_name(name: &str) -> Theme {
match name {
"default" => Theme::default_palette(),
"mono" => Theme::mono(),
other => {
tracing::warn!(theme = %other, "unknown theme name; falling back to default");
Theme::default_palette()
}
}
}
pub fn install(ui: &UiConfig) {
let _ = ACTIVE.set(from_name(&ui.theme));
}
pub fn active() -> &'static Theme {
static FALLBACK: Theme = Theme::default_palette();
ACTIVE.get().unwrap_or(&FALLBACK)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn from_name_recognises_known() {
assert_eq!(
std::mem::discriminant(&from_name("default").pass),
std::mem::discriminant(&Color::Green)
);
assert_eq!(from_name("mono").pass, Color::White);
}
#[test]
fn from_name_falls_back_on_unknown() {
let t = from_name("not-a-real-theme");
assert_eq!(t.pass, Theme::default_palette().pass);
}
#[test]
fn active_returns_fallback_when_not_installed() {
let _ = active();
}
}