use failure::{Error, ResultExt};
use std::{
fs::File,
io::{stdin, stdout, Read, Write},
path::PathBuf,
};
use structopt::{clap::AppSettings::ColoredHelp, StructOpt};
#[derive(Debug, StructOpt)]
#[structopt(raw(global_settings = "&[ColoredHelp]"))]
struct MyArgs {
#[structopt(long = "compact", short = "c")]
compact: bool,
#[structopt(long = "yaml", short = "y")]
yaml: bool,
#[structopt(long = "json", short = "j")]
json: bool,
#[structopt(long = "output", parse(from_os_str), short = "o")]
output: Option<PathBuf>,
#[structopt(parse(from_os_str))]
input: Option<PathBuf>,
}
fn from_json(input: Box<Read>, mut output: Box<Write>, args: &MyArgs) -> Result<(), Error> {
let data: serde_json::Value =
serde_json::from_reader(input).context("Failed to parse input JSON file")?;
if args.yaml {
serde_yaml::to_writer(output.as_mut(), &data).context("Failed to format output as YAML")?;
let _result = output.write(b"\n");
} else if args.compact {
serde_json::to_writer(output, &data).context("Failed to format output as JSON")?;
} else {
serde_json::to_writer_pretty(output.as_mut(), &data)
.context("Failed to format output as JSON")?;
let _result = output.write(b"\n");
};
Ok(())
}
fn from_yaml(input: Box<Read>, mut output: Box<Write>, args: &MyArgs) -> Result<(), Error> {
let data: serde_yaml::Value =
serde_yaml::from_reader(input).context("Failed to parse input YAML file")?;
if args.yaml {
serde_yaml::to_writer(output.as_mut(), &data).context("Failed to format output as YAML")?;
let _result = output.write(b"\n");
} else if args.compact {
serde_json::to_writer(output, &data).context("Failed to format output as JSON")?;
} else {
serde_json::to_writer_pretty(output.as_mut(), &data)
.context("Failed to format output as JSON")?;
let _result = output.write(b"\n");
};
Ok(())
}
fn main() -> Result<(), Error> {
let args = MyArgs::from_args();
let input: Box<Read> = match &args.input {
Some(filename) => Box::new(
File::open(&filename).context(format!("Failed to open input file {:?}", filename))?,
),
None => Box::new(stdin()),
};
let output: Box<Write> = match &args.output {
Some(filename) => Box::new(
File::create(&filename)
.context(format!("Failed to create output file {:?}", filename))?,
),
None => Box::new(stdout()),
};
if args.json {
from_json(input, output, &args)?;
} else {
from_yaml(input, output, &args)?;
}
Ok(())
}