Skip to main content

holodeck_simctl_core/models/
theme_name.rs

1use serde::{Deserialize, Serialize};
2
3/// Which built-in TUI color theme to use. The actual colors live in
4/// `holodeck-tui::theme` (a rendering concern) — this is just the selector,
5/// kept in Core alongside the rest of `Config` so it round-trips through
6/// `~/.config/holodeck/config.json` like `videoCodec`/`screenshotType`.
7#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
8#[serde(rename_all = "kebab-case")]
9pub enum ThemeName {
10    /// The [Default+](https://github.com/otaviocc/default-plus) colorscheme.
11    #[default]
12    DefaultPlus,
13    /// The terminal's own 16-color scheme — correct on a background whose
14    /// depth/appearance nothing has detected, at the cost of not matching
15    /// any specific palette exactly on every terminal.
16    Ansi,
17    /// [Tokyo Night](https://github.com/folke/tokyonight.nvim).
18    TokyoNight,
19    /// [Nord](https://www.nordtheme.com).
20    Nord,
21    /// [Dracula](https://draculatheme.com).
22    Dracula,
23    /// [Gruvbox](https://github.com/morhetz/gruvbox) dark.
24    Gruvbox,
25    /// [Catppuccin](https://catppuccin.com) Mocha.
26    CatppuccinMocha,
27    /// [Solarized](https://ethanschoonover.com/solarized/) Dark.
28    SolarizedDark,
29    /// [Vesper](https://github.com/raunofreiberg/vesper) — a restrained,
30    /// mostly-monochrome palette with warm pastel accents.
31    Vesper,
32}
33
34impl ThemeName {
35    pub const ALL: [ThemeName; 9] = [
36        ThemeName::DefaultPlus,
37        ThemeName::Ansi,
38        ThemeName::TokyoNight,
39        ThemeName::Nord,
40        ThemeName::Dracula,
41        ThemeName::Gruvbox,
42        ThemeName::CatppuccinMocha,
43        ThemeName::SolarizedDark,
44        ThemeName::Vesper,
45    ];
46
47    pub fn raw_value(self) -> &'static str {
48        match self {
49            ThemeName::DefaultPlus => "default-plus",
50            ThemeName::Ansi => "ansi",
51            ThemeName::TokyoNight => "tokyo-night",
52            ThemeName::Nord => "nord",
53            ThemeName::Dracula => "dracula",
54            ThemeName::Gruvbox => "gruvbox",
55            ThemeName::CatppuccinMocha => "catppuccin-mocha",
56            ThemeName::SolarizedDark => "solarized-dark",
57            ThemeName::Vesper => "vesper",
58        }
59    }
60}
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65
66    #[test]
67    fn default_is_default_plus() {
68        assert_eq!(ThemeName::default(), ThemeName::DefaultPlus);
69    }
70
71    #[test]
72    fn serializes_as_kebab_case() {
73        assert_eq!(serde_json::to_string(&ThemeName::DefaultPlus).unwrap(), "\"default-plus\"");
74        assert_eq!(serde_json::to_string(&ThemeName::Ansi).unwrap(), "\"ansi\"");
75        assert_eq!(serde_json::to_string(&ThemeName::TokyoNight).unwrap(), "\"tokyo-night\"");
76        assert_eq!(serde_json::to_string(&ThemeName::Nord).unwrap(), "\"nord\"");
77        assert_eq!(serde_json::to_string(&ThemeName::Dracula).unwrap(), "\"dracula\"");
78        assert_eq!(serde_json::to_string(&ThemeName::Gruvbox).unwrap(), "\"gruvbox\"");
79        assert_eq!(serde_json::to_string(&ThemeName::CatppuccinMocha).unwrap(), "\"catppuccin-mocha\"");
80        assert_eq!(serde_json::to_string(&ThemeName::SolarizedDark).unwrap(), "\"solarized-dark\"");
81        assert_eq!(serde_json::to_string(&ThemeName::Vesper).unwrap(), "\"vesper\"");
82    }
83
84    #[test]
85    fn raw_value_round_trips_through_json_for_every_variant() {
86        for name in ThemeName::ALL {
87            let json = serde_json::to_string(&name).unwrap();
88            assert_eq!(json, format!("\"{}\"", name.raw_value()));
89            let parsed: ThemeName = serde_json::from_str(&json).unwrap();
90            assert_eq!(parsed, name);
91        }
92    }
93}