From 462de6b7edb97a137fe31ae067cdfae9115f688a Mon Sep 17 00:00:00 2001 From: cetra3 Date: Tue, 20 Jan 2026 09:54:46 +1030 Subject: [PATCH] Convert to workspace --- .github/workflows/ci.yml | 63 +++++--------- .gitignore | 4 +- .travis.yml | 47 ----------- CHANGELOG.md | 5 ++ Cargo.toml | 13 +++ jmespath-cli/Cargo.lock | 171 -------------------------------------- jmespath-cli/Cargo.toml | 17 ++-- jmespath-cli/src/lib.rs | 123 +++++++++++++++++++++++++++ jmespath-cli/src/main.rs | 137 +++--------------------------- jmespath-cli/tests/cli.rs | 155 +++++++++++++++++----------------- jmespath/Cargo.toml | 8 +- jmespath/src/parser.rs | 6 +- jmespath/src/variable.rs | 16 ++-- 13 files changed, 272 insertions(+), 493 deletions(-) delete mode 100644 .travis.yml create mode 100644 Cargo.toml delete mode 100644 jmespath-cli/Cargo.lock create mode 100644 jmespath-cli/src/lib.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3b2ddb9e..f7bc880d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,9 +6,6 @@ on: pull_request: branches: [master] -env: - CARGO_TERM_COLOR: always - jobs: test: name: Test (${{ matrix.rust }}) @@ -19,88 +16,72 @@ jobs: rust: [stable, beta, nightly] steps: - uses: actions/checkout@v4 - + - name: Install Rust ${{ matrix.rust }} uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - + - name: Cache dependencies uses: Swatinem/rust-cache@v2 - with: - workspaces: | - jmespath - jmespath-cli - - - name: Build jmespath - run: cargo build --manifest-path jmespath/Cargo.toml - + + - name: Check jmespath + run: cargo check + - name: Test jmespath - run: cargo test --manifest-path jmespath/Cargo.toml - + run: cargo test + - name: Test jmespath with specialized feature (nightly only) if: matrix.rust == 'nightly' - run: cargo test --manifest-path jmespath/Cargo.toml --features specialized - - - name: Build jmespath-cli - run: cargo build --manifest-path jmespath-cli/Cargo.toml - - - name: Test jmespath-cli - run: cargo test --manifest-path jmespath-cli/Cargo.toml + run: cargo +nightly test --features specialized clippy: name: Clippy runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - + - name: Install Rust stable uses: dtolnay/rust-toolchain@stable with: components: clippy - + - name: Cache dependencies uses: Swatinem/rust-cache@v2 - with: - workspaces: | - jmespath - jmespath-cli - + - name: Run clippy on jmespath - run: cargo clippy --manifest-path jmespath/Cargo.toml -- -D warnings - + run: cargo clippy -- -D warnings + - name: Run clippy on jmespath-cli - run: cargo clippy --manifest-path jmespath-cli/Cargo.toml -- -D warnings + run: cargo clippy -- -D warnings fmt: name: Rustfmt runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - + - name: Install Rust stable uses: dtolnay/rust-toolchain@stable with: components: rustfmt - + - name: Check formatting - run: | - cargo fmt --manifest-path jmespath/Cargo.toml -- --check - cargo fmt --manifest-path jmespath-cli/Cargo.toml -- --check + run: cargo fmt -- --check bench: name: Benchmarks runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - + - name: Install Rust nightly uses: dtolnay/rust-toolchain@nightly - + - name: Cache dependencies uses: Swatinem/rust-cache@v2 with: workspaces: jmespath - + - name: Run benchmarks - run: cargo bench --manifest-path jmespath/Cargo.toml --no-run + run: cargo bench diff --git a/.gitignore b/.gitignore index 328f8ec2..8ce036bb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ **/target -jmespath/Cargo.lock -.idea/** \ No newline at end of file +Cargo.lock +.idea/** diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 9402973d..00000000 --- a/.travis.yml +++ /dev/null @@ -1,47 +0,0 @@ -language: rust - -sudo: false - -# run builds for all the trains (and more) -rust: - - stable - - beta - - nightly - -matrix: - allow_failure: - - rust: nightly - -# load travis-cargo -before_script: - - | - export TRAVIS_CARGO_NIGHTLY_FEATURE="" && - pip install 'travis-cargo<0.2' --user && - export PATH=$HOME/.local/bin:$PATH - -# the main build -script: - - | - cd jmespath && - travis-cargo build && - travis-cargo test && - travis-cargo --only nightly test -- --features specialized && - travis-cargo bench - -after_success: - # measure code coverage and upload to coveralls.io - - travis-cargo coveralls --no-sudo --verify - -# necessary for pushing github docs -env: - global: - - secure: "sK8Vz18E5bqkpWc4yRRmZGWOf8Z5Lp8yKFEq8z9nXPY6XTCOmR5Pf2Zh82wFWwxBYILxfmoONaEDNWE0CHv0grbTX3Cv613wd9mgTi/MyV/O1wC2E52S8JE+JbiHcImqFjhr0ycafvLL+hSSY0/h8ZCkektzu/1Ut2ahPmT+KZI=" - -# necessary for `travis-cargo coveralls --no-sudo` -addons: - apt: - packages: - - libcurl4-openssl-dev - - libelf-dev - - libdw-dev - - binutils-dev # optional: only required for the --verify flag of coveralls diff --git a/CHANGELOG.md b/CHANGELOG.md index 79c8e6f9..11bce59b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # CHANGELOG +## 0.5.0 - 2026-01-20 + +* Moved to a Cargo workspace +* Added `interpret` as a public function: https://github.com/jmespath/jmespath.rs/pull/60 + ## 0.4.0 - 2025-07-10 * Upgrade to Rust 2024 edition diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..588b5248 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,13 @@ +[workspace] +members = [ + "jmespath", + "jmespath-cli", +] +resolver = "2" + +[workspace.package] +edition = "2024" + +[workspace.dependencies] +serde = { version = "1", features = ["rc"] } +serde_json = "1" diff --git a/jmespath-cli/Cargo.lock b/jmespath-cli/Cargo.lock deleted file mode 100644 index 3bce0104..00000000 --- a/jmespath-cli/Cargo.lock +++ /dev/null @@ -1,171 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "clap" -version = "2.34.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" -dependencies = [ - "ansi_term", - "atty", - "bitflags", - "strsim", - "textwrap", - "unicode-width", - "vec_map", -] - -[[package]] -name = "deunicode" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "850878694b7933ca4c9569d30a34b55031b9b139ee1fc7b94a527c4ef960d690" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "itoa" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" - -[[package]] -name = "jmespath" -version = "0.4.0" -dependencies = [ - "serde", - "serde_json", - "slug", -] - -[[package]] -name = "jmespath-cli" -version = "0.4.0" -dependencies = [ - "clap", - "jmespath", - "serde", - "serde_json", -] - -[[package]] -name = "libc" -version = "0.2.155" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" - -[[package]] -name = "ryu" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" - -[[package]] -name = "serde" -version = "1.0.152" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" - -[[package]] -name = "serde_json" -version = "1.0.93" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cad406b69c91885b5107daf2c29572f6c8cdb3c66826821e286c533490c0bc76" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "slug" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3bc762e6a4b6c6fcaade73e77f9ebc6991b676f88bb2358bddb56560f073373" -dependencies = [ - "deunicode", -] - -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/jmespath-cli/Cargo.toml b/jmespath-cli/Cargo.toml index 6ead3d43..1522dcc7 100644 --- a/jmespath-cli/Cargo.toml +++ b/jmespath-cli/Cargo.toml @@ -1,19 +1,20 @@ [package] name = "jmespath-cli" -version = "0.4.0" +version = "0.5.0" authors = ["Michael Dowling "] license = "MIT" edition = "2024" +[lib] +name = "jmespath_cli" +path = "src/lib.rs" + [[bin]] name = "jp" path = "src/main.rs" [dependencies] -serde = "1" -serde_json = "1" -clap = "2.33" - -[dependencies.jmespath] -path = "../jmespath" -version = "*" +serde = { workspace = true } +serde_json = { workspace = true } +jmespath = { path = "../jmespath", version = "0.5.0" } +clap = { version = "4", features = ["derive"] } diff --git a/jmespath-cli/src/lib.rs b/jmespath-cli/src/lib.rs new file mode 100644 index 00000000..52b5eb33 --- /dev/null +++ b/jmespath-cli/src/lib.rs @@ -0,0 +1,123 @@ +use std::fs::File; +use std::io; +use std::io::prelude::*; +use std::rc::Rc; + +use clap::Parser; +use jmespath::{Rcvar, Variable, compile}; + +#[derive(Parser, Debug, Default)] +#[command(name = "jp", version, about = "JMESPath command line interface")] +pub struct JpArgs { + /// Read input JSON from a file instead of stdin. + #[arg(short = 'f', long = "filename")] + pub filename: Option, + + /// If the final result is a string, it will be printed without quotes. + #[arg(short = 'u', long = "unquoted")] + pub unquoted: bool, + + /// Only print the AST of the parsed expression. Do not rely on this output, only useful for debugging purposes. + #[arg(long = "ast")] + pub ast: bool, + + /// Read JMESPath expression from the specified file. + #[arg(short = 'e', long = "expr-file", conflicts_with = "expression")] + pub expr_file: Option, + + /// JMESPath expression to evaluate + #[arg(conflicts_with = "expr_file", required_unless_present = "expr_file")] + pub expression: Option, +} + +#[derive(Debug)] +pub struct JpOutput { + pub stdout: String, + pub stderr: String, +} + +pub fn run(args: JpArgs) -> Result { + run_with_stdin(args, None) +} + +pub fn run_with_stdin(args: JpArgs, stdin_data: Option<&str>) -> Result { + let make_error = |msg: String| JpOutput { + stdout: String::new(), + stderr: format!("{}\n", msg), + }; + + let expr_str = if let Some(ref expr_file) = args.expr_file { + match read_file("expression", expr_file) { + Ok(content) => content, + Err(e) => return Err(make_error(e)), + } + } else { + args.expression.clone().unwrap() + }; + + let expr = match compile(&expr_str) { + Ok(e) => e, + Err(e) => return Err(make_error(e.to_string())), + }; + + if args.ast { + return Ok(JpOutput { + stdout: format!("{:#?}\n", expr.as_ast()), + stderr: String::new(), + }); + } + + let json = match get_json(args.filename.as_deref(), stdin_data) { + Ok(j) => Rc::new(j), + Err(e) => return Err(make_error(e)), + }; + + match expr.search(&json) { + Err(e) => Err(make_error(e.to_string())), + Ok(result) => Ok(JpOutput { + stdout: format_result(result, args.unquoted), + stderr: String::new(), + }), + } +} + +fn format_result(result: Rcvar, unquoted: bool) -> String { + if unquoted && result.is_string() { + format!("{}\n", result.as_string().unwrap()) + } else { + let mut buf = Vec::new(); + serde_json::to_writer_pretty(&mut buf, &result).unwrap(); + buf.push(b'\n'); + String::from_utf8(buf).unwrap() + } +} + +fn read_file(label: &str, filename: &str) -> Result { + match File::open(filename) { + Err(e) => Err(format!( + "Error opening {} file at {}: {}", + label, filename, e + )), + Ok(mut file) => { + let mut buffer = String::new(); + file.read_to_string(&mut buffer) + .map_err(|e| format!("Error reading {} from {}: {}", label, filename, e))?; + Ok(buffer) + } + } +} + +fn get_json(filename: Option<&str>, stdin_data: Option<&str>) -> Result { + let buffer = match (filename, stdin_data) { + (Some(f), _) => read_file("JSON", f)?, + (None, Some(data)) => data.to_string(), + (None, None) => { + let mut buffer = String::new(); + io::stdin() + .read_to_string(&mut buffer) + .map_err(|e| format!("Error reading JSON from stdin: {}", e))?; + buffer + } + }; + Variable::from_json(&buffer).map_err(|e| format!("Error parsing JSON: {}", e)) +} diff --git a/jmespath-cli/src/main.rs b/jmespath-cli/src/main.rs index fc76d7e0..9e0398ff 100644 --- a/jmespath-cli/src/main.rs +++ b/jmespath-cli/src/main.rs @@ -1,132 +1,15 @@ -use std::fs::File; -use std::io; -use std::io::prelude::*; -use std::process::exit; -use std::rc::Rc; - -use clap::{App, Arg}; -use jmespath::Rcvar; -use jmespath::{Variable, compile}; - -macro_rules! die( - ($msg:expr) => ( - match writeln!(&mut ::std::io::stderr(), "{}", $msg) { - Ok(_) => exit(1), - Err(x) => panic!("Unable to write to stderr: {}", x), - } - ) -); +use clap::Parser; +use jmespath_cli::{JpArgs, run}; fn main() { - let matches = App::new("jp") - .version(env!("CARGO_PKG_VERSION")) - .about("JMESPath command line interface") - .arg( - Arg::with_name("filename") - .help("Read input JSON from a file instead of stdin.") - .short("f") - .takes_value(true) - .long("filename"), - ) - .arg( - Arg::with_name("unquoted") - .help("If the final result is a string, it will be printed without quotes.") - .short("u") - .long("unquoted") - .multiple(false), - ) - .arg( - Arg::with_name("ast") - .help( - "Only print the AST of the parsed expression. Do not rely on this output, \ - only useful for debugging purposes.", - ) - .long("ast") - .multiple(false), - ) - .arg( - Arg::with_name("expr-file") - .help("Read JMESPath expression from the specified file.") - .short("e") - .takes_value(true) - .long("expr-file") - .conflicts_with("expression") - .required(true), - ) - .arg( - Arg::with_name("expression") - .help("JMESPath expression to evaluate") - .index(1) - .conflicts_with("expr-file") - .required(true), - ) - .get_matches(); - - let file_expression = matches - .value_of("expr-file") - .map(|f| read_file("expression", f)); - - let expr = if let Some(ref e) = file_expression { - compile(e) - } else { - compile(matches.value_of("expression").unwrap()) - } - .map_err(|e| die!(e.to_string())) - .unwrap(); - - if matches.is_present("ast") { - println!("{:#?}", expr.as_ast()); - exit(0); - } - - let json = Rc::new(get_json(matches.value_of("filename"))); - - match expr.search(&json) { - Err(e) => die!(e.to_string()), - Ok(result) => show_result(result, matches.is_present("unquoted")), - } -} - -fn show_result(result: Rcvar, unquoted: bool) { - if unquoted && result.is_string() { - println!("{}", result.as_string().unwrap()); - } else { - let mut out = io::stdout(); - serde_json::to_writer_pretty(&mut out, &result) - .map(|_| out.write(b"\n")) - .map_err(|e| die!(format!("Error converting result to string: {}", e))) - .ok(); - } -} - -fn read_file(label: &str, filename: &str) -> String { - match File::open(filename) { - Err(e) => die!(format!( - "Error opening {} file at {}: {}", - label, filename, e - )), - Ok(mut file) => { - let mut buffer = String::new(); - file.read_to_string(&mut buffer) - .map_err(|e| die!(format!("Error reading {} from {}: {}", label, filename, e))) - .map(|_| buffer) - .unwrap() + let args = JpArgs::parse(); + match run(args) { + Ok(output) => { + print!("{}", output.stdout); } - } -} - -fn get_json(filename: Option<&str>) -> Variable { - let buffer = match filename { - Some(f) => read_file("JSON", f), - None => { - let mut buffer = String::new(); - match io::stdin().read_to_string(&mut buffer) { - Ok(_) => buffer, - Err(e) => die!(format!("Error reading JSON from stdin: {}", e)), - } + Err(output) => { + eprint!("{}", output.stderr); + std::process::exit(1); } - }; - Variable::from_json(&buffer) - .map_err(|e| die!(format!("Error parsing JSON: {}", e))) - .unwrap() + } } diff --git a/jmespath-cli/tests/cli.rs b/jmespath-cli/tests/cli.rs index b69345a3..7e4dc995 100644 --- a/jmespath-cli/tests/cli.rs +++ b/jmespath-cli/tests/cli.rs @@ -1,29 +1,38 @@ -use std::process::{Command, Stdio}; +use jmespath_cli::{JpArgs, run, run_with_stdin}; -const JPBIN: &str = "target/debug/jp"; - -fn get_output(args: Vec<&str>) -> Result { - let mut cmd = Command::new(JPBIN); - for arg in args { - cmd.arg(arg); +fn run_jp(args: JpArgs) -> Result { + match run(args) { + Ok(output) => Ok(output.stdout), + Err(output) => Err(output.stderr), } - let output = cmd.output().unwrap(); - if output.status.success() { - Ok(String::from_utf8(output.stdout).unwrap()) - } else { - Err(String::from_utf8(output.stderr).unwrap()) +} + +fn run_jp_with_stdin(args: JpArgs, stdin: &str) -> Result { + match run_with_stdin(args, Some(stdin)) { + Ok(output) => Ok(output.stdout), + Err(output) => Err(output.stderr), } } #[test] fn prints_ast() { - let output = get_output(vec!["--ast", "foo"]).unwrap(); + let args = JpArgs { + ast: true, + expression: Some("foo".to_string()), + ..Default::default() + }; + let output = run_jp(args).unwrap(); assert_eq!("Field {\n offset: 0,\n name: \"foo\",\n}\n", output); } #[test] -fn shows_parse_error_information_with_non_zero_rc() { - let output = get_output(vec!["--ast", "foo{"]).unwrap_err(); +fn shows_parse_error_information() { + let args = JpArgs { + ast: true, + expression: Some("foo{".to_string()), + ..Default::default() + }; + let output = run_jp(args).unwrap_err(); assert_eq!( "Parse error: Unexpected led token -- found Lbrace (line 0, column 3)\nfoo{\ \n ^\n\n", @@ -31,52 +40,49 @@ fn shows_parse_error_information_with_non_zero_rc() { ); } -#[test] -fn shows_help_info() { - let output = get_output(vec!["--help"]).unwrap(); - assert!(output.contains("JMESPath command line interface")); -} - #[test] fn executes_query_against_files() { - let output = get_output(vec![ - "-e", - "tests/fixtures/valid-expression", - "-f", - "tests/fixtures/valid-json", - ]) - .unwrap(); + let args = JpArgs { + expr_file: Some("tests/fixtures/valid-expression".to_string()), + filename: Some("tests/fixtures/valid-json".to_string()), + ..Default::default() + }; + let output = run_jp(args).unwrap(); assert_eq!("\"bar\"\n", output); } #[test] fn allows_unquoted_strings() { - let output = get_output(vec![ - "-e", - "tests/fixtures/valid-expression", - "-f", - "tests/fixtures/valid-json", - "-u", - ]) - .unwrap(); + let args = JpArgs { + expr_file: Some("tests/fixtures/valid-expression".to_string()), + filename: Some("tests/fixtures/valid-json".to_string()), + unquoted: true, + ..Default::default() + }; + let output = run_jp(args).unwrap(); assert_eq!("bar\n", output); } #[test] fn unquoted_does_nothing_for_non_strings() { - let output = get_output(vec!["-f", "tests/fixtures/valid-json", "-u", "`[\"foo\"]`"]).unwrap(); + let args = JpArgs { + filename: Some("tests/fixtures/valid-json".to_string()), + unquoted: true, + expression: Some("`[\"foo\"]`".to_string()), + ..Default::default() + }; + let output = run_jp(args).unwrap(); assert_eq!("[\n \"foo\"\n]\n", output); } #[test] fn validates_json_file_exists() { - let output = get_output(vec![ - "-e", - "tests/fixtures/valid-expression", - "-f", - "tests/fixtures/not-there", - ]) - .unwrap_err(); + let args = JpArgs { + expr_file: Some("tests/fixtures/valid-expression".to_string()), + filename: Some("tests/fixtures/not-there".to_string()), + ..Default::default() + }; + let output = run_jp(args).unwrap_err(); assert_eq!( "Error opening JSON file at tests/fixtures/not-there: \ No such file or directory (os error 2)\n", @@ -86,13 +92,12 @@ fn validates_json_file_exists() { #[test] fn validates_expression_file_exists() { - let output = get_output(vec![ - "-e", - "tests/fixtures/not-there", - "-f", - "tests/fixtures/valid-json", - ]) - .unwrap_err(); + let args = JpArgs { + expr_file: Some("tests/fixtures/not-there".to_string()), + filename: Some("tests/fixtures/valid-json".to_string()), + ..Default::default() + }; + let output = run_jp(args).unwrap_err(); assert_eq!( "Error opening expression file at tests/fixtures/not-there: \ No such file or directory (os error 2)\n", @@ -102,44 +107,32 @@ fn validates_expression_file_exists() { #[test] fn validates_json_file_is_valid_json() { - let output = get_output(vec![ - "-e", - "tests/fixtures/valid-expression", - "-f", - "tests/fixtures/invalid-json", - ]) - .unwrap_err(); + let args = JpArgs { + expr_file: Some("tests/fixtures/valid-expression".to_string()), + filename: Some("tests/fixtures/invalid-json".to_string()), + ..Default::default() + }; + let output = run_jp(args).unwrap_err(); assert!(output.contains("Error parsing JSON")); } #[test] fn validates_expression_file_is_valid_expression() { - let output = get_output(vec![ - "-e", - "tests/fixtures/invalid-expression", - "-f", - "tests/fixtures/valid-json", - ]) - .unwrap_err(); + let args = JpArgs { + expr_file: Some("tests/fixtures/invalid-expression".to_string()), + filename: Some("tests/fixtures/valid-json".to_string()), + ..Default::default() + }; + let output = run_jp(args).unwrap_err(); assert!(output.contains("Parse error")); } #[test] fn can_read_json_from_stdin() { - use std::io::prelude::*; - let mut child = Command::new(JPBIN) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .arg("foo") - .spawn() - .expect("Failed to spawn process"); - child - .stdin - .as_mut() - .unwrap() - .write_all("{\"foo\":\"bar\"}".as_bytes()) - .ok(); - let output = child.wait_with_output().unwrap(); - let stdout = String::from_utf8(output.stdout).unwrap(); - assert_eq!("\"bar\"\n", stdout); + let args = JpArgs { + expression: Some("foo".to_string()), + ..Default::default() + }; + let output = run_jp_with_stdin(args, "{\"foo\":\"bar\"}").unwrap(); + assert_eq!("\"bar\"\n", output); } diff --git a/jmespath/Cargo.toml b/jmespath/Cargo.toml index 9762303d..bb67205f 100644 --- a/jmespath/Cargo.toml +++ b/jmespath/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jmespath" -version = "0.4.0" +version = "0.5.0" authors = ["Michael Dowling "] description = "Rust implementation of JMESPath, a query language for JSON" readme = "../README.md" @@ -13,11 +13,11 @@ build = "build.rs" edition = "2024" [dependencies] -serde = { version = "1", features = ["rc"] } -serde_json = "1" +serde = { workspace = true } +serde_json = { workspace = true } [build-dependencies] -serde_json = "1" +serde_json = { workspace = true } slug = "0.1" [dev-dependencies] diff --git a/jmespath/src/parser.rs b/jmespath/src/parser.rs index 2a542fdd..ac37a9fa 100644 --- a/jmespath/src/parser.rs +++ b/jmespath/src/parser.rs @@ -83,10 +83,8 @@ impl<'a> Parser<'a> { let mut actual_pos = self.offset; let mut buff = error_msg.to_string(); buff.push_str(&format!(" -- found {current_token:?}")); - if is_peek { - if let Some(&(p, _)) = self.token_queue.front() { - actual_pos = p; - } + if is_peek && let Some(&(p, _)) = self.token_queue.front() { + actual_pos = p; } JmespathError::new(self.expr, actual_pos, ErrorReason::Parse(buff)) } diff --git a/jmespath/src/variable.rs b/jmespath/src/variable.rs index 40980c8b..d2f3b032 100644 --- a/jmespath/src/variable.rs +++ b/jmespath/src/variable.rs @@ -348,10 +348,10 @@ impl Variable { /// Otherwise, returns Null. #[inline] pub fn get_field(&self, key: &str) -> Rcvar { - if let Variable::Object(map) = self { - if let Some(result) = map.get(key) { - return result.clone(); - } + if let Variable::Object(map) = self + && let Some(result) = map.get(key) + { + return result.clone(); } Rcvar::new(Variable::Null) } @@ -359,10 +359,10 @@ impl Variable { /// If the value is an array, then gets an array value by index. Otherwise returns Null. #[inline] pub fn get_index(&self, index: usize) -> Rcvar { - if let Variable::Array(array) = self { - if let Some(result) = array.get(index) { - return result.clone(); - } + if let Variable::Array(array) = self + && let Some(result) = array.get(index) + { + return result.clone(); } Rcvar::new(Variable::Null) }