modcli/output/
themes.rs

1use crate::output::colors::*;
2use crossterm::style::{Color, ResetColor, SetBackgroundColor, SetForegroundColor};
3use std::collections::HashMap;
4use std::io::{stdout, Write};
5use std::sync::OnceLock;
6
7#[derive(Clone)]
8pub struct Theme {
9    pub name: String,
10    pub fg: Color,
11    pub bg: Color,
12    pub log_styles: HashMap<&'static str, Color>,
13}
14
15impl Theme {
16    pub fn apply(&self) {
17        let _ = write!(
18            stdout(),
19            "{}{}",
20            SetForegroundColor(self.fg),
21            SetBackgroundColor(self.bg)
22        );
23        let _ = stdout().flush();
24    }
25
26    pub fn reset() {
27        let _ = write!(stdout(), "{ResetColor}");
28        let _ = stdout().flush();
29    }
30
31    pub fn get_log_color(&self, key: &str) -> Color {
32        self.log_styles.get(key).copied().unwrap_or(self.fg)
33    }
34}
35
36static THEME: OnceLock<Theme> = OnceLock::new();
37
38fn log_defaults(base: Color) -> HashMap<&'static str, Color> {
39    let mut map = HashMap::new();
40    map.insert("error", COLOR_ERROR);
41    map.insert("warn", COLOR_WARNING);
42    map.insert("success", COLOR_SUCCESS);
43    map.insert("debug", COLOR_DEBUG);
44    map.insert("info", COLOR_INFO);
45    map.insert("trace", COLOR_TRACE);
46    map.insert("notice", COLOR_NOTICE);
47    map.insert("status", COLOR_STATUS);
48    map.insert("default", base);
49    map
50}
51
52pub fn apply_theme(name: &str) {
53    let theme = match name.to_lowercase().as_str() {
54        "monochrome" => Theme {
55            name: "monochrome".into(),
56            fg: GREY,
57            bg: BLACK,
58            log_styles: log_defaults(GREY),
59        },
60        "inverted" => Theme {
61            name: "inverted".into(),
62            fg: BLACK,
63            bg: WHITE,
64            log_styles: log_defaults(BLACK),
65        },
66        "blue" => Theme {
67            name: "blue".into(),
68            fg: WHITE,
69            bg: BLUE,
70            log_styles: log_defaults(WHITE),
71        },
72        "green" => Theme {
73            name: "green".into(),
74            fg: BLACK,
75            bg: GREEN,
76            log_styles: log_defaults(BLACK),
77        },
78        _ => Theme {
79            name: "default".into(),
80            fg: WHITE,
81            bg: BLACK,
82            log_styles: log_defaults(WHITE),
83        },
84    };
85
86    let _ = THEME.set(theme.clone()); // only sets once
87    theme.apply();
88}
89
90pub fn current_theme() -> Theme {
91    THEME.get().cloned().unwrap_or_else(|| Theme {
92        name: "default".into(),
93        fg: WHITE,
94        bg: BLACK,
95        log_styles: log_defaults(WHITE),
96    })
97}