Skip to content

Commit

Permalink
Merge pull request #2 from amzn/ion-dump
Browse files Browse the repository at this point in the history
Added a `dump` command to convert between Ion formats.
  • Loading branch information
zslayton authored Jul 8, 2020
2 parents 6e4bb4d + 23355d8 commit 79aeb06
Show file tree
Hide file tree
Showing 9 changed files with 361 additions and 7 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "ion-c"]
path = ion-c
url = https://github.com/amzn/ion-c.git
126 changes: 126 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "ion-cli"
version = "0.1.0"
authors = ["The Ion Team <ion-team@amazon.com>"]
edition = "2018"
description = "Command line tool for working with the Ion data format."
repository = "https://github.com/amzn/ion-cli"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
clap = "~2.27.0"
libc = "0.2"

[build-dependencies]
cmake = "0.1.44"

[[bin]]
name = "ion"
test = false
bench = false

16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
## My Project
## `ion-cli`

TODO: Fill this README out!
_This package is considered experimental. It is under active/early development,
and the API is subject to change._

Be sure to:
## Developer notes

* Change the title in this README
* Edit your repository description on GitHub
Run the following command to initialize all of the necessary git submodules.
```
git submodule update --init --recursive
```

## Security

See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information.

## License

This project is licensed under the Apache-2.0 License.

This project is licensed under the Apache-2.0 License.
54 changes: 54 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use cmake::Config;
use std::fs::create_dir_all;
use std::path::Path;
use std::process;

fn main() {
let build_dir = Path::new("./ion-c/build/release");

// Create the ion-c build directory if necessary
if !build_dir.is_dir() {
println!("Creating build directory {}", build_dir.display());
if let Err(error) = create_dir_all(build_dir) {
eprintln!("Could not create build directory: {:?}", error);
process::exit(1);
}
}

// Configure and run CMake
Config::new("ion-c")
.define("CMAKE_BUILD_TYPE", "Release")
.out_dir("./ion-c/build/release")
.build();

// Output lines that start with "cargo:" are interpreted by Cargo. See the docs for details:
// https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script

// The `ion` executable statically links to the `ion-c` CLI. The following output tells Cargo
// which libraries to link against and in which directories they can be found.

// ion_events library
println!("cargo:rustc-link-search=native=./ion-c/build/release/build/tools/events");
println!("cargo:rustc-link-lib=static=ion_events_static");

// ion_c library
println!("cargo:rustc-link-search=native=./ion-c/build/release/build/ionc");
println!("cargo:rustc-link-lib=static=ionc_static");

// decNumber library
println!("cargo:rustc-link-search=native=./ion-c/build/release/build/decNumber");
println!("cargo:rustc-link-lib=static=decNumberStatic");

// C++ library
println!("cargo:rustc-link-search=native=/usr/lib");
println!("cargo:rustc-link-lib=c++");

// ion-c CLI library
println!("cargo:rustc-link-search=native=./ion-c/build/release/build/tools/cli/");
println!("cargo:rustc-link-lib=static=ion_cli_main");

// Only rebuild ion-c if that submodule directory is updated
println!("cargo:rereun-if-changed={}", build_dir.display());
// ...or if this build script is changed.
println!("cargo:rereun-if-changed=build.rs");
}
1 change: 1 addition & 0 deletions ion-c
Submodule ion-c added at 65561e
96 changes: 96 additions & 0 deletions src/bin/ion/commands/dump.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use clap::{App, Arg, ArgMatches};

use libc::c_char;
use libc::c_int;
use std::ffi::CString;
use std::ptr;

// ion_c_cli_main is a C function that lives in the ion-c CLI, to which ion-cli is
// statically linked.
extern "C" {
fn ion_c_cli_main(argc: c_int, argv: *const *const c_char);
}

fn run_ion_c_cli(args: &[&str]) {
// Convert the length-prefixed Rust str arguments to null-terminated C strings
let argv_as_c_str = args
.iter()
.map(|arg| CString::new(*arg).unwrap())
.collect::<Vec<CString>>();

// Convert the C strings to char * pointers. Note: it's important that we collect()
// the values below into a separate vector from the values above; it guarantees that
// the memory being pointed to will still be valid by the time the ion_c_cli accesses it.
let mut argv_as_char_star = argv_as_c_str
.iter()
.map(|arg| arg.as_ptr())
.collect::<Vec<*const c_char>>();

// The number of arguments as a C int
let argc = argv_as_char_star.len() as c_int;

// Programs sometimes rely on argv being null-terminated, so we'll push a null onto the array.
argv_as_char_star.push(ptr::null());

let argv = argv_as_char_star.as_ptr();

unsafe {
ion_c_cli_main(argc, argv);
}
}

pub fn app() -> App<'static, 'static> {
App::new("dump")
.about("Prints Ion in the requested format")
.arg(
Arg::with_name("format")
.long("format")
.short("f")
.takes_value(true)
.default_value("pretty")
.possible_values(&["binary", "text", "pretty"])
.help("Output format"),
)
.arg(
Arg::with_name("output")
.long("output")
.short("o")
.takes_value(true)
.help("Output file [default: STDOUT]"),
)
.arg(
// All argv entries after the program name (argv[0])
// and any `clap`-managed options are considered input files.
Arg::with_name("input")
.index(1)
.multiple(true)
.help("Input file [default: STDIN]"),
)
}

pub fn run(command_name: &str, matches: &ArgMatches<'static>) {
let mut args: Vec<&str> = vec![command_name, "process"];

// -f pretty|text|binary
if let Some(format) = matches.value_of("format") {
args.push("-f");
args.push(format);
}

// -o filename
if let Some(output_file) = matches.value_of("output") {
args.push("-o");
args.push(output_file);
}

// ...files
if let Some(input_file_iter) = matches.values_of("input") {
for input_file in input_file_iter {
args.push(input_file);
}
} else {
args.push("-"); // Signifies STDIN
}

run_ion_c_cli(&args);
}
17 changes: 17 additions & 0 deletions src/bin/ion/commands/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use clap::{App, ArgMatches};

pub mod dump;

// Creates a Vec of CLI configurations for all of the available built-in commands
pub fn built_in_commands() -> Vec<App<'static, 'static>> {
vec![dump::app()]
}

// Maps the given command name to the entry point for that command if it exists
pub fn runner_for_built_in_command(command_name: &str) -> Option<fn(&str, &ArgMatches<'static>)> {
let runner = match command_name {
"dump" => dump::run,
_ => return None,
};
Some(runner)
}
33 changes: 33 additions & 0 deletions src/bin/ion/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
mod commands;

use crate::commands::{built_in_commands, runner_for_built_in_command};
use clap::{crate_authors, crate_version, App, AppSettings};

const PROGRAM_NAME: &str = "ion";

fn main() {
let mut app = App::new(PROGRAM_NAME)
.version(crate_version!())
.author(crate_authors!())
.setting(AppSettings::SubcommandRequiredElseHelp)
.setting(AppSettings::TrailingVarArg);

for command in built_in_commands() {
app = app.subcommand(command);
}

let args = app.get_matches();
let (command_name, command_args) = args.subcommand();

if let Some(runner) = runner_for_built_in_command(command_name) {
// If a runner is registered for the given command name, command_args is guaranteed to
// be defined.
runner(command_name, command_args.unwrap());
} else {
let message = format!(
"The requested command ('{}') is not supported and clap did not generate an error message.",
command_name
);
unreachable!(message);
}
}

0 comments on commit 79aeb06

Please sign in to comment.