main.rs

  1mod color;
  2mod vscode;
  3
  4use std::fs::File;
  5use std::io::Write;
  6use std::path::PathBuf;
  7
  8use anyhow::{Context as _, Result};
  9use clap::Parser;
 10use collections::IndexMap;
 11use log::LevelFilter;
 12use serde::Deserialize;
 13use simplelog::ColorChoice;
 14use simplelog::{TermLogger, TerminalMode};
 15use theme::{Appearance, AppearanceContent};
 16
 17use crate::vscode::VsCodeTheme;
 18use crate::vscode::VsCodeThemeConverter;
 19
 20const ZED_THEME_SCHEMA_URL: &str = "https://zed.dev/schema/themes/v0.2.0.json";
 21
 22#[derive(Debug, Clone, Copy, Deserialize)]
 23#[serde(rename_all = "snake_case")]
 24pub enum ThemeAppearanceJson {
 25    Light,
 26    Dark,
 27}
 28
 29impl From<ThemeAppearanceJson> for AppearanceContent {
 30    fn from(value: ThemeAppearanceJson) -> Self {
 31        match value {
 32            ThemeAppearanceJson::Light => Self::Light,
 33            ThemeAppearanceJson::Dark => Self::Dark,
 34        }
 35    }
 36}
 37
 38impl From<ThemeAppearanceJson> for Appearance {
 39    fn from(value: ThemeAppearanceJson) -> Self {
 40        match value {
 41            ThemeAppearanceJson::Light => Self::Light,
 42            ThemeAppearanceJson::Dark => Self::Dark,
 43        }
 44    }
 45}
 46
 47#[derive(Debug, Deserialize)]
 48pub struct ThemeMetadata {
 49    pub name: String,
 50    pub file_name: String,
 51    pub appearance: ThemeAppearanceJson,
 52}
 53
 54#[derive(Parser)]
 55#[command(author, version, about, long_about = None)]
 56struct Args {
 57    /// The path to the theme to import.
 58    theme_path: PathBuf,
 59
 60    /// Whether to warn when values are missing from the theme.
 61    #[arg(long)]
 62    warn_on_missing: bool,
 63
 64    /// The path to write the output to.
 65    #[arg(long, short)]
 66    output: Option<PathBuf>,
 67}
 68
 69fn main() -> Result<()> {
 70    let args = Args::parse();
 71
 72    let log_config = {
 73        let mut config = simplelog::ConfigBuilder::new();
 74
 75        if !args.warn_on_missing {
 76            config.add_filter_ignore_str("theme_printer");
 77        }
 78
 79        config.build()
 80    };
 81
 82    TermLogger::init(
 83        LevelFilter::Trace,
 84        log_config,
 85        TerminalMode::Stderr,
 86        ColorChoice::Auto,
 87    )
 88    .expect("could not initialize logger");
 89
 90    let theme_file_path = args.theme_path;
 91
 92    let theme_file = match File::open(&theme_file_path) {
 93        Ok(file) => file,
 94        Err(err) => {
 95            log::info!("Failed to open file at path: {:?}", theme_file_path);
 96            return Err(err)?;
 97        }
 98    };
 99
100    let vscode_theme: VsCodeTheme = serde_json_lenient::from_reader(theme_file)
101        .context(format!("failed to parse theme {theme_file_path:?}"))?;
102
103    let theme_metadata = ThemeMetadata {
104        name: vscode_theme.name.clone().unwrap_or("".to_string()),
105        appearance: ThemeAppearanceJson::Dark,
106        file_name: "".to_string(),
107    };
108
109    let converter = VsCodeThemeConverter::new(vscode_theme, theme_metadata, IndexMap::default());
110
111    let theme = converter.convert()?;
112    let mut theme = serde_json::to_value(theme).unwrap();
113    theme.as_object_mut().unwrap().insert(
114        "$schema".to_string(),
115        serde_json::Value::String(ZED_THEME_SCHEMA_URL.to_string()),
116    );
117    let theme_json = serde_json::to_string_pretty(&theme).unwrap();
118
119    if let Some(output) = args.output {
120        let mut file = File::create(output)?;
121        file.write_all(theme_json.as_bytes())?;
122    } else {
123        println!("{}", theme_json);
124    }
125
126    log::info!("Done!");
127
128    Ok(())
129}