git_iris/theme/
schema.rs

1//! TOML schema types for theme files.
2//!
3//! These types define the structure of theme TOML files and handle deserialization.
4
5use std::collections::HashMap;
6
7use serde::Deserialize;
8
9/// Root structure of a theme TOML file.
10#[derive(Debug, Deserialize)]
11pub struct ThemeFile {
12    /// Theme metadata.
13    pub meta: ThemeMeta,
14
15    /// Color palette - named color primitives.
16    #[serde(default)]
17    pub palette: HashMap<String, String>,
18
19    /// Semantic tokens - map to palette colors or other tokens.
20    #[serde(default)]
21    pub tokens: HashMap<String, String>,
22
23    /// Composed styles with modifiers.
24    #[serde(default)]
25    pub styles: HashMap<String, StyleDef>,
26
27    /// Gradient definitions.
28    #[serde(default)]
29    pub gradients: HashMap<String, Vec<String>>,
30}
31
32/// Theme metadata.
33#[derive(Debug, Clone, Deserialize)]
34pub struct ThemeMeta {
35    /// Display name of the theme.
36    pub name: String,
37
38    /// Theme author.
39    #[serde(default)]
40    pub author: Option<String>,
41
42    /// Theme variant (dark/light).
43    #[serde(default = "default_variant")]
44    pub variant: ThemeVariant,
45
46    /// Theme version.
47    #[serde(default)]
48    pub version: Option<String>,
49
50    /// Theme description.
51    #[serde(default)]
52    pub description: Option<String>,
53}
54
55fn default_variant() -> ThemeVariant {
56    ThemeVariant::Dark
57}
58
59/// Theme variant - light or dark.
60#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Deserialize)]
61#[serde(rename_all = "lowercase")]
62pub enum ThemeVariant {
63    #[default]
64    Dark,
65    Light,
66}
67
68/// Style definition from TOML.
69#[derive(Debug, Clone, Deserialize, Default)]
70#[allow(clippy::struct_excessive_bools)] // Text style modifiers are inherently boolean flags
71pub struct StyleDef {
72    /// Foreground color token reference.
73    #[serde(default)]
74    pub fg: Option<String>,
75
76    /// Background color token reference.
77    #[serde(default)]
78    pub bg: Option<String>,
79
80    /// Bold modifier.
81    #[serde(default)]
82    pub bold: bool,
83
84    /// Italic modifier.
85    #[serde(default)]
86    pub italic: bool,
87
88    /// Underline modifier.
89    #[serde(default)]
90    pub underline: bool,
91
92    /// Dim modifier.
93    #[serde(default)]
94    pub dim: bool,
95}
96
97#[cfg(test)]
98mod tests {
99    use super::*;
100
101    #[test]
102    fn test_parse_minimal_theme() {
103        let toml = r##"
104            [meta]
105            name = "Test Theme"
106
107            [palette]
108            red = "#ff0000"
109
110            [tokens]
111            error = "red"
112        "##;
113
114        let theme: ThemeFile = toml::from_str(toml).unwrap();
115        assert_eq!(theme.meta.name, "Test Theme");
116        assert_eq!(theme.palette.get("red"), Some(&"#ff0000".to_string()));
117        assert_eq!(theme.tokens.get("error"), Some(&"red".to_string()));
118    }
119
120    #[test]
121    fn test_parse_style_def() {
122        let toml = r##"
123            [meta]
124            name = "Test"
125
126            [styles]
127            keyword = { fg = "purple", bold = true }
128            selected = { fg = "cyan", bg = "highlight" }
129        "##;
130
131        let theme: ThemeFile = toml::from_str(toml).unwrap();
132        let keyword = theme.styles.get("keyword").unwrap();
133        assert_eq!(keyword.fg, Some("purple".to_string()));
134        assert!(keyword.bold);
135        assert!(!keyword.italic);
136    }
137
138    #[test]
139    fn test_parse_gradients() {
140        let toml = r##"
141            [meta]
142            name = "Test"
143
144            [gradients]
145            primary = ["purple", "cyan"]
146            warm = ["coral", "yellow", "orange"]
147        "##;
148
149        let theme: ThemeFile = toml::from_str(toml).unwrap();
150        assert_eq!(
151            theme.gradients.get("primary"),
152            Some(&vec!["purple".to_string(), "cyan".to_string()])
153        );
154    }
155}