zed/crates/theme_importer/src/main.rs

165 lines
4.8 KiB
Rust
Raw Normal View History

2023-11-02 22:57:13 +00:00
mod theme_printer;
mod util;
2023-11-02 22:57:13 +00:00
mod vscode;
2023-11-02 22:00:55 +00:00
use std::fs::{self, File};
2023-11-02 23:14:51 +00:00
use std::io::Write;
use std::path::PathBuf;
use std::str::FromStr;
use anyhow::{anyhow, Context, Result};
2023-11-02 23:14:51 +00:00
use convert_case::{Case, Casing};
2023-11-02 22:24:38 +00:00
use gpui::serde_json;
use log::LevelFilter;
use serde::Deserialize;
use simplelog::SimpleLogger;
2023-11-02 22:57:13 +00:00
use theme::{default_color_scales, Appearance, ThemeFamily};
2023-11-02 22:24:38 +00:00
use vscode::VsCodeThemeConverter;
2023-11-02 22:00:55 +00:00
2023-11-02 22:57:13 +00:00
use crate::theme_printer::ThemeFamilyPrinter;
2023-11-02 22:00:55 +00:00
use crate::vscode::VsCodeTheme;
2023-11-02 22:00:55 +00:00
#[derive(Debug, Deserialize)]
2023-11-02 22:24:38 +00:00
struct FamilyMetadata {
pub name: String,
2023-11-02 22:00:55 +00:00
pub author: String,
2023-11-02 22:24:38 +00:00
pub themes: Vec<ThemeMetadata>,
}
2023-11-02 22:00:55 +00:00
#[derive(Debug, Deserialize)]
#[serde(rename_all = "snake_case")]
2023-11-02 22:57:13 +00:00
pub enum ThemeAppearanceJson {
Light,
Dark,
}
2023-11-02 22:24:38 +00:00
impl From<ThemeAppearanceJson> for Appearance {
fn from(value: ThemeAppearanceJson) -> Self {
match value {
ThemeAppearanceJson::Light => Self::Light,
ThemeAppearanceJson::Dark => Self::Dark,
}
}
}
2023-11-02 22:24:38 +00:00
#[derive(Debug, Deserialize)]
2023-11-02 22:57:13 +00:00
pub struct ThemeMetadata {
pub name: String,
2023-11-02 22:24:38 +00:00
pub file_name: String,
pub appearance: ThemeAppearanceJson,
}
// Load a vscode theme from json
// Load it's LICENSE from the same folder
// Create a ThemeFamily for the theme
// Create a ThemeVariant or Variants for the theme
// Output a rust file with the ThemeFamily and ThemeVariant(s) in it
fn main() -> Result<()> {
SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
2023-11-02 22:00:55 +00:00
let vscode_themes_path = PathBuf::from_str("assets/themes/src/vscode/")?;
2023-11-02 22:00:55 +00:00
let mut theme_families = Vec::new();
2023-11-02 22:00:55 +00:00
for theme_family_dir in fs::read_dir(&vscode_themes_path)? {
let theme_family_dir = theme_family_dir?;
2023-11-02 22:00:55 +00:00
let theme_family_slug = theme_family_dir
.path()
.file_stem()
.ok_or(anyhow!("no file stem"))
.map(|stem| stem.to_string_lossy().to_string())?;
2023-11-02 22:00:55 +00:00
let family_metadata_file = File::open(theme_family_dir.path().join("family.json"))
.context(format!("no `family.json` found for '{theme_family_slug}'"))?;
2023-11-02 22:24:38 +00:00
let family_metadata: FamilyMetadata = serde_json::from_reader(family_metadata_file)
.context(format!(
"failed to parse `family.json` for '{theme_family_slug}'"
))?;
2023-11-02 22:00:55 +00:00
let mut themes = Vec::new();
2023-11-02 22:24:38 +00:00
for theme_metadata in family_metadata.themes {
let theme_file_path = theme_family_dir.path().join(&theme_metadata.file_name);
2023-11-02 22:00:55 +00:00
let theme_file = File::open(&theme_file_path)?;
2023-11-02 22:24:38 +00:00
let vscode_theme: VsCodeTheme = serde_json::from_reader(theme_file)
2023-11-02 22:00:55 +00:00
.context(format!("failed to parse theme {theme_file_path:?}"))?;
2023-11-02 22:24:38 +00:00
let converter = VsCodeThemeConverter::new(vscode_theme, theme_metadata);
let theme = converter.convert()?;
2023-11-02 22:00:55 +00:00
themes.push(theme);
}
let theme_family = ThemeFamily {
id: uuid::Uuid::new_v4().to_string(),
name: family_metadata.name.into(),
author: family_metadata.author.into(),
2023-11-02 22:24:38 +00:00
themes,
2023-11-02 22:00:55 +00:00
scales: default_color_scales(),
};
theme_families.push(theme_family);
}
2023-11-02 23:14:51 +00:00
let themes_output_path = PathBuf::from_str("crates/theme2/src/themes")?;
let mut theme_modules = Vec::new();
2023-11-02 22:57:13 +00:00
for theme_family in theme_families {
2023-11-02 23:14:51 +00:00
let theme_family_slug = theme_family.name.to_string().to_case(Case::Snake);
let mut output_file =
File::create(themes_output_path.join(format!("{theme_family_slug}.rs")))?;
let theme_module = format!(
r#"
2023-11-03 14:58:06 +00:00
use gpui::rgba;
2023-11-02 23:14:51 +00:00
use crate::{{
default_color_scales, Appearance, GitStatusColors, PlayerColor, PlayerColors, StatusColors,
SyntaxTheme, SystemColors, ThemeColors, ThemeFamily, ThemeStyles, ThemeVariant,
}};
pub fn {theme_family_slug}() -> ThemeFamily {{
{theme_family_definition}
}}
"#,
theme_family_definition = format!("{:#?}", ThemeFamilyPrinter::new(theme_family))
);
output_file.write_all(theme_module.as_bytes())?;
theme_modules.push(theme_family_slug);
2023-11-02 22:57:13 +00:00
}
2023-11-02 23:14:51 +00:00
let mut mod_rs_file = File::create(themes_output_path.join(format!("mod.rs")))?;
let mod_rs_contents = format!(
r#"
{mod_statements}
{use_statements}
"#,
mod_statements = theme_modules
.iter()
.map(|module| format!("mod {module};"))
.collect::<Vec<_>>()
.join("\n"),
use_statements = theme_modules
.iter()
.map(|module| format!("pub use {module}::*;"))
.collect::<Vec<_>>()
.join("\n")
);
mod_rs_file.write_all(mod_rs_contents.as_bytes())?;
Ok(())
}