theme_converter/
vscode.rs1use std::fs;
2
3use plist::{Dictionary, Value};
4use serde::Deserialize;
5
6use crate::parser::Parser;
7
8#[derive(Debug, Deserialize)]
9#[serde(untagged)]
10enum TokenColour {
11 Single {
12 scope: String,
13 settings: Settings,
14 },
15 Multi {
16 scope: Vec<String>,
17 settings: Settings,
18 },
19}
20
21#[derive(Debug, Deserialize)]
22struct VSCodeTheme {
23 #[serde(default)]
24 name: Option<String>,
25 #[serde(default)]
26 colors: serde_json::Value,
27 #[serde(rename = "tokenColors", default)]
28 token_colors: Vec<TokenColour>,
29}
30
31#[derive(Debug, Clone, Deserialize)]
32struct Settings {
33 #[serde(default)]
34 foreground: Option<String>,
35 #[serde(default)]
36 background: Option<String>,
37 #[serde(default, rename = "fontStyle")]
38 font_style: Option<String>,
39}
40
41pub struct VSCodeThemeParser {
42 theme: VSCodeTheme,
43}
44
45impl Parser for VSCodeThemeParser {
46 fn parse(&self, name: &str) -> Dictionary {
47 let get_color = |key: &str| -> Option<String> {
48 self.theme
49 .colors
50 .get(key)
51 .and_then(|v| v.as_str())
52 .map(|s| s.into())
53 };
54
55 let global_settings = Some(self.build_settings_dict(
56 "",
57 &Settings {
58 foreground: get_color("editor.foreground"),
59 background: get_color("editor.background"),
60 font_style: get_color("editor.fontStyle"),
61 },
62 ));
63
64 let token_settings = self.theme.token_colors.iter().map(|tok| match tok {
65 TokenColour::Single { scope, settings } => self.build_settings_dict(scope, settings),
66 TokenColour::Multi { scope, settings } => {
67 self.build_settings_dict(&scope.join(", "), settings)
68 }
69 });
70
71 let settings_array: Vec<Value> =
72 global_settings.into_iter().chain(token_settings).collect();
73
74 Dictionary::from_iter([
75 ("name", Value::String(name.to_string())),
76 ("uuid", Value::String(uuid::Uuid::new_v4().to_string())),
77 ("settings", Value::Array(settings_array)),
78 ])
79 }
80
81 fn from_config(file_name: &str) -> anyhow::Result<Self> {
82 let theme: VSCodeTheme = serde_json::from_str(&fs::read_to_string(file_name)?)?;
83
84 Ok(Self { theme })
85 }
86}
87
88impl VSCodeThemeParser {
89 fn build_settings_dict(&self, scope: impl Into<String>, s: &Settings) -> Value {
90 let inner = Dictionary::from_iter(
91 [
92 s.foreground
93 .clone()
94 .map(|v| ("foreground", Value::String(v))),
95 s.background
96 .clone()
97 .map(|v| ("background", Value::String(v))),
98 s.font_style
99 .clone()
100 .map(|v| ("fontStyle", Value::String(v))),
101 ]
102 .into_iter()
103 .flatten(),
104 );
105
106 Value::Dictionary(Dictionary::from_iter([
107 ("scope", Value::String(scope.into())),
108 ("settings", Value::Dictionary(inner)),
109 ]))
110 }
111}