mirror of
https://mzte.de/git/LordMZTE/sercon.git
synced 2024-05-18 23:14:26 +02:00
130 lines
3.4 KiB
Rust
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(())
|
|
}
|