diff --git a/README.md b/README.md index 0452b4a..9cf46a0 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,21 @@ -## `ion-cli` +# `ion-cli` + This repository is home to the `ion` command line tool, which provides subcommands for working with [the Ion data format](https://amzn.github.io/ion-docs/docs/spec.html). ## Table of contents + * [Examples](#examples) - * [Converting between formats with `dump`](#converting-between-formats-with-dump) - * [Converting between Ion and other formats with `to` and `from`](#converting-between-ion-and-other-formats-with-to-and-from) - * [Analyzing binary Ion file encodings with `inspect`](#analyzing-binary-ion-file-encodings-with-inspect) + * [Viewing the contents of an Ion file](#viewing-the-contents-of-an-ion-file) + * [Converting between Ion formats](#converting-between-ion-formats) + * [Converting between Ion and other formats with `to` and `from`](#converting-between-ion-and-other-formats-with-to-and-from) + * [Analyzing binary Ion file encodings with `inspect`](#analyzing-binary-ion-file-encodings-with-inspect) * [Installation](#installation) - * [via `brew`](#via-brew) - * [via `cargo`](#via-cargo) + * [via `brew`](#via-brew) + * [via `cargo`](#via-cargo) * [Build Instructions](#build-instructions) - * [From source](#from-source) - * [Using Docker](#using-docker) + * [From source](#from-source) + * [Using Docker](#using-docker) ## Examples @@ -22,21 +25,40 @@ evaluate the file extension. Unless otherwise noted, these commands can accept any Ion format as input. -### Converting between formats with `dump` +### Viewing the contents of an Ion file + +The `ion cat` command reads the contents of the specified files (or `STDIN`) sequentially +and writes their content to `STDOUT` in the requested Ion format. + +```shell +ion cat my_file.ion +``` + +You can use the `--format`/`-f` flag to specify the desired format. The supported formats are: + +* `pretty` - Generously spaced, human-friendly text Ion. This is the default. +* `text` - Minimally spaced text Ion. +* `lines` - Text Ion that places each value on its own line. +* `binary`- Binary Ion + +### Converting between Ion formats Convert Ion text (or JSON) to Ion binary: + ```shell -ion dump --format binary my_file.ion +ion cat --format binary my_text_file.ion -o my_binary_file.ion ``` Convert Ion binary to generously-spaced, human-friendly text: + ```shell -ion dump --format pretty my_file.10n +ion cat --format pretty my_binary_file.ion -o my_text_file.ion ``` Convert Ion binary to minimally-spaced, compact text: + ```shell -ion dump --format text my_file.10n +ion cat --format text my_binary_file.ion -o my_text_file.ion ``` ### Converting between Ion and other formats with `to` and `from` @@ -45,18 +67,20 @@ The `beta to` and `beta from` commands can convert Ion to and from other formats Currently, JSON is supported. Convert Ion to JSON: + ```shell ion beta to json my_file.10n ``` Convert JSON to Ion: + ```shell ion beta from json my_file.json ``` ### Analyzing binary Ion file encodings with `inspect` -The `beta inspect` command can display the hex bytes of a binary Ion file alongside +The `inspect` command can display the hex bytes of a binary Ion file alongside the equivalent text Ion for easier analysis. ```shell @@ -64,99 +88,60 @@ the equivalent text Ion for easier analysis. echo '{foo: null, bar: true, baz: [1, 2, 3]}' > my_file.ion # Convert the text Ion to binary Ion -ion dump --format binary my_file.ion > my_file.10n +ion cat --format binary my_file.ion > my_file.10n # Show the binary encoding alongside its equivalent text -ion beta inspect my_file.10n - ---------------------------------------------------------------------------- - Offset | Length | Binary Ion | Text Ion ---------------------------------------------------------------------------- - | 4 | e0 01 00 ea | // Ion 1.0 Version Marker - 4 | 4 | ee 95 81 83 | '$ion_symbol_table':: // $3:: - 8 | 19 | de 91 | { - 10 | 1 | 86 | 'imports': // $6: - 11 | 2 | 71 03 | $ion_symbol_table, // $3 - 13 | 1 | 87 | 'symbols': // $7: - 14 | 13 | bc | [ - 15 | 4 | 83 66 6f 6f | "foo", - 19 | 4 | 83 62 61 72 | "bar", - 23 | 4 | 83 62 61 7a | "baz", - | | | ], - | | | } - 27 | 13 | dc | { - 28 | 1 | 8a | 'foo': // $10: - 29 | 1 | 0f | null, - 30 | 1 | 8b | 'bar': // $11: - 31 | 1 | 11 | true, - 32 | 1 | 8c | 'baz': // $12: - 33 | 7 | b6 | [ - 34 | 2 | 21 01 | 1, - 36 | 2 | 21 02 | 2, - 38 | 2 | 21 03 | 3, - | | | ], - | | | } +ion inspect my_file.10n ``` -To skip to a particular offset in the stream, you can use the `--skip-bytes` flag: +![example_inspect_output.png](images/example_inspect_output.png) -``` -ion beta inspect --skip-bytes 30 my_file.10n ---------------------------------------------------------------------------- - Offset | Length | Binary Ion | Text Ion ---------------------------------------------------------------------------- - | 4 | e0 01 00 ea | // Ion 1.0 Version Marker - | | ... | // Skipped 23 bytes of user-level data - 27 | 13 | dc | { - | | ... | // Skipped 2 bytes of user-level data - 30 | 1 | 8b | 'bar': // $11: - 31 | 1 | 11 | true, - 32 | 1 | 8c | 'baz': // $12: - 33 | 7 | b6 | [ - 34 | 2 | 21 01 | 1, - 36 | 2 | 21 02 | 2, - 38 | 2 | 21 03 | 3, - | | | ], - | | | } +---- +**The `--skip-bytes` flag** + +To skip to a particular offset in the stream, you can use the `--skip-bytes` flag. + +```shell +ion inspect --skip-bytes 30 my_file.10n ``` +![img.png](images/example_inspect_skip_bytes.png) + Notice that the text column adds comments indicating where data has been skipped. Also, if the requested index is nested inside one or more containers, the beginnings of those containers (along with their lengths and offsets) will still be included in the output. +----- +**The `--limit-bytes` flag** + You can limit the amount of data that `inspect` displays by using the `--limit-bytes` flag: ```shell -ion beta inspect --skip-bytes 30 --limit-bytes 2 my_file.10n ---------------------------------------------------------------------------- - Offset | Length | Binary Ion | Text Ion ---------------------------------------------------------------------------- - | 4 | e0 01 00 ea | // Ion 1.0 Version Marker - | | ... | // Skipped 23 bytes of user-level data - 27 | 13 | dc | { - | | ... | // Skipped 2 bytes of user-level data - 30 | 1 | 8b | 'bar': // $11: - 31 | 1 | 11 | true, - | | ... | // --limit-bytes reached, stepping out. - | | | } +ion inspect --skip-bytes 30 --limit-bytes 2 my_file.10n ``` +![img.png](images/example_inspect_limit_bytes.png) + ### Schema subcommands + All the subcommand to load or validate schema are under the `beta schema` subcommand. To load a schema: + ```bash ion beta schema load --directory --schema ``` To validate an ion value against a schema type: + ```bash ion beta schema validate --directory --schema --input --type ``` For more information on how to use the schema subcommands using CLI, run the following command: + ```bash ion beta schema help ``` @@ -196,21 +181,23 @@ ion help You should see output that resembles the following: ``` -ion 0.4.0 -The Ion Team - -USAGE: - ion - -FLAGS: - -h, --help Prints help information - -V, --version Prints version information - -SUBCOMMANDS: - beta The 'beta' command is a namespace for commands whose interfaces are - not yet stable. - dump Prints Ion in the requested format - help Prints this message or the help of the given subcommand(s) +A collection of tools for working with Ion data. + +Usage: ion [OPTIONS] + +Commands: + beta The 'beta' command is a namespace for commands whose interfaces are not yet stable. + cat Prints all Ion input files to the specified output in the requested format. + head Prints the specified number of top-level values in the input stream. + inspect Displays hex-encoded binary Ion alongside its equivalent text Ion. + Its output prioritizes human readability and is likely to change + between versions. Stable output for programmatic use cases is a + non-goal. + help Print this message or the help of the given subcommand(s) + +Options: + -h, --help Print help + -V, --version Print version ``` ## Build instructions @@ -270,8 +257,8 @@ SUBCOMMANDS: # print the help message docker run -it --rm ion-cli:0.1.1 ion -V - # mount current directory to /data volume and dump an ion file - docker run -it --rm -v $PWD:/data ion-cli:0.1.1 ion dump /data/test.ion + # mount current directory to /data volume and cat an ion file + docker run -it --rm -v $PWD:/data ion-cli:0.1.1 ion cat /data/test.ion ``` diff --git a/images/example_inspect_limit_bytes.png b/images/example_inspect_limit_bytes.png new file mode 100644 index 0000000..70174a8 Binary files /dev/null and b/images/example_inspect_limit_bytes.png differ diff --git a/images/example_inspect_output.png b/images/example_inspect_output.png new file mode 100644 index 0000000..4d100d8 Binary files /dev/null and b/images/example_inspect_output.png differ diff --git a/images/example_inspect_skip_bytes.png b/images/example_inspect_skip_bytes.png new file mode 100644 index 0000000..3b08874 Binary files /dev/null and b/images/example_inspect_skip_bytes.png differ diff --git a/src/bin/ion/commands/inspect.rs b/src/bin/ion/commands/inspect.rs index 85cdf9f..a984a73 100644 --- a/src/bin/ion/commands/inspect.rs +++ b/src/bin/ion/commands/inspect.rs @@ -212,11 +212,11 @@ impl<'a, 'b> IonInspector<'a, 'b> { loop { let item = reader.next_item()?; let is_last_item = matches!(item, SystemStreamItem::EndOfStream(_)); + item.raw_stream_item() + .map(|i| self.confirm_encoding_is_supported(i.encoding())) + .unwrap_or(Ok(()))?; if is_first_item { - // The first item in a stream cannot be ephemeral, so we can safely unwrap this. - let encoding = item.raw_stream_item().unwrap().encoding(); - self.confirm_encoding_is_supported(encoding)?; self.write_table_header()?; } @@ -227,7 +227,10 @@ impl<'a, 'b> IonInspector<'a, 'b> { "stream items", "ending", )? { - InspectorAction::Skip => continue, + InspectorAction::Skip => { + is_first_item = false; + continue; + } InspectorAction::Inspect => {} InspectorAction::LimitReached => break, } diff --git a/src/bin/ion/commands/mod.rs b/src/bin/ion/commands/mod.rs index b3700cc..caa0629 100644 --- a/src/bin/ion/commands/mod.rs +++ b/src/bin/ion/commands/mod.rs @@ -53,7 +53,7 @@ pub trait IonCliCommand { .version(crate_version!()) .author(crate_authors!()) .hide(self.hide_from_help_message()) - .with_compression_control(); + .with_decompression_control(); // If there are subcommands, add them to the configuration and set 'subcommand_required'. if !clap_subcommands.is_empty() { @@ -121,7 +121,7 @@ pub trait WithIonCliArgument { fn with_input(self) -> Self; fn with_output(self) -> Self; fn with_format(self) -> Self; - fn with_compression_control(self) -> Self; + fn with_decompression_control(self) -> Self; } impl WithIonCliArgument for ClapCommand { @@ -155,12 +155,18 @@ impl WithIonCliArgument for ClapCommand { ) } - fn with_compression_control(self) -> Self { + fn with_decompression_control(self) -> Self { + let accepts_input = self + .get_arguments() + .any(|flag| dbg!(dbg!(flag.get_id()) == "input")); self.arg( Arg::new("no-auto-decompress") .long("no-auto-decompress") + .default_value("false") .action(ArgAction::SetTrue) - .help("Turn off automatic decompression detection."), + .help("Turn off automatic decompression detection.") + // Do not show this flag in `help` for commands that don't take an `--input` flag. + .hide(!accepts_input), ) } } diff --git a/src/bin/ion/main.rs b/src/bin/ion/main.rs index 0fa3d52..fe33327 100644 --- a/src/bin/ion/main.rs +++ b/src/bin/ion/main.rs @@ -23,8 +23,8 @@ fn main() -> Result<()> { if let Err(e) = root_command.run(&mut command_path, &args) { match e.downcast_ref::() { - // If `ion-cli` is being invoked as part of a pipeline we want to allow the pipeline to - // to shut off without printing an error to stderr + // If `ion-cli` is being invoked as part of a pipeline we want to allow the pipeline + // to shut off without printing an error to STDERR. Some(IonError::Io(error)) if error.source().kind() == ErrorKind::BrokenPipe => { return Ok(()); }