1use std::path::{Path, PathBuf};
2
3use anyhow::Result;
4use gpui::{Hsla, Refineable, Rgba};
5use serde::Deserialize;
6use theme::{
7 default_color_scales, Appearance, ColorScales, GitStatusColors, PlayerColors, StatusColors,
8 SyntaxTheme, SystemColors, ThemeColors, ThemeColorsRefinement, ThemeFamily, ThemeStyles,
9 ThemeVariant,
10};
11use uuid::Uuid;
12
13use crate::ThemeMetadata;
14
15#[derive(Deserialize, Debug)]
16pub struct VsCodeTheme {
17 #[serde(rename = "$schema")]
18 pub schema: Option<String>,
19 pub name: Option<String>,
20 pub author: Option<String>,
21 pub maintainers: Option<Vec<String>>,
22 #[serde(rename = "semanticClass")]
23 pub semantic_class: Option<String>,
24 #[serde(rename = "semanticHighlighting")]
25 pub semantic_highlighting: Option<bool>,
26 pub colors: VsCodeColors,
27}
28
29#[derive(Debug, Deserialize)]
30pub struct VsCodeColors {
31 #[serde(rename = "editor.foreground")]
32 text: String,
33 #[serde(rename = "editor.background")]
34 editor: String,
35}
36
37pub(crate) fn new_theme_family_from_vsc(path: &Path) -> Result<ThemeFamily> {
38 todo!()
39
40 // let path_str = path.to_str().unwrap();
41 // let family_name = path_str.split('/').last().unwrap();
42
43 // let mut json_files: Vec<String> = Vec::new();
44
45 // if path.is_dir() {
46 // for entry in std::fs::read_dir(path).unwrap() {
47 // let entry = entry.unwrap();
48 // let path = entry.path();
49 // if path.is_file() {
50 // if let Some(extension) = path.extension() {
51 // if extension == "json" {
52 // json_files.push(path.file_name().unwrap().to_str().unwrap().to_string());
53 // }
54 // }
55 // }
56 // }
57 // } else {
58 // anyhow::bail!("Path is not a directory");
59 // }
60
61 // let mut theme_family = ThemeFamily {
62 // id: uuid::Uuid::new_v4().to_string(),
63 // name: family_name.into(),
64 // author: "New Theme Family".into(),
65 // themes: Vec::new(),
66 // scales: default_color_scales(),
67 // };
68
69 // Ok(theme_family)
70}
71
72fn try_parse_color(color: &str) -> Result<Hsla> {
73 Ok(Rgba::try_from(color)?.into())
74}
75
76pub struct VsCodeThemeConverter {
77 theme: VsCodeTheme,
78 theme_metadata: ThemeMetadata,
79}
80
81impl VsCodeThemeConverter {
82 pub fn new(theme: VsCodeTheme, theme_metadata: ThemeMetadata) -> Self {
83 Self {
84 theme,
85 theme_metadata,
86 }
87 }
88
89 pub fn convert(self) -> Result<ThemeVariant> {
90 let appearance = self.theme_metadata.appearance.into();
91
92 let mut theme_colors = match appearance {
93 Appearance::Light => ThemeColors::default_light(),
94 Appearance::Dark => ThemeColors::default_dark(),
95 };
96
97 let vscode_colors = &self.theme.colors;
98
99 let theme_colors_refinements = ThemeColorsRefinement {
100 background: Some(try_parse_color(&vscode_colors.editor)?),
101 text: Some(try_parse_color(&vscode_colors.text)?),
102 ..Default::default()
103 };
104
105 theme_colors.refine(&theme_colors_refinements);
106
107 Ok(ThemeVariant {
108 id: uuid::Uuid::new_v4().to_string(),
109 name: self.theme_metadata.name.into(),
110 appearance,
111 styles: ThemeStyles {
112 system: SystemColors::default(),
113 colors: theme_colors,
114 status: StatusColors::default(),
115 git: GitStatusColors::default(),
116 player: PlayerColors::default(),
117 syntax: SyntaxTheme::default_dark(),
118 },
119 })
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126 use std::path::PathBuf;
127
128 #[test]
129 fn test_deserialize_theme() {
130 let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
131 let root_dir = manifest_dir.parent().unwrap().parent().unwrap();
132
133 let mut d = root_dir.to_path_buf();
134 d.push("assets/themes/src/vsc/dracula/dracula.json");
135
136 let data = std::fs::read_to_string(d).expect("Unable to read file");
137
138 let result: Theme = serde_json::from_str(&data).unwrap();
139 println!("{:#?}", result);
140 }
141}