From 4c026d70478d811738bec46bd836e10ad58ecfde Mon Sep 17 00:00:00 2001 From: Almann Goo Date: Tue, 8 Jun 2021 13:08:56 -0700 Subject: [PATCH] Adds `pest2ion` command Adds a basic CLI driver over the `pest-ion` crate. This driver supports setting the input file (defaulting to STDIN), an output file (defaulting to STDOUT), and flags around the Ion data format. An explicit TODO is to figure out a good way to test this probably using integration tests and spawning the command. (See #42) Resolves #36. --- pest-ion/Cargo.toml | 9 ++++ pest-ion/src/bin/pest2ion/main.rs | 82 +++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 pest-ion/src/bin/pest2ion/main.rs diff --git a/pest-ion/Cargo.toml b/pest-ion/Cargo.toml index e6cb585b..66bc4b8d 100644 --- a/pest-ion/Cargo.toml +++ b/pest-ion/Cargo.toml @@ -25,5 +25,14 @@ pest = "~2.1.3" pest_meta = "~2.1.3" ion-rs = "~0.6.0" +# for pest2ion binary--not sure if this can be modeled separately +clap = "~2.33.3" +anyhow = "~1.0.40" + [dev-dependencies] rstest = "~0.10.0" + +[[bin]] +name = "pest2ion" +test = false +bench = false \ No newline at end of file diff --git a/pest-ion/src/bin/pest2ion/main.rs b/pest-ion/src/bin/pest2ion/main.rs new file mode 100644 index 00000000..4e4bcd38 --- /dev/null +++ b/pest-ion/src/bin/pest2ion/main.rs @@ -0,0 +1,82 @@ +// Copyright Amazon.com, Inc. or its affiliates. + +use anyhow::Result; +use clap::{crate_authors, crate_description, crate_version, App, Arg, ArgGroup}; +use ion_rs::value::writer::{ElementWriter, Format, TextKind}; +use pest_ion::{from_read, TryPestToElement}; +use std::fs::File; +use std::io::{stdin, stdout, Write}; +use std::path::Path; + +fn main() -> Result<()> { + let matches = App::new("Pest to Ion Converter") + .version(crate_version!()) + .author(crate_authors!()) + .about(crate_description!()) + .arg( + Arg::with_name("FILE") + .help("The input file to parse (defaults to STDIN)") + .index(1), + ) + .arg( + Arg::with_name("OUTPUT_FILE") + .long("output") + .short("o") + .takes_value(true) + .help("Writes output to the given file (defaults to STDOUT)"), + ) + .arg( + Arg::with_name("text") + .long("text") + .short("t") + .help("Generate Ion text (default)"), + ) + .arg( + Arg::with_name("binary") + .long("binary") + .short("b") + .help("Generate Ion binary"), + ) + .arg( + Arg::with_name("pretty") + .long("pretty") + .short("p") + .help("Generate Ion pretty printed text"), + ) + .group(ArgGroup::with_name("format").args(&["text", "binary", "pretty"])) + .get_matches(); + + let elem = if let Some(file_name) = matches.value_of("FILE") { + Path::new(file_name).try_pest_to_element()? + } else { + // no file argument means read from stdin + from_read(stdin()).try_pest_to_element()? + }; + + // currently Ion element requires a fixed buffer to serialize to, let's choose something + // relatively big until this limitation is lifted + const BUFFER_SIZE: usize = 32 * 1024 * 1024; + let mut out_buf = vec![0u8; BUFFER_SIZE]; + + let format = if matches.is_present("binary") { + Format::Binary + } else if matches.is_present("pretty") { + Format::Text(TextKind::Pretty) + } else { + Format::Text(TextKind::Compact) + }; + + let mut writer = format.element_writer_for_slice(&mut out_buf)?; + writer.write(&elem)?; + let out_slice = writer.finish()?; + + // TODO make output (file) configurable + let mut out: Box = if let Some(out_file_name) = matches.value_of("OUTPUT_FILE") { + Box::new(File::create(out_file_name)?) + } else { + Box::new(stdout()) + }; + out.write_all(&out_slice)?; + + Ok(()) +}