1use std::str::FromStr;
5
6use super::selector::*;
7use super::settings::{ParseSettings, Settings};
8use super::style::*;
9use super::theme::*;
10use crate::parsing::ParseScopeError;
11
12use self::ParseThemeError::*;
13
14#[derive(Debug, thiserror::Error)]
15#[non_exhaustive]
16pub enum ParseThemeError {
17 #[error("Incorrect underline option")]
18 IncorrectUnderlineOption,
19 #[error("Incorrect font style: {0}")]
20 IncorrectFontStyle(String),
21 #[error("Incorrect color")]
22 IncorrectColor,
23 #[error("Incorrect syntax")]
24 IncorrectSyntax,
25 #[error("Incorrect settings")]
26 IncorrectSettings,
27 #[error("Undefined settings")]
28 UndefinedSettings,
29 #[error("Undefined scope settings: {0}")]
30 UndefinedScopeSettings(String),
31 #[error("Color sheme scope is not object")]
32 ColorShemeScopeIsNotObject,
33 #[error("Color sheme settings is not object")]
34 ColorShemeSettingsIsNotObject,
35 #[error("Scope selector is not string: {0}")]
36 ScopeSelectorIsNotString(String),
37 #[error("Duplicate settings")]
38 DuplicateSettings,
39 #[error("Scope parse error: {0}")]
40 ScopeParse(#[from] ParseScopeError),
41}
42
43impl FromStr for UnderlineOption {
44 type Err = ParseThemeError;
45
46 fn from_str(s: &str) -> Result<UnderlineOption, Self::Err> {
47 Ok(match s {
48 "underline" => UnderlineOption::Underline,
49 "stippled_underline" => UnderlineOption::StippledUnderline,
50 "squiggly_underline" => UnderlineOption::SquigglyUnderline,
51 _ => return Err(IncorrectUnderlineOption),
52 })
53 }
54}
55
56impl ParseSettings for UnderlineOption {
57 type Error = ParseThemeError;
58
59 fn parse_settings(settings: Settings) -> Result<UnderlineOption, Self::Error> {
60 match settings {
61 Settings::String(value) => UnderlineOption::from_str(&value),
62 _ => Err(IncorrectUnderlineOption),
63 }
64 }
65}
66
67impl FromStr for FontStyle {
68 type Err = ParseThemeError;
69
70 fn from_str(s: &str) -> Result<FontStyle, Self::Err> {
71 let mut font_style = FontStyle::empty();
72 for i in s.split_whitespace() {
73 font_style.insert(match i {
74 "bold" => FontStyle::BOLD,
75 "underline" => FontStyle::UNDERLINE,
76 "italic" => FontStyle::ITALIC,
77 "normal" | "regular" => FontStyle::empty(),
78 s => return Err(IncorrectFontStyle(s.to_owned())),
79 })
80 }
81 Ok(font_style)
82 }
83}
84
85impl ParseSettings for FontStyle {
86 type Error = ParseThemeError;
87
88 fn parse_settings(settings: Settings) -> Result<FontStyle, Self::Error> {
89 match settings {
90 Settings::String(value) => FontStyle::from_str(&value),
91 c => Err(IncorrectFontStyle(c.to_string())),
92 }
93 }
94}
95
96impl FromStr for Color {
97 type Err = ParseThemeError;
98
99 fn from_str(s: &str) -> Result<Color, Self::Err> {
100 let mut chars = s.chars();
101 if chars.next() != Some('#') {
102 return Err(IncorrectColor);
103 }
104 let mut d = Vec::new();
105 for char in chars {
106 d.push(char.to_digit(16).ok_or(IncorrectColor)? as u8);
107 }
108 Ok(match d.len() {
109 3 => Color {
110 r: d[0],
111 g: d[1],
112 b: d[2],
113 a: 255,
114 },
115 6 => Color {
116 r: d[0] * 16 + d[1],
117 g: d[2] * 16 + d[3],
118 b: d[4] * 16 + d[5],
119 a: 255,
120 },
121 8 => Color {
122 r: d[0] * 16 + d[1],
123 g: d[2] * 16 + d[3],
124 b: d[4] * 16 + d[5],
125 a: d[6] * 16 + d[7],
126 },
127 _ => return Err(IncorrectColor),
128 })
129 }
130}
131
132impl ParseSettings for Color {
133 type Error = ParseThemeError;
134
135 fn parse_settings(settings: Settings) -> Result<Color, Self::Error> {
136 match settings {
137 Settings::String(value) => Color::from_str(&value),
138 _ => Err(IncorrectColor),
139 }
140 }
141}
142
143impl ParseSettings for StyleModifier {
144 type Error = ParseThemeError;
145
146 fn parse_settings(settings: Settings) -> Result<StyleModifier, Self::Error> {
147 let mut obj = match settings {
148 Settings::Object(obj) => obj,
149 _ => return Err(ColorShemeScopeIsNotObject),
150 };
151 let font_style = match obj.remove("fontStyle") {
152 Some(Settings::String(value)) => Some(FontStyle::from_str(&value)?),
153 None => None,
154 Some(c) => return Err(IncorrectFontStyle(c.to_string())),
155 };
156 let foreground = match obj.remove("foreground") {
157 Some(Settings::String(value)) => Some(Color::from_str(&value)?),
158 None => None,
159 _ => return Err(IncorrectColor),
160 };
161 let background = match obj.remove("background") {
162 Some(Settings::String(value)) => Some(Color::from_str(&value)?),
163 None => None,
164 _ => return Err(IncorrectColor),
165 };
166
167 Ok(StyleModifier {
168 foreground,
169 background,
170 font_style,
171 })
172 }
173}
174
175impl ParseSettings for ThemeItem {
176 type Error = ParseThemeError;
177
178 fn parse_settings(settings: Settings) -> Result<ThemeItem, Self::Error> {
179 let mut obj = match settings {
180 Settings::Object(obj) => obj,
181 _ => return Err(ColorShemeScopeIsNotObject),
182 };
183 let scope = match obj.remove("scope") {
184 Some(Settings::String(value)) => ScopeSelectors::from_str(&value)?,
185 _ => return Err(ScopeSelectorIsNotString(format!("{:?}", obj))),
186 };
187 let style = match obj.remove("settings") {
188 Some(settings) => StyleModifier::parse_settings(settings)?,
189 None => return Err(IncorrectSettings),
190 };
191 Ok(ThemeItem { scope, style })
192 }
193}
194
195impl ParseSettings for ThemeSettings {
196 type Error = ParseThemeError;
197
198 fn parse_settings(json: Settings) -> Result<ThemeSettings, Self::Error> {
199 let mut settings = ThemeSettings::default();
200
201 let obj = match json {
202 Settings::Object(obj) => obj,
203 _ => return Err(ColorShemeSettingsIsNotObject),
204 };
205
206 for (key, value) in obj {
207 match &key[..] {
208 "foreground" => settings.foreground = Color::parse_settings(value).ok(),
209 "background" => settings.background = Color::parse_settings(value).ok(),
210 "caret" => settings.caret = Color::parse_settings(value).ok(),
211 "lineHighlight" => settings.line_highlight = Color::parse_settings(value).ok(),
212 "misspelling" => settings.misspelling = Color::parse_settings(value).ok(),
213 "minimapBorder" => settings.minimap_border = Color::parse_settings(value).ok(),
214 "accent" => settings.accent = Color::parse_settings(value).ok(),
215
216 "popupCss" => settings.popup_css = value.as_str().map(|s| s.to_owned()),
217 "phantomCss" => settings.phantom_css = value.as_str().map(|s| s.to_owned()),
218
219 "bracketContentsForeground" => {
220 settings.bracket_contents_foreground = Color::parse_settings(value).ok()
221 }
222 "bracketContentsOptions" => {
223 settings.bracket_contents_options = UnderlineOption::parse_settings(value).ok()
224 }
225 "bracketsForeground" => {
226 settings.brackets_foreground = Color::parse_settings(value).ok()
227 }
228 "bracketsBackground" => {
229 settings.brackets_background = Color::parse_settings(value).ok()
230 }
231 "bracketsOptions" => {
232 settings.brackets_options = UnderlineOption::parse_settings(value).ok()
233 }
234 "tagsForeground" => settings.tags_foreground = Color::parse_settings(value).ok(),
235 "tagsOptions" => {
236 settings.tags_options = UnderlineOption::parse_settings(value).ok()
237 }
238 "highlight" => settings.highlight = Color::parse_settings(value).ok(),
239 "findHighlight" => settings.find_highlight = Color::parse_settings(value).ok(),
240 "findHighlightForeground" => {
241 settings.find_highlight_foreground = Color::parse_settings(value).ok()
242 }
243 "gutter" => settings.gutter = Color::parse_settings(value).ok(),
244 "gutterForeground" => {
245 settings.gutter_foreground = Color::parse_settings(value).ok()
246 }
247 "selection" => settings.selection = Color::parse_settings(value).ok(),
248 "selectionForeground" => {
249 settings.selection_foreground = Color::parse_settings(value).ok()
250 }
251 "selectionBorder" => settings.selection_border = Color::parse_settings(value).ok(),
252 "inactiveSelection" => {
253 settings.inactive_selection = Color::parse_settings(value).ok()
254 }
255 "inactiveSelectionForeground" => {
256 settings.inactive_selection_foreground = Color::parse_settings(value).ok()
257 }
258 "guide" => settings.guide = Color::parse_settings(value).ok(),
259 "activeGuide" => settings.active_guide = Color::parse_settings(value).ok(),
260 "stackGuide" => settings.stack_guide = Color::parse_settings(value).ok(),
261 "shadow" => settings.shadow = Color::parse_settings(value).ok(),
262 _ => (), }
264 }
265 Ok(settings)
266 }
267}
268
269impl ParseSettings for Theme {
270 type Error = ParseThemeError;
271
272 fn parse_settings(settings: Settings) -> Result<Theme, Self::Error> {
273 let mut obj = match settings {
274 Settings::Object(obj) => obj,
275 _ => return Err(IncorrectSyntax),
276 };
277 let name = match obj.remove("name") {
278 Some(Settings::String(name)) => Some(name),
279 None => None,
280 _ => return Err(IncorrectSyntax),
281 };
282 let author = match obj.remove("author") {
283 Some(Settings::String(author)) => Some(author),
284 None => None,
285 _ => return Err(IncorrectSyntax),
286 };
287 let items = match obj.remove("settings") {
288 Some(Settings::Array(items)) => items,
289 _ => return Err(IncorrectSyntax),
290 };
291 let mut iter = items.into_iter();
292 let mut settings = match iter.next() {
293 Some(Settings::Object(mut obj)) => match obj.remove("settings") {
294 Some(settings) => ThemeSettings::parse_settings(settings)?,
295 None => return Err(UndefinedSettings),
296 },
297 _ => return Err(UndefinedSettings),
298 };
299 if let Some(Settings::Object(obj)) = obj.remove("gutterSettings") {
300 for (key, value) in obj {
301 let color = Color::parse_settings(value).ok();
302 match &key[..] {
303 "background" => settings.gutter = settings.gutter.or(color),
304 "foreground" => {
305 settings.gutter_foreground = settings.gutter_foreground.or(color)
306 }
307 _ => (),
308 }
309 }
310 }
311 let mut scopes = Vec::new();
312 for json in iter {
313 if let Ok(item) = ThemeItem::parse_settings(json) {
315 scopes.push(item);
316 }
317 }
318 Ok(Theme {
319 name,
320 author,
321 settings,
322 scopes,
323 })
324 }
325}