pub mod cli;
pub mod conversion_map;
pub mod converters;
pub mod pipeline;
use std::path::{Path, PathBuf};
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Input file {0} does not exist")]
InputMissing(PathBuf),
#[error("Output file {0} already exists")]
OutputExists(PathBuf),
#[error("Input file {0} has no extension")]
InputNoExtension(PathBuf),
#[error("Output file {0} has no extension")]
OutputNoExtension(PathBuf),
#[error("Conversion from {from} to {to} is not yet supported.")]
Unsupported { from: String, to: String },
#[error("Conversion from {from} to {to} is not yet implemented.")]
NotImplemented { from: String, to: String },
#[error(
"Conversion from {from} to {to} requires `{tool}`, which was not found on PATH."
)]
MissingTool {
from: String,
to: String,
tool: &'static str,
},
#[error("External tool `{tool}` failed: {message}")]
ToolFailed { tool: &'static str, message: String },
#[error(transparent)]
Io(#[from] std::io::Error),
#[error(transparent)]
Csv(#[from] csv::Error),
#[error("{0}")]
Other(String),
}
impl Error {
pub fn other(msg: impl Into<String>) -> Self {
Error::Other(msg.into())
}
}
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Clone, Default)]
pub struct Options {
pub force: bool,
pub split: bool,
}
pub struct Job<'a> {
pub input: &'a Path,
pub output: &'a Path,
pub options: &'a Options,
}
pub fn normalize_extension(ext: &str) -> String {
let lower = ext.to_ascii_lowercase();
match lower.as_str() {
"jpg" => "jpeg".to_string(),
_ => lower,
}
}
pub fn extension_of(path: &Path) -> Option<String> {
path
.extension()
.and_then(|e| e.to_str())
.map(normalize_extension)
}
pub fn convert(input: &Path, output: &Path, options: &Options) -> Result<()> {
if !input.exists() {
return Err(Error::InputMissing(input.to_path_buf()));
}
if output.exists() && !options.force {
return Err(Error::OutputExists(output.to_path_buf()));
}
let from = extension_of(input)
.ok_or_else(|| Error::InputNoExtension(input.to_path_buf()))?;
let to = extension_of(output)
.ok_or_else(|| Error::OutputNoExtension(output.to_path_buf()))?;
let entry = conversion_map::lookup(&from, &to).ok_or(Error::Unsupported {
from: from.clone(),
to: to.clone(),
})?;
let job = Job {
input,
output,
options,
};
pipeline::run(&from, &to, entry, &job)
}