univert 0.2.0

Universal file converter (library and CLI)
Documentation
use crate::{Job, Result};

pub type ConvertFn = fn(&Job) -> Result<()>;

#[derive(Clone, Copy)]
pub struct Entry {
  /// Direct converter, if this pair is a single-step conversion.
  pub direct: Option<ConvertFn>,
  /// Ordered list of intermediate extensions for multi-step conversions.
  /// Empty when `direct` is Some.
  pub intermediates: &'static [&'static str],
}

impl Entry {
  const fn direct(f: ConvertFn) -> Self {
    Self {
      direct: Some(f),
      intermediates: &[],
    }
  }

  const fn chain(intermediates: &'static [&'static str]) -> Self {
    Self {
      direct: None,
      intermediates,
    }
  }
}

use crate::converters::*;

/// Returns `Some(entry)` when a conversion is registered.
pub fn lookup(from: &str, to: &str) -> Option<Entry> {
  // NOTE: order below mirrors javascript/src/conversionMap.yaml.
  Some(match (from, to) {
    // abc -> ly (direct), everything else chains through ly
    ("abc", "ly") => Entry::direct(lilypond::abc_to_ly),
    ("abc", "midi") => Entry::chain(&["ly"]),
    ("abc", "pdf") => Entry::chain(&["ly"]),
    ("abc", "png") => Entry::chain(&["ly"]),
    ("abc", "ps") => Entry::chain(&["ly"]),
    ("abc", "svg") => Entry::chain(&["ly"]),
    ("abc", "jpeg") => Entry::chain(&["ly", "png"]),

    // bmp cross-matrix (image crate)
    ("bmp", "gif")
    | ("bmp", "ico")
    | ("bmp", "jpeg")
    | ("bmp", "png")
    | ("bmp", "tga")
    | ("bmp", "tif") => Entry::direct(image::convert),
    ("bmp", "jp2") => Entry::direct(image::to_jp2),

    // coffee -> js: stub
    ("coffee", "js") => Entry::direct(stubs::not_implemented),

    // csv
    ("csv", "json") => Entry::direct(csv_tsv::csv_to_json),
    ("csv", "tsv") => Entry::direct(csv_tsv::csv_to_tsv),
    ("csv", "html") => Entry::direct(csv_tsv::csv_to_html),
    ("csv", "json5") => Entry::chain(&["json"]),
    ("csv", "yaml") => Entry::chain(&["json"]),

    // gif
    ("gif", "bmp")
    | ("gif", "ico")
    | ("gif", "jpeg")
    | ("gif", "png")
    | ("gif", "tga")
    | ("gif", "tif") => Entry::direct(image::convert),
    ("gif", "jp2") => Entry::direct(image::to_jp2),

    // html
    ("html", "md") => Entry::direct(html_md::to_md),
    ("html", "jade") => Entry::direct(stubs::not_implemented),

    // ico
    ("ico", "bmp")
    | ("ico", "gif")
    | ("ico", "jpeg")
    | ("ico", "png")
    | ("ico", "tga")
    | ("ico", "tif") => Entry::direct(image::convert),
    ("ico", "jp2") => Entry::direct(image::to_jp2),

    // jpeg
    ("jpeg", "bmp")
    | ("jpeg", "gif")
    | ("jpeg", "ico")
    | ("jpeg", "png")
    | ("jpeg", "tga")
    | ("jpeg", "tif") => Entry::direct(image::convert),
    ("jpeg", "jp2") => Entry::direct(image::to_jp2),

    // jp2
    ("jp2", "bmp")
    | ("jp2", "gif")
    | ("jp2", "ico")
    | ("jp2", "jpeg")
    | ("jp2", "png")
    | ("jp2", "tga")
    | ("jp2", "tif") => Entry::direct(image::from_jp2),

    // js -> coffee: stub
    ("js", "coffee") => Entry::direct(stubs::not_implemented),

    // json
    ("json", "json5") => Entry::direct(json_yaml::json_to_json5),
    ("json", "yaml") => Entry::direct(json_yaml::json_to_yaml),

    // json5
    ("json5", "json") => Entry::direct(json_yaml::json5_to_json),

    // ly
    ("ly", "midi")
    | ("ly", "pdf")
    | ("ly", "png")
    | ("ly", "ps")
    | ("ly", "svg") => Entry::direct(lilypond::ly_to),

    // midi
    ("midi", "wav") => Entry::direct(audio::midi_to_wav),

    // mp3
    ("mp3", "png") => Entry::direct(audio::mp3_to_png),

    // png
    ("png", "bmp")
    | ("png", "gif")
    | ("png", "ico")
    | ("png", "jpeg")
    | ("png", "tga")
    | ("png", "tif") => Entry::direct(image::convert),
    ("png", "jp2") => Entry::direct(image::to_jp2),

    // psd (decode via psd crate, encode via image)
    ("psd", "bmp")
    | ("psd", "gif")
    | ("psd", "ico")
    | ("psd", "jpeg")
    | ("psd", "png")
    | ("psd", "tga")
    | ("psd", "tif") => Entry::direct(image::from_psd),
    ("psd", "jp2") => Entry::direct(image::psd_to_jp2),

    // scad
    ("scad", "csg") => Entry::direct(scad::to_csg),
    ("scad", "off") => Entry::direct(scad::to_off),
    ("scad", "png") => Entry::direct(scad::to_png),
    ("scad", "stl") => Entry::direct(scad::to_stl),

    // stl
    ("stl", "obj") => Entry::direct(stl::stl_to_obj),
    ("stl", "png") => Entry::direct(stl::stl_to_png),
    ("stl", "gcode") => Entry::direct(stubs::not_implemented),

    // styl -> css: stub
    ("styl", "css") => Entry::direct(stubs::not_implemented),

    // svg
    ("svg", "png") => Entry::direct(svg::to_png),
    ("svg", "pdf") => Entry::direct(svg::to_pdf),
    ("svg", "eps") => Entry::direct(svg::to_eps),
    ("svg", "ps") => Entry::direct(svg::to_ps),

    // tif
    ("tif", "bmp")
    | ("tif", "gif")
    | ("tif", "ico")
    | ("tif", "jpeg")
    | ("tif", "png")
    | ("tif", "tga") => Entry::direct(image::convert),
    ("tif", "jp2") => Entry::direct(image::to_jp2),

    // tsv
    ("tsv", "csv") => Entry::direct(csv_tsv::tsv_to_csv),
    ("tsv", "json") => Entry::direct(csv_tsv::tsv_to_json),
    ("tsv", "html") => Entry::direct(csv_tsv::tsv_to_html),
    ("tsv", "json5") => Entry::chain(&["json"]),
    ("tsv", "yaml") => Entry::chain(&["json"]),

    // vcf
    ("vcf", "json") => Entry::direct(vcard::vcf_to_json),
    ("vcf", "json5") => Entry::chain(&["json"]),
    ("vcf", "yaml") => Entry::chain(&["json"]),

    // wav
    ("wav", "aac") => Entry::direct(audio::wav_to_aac),
    ("wav", "mp3") => Entry::direct(audio::wav_to_mp3),

    // yaml
    ("yaml", "json") => Entry::direct(json_yaml::yaml_to_json),
    ("yaml", "json5") => Entry::chain(&["json"]),
    ("yaml", "vcf") => Entry::direct(vcard::yaml_to_vcf),

    _ => return None,
  })
}

/// Sorted list of all registered (from, to) pairs.
pub fn list_all() -> Vec<(&'static str, &'static str)> {
  const PAIRS: &[(&str, &str)] = &[
    ("abc", "ly"),
    ("abc", "midi"),
    ("abc", "pdf"),
    ("abc", "png"),
    ("abc", "ps"),
    ("abc", "svg"),
    ("abc", "jpeg"),
    ("bmp", "gif"),
    ("bmp", "ico"),
    ("bmp", "jp2"),
    ("bmp", "jpeg"),
    ("bmp", "png"),
    ("bmp", "tga"),
    ("bmp", "tif"),
    ("coffee", "js"),
    ("csv", "html"),
    ("csv", "json"),
    ("csv", "json5"),
    ("csv", "tsv"),
    ("csv", "yaml"),
    ("gif", "bmp"),
    ("gif", "ico"),
    ("gif", "jp2"),
    ("gif", "jpeg"),
    ("gif", "png"),
    ("gif", "tga"),
    ("gif", "tif"),
    ("html", "jade"),
    ("html", "md"),
    ("ico", "bmp"),
    ("ico", "gif"),
    ("ico", "jp2"),
    ("ico", "jpeg"),
    ("ico", "png"),
    ("ico", "tga"),
    ("ico", "tif"),
    ("jpeg", "bmp"),
    ("jpeg", "gif"),
    ("jpeg", "ico"),
    ("jpeg", "jp2"),
    ("jpeg", "png"),
    ("jpeg", "tga"),
    ("jpeg", "tif"),
    ("jp2", "bmp"),
    ("jp2", "gif"),
    ("jp2", "ico"),
    ("jp2", "jpeg"),
    ("jp2", "png"),
    ("jp2", "tga"),
    ("jp2", "tif"),
    ("js", "coffee"),
    ("json", "json5"),
    ("json", "yaml"),
    ("json5", "json"),
    ("ly", "midi"),
    ("ly", "pdf"),
    ("ly", "png"),
    ("ly", "ps"),
    ("ly", "svg"),
    ("midi", "wav"),
    ("mp3", "png"),
    ("png", "bmp"),
    ("png", "gif"),
    ("png", "ico"),
    ("png", "jp2"),
    ("png", "jpeg"),
    ("png", "tga"),
    ("png", "tif"),
    ("psd", "bmp"),
    ("psd", "gif"),
    ("psd", "ico"),
    ("psd", "jp2"),
    ("psd", "jpeg"),
    ("psd", "png"),
    ("psd", "tga"),
    ("psd", "tif"),
    ("scad", "csg"),
    ("scad", "off"),
    ("scad", "png"),
    ("scad", "stl"),
    ("stl", "gcode"),
    ("stl", "obj"),
    ("stl", "png"),
    ("styl", "css"),
    ("svg", "eps"),
    ("svg", "pdf"),
    ("svg", "png"),
    ("svg", "ps"),
    ("tif", "bmp"),
    ("tif", "gif"),
    ("tif", "ico"),
    ("tif", "jp2"),
    ("tif", "jpeg"),
    ("tif", "png"),
    ("tif", "tga"),
    ("tsv", "csv"),
    ("tsv", "html"),
    ("tsv", "json"),
    ("tsv", "json5"),
    ("tsv", "yaml"),
    ("vcf", "json"),
    ("vcf", "json5"),
    ("vcf", "yaml"),
    ("wav", "aac"),
    ("wav", "mp3"),
    ("yaml", "json"),
    ("yaml", "json5"),
    ("yaml", "vcf"),
  ];
  PAIRS.to_vec()
}