1use std::collections::HashMap;
6
7use serde::Deserialize;
8
9#[derive(Debug, Deserialize)]
11pub struct ThemeFile {
12 pub meta: ThemeMeta,
14
15 #[serde(default)]
17 pub palette: HashMap<String, String>,
18
19 #[serde(default)]
21 pub tokens: HashMap<String, String>,
22
23 #[serde(default)]
25 pub styles: HashMap<String, StyleDef>,
26
27 #[serde(default)]
29 pub gradients: HashMap<String, Vec<String>>,
30}
31
32#[derive(Debug, Clone, Deserialize)]
34pub struct ThemeMeta {
35 pub name: String,
37
38 #[serde(default)]
40 pub author: Option<String>,
41
42 #[serde(default = "default_variant")]
44 pub variant: ThemeVariant,
45
46 #[serde(default)]
48 pub version: Option<String>,
49
50 #[serde(default)]
52 pub description: Option<String>,
53}
54
55fn default_variant() -> ThemeVariant {
56 ThemeVariant::Dark
57}
58
59#[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#[derive(Debug, Clone, Deserialize, Default)]
70#[allow(clippy::struct_excessive_bools)] pub struct StyleDef {
72 #[serde(default)]
74 pub fg: Option<String>,
75
76 #[serde(default)]
78 pub bg: Option<String>,
79
80 #[serde(default)]
82 pub bold: bool,
83
84 #[serde(default)]
86 pub italic: bool,
87
88 #[serde(default)]
90 pub underline: bool,
91
92 #[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}