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