Skip to main content

hjkl_theme/
theme.rs

1use std::{collections::HashMap, path::Path};
2
3use crate::{
4    ThemeError,
5    captures::CaptureMap,
6    color::{Color, RawPalette},
7    palette::Palette,
8    style::{RawStyleSpec, RawUiStyles, StyleSpec, UiStyles},
9};
10
11/// Fully resolved theme.
12#[derive(Clone, Default, Debug)]
13pub struct Theme {
14    /// Resolved palette kept for introspection.
15    pub palette: HashMap<String, Color>,
16    /// UI surface styles.
17    pub ui: UiStyles,
18    /// Tree-sitter capture styles with fallback-chain support.
19    pub captures: CaptureMap,
20}
21
22impl Theme {
23    /// Parse a theme from a TOML string.
24    pub fn from_toml_str(s: &str) -> Result<Self, ThemeError> {
25        // Parse to Value first, then extract sections to avoid toml-crate
26        // flatten issues when combining named table keys with catch-all maps.
27        let mut table: toml::Table = toml::from_str(s)?;
28
29        // 1. Extract and resolve palette.
30        let palette = if let Some(v) = table.remove("palette") {
31            let raw: RawPalette = v.try_into()?;
32            Palette::from_raw(raw)
33        } else {
34            Palette::default()
35        };
36
37        // 2. Extract ui section.
38        let raw_ui: Option<RawUiStyles> = if let Some(v) = table.remove("ui") {
39            Some(v.try_into()?)
40        } else {
41            None
42        };
43        let ui = raw_ui
44            .map(|raw| raw.resolve(&palette.0))
45            .transpose()?
46            .unwrap_or_default();
47
48        // 3. Remaining keys are capture entries.
49        let mut flat: HashMap<String, StyleSpec> = HashMap::new();
50        for (key, val) in table {
51            let raw: RawStyleSpec = val.try_into()?;
52            flat.insert(key, raw.resolve(&palette.0)?);
53        }
54
55        Ok(Theme {
56            palette: palette.0,
57            ui,
58            captures: CaptureMap::from_map(flat),
59        })
60    }
61
62    /// Parse a theme from a file path.
63    pub fn from_path(p: &Path) -> Result<Self, ThemeError> {
64        let s = std::fs::read_to_string(p)?;
65        Self::from_toml_str(&s)
66    }
67}