2023-12-22 18:47:30 +00:00
|
|
|
mod assets;
|
2023-12-07 23:34:03 +00:00
|
|
|
mod color;
|
2023-11-02 22:57:13 +00:00
|
|
|
mod vscode;
|
|
|
|
|
2024-01-27 21:55:37 +00:00
|
|
|
use std::fs::File;
|
2024-02-07 21:23:36 +00:00
|
|
|
use std::io::Write;
|
2023-11-02 21:21:11 +00:00
|
|
|
use std::path::PathBuf;
|
|
|
|
|
2024-01-27 21:55:37 +00:00
|
|
|
use anyhow::{Context, Result};
|
2024-01-31 04:33:54 +00:00
|
|
|
use clap::{Parser, Subcommand};
|
2023-12-08 04:37:49 +00:00
|
|
|
use indexmap::IndexMap;
|
2023-11-02 21:21:11 +00:00
|
|
|
use log::LevelFilter;
|
2024-01-31 04:33:54 +00:00
|
|
|
use schemars::schema_for;
|
2023-11-02 21:21:11 +00:00
|
|
|
use serde::Deserialize;
|
2023-12-08 03:11:31 +00:00
|
|
|
use simplelog::{TermLogger, TerminalMode};
|
2024-01-31 04:33:54 +00:00
|
|
|
use theme::{Appearance, AppearanceContent, ThemeFamilyContent};
|
2023-11-02 22:00:55 +00:00
|
|
|
|
|
|
|
use crate::vscode::VsCodeTheme;
|
2023-11-09 18:07:32 +00:00
|
|
|
use crate::vscode::VsCodeThemeConverter;
|
2023-11-02 21:21:11 +00:00
|
|
|
|
2023-11-02 22:00:55 +00:00
|
|
|
#[derive(Debug, Deserialize)]
|
2023-11-02 22:24:38 +00:00
|
|
|
struct FamilyMetadata {
|
2023-11-02 21:21:11 +00:00
|
|
|
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-12-08 04:37:49 +00:00
|
|
|
|
|
|
|
/// Overrides for specific syntax tokens.
|
|
|
|
///
|
|
|
|
/// Use this to ensure certain Zed syntax tokens are matched
|
|
|
|
/// to an exact set of scopes when it is not otherwise possible
|
|
|
|
/// to rely on the default mappings in the theme importer.
|
|
|
|
#[serde(default)]
|
|
|
|
pub syntax: IndexMap<String, Vec<String>>,
|
2023-11-02 21:21:11 +00:00
|
|
|
}
|
|
|
|
|
2023-11-09 17:46:37 +00:00
|
|
|
#[derive(Debug, Clone, Copy, Deserialize)]
|
2023-11-02 21:21:11 +00:00
|
|
|
#[serde(rename_all = "snake_case")]
|
2023-11-02 22:57:13 +00:00
|
|
|
pub enum ThemeAppearanceJson {
|
2023-11-02 21:21:11 +00:00
|
|
|
Light,
|
|
|
|
Dark,
|
|
|
|
}
|
|
|
|
|
2024-01-27 21:55:37 +00:00
|
|
|
impl From<ThemeAppearanceJson> for AppearanceContent {
|
|
|
|
fn from(value: ThemeAppearanceJson) -> Self {
|
|
|
|
match value {
|
|
|
|
ThemeAppearanceJson::Light => Self::Light,
|
|
|
|
ThemeAppearanceJson::Dark => Self::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 21:21:11 +00:00
|
|
|
}
|
|
|
|
|
2023-11-02 22:24:38 +00:00
|
|
|
#[derive(Debug, Deserialize)]
|
2023-11-02 22:57:13 +00:00
|
|
|
pub struct ThemeMetadata {
|
2023-11-02 21:21:11 +00:00
|
|
|
pub name: String,
|
2023-11-02 22:24:38 +00:00
|
|
|
pub file_name: String,
|
|
|
|
pub appearance: ThemeAppearanceJson,
|
2023-11-02 21:21:11 +00:00
|
|
|
}
|
|
|
|
|
2023-12-18 22:18:49 +00:00
|
|
|
#[derive(Parser)]
|
|
|
|
#[command(author, version, about, long_about = None)]
|
|
|
|
struct Args {
|
2024-01-27 21:55:37 +00:00
|
|
|
/// The path to the theme to import.
|
|
|
|
theme_path: PathBuf,
|
|
|
|
|
2023-12-18 22:18:49 +00:00
|
|
|
/// Whether to warn when values are missing from the theme.
|
|
|
|
#[arg(long)]
|
|
|
|
warn_on_missing: bool,
|
2024-01-31 04:33:54 +00:00
|
|
|
|
2024-02-07 21:23:36 +00:00
|
|
|
/// The path to write the output to.
|
|
|
|
#[arg(long, short)]
|
|
|
|
output: Option<PathBuf>,
|
|
|
|
|
2024-01-31 04:33:54 +00:00
|
|
|
#[command(subcommand)]
|
|
|
|
command: Option<Command>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Subcommand)]
|
|
|
|
enum Command {
|
|
|
|
/// Prints the JSON schema for a theme.
|
|
|
|
PrintSchema,
|
2023-12-18 22:18:49 +00:00
|
|
|
}
|
|
|
|
|
2023-11-02 21:21:11 +00:00
|
|
|
fn main() -> Result<()> {
|
2023-12-18 22:18:49 +00:00
|
|
|
let args = Args::parse();
|
|
|
|
|
|
|
|
let log_config = {
|
|
|
|
let mut config = simplelog::ConfigBuilder::new();
|
|
|
|
config
|
|
|
|
.set_level_color(log::Level::Trace, simplelog::Color::Cyan)
|
|
|
|
.set_level_color(log::Level::Info, simplelog::Color::Blue)
|
|
|
|
.set_level_color(log::Level::Warn, simplelog::Color::Yellow)
|
|
|
|
.set_level_color(log::Level::Error, simplelog::Color::Red);
|
|
|
|
|
|
|
|
if !args.warn_on_missing {
|
|
|
|
config.add_filter_ignore_str("theme_printer");
|
|
|
|
}
|
|
|
|
|
|
|
|
config.build()
|
|
|
|
};
|
2023-11-02 21:21:11 +00:00
|
|
|
|
2023-12-08 03:11:31 +00:00
|
|
|
TermLogger::init(LevelFilter::Trace, log_config, TerminalMode::Mixed)
|
|
|
|
.expect("could not initialize logger");
|
|
|
|
|
2024-01-31 04:33:54 +00:00
|
|
|
if let Some(command) = args.command {
|
|
|
|
match command {
|
|
|
|
Command::PrintSchema => {
|
|
|
|
let theme_family_schema = schema_for!(ThemeFamilyContent);
|
|
|
|
|
|
|
|
println!(
|
|
|
|
"{}",
|
|
|
|
serde_json::to_string_pretty(&theme_family_schema).unwrap()
|
|
|
|
);
|
|
|
|
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-27 21:55:37 +00:00
|
|
|
let theme_file_path = args.theme_path;
|
2023-11-06 19:54:21 +00:00
|
|
|
|
2024-01-27 21:55:37 +00:00
|
|
|
let theme_file = match File::open(&theme_file_path) {
|
|
|
|
Ok(file) => file,
|
|
|
|
Err(err) => {
|
|
|
|
log::info!("Failed to open file at path: {:?}", theme_file_path);
|
|
|
|
return Err(err)?;
|
2023-12-22 22:37:53 +00:00
|
|
|
}
|
2024-01-27 21:55:37 +00:00
|
|
|
};
|
2023-11-02 23:14:51 +00:00
|
|
|
|
2024-02-02 17:09:05 +00:00
|
|
|
let vscode_theme: VsCodeTheme = serde_json_lenient::from_reader(theme_file)
|
2024-01-27 21:55:37 +00:00
|
|
|
.context(format!("failed to parse theme {theme_file_path:?}"))?;
|
2024-01-05 18:38:12 +00:00
|
|
|
|
2024-01-27 21:55:37 +00:00
|
|
|
let theme_metadata = ThemeMetadata {
|
2024-02-07 15:13:46 +00:00
|
|
|
name: vscode_theme.name.clone().unwrap_or("".to_string()),
|
2024-01-27 21:55:37 +00:00
|
|
|
appearance: ThemeAppearanceJson::Dark,
|
|
|
|
file_name: "".to_string(),
|
|
|
|
};
|
2024-01-05 18:38:12 +00:00
|
|
|
|
2024-01-27 21:55:37 +00:00
|
|
|
let converter = VsCodeThemeConverter::new(vscode_theme, theme_metadata, IndexMap::new());
|
2024-01-05 18:38:12 +00:00
|
|
|
|
2024-01-27 21:55:37 +00:00
|
|
|
let theme = converter.convert()?;
|
2023-11-07 17:45:09 +00:00
|
|
|
|
2024-01-27 21:55:37 +00:00
|
|
|
let theme_json = serde_json::to_string_pretty(&theme).unwrap();
|
2023-11-07 17:45:09 +00:00
|
|
|
|
2024-02-07 21:23:36 +00:00
|
|
|
if let Some(output) = args.output {
|
|
|
|
let mut file = File::create(output)?;
|
|
|
|
file.write_all(theme_json.as_bytes())?;
|
|
|
|
} else {
|
|
|
|
println!("{}", theme_json);
|
|
|
|
}
|
2023-11-07 17:45:09 +00:00
|
|
|
|
2023-12-08 03:11:31 +00:00
|
|
|
log::info!("Done!");
|
2023-11-07 17:45:09 +00:00
|
|
|
|
2023-11-02 21:21:11 +00:00
|
|
|
Ok(())
|
|
|
|
}
|