diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 0b9c831..5179bb7 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -10,5 +10,11 @@ jobs: - uses: actions-rs/toolchain@v1 with: toolchain: stable - - run: cargo test - - run: cargo build --release --all-features + - name: Format + run: cargo fmt -- --check + - name: Lint + run: cargo clippy -- -D warnings + - name: Test + run: cargo test + - name: Build + run: cargo build --release --all-features diff --git a/Cargo.lock b/Cargo.lock index de62fd9..9f5dde7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -293,7 +293,7 @@ dependencies = [ [[package]] name = "tq" -version = "0.1.3" +version = "0.1.4" dependencies = [ "assert_cmd", "clap", diff --git a/Cargo.toml b/Cargo.toml index a2b5cd5..36225ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ authors = ["Brandon Slinkard "] edition = "2018" name = "tq" -version = "0.1.3" +version = "0.1.4" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..b196eaa --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +tab_spaces = 2 diff --git a/src/lib.rs b/src/lib.rs index 4cf9ec0..fc99032 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,29 +6,29 @@ extern crate error_chain; use clap::{App, Arg, ArgMatches}; pub mod errors { - error_chain! {} + error_chain! {} } pub mod load_toml; pub fn read_args() -> ArgMatches<'static> { - App::new(crate_name!()) - .version(crate_version!()) - .author(crate_authors!()) - .about(crate_description!()) - .arg( - Arg::with_name("file") - .short("f") - .long("file") - .value_name("TOML_FILE") - .help("TOML file to ingest") - .takes_value(true), - ) - .arg( - Arg::with_name("filter") - .help("Applies to the TOML input and produces filter results as TOML on standard output.") - .required(true) - .index(1), - ) - .get_matches() + App::new(crate_name!()) + .version(crate_version!()) + .author(crate_authors!()) + .about(crate_description!()) + .arg( + Arg::with_name("file") + .short("f") + .long("file") + .value_name("TOML_FILE") + .help("TOML file to ingest") + .takes_value(true), + ) + .arg( + Arg::with_name("filter") + .help("Applies to the TOML input and produces filter results as TOML on standard output.") + .required(true) + .index(1), + ) + .get_matches() } diff --git a/src/load_toml.rs b/src/load_toml.rs index d4fd39b..662650f 100644 --- a/src/load_toml.rs +++ b/src/load_toml.rs @@ -1,25 +1,24 @@ use std::fs::File; use std::io::{self, Read}; -use toml; use crate::errors::*; pub fn load_toml_from_file(name: &str) -> Result { - let mut file = File::open(name).chain_err(|| format!("Failed to open file: {:?}", &name))?; - let mut contents = String::new(); - let _ = file.read_to_string(&mut contents); + let mut file = File::open(name).chain_err(|| format!("Failed to open file: {:?}", &name))?; + let mut contents = String::new(); + let _ = file.read_to_string(&mut contents); - toml::from_str(&contents).chain_err(|| "File is not valid TOML.") + toml::from_str(&contents).chain_err(|| "File is not valid TOML.") } pub fn load_toml_from_stdin() -> Result { - let mut content = String::new(); - let _ = io::stdin().lock().read_to_string(&mut content); + let mut content = String::new(); + let _ = io::stdin().lock().read_to_string(&mut content); - toml::from_str(&content).chain_err(|| "File is not valid TOML.") + toml::from_str(&content).chain_err(|| "File is not valid TOML.") } #[test] fn load_toml_from_file_without_crash() { - let _foo = load_toml_from_file("../tests/fixtures/test_01.toml"); + let _foo = load_toml_from_file("../tests/fixtures/test_01.toml"); } diff --git a/src/main.rs b/src/main.rs index be440b9..794a4b7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,90 +1,88 @@ -use toml; - fn main() { - let matches = tq::read_args(); - - let toml_file: toml::Value = match matches.value_of("file") { - Some(file) => tq::load_toml::load_toml_from_file(file).unwrap(), - None => tq::load_toml::load_toml_from_stdin().unwrap(), - }; + let matches = tq::read_args(); - /*** - * One of the first things we'll want to do is deal with filters - * and how to pipe results from one filter to another. From the JQ manual: - * "Generally, things that would be done with loops and iteration in other languages are just done by gluing filters together in jq." - * The same can be said for tq. - * - * It's important to remember that every filter has an input and an output. - * Even literals like "hello" or 42 are filters - they take an input but - * always produce the same literal as output. Operations that combine two - * filters, like addition, generally feed the same input to both and combine - * the results. So, you can implement an averaging filter as add / length - - * feeding the input array both to the add filter and the length filter - * and then performing the division. - */ + let toml_file: toml::Value = match matches.value_of("file") { + Some(file) => tq::load_toml::load_toml_from_file(file).unwrap(), + None => tq::load_toml::load_toml_from_stdin().unwrap(), + }; - /*** - * Basic filters: - * - * Identity: . - * - The absolute simplest filter is: . - * This is a filter that takes its input and produces it unchanged as output. That is, this is the identity operator. - * - * Object Identifier-Index: .foo, .foo.bar - * - The simplest useful filter is: .foo - * When given TOML as an input, it gets the value in the table row "foo" - * - * Optional Object Identifier Index: .foo? - * - Just like .foo, but does not output even an error when . is not an array or an object. - * - * Generic Object Index: .[] - * - You can also look up fields of an object using syntax like .["foo"] - * (.foo above is a shorthand version of this, but only for identifier-like strings). - * - * Array Index: .[2] - * When the index value is an integer, .[] can index arrays. Arrays are zero-based, so .[2] returns the third element. - * - * Pipe: | - * Combines two filters by feeding the output(s) of the one on the left into - * the input of the one on the right. It's pretty much the same as the Unix shell's - * pipe, if you're used to that. If the one on the left produces multiple results, - * the one on the right will be run for each of those results. So, the expression - * .[] | .foo retrieves the "foo" field of each element of the input array. Note - * that .a.b.c is the same as .a | .b | .c. Note too that . is the input value at - * the particular stage in a "pipeline", specifically: where the . expression - * appears. Thus .a | . | .b is the same as .a.b, as the . in the middle refers - * to whatever value .a produced. - */ + /*** + * One of the first things we'll want to do is deal with filters + * and how to pipe results from one filter to another. From the JQ manual: + * "Generally, things that would be done with loops and iteration in other languages are just done by gluing filters together in jq." + * The same can be said for tq. + * + * It's important to remember that every filter has an input and an output. + * Even literals like "hello" or 42 are filters - they take an input but + * always produce the same literal as output. Operations that combine two + * filters, like addition, generally feed the same input to both and combine + * the results. So, you can implement an averaging filter as add / length - + * feeding the input array both to the add filter and the length filter + * and then performing the division. + */ - // Step 1, read the input string, determine execution order - // Step 2: access the toml_file to get strings, tables, etc - // Step 3: handle various piping scenarios - // Step 4: output - let full_filter_string = matches.value_of("filter").unwrap(); - let filter_pass = full_filter_string.split("|"); + /*** + * Basic filters: + * + * Identity: . + * - The absolute simplest filter is: . + * This is a filter that takes its input and produces it unchanged as output. That is, this is the identity operator. + * + * Object Identifier-Index: .foo, .foo.bar + * - The simplest useful filter is: .foo + * When given TOML as an input, it gets the value in the table row "foo" + * + * Optional Object Identifier Index: .foo? + * - Just like .foo, but does not output even an error when . is not an array or an object. + * + * Generic Object Index: .[] + * - You can also look up fields of an object using syntax like .["foo"] + * (.foo above is a shorthand version of this, but only for identifier-like strings). + * + * Array Index: .[2] + * When the index value is an integer, .[] can index arrays. Arrays are zero-based, so .[2] returns the third element. + * + * Pipe: | + * Combines two filters by feeding the output(s) of the one on the left into + * the input of the one on the right. It's pretty much the same as the Unix shell's + * pipe, if you're used to that. If the one on the left produces multiple results, + * the one on the right will be run for each of those results. So, the expression + * .[] | .foo retrieves the "foo" field of each element of the input array. Note + * that .a.b.c is the same as .a | .b | .c. Note too that . is the input value at + * the particular stage in a "pipeline", specifically: where the . expression + * appears. Thus .a | . | .b is the same as .a.b, as the . in the middle refers + * to whatever value .a produced. + */ - let mut value: toml::Value = toml_file.clone(); - for filter_str in filter_pass { - if filter_str.trim() == "." { - continue; - } + // Step 1, read the input string, determine execution order + // Step 2: access the toml_file to get strings, tables, etc + // Step 3: handle various piping scenarios + // Step 4: output + let full_filter_string = matches.value_of("filter").unwrap(); + let filter_pass = full_filter_string.split('|'); - let keys = filter_str.split("."); - let _count = filter_str.split(".").count(); + let mut value: toml::Value = toml_file; + for filter_str in filter_pass { + if filter_str.trim() == "." { + continue; + } - let mut val: toml::Value = value; - for key in keys { - let trimmed_key = key.trim(); - if trimmed_key == "" { - continue; - } + let keys = filter_str.split('.'); + let _count = filter_str.split('.').count(); - val = val.get(trimmed_key).unwrap().clone(); - } + let mut val: toml::Value = value; + for key in keys { + let trimmed_key = key.trim(); + if trimmed_key == "" { + continue; + } - value = val; + val = val.get(trimmed_key).unwrap().clone(); } - println!("{}", &value); - std::process::exit(0); + value = val; + } + + println!("{}", &value); + std::process::exit(0); }