use std::{
fs::File,
io::{BufReader, BufWriter, Read, Write},
path::PathBuf,
};
use clap::{Args, Parser};
use error_stack::{report, Report, ResultExt};
use crate::{conversion::ConversionDirection, Converter, Error};
type DynamicConverter = Converter<Box<dyn Read>, Box<dyn Write>>;
#[derive(Debug, Parser)]
#[command(author, version, about, long_about = None)]
pub struct Cli {
#[command(flatten)]
direction: ConversionDirectionArgs,
#[arg(short, long)]
input: Option<PathBuf>,
#[arg(short, long)]
output: Option<PathBuf>,
}
#[derive(Debug, Args)]
#[group(required = false, multiple = false)]
struct ConversionDirectionArgs {
#[arg(long = "m2j")]
msgpack2json: bool,
#[arg(long = "j2m")]
json2msgpack: bool,
}
impl TryFrom<ConversionDirectionArgs> for ConversionDirection {
type Error = Report<Error>;
fn try_from(direction: ConversionDirectionArgs) -> Result<Self, Self::Error> {
if direction.msgpack2json && direction.json2msgpack {
return Err(report!(Error::MultipleConversionDirections));
}
if direction.msgpack2json {
Ok(Self::MsgPack2Json)
} else if direction.json2msgpack {
Ok(Self::Json2MsgPack)
} else {
Ok(Self::Auto)
}
}
}
impl Cli {
pub fn into_converter(self) -> Result<DynamicConverter, Report<Error>> {
let mut direction = self.direction.try_into()?;
let input: Box<dyn Read> = if let Some(input_file) = self.input {
if direction == ConversionDirection::Auto && input_file.ends_with(".json") {
direction = ConversionDirection::Json2MsgPack;
}
let file = File::open(input_file).change_context(Error::FileRead)?;
Box::new(BufReader::new(file))
} else {
Box::new(std::io::stdin().lock())
};
let output: Box<dyn Write> = if let Some(output_file) = self.output {
let file = File::create(output_file).change_context(Error::FileWrite)?;
Box::new(BufWriter::new(file))
} else {
Box::new(std::io::stdout().lock())
};
Ok(Converter::new(input, output, direction))
}
}
#[cfg(test)]
mod tests {
use clap::CommandFactory;
use super::Cli;
#[test]
fn cli() {
Cli::command().debug_assert();
}
}