main.rs

  1mod color;
  2mod vscode;
  3
  4use std::fs::File;
  5use std::io::{Read, 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 mut buffer = Vec::new();
 93    match File::open(&theme_file_path).and_then(|mut file| file.read_to_end(&mut buffer)) {
 94        Ok(_) => {}
 95        Err(err) => {
 96            log::info!("Failed to open file at path: {:?}", theme_file_path);
 97            return Err(err)?;
 98        }
 99    };
100
101    let vscode_theme: VsCodeTheme = serde_json_lenient::from_slice(&buffer)
102        .context(format!("failed to parse theme {theme_file_path:?}"))?;
103
104    let theme_metadata = ThemeMetadata {
105        name: vscode_theme.name.clone().unwrap_or("".to_string()),
106        appearance: ThemeAppearanceJson::Dark,
107        file_name: "".to_string(),
108    };
109
110    let converter = VsCodeThemeConverter::new(vscode_theme, theme_metadata, IndexMap::default());
111
112    let theme = converter.convert()?;
113    let mut theme = serde_json::to_value(theme).unwrap();
114    theme.as_object_mut().unwrap().insert(
115        "$schema".to_string(),
116        serde_json::Value::String(ZED_THEME_SCHEMA_URL.to_string()),
117    );
118    let theme_json = serde_json::to_string_pretty(&theme).unwrap();
119
120    if let Some(output) = args.output {
121        let mut file = File::create(output)?;
122        file.write_all(theme_json.as_bytes())?;
123    } else {
124        println!("{}", theme_json);
125    }
126
127    log::info!("Done!");
128
129    Ok(())
130}