sercon/sercon-cli/src/main.rs
2022-02-22 21:27:36 +01:00

130 lines
3.4 KiB
Rust

use std::{
fs::{File, OpenOptions},
io::{self, Read, Write},
path::PathBuf,
};
use clap::Parser;
use miette::{miette, Context, IntoDiagnostic};
/// Convert between different data formats.
#[derive(Parser)]
struct Opt {
/// The input file to convert. stdin is used if omitted.
/// The type will be inferred if not explicitly specified.
#[clap(short, long)]
infile: Option<PathBuf>,
/// The output file to write the output to. stdout is used if omitted.
/// The type will be inferred if not explicitly specified.
#[clap(short, long)]
outfile: Option<PathBuf>,
/// The format used to parse the input.
#[clap(
short = 'f',
long,
required_unless_present_any = &["infile", "list-formats"],
)]
informat: Option<String>,
/// The format to convert the input into.
#[clap(
short = 't',
long,
required_unless_present_any = &["outfile", "list-formats"],
)]
outformat: Option<String>,
/// List all supported formats and exit.
#[clap(long, exclusive = true)]
list_formats: bool,
}
fn main() -> miette::Result<()> {
let opt = Opt::parse();
if opt.list_formats {
println!(
"{}",
sercon_backends::all()
.into_iter()
.flat_map(|b| b.supported_filetypes())
.collect::<Vec<_>>()
.join("\n")
);
return Ok(());
}
let informat = opt
.informat
.or_else(|| {
opt.infile
.as_ref()
.and_then(|p| p.extension().map(|s| s.to_string_lossy().to_string()))
})
.ok_or_else(|| miette!("Couldn't infer input format. Please specify explicitly."))?;
let outformat = opt
.outformat
.or_else(|| {
opt.outfile
.as_ref()
.and_then(|p| p.extension().map(|s| s.to_string_lossy().to_string()))
})
.ok_or_else(|| miette!("Couldn't infer output format. Please specify explicitly."))?;
let backends = sercon_backends::all();
let in_backend = backends
.iter()
.find(|b| b.supported_filetypes().contains(&informat))
.ok_or_else(|| miette!("No parser found for input format '{}'!", &informat))?;
let out_backend = backends
.iter()
.find(|b| b.supported_filetypes().contains(&outformat))
.ok_or_else(|| miette!("No parser found for output format '{}'!", &outformat))?;
let mut input: Box<dyn Read> = if let Some(p) = opt.infile {
Box::new(
File::open(p)
.into_diagnostic()
.wrap_err("Failed to open infile")?,
)
} else {
Box::new(io::stdin())
};
let mut using_stdout = false;
let mut output: Box<dyn Write> = if let Some(p) = opt.outfile {
Box::new(
OpenOptions::new()
.write(true)
.create(true)
.open(p)
.into_diagnostic()
.wrap_err("Failed to open outfile")?,
)
} else {
using_stdout = true;
Box::new(io::stdout())
};
sercon_base::transcode(
&mut input,
&mut output,
out_backend.as_backend(),
in_backend.as_backend(),
)
.wrap_err("error during transcoding")?;
if using_stdout {
println!();
io::stdout().flush().into_diagnostic()?;
}
Ok(())
}