From 9dbe4134249177587b24c1665a5aa2d28652800e Mon Sep 17 00:00:00 2001 From: Liam Date: Tue, 23 Feb 2021 12:54:16 +0000 Subject: [PATCH 01/20] allow cloning of selector mut --- src/select/expr_term.rs | 2 +- src/select/mod.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/select/expr_term.rs b/src/select/expr_term.rs index ddbf64e5..c0565e13 100644 --- a/src/select/expr_term.rs +++ b/src/select/expr_term.rs @@ -2,7 +2,7 @@ use serde_json::{Number, Value}; use select::cmp::*; use select::{FilterKey, to_f64}; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub(super) enum ExprTerm<'a> { String(String), Number(Number), diff --git a/src/select/mod.rs b/src/select/mod.rs index a3d0ec43..8b40787c 100644 --- a/src/select/mod.rs +++ b/src/select/mod.rs @@ -31,7 +31,7 @@ fn abs_index(n: isize, len: usize) -> usize { } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] enum FilterKey { String(String), All, @@ -61,7 +61,7 @@ impl fmt::Display for JsonPathError { } } -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] struct FilterTerms<'a>(Vec>>); impl<'a> FilterTerms<'a> { @@ -325,7 +325,7 @@ impl<'a> FilterTerms<'a> { } } -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub struct Selector<'a, 'b> { node: Option, node_ref: Option<&'b Node>, @@ -774,7 +774,7 @@ impl<'a, 'b> NodeVisitor for Selector<'a, 'b> { } } -#[derive(Default)] +#[derive(Default, Clone)] pub struct SelectorMut { path: Option, value: Option, From 7c6ee4aca316fb6f610bba58b16f103d838e2369 Mon Sep 17 00:00:00 2001 From: Liam Date: Tue, 23 Feb 2021 17:41:02 +0000 Subject: [PATCH 02/20] add cargo config --- .cargo/config | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .cargo/config diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 00000000..e67a5f26 --- /dev/null +++ b/.cargo/config @@ -0,0 +1,2 @@ +[registries] +evervault-jsonpath = { index = "https://dl.cloudsmith.io/basic/evervault/jsonpath/cargo/index.git" } \ No newline at end of file From 519d03e3405bfad5627d7cf0cf51cc5829ddf7f1 Mon Sep 17 00:00:00 2001 From: Liam Date: Tue, 23 Feb 2021 17:55:29 +0000 Subject: [PATCH 03/20] delete config --- .cargo/config | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 .cargo/config diff --git a/.cargo/config b/.cargo/config deleted file mode 100644 index e67a5f26..00000000 --- a/.cargo/config +++ /dev/null @@ -1,2 +0,0 @@ -[registries] -evervault-jsonpath = { index = "https://dl.cloudsmith.io/basic/evervault/jsonpath/cargo/index.git" } \ No newline at end of file From 2798fa18cbd12f533fb120c4fd06fb8b8e4c31ac Mon Sep 17 00:00:00 2001 From: Liam Date: Mon, 21 Jun 2021 11:11:26 +0100 Subject: [PATCH 04/20] wrap the option return type in a result to allow for errors during replace with --- src/lib.rs | 4 ++-- src/select/mod.rs | 23 +++++++++++++---------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 55ac5194..e589275b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -450,7 +450,7 @@ pub fn delete(value: Value, path: &str) -> Result { /// 0 /// }; /// -/// Some(json!(age)) +/// Ok(json!(age)) /// }).unwrap(); /// /// assert_eq!(ret, json!({ @@ -467,7 +467,7 @@ pub fn delete(value: Value, path: &str) -> Result { /// ``` pub fn replace_with(value: Value, path: &str, fun: &mut F) -> Result where - F: FnMut(Value) -> Option, + F: FnMut(Value) -> Result, JsonPathError>, { let mut selector = SelectorMut::default(); let value = selector.str_path(path)?.value(value).replace_with(fun)?; diff --git a/src/select/mod.rs b/src/select/mod.rs index 8b40787c..d87a578e 100644 --- a/src/select/mod.rs +++ b/src/select/mod.rs @@ -42,6 +42,7 @@ pub enum JsonPathError { EmptyValue, Path(String), Serde(String), + Replacement(String) } impl fmt::Debug for JsonPathError { @@ -57,6 +58,7 @@ impl fmt::Display for JsonPathError { JsonPathError::EmptyValue => f.write_str("json value not set"), JsonPathError::Path(msg) => f.write_str(&format!("path error: \n{}\n", msg)), JsonPathError::Serde(msg) => f.write_str(&format!("serde error: \n{}\n", msg)), + JsonPathError::Replacement(msg) => f.write_str(&format!("error occurred during jsonpath replacement: \n{}\n", msg)) } } } @@ -780,11 +782,11 @@ pub struct SelectorMut { value: Option, } -fn replace_value Option>( +fn replace_value Result, JsonPathError>>( mut tokens: Vec, value: &mut Value, fun: &mut F, -) { +) -> Result<(), JsonPathError> { let mut target = value; let last_index = tokens.len().saturating_sub(1); @@ -796,13 +798,13 @@ fn replace_value Option>( if is_last { if let Entry::Occupied(mut e) = map.entry(token) { let v = e.insert(Value::Null); - if let Some(res) = fun(v) { + if let Some(res) = fun(v)? { e.insert(res); } else { e.remove(); } } - return; + return Ok(()); } map.get_mut(&token) } @@ -810,12 +812,12 @@ fn replace_value Option>( if let Ok(x) = token.parse::() { if is_last { let v = std::mem::replace(&mut vec[x], Value::Null); - if let Some(res) = fun(v) { + if let Some(res) = fun(v)? { vec[x] = res; } else { vec.remove(x); } - return; + return Ok(()); } vec.get_mut(x) } else { @@ -831,6 +833,7 @@ fn replace_value Option>( break; } } + Ok(()) } impl SelectorMut { @@ -920,11 +923,11 @@ impl SelectorMut { } pub fn delete(&mut self) -> Result<&mut Self, JsonPathError> { - self.replace_with(&mut |_| Some(Value::Null)) + self.replace_with(&mut |_| Ok(Some(Value::Null))) } pub fn remove(&mut self) -> Result<&mut Self, JsonPathError> { - self.replace_with(&mut |_| None) + self.replace_with(&mut |_| Ok(None)) } fn select(&self) -> Result, JsonPathError> { @@ -942,7 +945,7 @@ impl SelectorMut { } } - pub fn replace_with Option>( + pub fn replace_with Result, JsonPathError>>( &mut self, fun: &mut F, ) -> Result<&mut Self, JsonPathError> { @@ -953,7 +956,7 @@ impl SelectorMut { if let Some(ref mut value) = &mut self.value { for tokens in paths { - replace_value(tokens, value, fun); + replace_value(tokens, value, fun)?; } } From c1b2f01c2a618ae6cc029c9ebc7ff7d284b330fa Mon Sep 17 00:00:00 2001 From: Liam Date: Mon, 21 Jun 2021 11:25:38 +0100 Subject: [PATCH 05/20] add test to make sure error is surfaced from replace_with --- Cargo.toml | 2 +- src/lib.rs | 2 +- tests/readme.rs | 4 ++-- tests/selector.rs | 23 +++++++++++++++++++++-- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1f325d9a..13397f88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jsonpath_lib" -version = "0.2.6" +version = "0.2.7" authors = ["Changseok Han "] description = "It is JsonPath engine written in Rust. it provide a similar API interface in Webassembly and Javascript too. - Webassembly Demo: https://freestrings.github.io/jsonpath" diff --git a/src/lib.rs b/src/lib.rs index e589275b..e8f03f49 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -450,7 +450,7 @@ pub fn delete(value: Value, path: &str) -> Result { /// 0 /// }; /// -/// Ok(json!(age)) +/// Ok(Some(json!(age))) /// }).unwrap(); /// /// assert_eq!(ret, json!({ diff --git a/tests/readme.rs b/tests/readme.rs index b8a3786d..4942e492 100644 --- a/tests/readme.rs +++ b/tests/readme.rs @@ -224,7 +224,7 @@ fn readme_selector_mut() { 0 }; - Some(json!(age)) + Ok(Some(json!(age))) }) .unwrap() .take() @@ -522,7 +522,7 @@ fn readme_replace_with() { 0 }; - Some(json!(age)) + Ok(Some(json!(age))) }) .unwrap(); diff --git a/tests/selector.rs b/tests/selector.rs index 113f66ba..12fce4f7 100644 --- a/tests/selector.rs +++ b/tests/selector.rs @@ -3,7 +3,7 @@ extern crate jsonpath_lib as jsonpath; extern crate serde_json; use common::{read_json, setup}; -use jsonpath::{Parser, Selector, SelectorMut}; +use jsonpath::{Parser, Selector, SelectorMut, JsonPathError}; use serde_json::Value; mod common; @@ -23,7 +23,7 @@ fn selector_mut() { if let Value::Number(n) = v { nums.push(n.as_f64().unwrap()); } - Some(Value::String("a".to_string())) + Ok(Some(Value::String("a".to_string()))) }) .unwrap() .take() @@ -54,6 +54,25 @@ fn selector_mut() { ); } +#[test] +fn selector_mut_err() { + setup(); + + let mut selector_mut = SelectorMut::default(); + let result = selector_mut + .str_path(r#"$.store..price"#) + .unwrap() + .value(read_json("./benchmark/example.json")) + .replace_with(&mut |_| { + Err(JsonPathError::EmptyValue) + }); + + assert_eq!( + result.is_err(), + true + ); +} + #[test] fn selector_node_ref() { let node = Parser::compile("$.*").unwrap(); From 78039de0e635605438c99d8d5a372e9153d357ef Mon Sep 17 00:00:00 2001 From: Eoin Boylan Date: Tue, 29 Jun 2021 14:50:53 +0100 Subject: [PATCH 06/20] Create pull_request_template.md --- .github/pull_request_template.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .github/pull_request_template.md diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..2d05be2d --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,9 @@ +# Why +Add a short description about this PR. +Add links to issues, tech plans etc. + +# Diagram +Does this PR require a change to the application or network diagram? + +# How +Describe how you've approached the problem% From 1557793aeaf32e78b6ba4699ffdf51b997ea8db4 Mon Sep 17 00:00:00 2001 From: Liam Date: Fri, 13 Aug 2021 16:14:24 +0100 Subject: [PATCH 07/20] add function to use jsonpath selectors to replace fields and expose the path's tokens --- src/lib.rs | 51 ++++++++++++++++++++++++++++++++ src/select/mod.rs | 75 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 125 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index e8f03f49..5e5a9280 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -474,6 +474,57 @@ where Ok(value.take().unwrap_or(Value::Null)) } +/// Select JSON properties using a jsonpath, exposing the path to the values with the value itself. Allows updating of the value while tracking field names/paths. +/// +/// ```rust +/// extern crate jsonpath_lib as jsonpath; +/// #[macro_use] extern crate serde_json; +/// +/// use serde_json::Value; +/// +/// let json_obj = json!({ +/// "school": { +/// "friends": [ +/// {"name": "친구1", "age": 20}, +/// {"name": "친구2", "age": 20} +/// ] +/// }, +/// "friends": [ +/// {"name": "친구3", "age": 30}, +/// {"name": "친구4"} +/// ]}); +/// +/// let ret = jsonpath::replace_with_tokens(json_obj, "$..[?(@.age == 20)].age", &mut |v, _tokens| { +/// let age = if let Value::Number(n) = v { +/// n.as_u64().unwrap() * 2 +/// } else { +/// 0 +/// }; +/// +/// Ok(Some(json!(age))) +/// }).unwrap(); +/// +/// assert_eq!(ret, json!({ +/// "school": { +/// "friends": [ +/// {"name": "친구1", "age": 40}, +/// {"name": "친구2", "age": 40} +/// ] +/// }, +/// "friends": [ +/// {"name": "친구3", "age": 30}, +/// {"name": "친구4"} +/// ]})); +/// ``` +pub fn replace_with_tokens(value: Value, path: &str, fun: &mut F) -> Result + where + F: FnMut(Value, &[String]) -> Result, JsonPathError>, +{ + let mut selector = SelectorMut::default(); + let value = selector.str_path(path)?.value(value).replace_with_tokens(fun)?; + Ok(value.take().unwrap_or(Value::Null)) +} + /// A pre-compiled expression. /// /// Calling the select function of this struct will re-use the existing, compiled expression. diff --git a/src/select/mod.rs b/src/select/mod.rs index d87a578e..cfc17f50 100644 --- a/src/select/mod.rs +++ b/src/select/mod.rs @@ -782,6 +782,61 @@ pub struct SelectorMut { value: Option, } +fn replace_value_with_tokens Result, JsonPathError>>( + mut tokens: Vec, + value: &mut Value, + fun: &mut F, +) -> Result<(), JsonPathError> { + let mut target = value; + + let tokens_clone = tokens.clone(); + let last_index = tokens.len().saturating_sub(1); + for (i, token) in tokens.drain(..).enumerate() { + let target_once = target; + let is_last = i == last_index; + let target_opt = match *target_once { + Value::Object(ref mut map) => { + if is_last { + if let Entry::Occupied(mut e) = map.entry(token) { + let v = e.insert(Value::Null); + if let Some(res) = fun(v,&tokens_clone)? { + e.insert(res); + } else { + e.remove(); + } + } + return Ok(()); + } + map.get_mut(&token) + } + Value::Array(ref mut vec) => { + if let Ok(x) = token.parse::() { + if is_last { + let v = std::mem::replace(&mut vec[x], Value::Null); + if let Some(res) = fun(v,&tokens_clone)? { + vec[x] = res; + } else { + vec.remove(x); + } + return Ok(()); + } + vec.get_mut(x) + } else { + None + } + } + _ => None, + }; + + if let Some(t) = target_opt { + target = t; + } else { + break; + } + } + Ok(()) +} + fn replace_value Result, JsonPathError>>( mut tokens: Vec, value: &mut Value, @@ -962,6 +1017,24 @@ impl SelectorMut { Ok(self) } + + pub fn replace_with_tokens Result, JsonPathError>>( + &mut self, + fun: &mut F + ) -> Result<&mut Self, JsonPathError> { + let paths = { + let result = self.select()?; + self.compute_paths(result) + }; + + if let Some(ref mut value) = &mut self.value { + for tokens in paths { + replace_value_with_tokens(tokens, value, fun)?; + } + } + + Ok(self) + } } @@ -1001,4 +1074,4 @@ mod select_inner_tests { panic!(); } } -} \ No newline at end of file +} From 9237076fb5b2223e0e90b297412c0bb4ca26a800 Mon Sep 17 00:00:00 2001 From: Liam Farrelly Date: Mon, 16 Aug 2021 11:09:43 +0100 Subject: [PATCH 08/20] Bump version --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 13397f88..43b3a9fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jsonpath_lib" -version = "0.2.7" +version = "0.2.8" authors = ["Changseok Han "] description = "It is JsonPath engine written in Rust. it provide a similar API interface in Webassembly and Javascript too. - Webassembly Demo: https://freestrings.github.io/jsonpath" @@ -30,4 +30,4 @@ crate-type = ["cdylib", "rlib"] #[profile.release] #debug = true -#lto = false \ No newline at end of file +#lto = false From 2a583218ebd745c85c3d5ed9bc1268fc46a76786 Mon Sep 17 00:00:00 2001 From: Liam Date: Wed, 4 May 2022 19:30:24 +0100 Subject: [PATCH 09/20] add tests for error in replace with handler for jsonpath selectors --- tests/selector.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/selector.rs b/tests/selector.rs index 648f0942..770501be 100644 --- a/tests/selector.rs +++ b/tests/selector.rs @@ -70,6 +70,24 @@ fn selector_mut_err() { ); } +#[test] +fn jsonselector_mut_err() { + setup(); + + let parser = PathParser::compile("$.store..price[?(@>13)]").unwrap(); + let mut selector_mut = JsonSelectorMut::new(parser); + let result = selector_mut + .value(read_json("./benchmark/example.json")) + .replace_with(&mut |_| { + Err(JsonPathError::EmptyValue) + }); + + assert_eq!( + result.is_err(), + true + ); +} + #[test] fn selector_node_ref() { let node = Parser::compile("$.*").unwrap(); From fcfa5126bce55cf52678cbd37f1192728703f9cb Mon Sep 17 00:00:00 2001 From: Liam Date: Thu, 5 May 2022 09:54:03 +0100 Subject: [PATCH 10/20] bump cargo version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 593dc7f3..3905cfb0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jsonpath_lib" -version = "0.2.8" +version = "0.3.0" authors = ["Changseok Han "] description = "It is JsonPath engine written in Rust. it provide a similar API interface in Webassembly and Javascript too. - Webassembly Demo: https://freestrings.github.io/jsonpath" From d1636decc23c0be29fd7de0cc9b60d73d531d9d3 Mon Sep 17 00:00:00 2001 From: Eoin Power-Moran Date: Fri, 13 Oct 2023 11:23:30 +0100 Subject: [PATCH 11/20] experiment with async --- Cargo.toml | 6 + benches/async.rs | 137 +++++++++++++ benches/common.rs | 67 +++++++ src/ffi/mod.rs | 62 ------ src/lib.rs | 67 +++---- src/parser/mod.rs | 37 ++-- src/parser/tokenizer.rs | 41 +--- src/paths/mod.rs | 6 +- src/paths/parser_node_visitor.rs | 14 +- src/paths/parser_token_handler.rs | 6 +- src/paths/path_parser.rs | 64 +++--- src/paths/str_reader.rs | 13 +- src/paths/tokenizer.rs | 68 +++---- src/paths/tokens.rs | 2 +- src/select/cmp.rs | 3 +- src/select/expr_term.rs | 4 +- src/select/mod.rs | 132 ++++++++----- src/select/value_walker.rs | 31 +-- src/selector/cmp.rs | 3 +- src/selector/mod.rs | 6 +- src/selector/selector_impl.rs | 223 +++++++++++++++------ src/selector/terms.rs | 313 +++++++++++++++++------------- src/selector/utils.rs | 6 +- src/selector/value_walker.rs | 105 +++++----- tests/array_filter.rs | 8 +- tests/async.rs | 144 ++++++++++++++ tests/common.rs | 17 +- tests/filter.rs | 98 +++++----- tests/op.rs | 81 ++++---- tests/paths.rs | 178 ++++++++--------- tests/precompile.rs | 14 +- tests/readme.rs | 17 +- tests/return_type.rs | 8 +- tests/selector.rs | 65 +++---- 34 files changed, 1247 insertions(+), 799 deletions(-) create mode 100644 benches/async.rs create mode 100644 benches/common.rs delete mode 100644 src/ffi/mod.rs create mode 100644 tests/async.rs diff --git a/Cargo.toml b/Cargo.toml index 3905cfb0..7bf50888 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "jsonpath_lib" version = "0.3.0" +edition = "2021" authors = ["Changseok Han "] description = "It is JsonPath engine written in Rust. it provide a similar API interface in Webassembly and Javascript too. - Webassembly Demo: https://freestrings.github.io/jsonpath" @@ -17,12 +18,17 @@ license = "MIT" travis-ci = { repository = "freestrings/jsonpath", branch = "master" } [dependencies] +futures = "0.3.28" log = "0.4" +reqwest = "0.11.22" serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["preserve_order"] } [dev-dependencies] +criterion = { version = "0.5.1", features = ["html_reports"] } env_logger = "0.8" +tokio = { version = "1.33.0", features = ["macros"] } +tokio-test = "0.4.3" [lib] name = "jsonpath_lib" diff --git a/benches/async.rs b/benches/async.rs new file mode 100644 index 00000000..b2e65055 --- /dev/null +++ b/benches/async.rs @@ -0,0 +1,137 @@ +extern crate jsonpath_lib as jsonpath; +#[macro_use] +extern crate serde_json; + +use std::{ + pin::Pin, + sync::{Arc, Mutex}, + task::{Context, Poll}, +}; + +use common::{read_json, setup}; +use criterion::{criterion_group, criterion_main}; +use futures::Future; +use jsonpath::{JsonSelector, MultiJsonSelectorMut, PathParser}; +use serde_json::Value; + +mod common; + +#[derive(Clone)] +struct ValueFuture { + inner: Arc>>, +} + +impl ValueFuture { + fn new() -> Self { + ValueFuture { + inner: Arc::new(Mutex::new(None)), + } + } + + fn set_value(&self, value: T) { + let mut inner = self.inner.lock().unwrap(); + *inner = Some(value); + } +} + +impl Future for ValueFuture { + type Output = T; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let inner = self.inner.lock().unwrap(); + if let Some(value) = inner.as_ref() { + Poll::Ready(value.clone()) + } else { + // This future isn't ready yet, so we'll notify the context when it is. + cx.waker().wake_by_ref(); + Poll::Pending + } + } +} + +struct CryptoRequest { + bags: Mutex>, +} + +impl CryptoRequest { + fn new() -> Self { + Self { + bags: Mutex::new(Vec::new()), + } + } + + fn new_field(&self, input: Value) -> CryptoField { + let bag = CryptoField::new(input); + self.bags.lock().unwrap().push(bag.clone()); + bag + } + + async fn send_request(&self) { + let mut bags = self.bags.lock().unwrap(); + for bag in bags.iter_mut() { + bag.value.set_value(bag.input.take().unwrap()); + } + } +} + +#[derive(Clone)] +struct CryptoField { + input: Option, + value: ValueFuture, +} + +impl CryptoField { + fn new(input: Value) -> Self { + Self { + input: Some(input), + value: ValueFuture::new(), + } + } + + pub fn value(self) -> ValueFuture { + self.value + } +} + +async fn selector_mut() { + setup(); + + let parser = PathParser::compile("$.store..price").unwrap(); + let parser_two = PathParser::compile("$.store..author").unwrap(); + let mut selector_mut = + MultiJsonSelectorMut::new_multi_parser(vec![parser.into(), parser_two.into()]); + + let crypto_request = Arc::new(CryptoRequest::new()); + + let result_futures = selector_mut + .value(read_json("./benchmark/example.json")) + .replace_with_async(|v| { + let bag: CryptoField = crypto_request.new_field(v); + + Box::pin(async move { + let val = bag.value().await; + Some(val) + }) + }) + .unwrap(); + + crypto_request.send_request().await; + + let result = result_futures.await.unwrap().take().unwrap(); + + let parser = PathParser::compile("$.store..price").unwrap(); + let mut selector = JsonSelector::new(parser); + let result = selector.value(&result).select().unwrap(); + + assert_eq!( + vec![&json!(42), &json!(42), &json!(42), &json!(42), &json!(42)], + result + ); +} + +fn setup_async_benchmark(c: &mut criterion::Criterion) { + c.bench_function("selector_mut", |b| b.iter(|| selector_mut())); +} + +criterion_group!(benches, setup_async_benchmark); +criterion_main!(benches); \ No newline at end of file diff --git a/benches/common.rs b/benches/common.rs new file mode 100644 index 00000000..eae36803 --- /dev/null +++ b/benches/common.rs @@ -0,0 +1,67 @@ +extern crate env_logger; +extern crate jsonpath_lib as jsonpath; +extern crate serde_json; + +use std::io::Read; +use std::io::Write; + +use log::LevelFilter; +use serde_json::Value; + +use self::jsonpath::{JsonSelector, PathParser}; + +#[allow(dead_code)] +pub fn setup() { + let _ = env_logger::Builder::new() + .format(|buf, record| { + writeln!( + buf, + "{}:{} {} - {}", + record.file().unwrap_or("unknown"), + record.line().unwrap_or(0), + record.level(), + record.args() + ) + }) + .parse_env("RUST_LOG") + // .filter(Some("logger_example"), LevelFilter::Trace) + .init(); +} + +#[allow(dead_code)] +pub fn read_json(path: &str) -> Value { + let mut f = std::fs::File::open(path).unwrap(); + let mut contents = String::new(); + f.read_to_string(&mut contents).unwrap(); + serde_json::from_str(&contents).unwrap() +} + +#[allow(dead_code)] +pub fn read_contents(path: &str) -> String { + let mut f = std::fs::File::open(path).unwrap(); + let mut contents = String::new(); + f.read_to_string(&mut contents).unwrap(); + contents +} + +#[allow(dead_code)] +pub fn select_and_then_compare(path: &str, json: Value, target: Value) { + let parser = PathParser::compile(path).unwrap(); + let mut selector = JsonSelector::new(parser); + let result = selector.value(&json).select_as::().unwrap(); + assert_eq!( + result, + match target { + Value::Array(vec) => vec, + _ => panic!("Give me the Array!"), + }, + "{}", + path + ); +} + +#[allow(dead_code)] +pub fn compare_result(result: Vec<&Value>, target: Value) { + let result = serde_json::to_value(result).unwrap(); + assert_eq!(result, target); +} diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs deleted file mode 100644 index 813486cd..00000000 --- a/src/ffi/mod.rs +++ /dev/null @@ -1,62 +0,0 @@ -use std::ffi::{CStr, CString}; -use std::os::raw::{c_char, c_void}; - -use {parser, select, select_as_str}; - -const INVALID_PATH: &str = "invalid path"; -const INVALID_JSON: &str = "invalud json"; - -fn to_str(v: *const c_char, err_msg: &str) -> &str { - unsafe { CStr::from_ptr(v) }.to_str().expect(err_msg) -} - -fn to_char_ptr(v: &str) -> *const c_char { - let s = CString::new(v).unwrap_or_else(|_| panic!("invalid string: {}", v)); - let ptr = s.as_ptr(); - std::mem::forget(s); - ptr -} - -#[no_mangle] -pub extern "C" fn ffi_select(json_str: *const c_char, path: *const c_char) -> *const c_char { - let json_str = to_str(json_str, INVALID_JSON); - let path = to_str(path, INVALID_PATH); - match select_as_str(json_str, path) { - Ok(v) => to_char_ptr(v.as_str()), - Err(e) => { - panic!("{:?}", e); - } - } -} - -#[no_mangle] -#[allow(clippy::forget_copy)] -pub extern "C" fn ffi_path_compile(path: *const c_char) -> *mut c_void { - let path = to_str(path, INVALID_PATH); - #[allow(deprecated)] - let ref_node = Box::into_raw(Box::new(parser::Parser::compile(path).unwrap())); - let ptr = ref_node as *mut c_void; - std::mem::forget(ref_node); - ptr -} - -#[no_mangle] -pub extern "C" fn ffi_select_with_compiled_path( - path_ptr: *mut c_void, - json_ptr: *const c_char, -) -> *const c_char { - #[allow(deprecated)] - let node = unsafe { Box::from_raw(path_ptr as *mut parser::Node) }; - let json_str = to_str(json_ptr, INVALID_JSON); - let json = serde_json::from_str(json_str) - .unwrap_or_else(|_| panic!("invalid json string: {}", json_str)); - - #[allow(deprecated)] - let mut selector = select::Selector::default(); - let found = selector.compiled_path(&node).value(&json).select().unwrap(); - std::mem::forget(node); - - let result = serde_json::to_string(&found) - .unwrap_or_else(|_| panic!("json serialize error: {:?}", found)); - to_char_ptr(result.as_str()) -} diff --git a/src/lib.rs b/src/lib.rs index 664e69a0..591d94b4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -137,22 +137,13 @@ pub use parser::Parser; #[allow(deprecated)] pub use select::{Selector, SelectorMut}; -#[deprecated( -since = "0.4.0", -note = "It will be move to common module. since 0.5" -)] +#[deprecated(since = "0.4.0", note = "It will be move to common module. since 0.5")] pub use select::JsonPathError; -pub use selector::{JsonSelector, JsonSelectorMut}; pub use paths::PathParser; -use std::rc::Rc; +pub use selector::{JsonSelector, MultiJsonSelectorMut}; +use std::{rc::Rc, sync::Arc}; -#[doc(hidden)] -#[deprecated( -since = "0.4.0", -note = "'ffi' is moved to another location like 'wasm' from version 0.5.x" -)] -mod ffi; #[doc(hidden)] mod parser; #[doc(hidden)] @@ -165,7 +156,9 @@ impl From<&paths::TokenError> for JsonPathError { fn from(e: &paths::TokenError) -> Self { match e { paths::TokenError::Eof => JsonPathError::Path("Eof".to_string()), - paths::TokenError::Position(pos) => JsonPathError::Path(["Position:", &pos.to_string()].concat()) + paths::TokenError::Position(pos) => { + JsonPathError::Path(["Position:", &pos.to_string()].concat()) + } } } } @@ -198,8 +191,8 @@ impl From<&paths::TokenError> for JsonPathError { /// ]); /// ``` #[deprecated( -since = "0.2.5", -note = "Please use the PathCompiled::compile function instead. It will be removed from 0.4.1" + since = "0.2.5", + note = "Please use the PathCompiled::compile function instead. It will be removed from 0.4.1" )] pub fn compile(path: &str) -> impl FnMut(&Value) -> Result, JsonPathError> { #[allow(deprecated)] @@ -249,11 +242,17 @@ pub fn compile(path: &str) -> impl FnMut(&Value) -> Result, JsonPath /// ]); /// ``` #[allow(clippy::needless_lifetimes)] -pub fn selector<'a>(json: &'a Value) -> impl FnMut(&'a str) -> Result, JsonPathError> { +pub fn selector<'a>( + json: &'a Value, +) -> impl FnMut(&'a str) -> Result, JsonPathError> { let mut selector = JsonSelector::default(); move |path| { let parser = PathParser::compile(path).map_err(|e| JsonPathError::from(&e))?; - selector.reset_parser(parser).value(json).reset_value().select() + selector + .reset_parser(parser) + .value(json) + .reset_value() + .select() } } @@ -303,9 +302,9 @@ pub fn selector<'a>(json: &'a Value) -> impl FnMut(&'a str) -> Result(json: &'a Value) - -> impl FnMut(&'a str) -> Result, JsonPathError> + '_ -{ +pub fn selector_as<'a, T: serde::de::DeserializeOwned>( + json: &'a Value, +) -> impl FnMut(&'a str) -> Result, JsonPathError> + '_ { let mut selector = JsonSelector::default(); let _ = selector.value(json); move |path: &str| { @@ -455,7 +454,7 @@ pub fn select_as( /// ``` pub fn delete(value: Value, path: &str) -> Result { let parser = PathParser::compile(path).map_err(|e| JsonPathError::from(&e))?; - let mut selector = JsonSelectorMut::new(parser); + let mut selector = MultiJsonSelectorMut::new(parser); let value = selector.value(value).delete()?; Ok(value.take().unwrap_or(Value::Null)) } @@ -507,7 +506,7 @@ where F: FnMut(Value) -> Result, JsonPathError>, { let parser = PathParser::compile(path).map_err(|e| JsonPathError::from(&e))?; - let mut selector = JsonSelectorMut::new(parser); + let mut selector = MultiJsonSelectorMut::new(parser); let value = selector.value(value).replace_with(fun)?; Ok(value.take().unwrap_or(Value::Null)) } @@ -555,11 +554,14 @@ where /// ]})); /// ``` pub fn replace_with_tokens(value: Value, path: &str, fun: &mut F) -> Result - where - F: FnMut(Value, &[String]) -> Result, JsonPathError>, +where + F: FnMut(Value, &[String]) -> Result, JsonPathError>, { let mut selector = SelectorMut::default(); - let value = selector.str_path(path)?.value(value).replace_with_tokens(fun)?; + let value = selector + .str_path(path)? + .value(value) + .replace_with_tokens(fun)?; Ok(value.take().unwrap_or(Value::Null)) } @@ -606,10 +608,7 @@ pub fn replace_with_tokens(value: Value, path: &str, fun: &mut F) -> Result Result { let node = parser::Parser::compile(path)?; - Ok(Self { - node - }) + Ok(Self { node }) } /// Execute the select operation on the pre-compiled path. @@ -678,7 +675,7 @@ impl Compiled { /// ``` #[derive(Clone, Debug)] pub struct PathCompiled<'a> { - parser: Rc>, + parser: Arc>, } impl<'a> PathCompiled<'a> { @@ -688,13 +685,13 @@ impl<'a> PathCompiled<'a> { pub fn compile(path: &str) -> Result { let parser = PathParser::compile(path).map_err(|e| JsonPathError::from(&e))?; Ok(PathCompiled { - parser: Rc::new(parser) + parser: Arc::new(parser), }) } /// Execute the select operation on the pre-compiled path. pub fn select(&self, value: &'a Value) -> Result, JsonPathError> { - let mut selector = JsonSelector::new_ref(Rc::clone(&self.parser)); + let mut selector = JsonSelector::new_ref(Arc::clone(&self.parser)); selector.value(value).select() } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index affb887a..160b7243 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -13,8 +13,8 @@ mod utils { use std::str::FromStr; pub fn string_to_num(string: &str, msg_handler: F) -> Result - where - F: Fn() -> String, + where + F: Fn() -> String, { match string.parse() { Ok(n) => Ok(n), @@ -481,14 +481,15 @@ impl Parser { let node = Self::term(tokenizer)?; Self::eat_whitespace(tokenizer); - if matches!(tokenizer.peek_token(), + if matches!( + tokenizer.peek_token(), Ok(Token::Equal(_)) - | Ok(Token::NotEqual(_)) - | Ok(Token::Little(_)) - | Ok(Token::LittleOrEqual(_)) - | Ok(Token::Greater(_)) - | Ok(Token::GreaterOrEqual(_))) - { + | Ok(Token::NotEqual(_)) + | Ok(Token::Little(_)) + | Ok(Token::LittleOrEqual(_)) + | Ok(Token::Greater(_)) + | Ok(Token::GreaterOrEqual(_)) + ) { Self::op(node, tokenizer) } else if has_prop_candidate { Ok(node) @@ -543,21 +544,15 @@ impl Parser { _ => Self::paths(node, tokenizer), } } - Ok(Token::Absolute(_)) => { - Self::json_path(tokenizer) - } + Ok(Token::Absolute(_)) => Self::json_path(tokenizer), Ok(Token::DoubleQuoted(_, _)) | Ok(Token::SingleQuoted(_, _)) => { Self::array_quote_value(tokenizer) } - Ok(Token::Key(_, key)) => { - match key.as_bytes()[0] { - b'-' | b'0'..=b'9' => Self::term_num(tokenizer), - _ => Self::boolean(tokenizer), - } - } - _ => { - Err(tokenizer.err_msg()) - } + Ok(Token::Key(_, key)) => match key.as_bytes()[0] { + b'-' | b'0'..=b'9' => Self::term_num(tokenizer), + _ => Self::boolean(tokenizer), + }, + _ => Err(tokenizer.err_msg()), } } diff --git a/src/parser/tokenizer.rs b/src/parser/tokenizer.rs index 3e079b9d..67d7d1e8 100644 --- a/src/parser/tokenizer.rs +++ b/src/parser/tokenizer.rs @@ -105,23 +105,9 @@ impl<'a> Tokenizer<'a> { fn dolla(&mut self, pos: usize, ch: char) -> Result { let fun = |c: &char| match c { - &CH_DOT - | &CH_ASTERISK - | &CH_LARRAY - | &CH_RARRAY - | &CH_LPAREN - | &CH_RPAREN - | &CH_AT - | &CH_QUESTION - | &CH_COMMA - | &CH_SEMICOLON - | &CH_LITTLE - | &CH_GREATER - | &CH_EQUAL - | &CH_AMPERSAND - | &CH_PIPE - | &CH_EXCLAMATION - => false, + &CH_DOT | &CH_ASTERISK | &CH_LARRAY | &CH_RARRAY | &CH_LPAREN | &CH_RPAREN | &CH_AT + | &CH_QUESTION | &CH_COMMA | &CH_SEMICOLON | &CH_LITTLE | &CH_GREATER | &CH_EQUAL + | &CH_AMPERSAND | &CH_PIPE | &CH_EXCLAMATION => false, _ => !c.is_whitespace(), }; let (_, mut vec) = self.input.take_while(fun).map_err(to_token_error)?; @@ -243,24 +229,9 @@ impl<'a> Tokenizer<'a> { fn other(&mut self, pos: usize, ch: char) -> Result { let fun = |c: &char| match c { - &CH_DOLLA - | &CH_DOT - | &CH_ASTERISK - | &CH_LARRAY - | &CH_RARRAY - | &CH_LPAREN - | &CH_RPAREN - | &CH_AT - | &CH_QUESTION - | &CH_COMMA - | &CH_SEMICOLON - | &CH_LITTLE - | &CH_GREATER - | &CH_EQUAL - | &CH_AMPERSAND - | &CH_PIPE - | &CH_EXCLAMATION - => false, + &CH_DOLLA | &CH_DOT | &CH_ASTERISK | &CH_LARRAY | &CH_RARRAY | &CH_LPAREN + | &CH_RPAREN | &CH_AT | &CH_QUESTION | &CH_COMMA | &CH_SEMICOLON | &CH_LITTLE + | &CH_GREATER | &CH_EQUAL | &CH_AMPERSAND | &CH_PIPE | &CH_EXCLAMATION => false, _ => !c.is_whitespace(), }; let (_, mut vec) = self.input.take_while(fun).map_err(to_token_error)?; diff --git a/src/paths/mod.rs b/src/paths/mod.rs index 59474c47..186008c5 100644 --- a/src/paths/mod.rs +++ b/src/paths/mod.rs @@ -4,9 +4,9 @@ pub use self::path_parser::PathParser; pub use self::str_reader::StrRange; pub use self::tokenizer::TokenError; +mod parser_node_visitor; +mod parser_token_handler; +mod path_parser; mod str_reader; mod tokenizer; pub mod tokens; -mod parser_token_handler; -mod parser_node_visitor; -mod path_parser; diff --git a/src/paths/parser_node_visitor.rs b/src/paths/parser_node_visitor.rs index ab35c1e3..9df56822 100644 --- a/src/paths/parser_node_visitor.rs +++ b/src/paths/parser_node_visitor.rs @@ -1,12 +1,12 @@ -use paths::{ParserTokenHandler, StrRange}; -use paths::path_parser::ParserNode; -use paths::tokens::{FilterToken, ParseToken}; +use crate::paths::path_parser::ParserNode; +use crate::paths::tokens::{FilterToken, ParseToken}; +use crate::paths::{ParserTokenHandler, StrRange}; pub trait ParserNodeVisitor<'a> { fn visit(&self, parse_node: &ParserNode, token_handler: &mut F, parse_value_reader: &F1) - where - F: ParserTokenHandler<'a>, - F1: Fn(&StrRange) -> &'a str + where + F: ParserTokenHandler<'a>, + F1: Fn(&StrRange) -> &'a str, { trace!("visit {:?}", parse_node); match &parse_node.token { @@ -70,4 +70,4 @@ pub trait ParserNodeVisitor<'a> { _ => {} } } -} \ No newline at end of file +} diff --git a/src/paths/parser_token_handler.rs b/src/paths/parser_token_handler.rs index ec06d410..65fcd24f 100644 --- a/src/paths/parser_token_handler.rs +++ b/src/paths/parser_token_handler.rs @@ -3,6 +3,6 @@ use super::tokens::ParseToken; pub trait ParserTokenHandler<'a> { fn handle(&mut self, token: &ParseToken, parse_value_reader: &F) - where - F: Fn(&StrRange) -> &'a str; -} \ No newline at end of file + where + F: Fn(&StrRange) -> &'a str; +} diff --git a/src/paths/path_parser.rs b/src/paths/path_parser.rs index 178e105d..1957d5f5 100644 --- a/src/paths/path_parser.rs +++ b/src/paths/path_parser.rs @@ -1,7 +1,7 @@ use std::str::FromStr; -use super::parser_token_handler::ParserTokenHandler; use super::parser_node_visitor::ParserNodeVisitor; +use super::parser_token_handler::ParserTokenHandler; use super::str_reader::StrRange; use super::tokenizer::{TokenError, TokenReader}; use super::tokens::{FilterToken, ParseToken, Token}; @@ -19,8 +19,8 @@ impl<'a> PathParser<'a> { } pub(crate) fn parse(&self, parse_token_handler: &mut F) -> Result<(), String> - where - F: ParserTokenHandler<'a>, + where + F: ParserTokenHandler<'a>, { if self.parser.parse_node.is_none() { unreachable!() @@ -28,7 +28,7 @@ impl<'a> PathParser<'a> { let token_reader = &self.parser.token_reader; if let Some(parse_node) = self.parser.parse_node.as_ref() { - self.visit(parse_node, parse_token_handler, &|s| { + self.visit(parse_node, parse_token_handler, &|s: &StrRange| { token_reader.read_value(s) }); } @@ -54,8 +54,8 @@ impl<'a> ParserImpl<'a> { } fn string_to_num(string: &str, msg_handler: F) -> Result - where - F: Fn() -> TokenError, + where + F: Fn() -> TokenError, { match string.parse() { Ok(n) => Ok(n), @@ -293,9 +293,7 @@ impl<'a> ParserImpl<'a> { self.eat_token(); self.range_to() } - Ok(Token::DoubleQuoted(_)) | Ok(Token::SingleQuoted(_)) => { - self.array_quote_value() - } + Ok(Token::DoubleQuoted(_)) | Ok(Token::SingleQuoted(_)) => self.array_quote_value(), Err(TokenError::Eof) => Ok(self.create_node(ParseToken::Eof)), _ => { self.eat_token(); @@ -472,14 +470,15 @@ impl<'a> ParserImpl<'a> { let node = self.term()?; self.eat_whitespace(); - if matches!(self.token_reader.peek_token(), + if matches!( + self.token_reader.peek_token(), Ok(Token::Equal(_)) - | Ok(Token::NotEqual(_)) - | Ok(Token::Little(_)) - | Ok(Token::LittleOrEqual(_)) - | Ok(Token::Greater(_)) - | Ok(Token::GreaterOrEqual(_))) - { + | Ok(Token::NotEqual(_)) + | Ok(Token::Little(_)) + | Ok(Token::LittleOrEqual(_)) + | Ok(Token::Greater(_)) + | Ok(Token::GreaterOrEqual(_)) + ) { self.op(node) } else if has_prop_candidate { Ok(node) @@ -511,7 +510,9 @@ impl<'a> ParserImpl<'a> { match self.token_reader.next_token() { Ok(Token::Key(s)) => { let frac = self.token_reader.read_value(&s); - let number = Self::string_to_num(&[num, ".", frac].concat(), || self.token_reader.to_error())?; + let number = Self::string_to_num(&[num, ".", frac].concat(), || { + self.token_reader.to_error() + })?; Ok(self.create_node(ParseToken::Number(number))) } _ => Err(self.token_reader.to_error()), @@ -552,15 +553,9 @@ impl<'a> ParserImpl<'a> { _ => self.paths(node), } } - Ok(Token::Absolute(_)) => { - self.json_path() - } - Ok(Token::DoubleQuoted(_)) | Ok(Token::SingleQuoted(_)) => { - self.array_quote_value() - } - _ => { - Err(self.token_reader.to_error()) - } + Ok(Token::Absolute(_)) => self.json_path(), + Ok(Token::DoubleQuoted(_)) | Ok(Token::SingleQuoted(_)) => self.array_quote_value(), + _ => Err(self.token_reader.to_error()), } } @@ -623,10 +618,10 @@ pub struct ParserNode { #[cfg(test)] mod path_parser_tests { - use paths::ParserTokenHandler; - use paths::path_parser::PathParser; - use paths::str_reader::StrRange; - use paths::tokens::{FilterToken, ParseToken}; + use crate::paths::path_parser::PathParser; + use crate::paths::str_reader::StrRange; + use crate::paths::tokens::{FilterToken, ParseToken}; + use crate::paths::ParserTokenHandler; struct NodeVisitorTestImpl<'a> { input: &'a str, @@ -650,8 +645,8 @@ mod path_parser_tests { impl<'a> ParserTokenHandler<'a> for NodeVisitorTestImpl<'a> { fn handle(&mut self, token: &ParseToken, _: &F) - where - F: Fn(&StrRange) -> &'a str + where + F: Fn(&StrRange) -> &'a str, { trace!("handle {:?}", token); self.stack.push(token.clone()); @@ -964,7 +959,10 @@ mod path_parser_tests { Ok(vec![ ParseToken::Absolute, ParseToken::Array, - ParseToken::Keys(vec![StrRange::new(2, "\"a\"".len()), StrRange::new(7, "'b'".len())]), + ParseToken::Keys(vec![ + StrRange::new(2, "\"a\"".len()), + StrRange::new(7, "'b'".len()) + ]), ParseToken::ArrayEof ]) ); diff --git a/src/paths/str_reader.rs b/src/paths/str_reader.rs index 11b74a70..1e5aca29 100644 --- a/src/paths/str_reader.rs +++ b/src/paths/str_reader.rs @@ -28,7 +28,12 @@ pub(crate) struct StrReader<'a> { impl<'a> StrReader<'a> { pub fn new(input: &'a str) -> Self { - StrReader { input, pos: 0, chars: input.chars(), peeked: None } + StrReader { + input, + pos: 0, + chars: input.chars(), + peeked: None, + } } pub fn peek_char(&mut self) -> Result { @@ -37,8 +42,8 @@ impl<'a> StrReader<'a> { } pub fn take_while(&mut self, fun: F) -> Result - where - F: Fn(&char) -> bool, + where + F: Fn(&char) -> bool, { let mut char_len: usize = 0; while let Some(c) = self.peek() { @@ -47,7 +52,7 @@ impl<'a> StrReader<'a> { } match self.next() { Some(ch) => char_len += ch.len_utf8(), - _ => return Err(ReaderError::Eof) + _ => return Err(ReaderError::Eof), } } diff --git a/src/paths/tokenizer.rs b/src/paths/tokenizer.rs index fd61c7e5..b4f12d69 100644 --- a/src/paths/tokenizer.rs +++ b/src/paths/tokenizer.rs @@ -50,23 +50,9 @@ impl<'a> Tokenizer<'a> { fn dolla(&mut self) -> Result { let fun = |c: &char| match c { - &CH_DOT - | &CH_ASTERISK - | &CH_LARRAY - | &CH_RARRAY - | &CH_LPAREN - | &CH_RPAREN - | &CH_AT - | &CH_QUESTION - | &CH_COMMA - | &CH_SEMICOLON - | &CH_LITTLE - | &CH_GREATER - | &CH_EQUAL - | &CH_AMPERSAND - | &CH_PIPE - | &CH_EXCLAMATION - => false, + &CH_DOT | &CH_ASTERISK | &CH_LARRAY | &CH_RARRAY | &CH_LPAREN | &CH_RPAREN | &CH_AT + | &CH_QUESTION | &CH_COMMA | &CH_SEMICOLON | &CH_LITTLE | &CH_GREATER | &CH_EQUAL + | &CH_AMPERSAND | &CH_PIPE | &CH_EXCLAMATION => false, _ => !c.is_whitespace(), }; let read = self.input.take_while(fun).map_err(to_token_error)?; @@ -78,11 +64,17 @@ impl<'a> Tokenizer<'a> { } fn quote(&mut self, ch: char) -> Result { - let span = self.input.take_while(|c| *c != ch).map_err(to_token_error)?; + let span = self + .input + .take_while(|c| *c != ch) + .map_err(to_token_error)?; let val = self.input.read(&span); if let Some('\\') = val.chars().last() { self.input.next_char().map_err(to_token_error)?; - let remain_span = self.input.take_while(|c| *c != ch).map_err(to_token_error)?; + let remain_span = self + .input + .take_while(|c| *c != ch) + .map_err(to_token_error)?; self.input.next_char().map_err(to_token_error)?; Ok(StrRange::new(span.pos, remain_span.offset)) } else { @@ -175,24 +167,9 @@ impl<'a> Tokenizer<'a> { fn other(&mut self) -> Result { let fun = |c: &char| match c { - &CH_DOLLA - | &CH_DOT - | &CH_ASTERISK - | &CH_LARRAY - | &CH_RARRAY - | &CH_LPAREN - | &CH_RPAREN - | &CH_AT - | &CH_QUESTION - | &CH_COMMA - | &CH_SEMICOLON - | &CH_LITTLE - | &CH_GREATER - | &CH_EQUAL - | &CH_AMPERSAND - | &CH_PIPE - | &CH_EXCLAMATION - => false, + &CH_DOLLA | &CH_DOT | &CH_ASTERISK | &CH_LARRAY | &CH_RARRAY | &CH_LPAREN + | &CH_RPAREN | &CH_AT | &CH_QUESTION | &CH_COMMA | &CH_SEMICOLON | &CH_LITTLE + | &CH_GREATER | &CH_EQUAL | &CH_AMPERSAND | &CH_PIPE | &CH_EXCLAMATION => false, _ => !c.is_whitespace(), }; let span = self.input.take_while(fun).map_err(to_token_error)?; @@ -270,7 +247,8 @@ impl<'a> TokenReader<'a> { let peeked = self.peeked.get_or_insert_with(|| { let mut token = tokenizer.next_token(); if let Ok(token) = &mut token { - let token = token.reset_span(StrRange::new(prev_pos, tokenizer.current_pos() - prev_pos)); + let token = + token.reset_span(StrRange::new(prev_pos, tokenizer.current_pos() - prev_pos)); return Ok(token); } token @@ -310,9 +288,9 @@ impl<'a> TokenReader<'a> { #[cfg(test)] mod tokenizer_tests { - use paths::str_reader::StrRange; - use paths::tokenizer::{TokenError, TokenReader}; - use paths::tokens::Token; + use crate::paths::str_reader::StrRange; + use crate::paths::tokenizer::{TokenError, TokenReader}; + use crate::paths::tokens::Token; fn setup() { let _ = env_logger::try_init(); @@ -393,7 +371,11 @@ mod tokenizer_tests { run( "$..", ( - vec![Token::Absolute(StrRange::new(0, 1)), Token::Dot(StrRange::new(1, 1)), Token::Dot(StrRange::new(2, 1))], + vec![ + Token::Absolute(StrRange::new(0, 1)), + Token::Dot(StrRange::new(1, 1)), + Token::Dot(StrRange::new(2, 1)), + ], Some(TokenError::Eof), ), ); @@ -581,4 +563,4 @@ mod tokenizer_tests { ), ); } -} \ No newline at end of file +} diff --git a/src/paths/tokens.rs b/src/paths/tokens.rs index 0d4cc919..f7cf272f 100644 --- a/src/paths/tokens.rs +++ b/src/paths/tokens.rs @@ -128,4 +128,4 @@ pub enum FilterToken { GreaterOrEqual, And, Or, -} \ No newline at end of file +} diff --git a/src/select/cmp.rs b/src/select/cmp.rs index 74834d34..37b23147 100644 --- a/src/select/cmp.rs +++ b/src/select/cmp.rs @@ -193,7 +193,7 @@ impl Cmp for CmpOr { let mut ret = [v1, v2].concat(); for x in (0..ret.len()).rev() { - for y in (x+1..ret.len()).rev() { + for y in (x + 1..ret.len()).rev() { if ret[x] == ret[y] { ret.remove(y); } @@ -204,7 +204,6 @@ impl Cmp for CmpOr { } } - // #[cfg(test)] // mod cmp_inner_tests { // use serde_json::Value; diff --git a/src/select/expr_term.rs b/src/select/expr_term.rs index f128a7cc..fbc41418 100644 --- a/src/select/expr_term.rs +++ b/src/select/expr_term.rs @@ -1,6 +1,6 @@ +use crate::select::cmp::*; +use crate::select::{to_f64, FilterKey}; use serde_json::{Number, Value}; -use select::cmp::*; -use select::{FilterKey, to_f64}; #[derive(Debug, PartialEq, Clone)] pub(super) enum ExprTerm<'a> { diff --git a/src/select/mod.rs b/src/select/mod.rs index 3c13a90d..b538a782 100644 --- a/src/select/mod.rs +++ b/src/select/mod.rs @@ -1,10 +1,10 @@ use std::collections::HashSet; use std::fmt; -use serde_json::{Number, Value}; use serde_json::map::Entry; +use serde_json::{Number, Value}; -use parser::*; +use crate::parser::*; use self::expr_term::*; use self::value_walker::ValueWalker; @@ -42,7 +42,7 @@ pub enum JsonPathError { EmptyValue, Path(String), Serde(String), - Replacement(String) + Replacement(String), } impl std::error::Error for JsonPathError {} @@ -60,7 +60,10 @@ impl fmt::Display for JsonPathError { JsonPathError::EmptyValue => f.write_str("json value not set"), JsonPathError::Path(msg) => f.write_str(&format!("path error: \n{}\n", msg)), JsonPathError::Serde(msg) => f.write_str(&format!("serde error: \n{}\n", msg)), - JsonPathError::Replacement(msg) => f.write_str(&format!("error occurred during jsonpath replacement: \n{}\n", msg)) + JsonPathError::Replacement(msg) => f.write_str(&format!( + "error occurred during jsonpath replacement: \n{}\n", + msg + )), } } } @@ -87,7 +90,9 @@ impl<'a> FilterTerms<'a> { self.0.pop() } - fn filter_json_term, &mut Vec<&'a Value>, &mut HashSet) -> FilterKey>( + fn filter_json_term< + F: Fn(&Vec<&'a Value>, &mut Vec<&'a Value>, &mut HashSet) -> FilterKey, + >( &mut self, e: ExprTerm<'a>, fun: F, @@ -98,33 +103,40 @@ impl<'a> FilterTerms<'a> { let mut tmp = Vec::new(); let mut not_matched = HashSet::new(); let filter_key = if let Some(FilterKey::String(key)) = fk { - let key_contained = &vec.iter().map(|v| match v { - Value::Object(map) if map.contains_key(&key) => map.get(&key).unwrap(), - _ => v, - }).collect(); + let key_contained = &vec + .iter() + .map(|v| match v { + Value::Object(map) if map.contains_key(&key) => map.get(&key).unwrap(), + _ => v, + }) + .collect(); fun(key_contained, &mut tmp, &mut not_matched) } else { fun(&vec, &mut tmp, &mut not_matched) }; if rel.is_some() { - self.0.push(Some(ExprTerm::Json(rel, Some(filter_key), tmp))); + self.0 + .push(Some(ExprTerm::Json(rel, Some(filter_key), tmp))); } else { - let filtered: Vec<&Value> = vec.iter().enumerate() - .filter( - |(idx, _)| !not_matched.contains(idx) - ) + let filtered: Vec<&Value> = vec + .iter() + .enumerate() + .filter(|(idx, _)| !not_matched.contains(idx)) .map(|(_, v)| *v) .collect(); - self.0.push(Some(ExprTerm::Json(Some(filtered), Some(filter_key), tmp))); + self.0 + .push(Some(ExprTerm::Json(Some(filtered), Some(filter_key), tmp))); } } else { unreachable!("unexpected: ExprTerm: {:?}", e); } } - fn push_json_term, &mut Vec<&'a Value>, &mut HashSet) -> FilterKey>( + fn push_json_term< + F: Fn(&Vec<&'a Value>, &mut Vec<&'a Value>, &mut HashSet) -> FilterKey, + >( &mut self, current: &Option>, fun: F, @@ -135,7 +147,8 @@ impl<'a> FilterTerms<'a> { let mut tmp = Vec::new(); let mut not_matched = HashSet::new(); let filter_key = fun(current, &mut tmp, &mut not_matched); - self.0.push(Some(ExprTerm::Json(None, Some(filter_key), tmp))); + self.0 + .push(Some(ExprTerm::Json(None, Some(filter_key), tmp))); } } @@ -196,7 +209,11 @@ impl<'a> FilterTerms<'a> { debug!("filter_next_with_str : {}, {:?}", key, self.0); } - fn collect_next_with_num(&mut self, current: &Option>, index: f64) -> Option> { + fn collect_next_with_num( + &mut self, + current: &Option>, + index: f64, + ) -> Option> { fn _collect<'a>(tmp: &mut Vec<&'a Value>, vec: &'a [Value], index: f64) { let index = abs_index(index as isize, vec.len()); if let Some(v) = vec.get(index) { @@ -230,10 +247,7 @@ impl<'a> FilterTerms<'a> { } } - debug!( - "collect_next_with_num : {:?}, {:?}", - &index, ¤t - ); + debug!("collect_next_with_num : {:?}, {:?}", &index, ¤t); None } @@ -264,7 +278,11 @@ impl<'a> FilterTerms<'a> { None } - fn collect_next_with_str(&mut self, current: &Option>, keys: &[String]) -> Option> { + fn collect_next_with_str( + &mut self, + current: &Option>, + keys: &[String], + ) -> Option> { if let Some(current) = current { let mut tmp = Vec::new(); for c in current { @@ -285,10 +303,7 @@ impl<'a> FilterTerms<'a> { } } - debug!( - "collect_next_with_str : {:?}, {:?}", - keys, ¤t - ); + debug!("collect_next_with_str : {:?}, {:?}", keys, ¤t); None } @@ -304,7 +319,11 @@ impl<'a> FilterTerms<'a> { None } - fn collect_all_with_str(&mut self, current: &Option>, key: &str) -> Option> { + fn collect_all_with_str( + &mut self, + current: &Option>, + key: &str, + ) -> Option> { if let Some(current) = current { let mut tmp = Vec::new(); ValueWalker::all_with_str(current, &mut tmp, key, false); @@ -316,7 +335,11 @@ impl<'a> FilterTerms<'a> { None } - fn collect_all_with_num(&mut self, current: &Option>, index: f64) -> Option> { + fn collect_all_with_num( + &mut self, + current: &Option>, + index: f64, + ) -> Option> { if let Some(current) = current { let mut tmp = Vec::new(); ValueWalker::all_with_num(current, &mut tmp, index); @@ -506,7 +529,8 @@ impl<'a, 'b> Selector<'a, 'b> { if self.is_last_before_token_match(ParseToken::Array) { if let Some(Some(e)) = self.selector_filter.pop_term() { if let ExprTerm::String(key) = e { - self.selector_filter.filter_next_with_str(&self.current, &key); + self.selector_filter + .filter_next_with_str(&self.current, &key); self.tokens.pop(); return; } @@ -521,12 +545,16 @@ impl<'a, 'b> Selector<'a, 'b> { if let Some(Some(e)) = self.selector_filter.pop_term() { let selector_filter_consumed = match &e { ExprTerm::Number(n) => { - self.current = self.selector_filter.collect_all_with_num(&self.current, to_f64(n)); + self.current = self + .selector_filter + .collect_all_with_num(&self.current, to_f64(n)); self.selector_filter.pop_term(); true } ExprTerm::String(key) => { - self.current = self.selector_filter.collect_all_with_str(&self.current, key); + self.current = self + .selector_filter + .collect_all_with_str(&self.current, key); self.selector_filter.pop_term(); true } @@ -545,10 +573,14 @@ impl<'a, 'b> Selector<'a, 'b> { if let Some(Some(e)) = self.selector_filter.pop_term() { match e { ExprTerm::Number(n) => { - self.current = self.selector_filter.collect_next_with_num(&self.current, to_f64(&n)); + self.current = self + .selector_filter + .collect_next_with_num(&self.current, to_f64(&n)); } ExprTerm::String(key) => { - self.current = self.selector_filter.collect_next_with_str(&self.current, &[key]); + self.current = self + .selector_filter + .collect_next_with_str(&self.current, &[key]); } ExprTerm::Json(rel, _, v) => { if v.is_empty() { @@ -599,7 +631,8 @@ impl<'a, 'b> Selector<'a, 'b> { fn visit_key(&mut self, key: &str) { if let Some(ParseToken::Array) = self.tokens.last() { - self.selector_filter.push_term(Some(ExprTerm::String(key.to_string()))); + self.selector_filter + .push_term(Some(ExprTerm::String(key.to_string()))); return; } @@ -607,10 +640,14 @@ impl<'a, 'b> Selector<'a, 'b> { if self.selector_filter.is_term_empty() { match t { ParseToken::Leaves => { - self.current = self.selector_filter.collect_all_with_str(&self.current, key) + self.current = self + .selector_filter + .collect_all_with_str(&self.current, key) } ParseToken::In => { - self.current = self.selector_filter.collect_next_with_str(&self.current, &[key.to_string()]) + self.current = self + .selector_filter + .collect_next_with_str(&self.current, &[key.to_string()]) } _ => {} } @@ -620,7 +657,8 @@ impl<'a, 'b> Selector<'a, 'b> { self.selector_filter.filter_all_with_str(&self.current, key); } ParseToken::In => { - self.selector_filter.filter_next_with_str(&self.current, key); + self.selector_filter + .filter_next_with_str(&self.current, key); } _ => {} } @@ -634,7 +672,9 @@ impl<'a, 'b> Selector<'a, 'b> { } if let Some(ParseToken::Array) = self.tokens.pop() { - self.current = self.selector_filter.collect_next_with_str(&self.current, keys); + self.current = self + .selector_filter + .collect_next_with_str(&self.current, keys); } else { unreachable!(); } @@ -772,7 +812,8 @@ impl<'a, 'b> NodeVisitor for Selector<'a, 'b> { ParseToken::Key(key) => self.visit_key(key), ParseToken::Keys(keys) => self.visit_keys(keys), ParseToken::Number(v) => { - self.selector_filter.push_term(Some(ExprTerm::Number(Number::from_f64(*v).unwrap()))); + self.selector_filter + .push_term(Some(ExprTerm::Number(Number::from_f64(*v).unwrap()))); } ParseToken::Filter(ref ft) => self.visit_filter(ft), ParseToken::Range(from, to, step) => self.visit_range(from, to, step), @@ -808,7 +849,7 @@ fn replace_value_with_tokens Result, if is_last { if let Entry::Occupied(mut e) = map.entry(token) { let v = e.insert(Value::Null); - if let Some(res) = fun(v,&tokens_clone)? { + if let Some(res) = fun(v, &tokens_clone)? { e.insert(res); } else { e.remove(); @@ -822,7 +863,7 @@ fn replace_value_with_tokens Result, if let Ok(x) = token.parse::() { if is_last { let v = std::mem::replace(&mut vec[x], Value::Null); - if let Some(res) = fun(v,&tokens_clone)? { + if let Some(res) = fun(v, &tokens_clone)? { vec[x] = res; } else { vec.remove(x); @@ -1030,9 +1071,11 @@ impl SelectorMut { Ok(self) } - pub fn replace_with_tokens Result, JsonPathError>>( + pub fn replace_with_tokens< + F: FnMut(Value, &[String]) -> Result, JsonPathError>, + >( &mut self, - fun: &mut F + fun: &mut F, ) -> Result<&mut Self, JsonPathError> { let paths = { let result = self.select()?; @@ -1049,7 +1092,6 @@ impl SelectorMut { } } - #[cfg(test)] mod select_inner_tests { use serde_json::Value; diff --git a/src/select/value_walker.rs b/src/select/value_walker.rs index 63be27df..a56aed8a 100644 --- a/src/select/value_walker.rs +++ b/src/select/value_walker.rs @@ -5,10 +5,12 @@ pub(super) struct ValueWalker; impl<'a> ValueWalker { pub fn all_with_num(vec: &[&'a Value], tmp: &mut Vec<&'a Value>, index: f64) { - Self::walk(vec, tmp, &|v| if v.is_array() { - v.get(index as usize).map(|item| vec![item]) - } else { - None + Self::walk(vec, tmp, &|v| { + if v.is_array() { + v.get(index as usize).map(|item| vec![item]) + } else { + None + } }); } @@ -40,13 +42,19 @@ impl<'a> ValueWalker { }); } - fn walk(vec: &[&'a Value], tmp: &mut Vec<&'a Value>, fun: &F) where F: Fn(&Value) -> Option> { + fn walk(vec: &[&'a Value], tmp: &mut Vec<&'a Value>, fun: &F) + where + F: Fn(&Value) -> Option>, + { for v in vec { Self::_walk(v, tmp, fun); } } - fn _walk(v: &'a Value, tmp: &mut Vec<&'a Value>, fun: &F) where F: Fn(&Value) -> Option> { + fn _walk(v: &'a Value, tmp: &mut Vec<&'a Value>, fun: &F) + where + F: Fn(&Value) -> Option>, + { if let Some(mut ret) = fun(v) { tmp.append(&mut ret); } @@ -66,10 +74,12 @@ impl<'a> ValueWalker { } } - pub fn walk_dedup(v: &'a Value, - tmp: &mut Vec<&'a Value>, - key: &str, - visited: &mut HashSet<*const Value>, ) { + pub fn walk_dedup( + v: &'a Value, + tmp: &mut Vec<&'a Value>, + key: &str, + visited: &mut HashSet<*const Value>, + ) { match v { Value::Object(map) => { if map.contains_key(key) { @@ -89,4 +99,3 @@ impl<'a> ValueWalker { } } } - diff --git a/src/selector/cmp.rs b/src/selector/cmp.rs index 951cfae2..40cd89bc 100644 --- a/src/selector/cmp.rs +++ b/src/selector/cmp.rs @@ -209,12 +209,11 @@ impl Cmp for CmpOr { } } - #[cfg(test)] mod cmp_inner_tests { use serde_json::Value; - use selector::cmp::*; + use crate::selector::cmp::*; #[test] fn cmp_eq() { diff --git a/src/selector/mod.rs b/src/selector/mod.rs index a188668c..a0f4acf6 100644 --- a/src/selector/mod.rs +++ b/src/selector/mod.rs @@ -1,7 +1,7 @@ -pub use self::selector_impl::{JsonSelector, JsonSelectorMut}; +pub use self::selector_impl::{JsonSelector, MultiJsonSelectorMut}; mod cmp; -mod terms; mod selector_impl; +mod terms; +mod utils; mod value_walker; -mod utils; \ No newline at end of file diff --git a/src/selector/selector_impl.rs b/src/selector/selector_impl.rs index 2a22c454..f75feca4 100644 --- a/src/selector/selector_impl.rs +++ b/src/selector/selector_impl.rs @@ -1,18 +1,25 @@ use std::collections::HashSet; +use std::future::Future; +use std::pin::Pin; use std::rc::Rc; +use std::sync::Arc; -use serde_json::{Number, Value}; +use futures::future::BoxFuture; +use futures::stream::FuturesOrdered; +use futures::StreamExt; +use rayon::prelude::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; use serde_json::map::Entry; +use serde_json::{Number, Value}; -use JsonPathError; -use paths::{ParserTokenHandler, PathParser, StrRange, tokens::*}; use super::utils; +use crate::paths::{tokens::*, ParserTokenHandler, PathParser, StrRange}; +use crate::{parser, JsonPathError}; use super::terms::*; #[derive(Debug, Default, Clone)] pub struct JsonSelector<'a> { - parser: Option>>, + parser: Option>>, value: Option<&'a Value>, tokens: Vec, current: Option>, @@ -20,10 +27,12 @@ pub struct JsonSelector<'a> { selector_filter: FilterTerms<'a>, } +type FutureValue = Pin> + Send>>; + impl<'a> JsonSelector<'a> { pub fn new(parser: PathParser<'a>) -> Self { JsonSelector { - parser: Some(Rc::new(parser)), + parser: Some(Arc::new(parser)), value: None, tokens: Vec::new(), current: None, @@ -32,7 +41,7 @@ impl<'a> JsonSelector<'a> { } } - pub fn new_ref(parser: Rc>) -> Self { + pub fn new_ref(parser: Arc>) -> Self { JsonSelector { parser: Some(parser), value: None, @@ -44,11 +53,11 @@ impl<'a> JsonSelector<'a> { } pub fn reset_parser(&mut self, parser: PathParser<'a>) -> &mut Self { - self.parser = Some(Rc::new(parser)); + self.parser = Some(Arc::new(parser)); self } - pub fn reset_parser_ref(&mut self, parser: Rc>) -> &mut Self { + pub fn reset_parser_ref(&mut self, parser: Arc>) -> &mut Self { self.parser = Some(parser); self } @@ -111,9 +120,13 @@ impl<'a> JsonSelector<'a> { } } - fn compute_absolute_path_filter(&mut self, token: &ParseToken, parse_value_reader: &F) -> bool - where - F: Fn(&StrRange) -> &'a str + fn compute_absolute_path_filter( + &mut self, + token: &ParseToken, + parse_value_reader: &F, + ) -> bool + where + F: Fn(&StrRange) -> &'a str, { if !self.selectors.is_empty() { match token { @@ -140,7 +153,10 @@ impl<'a> JsonSelector<'a> { return false; } - self.selectors.last_mut().unwrap().handle(token, parse_value_reader); + self.selectors + .last_mut() + .unwrap() + .handle(token, parse_value_reader); true } } @@ -168,6 +184,7 @@ impl<'a> JsonSelector<'a> { } fn visit_relative(&mut self) { + println!("visit_relative"); if let Some(ParseToken::Array) = self.tokens.last() { let array_token = self.tokens.pop(); if let Some(ParseToken::Leaves) = self.tokens.last() { @@ -183,7 +200,9 @@ impl<'a> JsonSelector<'a> { if self.is_last_before_token_match(ParseToken::Array) { if let Some(Some(e)) = self.selector_filter.pop_term() { if let ExprTerm::String(key) = e { - self.current = self.selector_filter.filter_next_with_str(self.current.take(), key); + self.current = self + .selector_filter + .filter_next_with_str(self.current.take(), key); self.tokens.pop(); return; } @@ -198,12 +217,16 @@ impl<'a> JsonSelector<'a> { if let Some(Some(e)) = self.selector_filter.pop_term() { let selector_filter_consumed = match e { ExprTerm::Number(n) => { - self.current = self.selector_filter.collect_all_with_num(self.current.take(), utils::to_f64(&n)); + self.current = self + .selector_filter + .collect_all_with_num(self.current.take(), utils::to_f64(&n)); self.selector_filter.pop_term(); true } ExprTerm::String(key) => { - self.current = self.selector_filter.collect_all_with_str(self.current.take(), key); + self.current = self + .selector_filter + .collect_all_with_str(self.current.take(), key); self.selector_filter.pop_term(); true } @@ -222,10 +245,14 @@ impl<'a> JsonSelector<'a> { if let Some(Some(e)) = self.selector_filter.pop_term() { match e { ExprTerm::Number(n) => { - self.current = self.selector_filter.collect_next_with_num(self.current.take(), utils::to_f64(&n)); + self.current = self + .selector_filter + .collect_next_with_num(self.current.take(), utils::to_f64(&n)); } ExprTerm::String(key) => { - self.current = self.selector_filter.collect_next_with_str(self.current.take(), &[key]); + self.current = self + .selector_filter + .collect_next_with_str(self.current.take(), &[key]); } ExprTerm::Json(rel, _, v) => { if v.is_empty() { @@ -284,20 +311,28 @@ impl<'a> JsonSelector<'a> { if self.selector_filter.is_term_empty() { match t { ParseToken::Leaves => { - self.current = self.selector_filter.collect_all_with_str(self.current.take(), key) + self.current = self + .selector_filter + .collect_all_with_str(self.current.take(), key) } ParseToken::In => { - self.current = self.selector_filter.collect_next_with_str(self.current.take(), &[key]) + self.current = self + .selector_filter + .collect_next_with_str(self.current.take(), &[key]) } _ => {} } } else { match t { ParseToken::Leaves => { - self.current = self.selector_filter.filter_all_with_str(self.current.take(), key); + self.current = self + .selector_filter + .filter_all_with_str(self.current.take(), key); } ParseToken::In => { - self.current = self.selector_filter.filter_next_with_str(self.current.take(), key); + self.current = self + .selector_filter + .filter_next_with_str(self.current.take(), key); } _ => {} } @@ -311,7 +346,9 @@ impl<'a> JsonSelector<'a> { } if let Some(ParseToken::Array) = self.tokens.pop() { - self.current = self.selector_filter.collect_next_with_str(self.current.take(), keys); + self.current = self + .selector_filter + .collect_next_with_str(self.current.take(), keys); } else { unreachable!(); } @@ -425,12 +462,13 @@ impl<'a> JsonSelector<'a> { impl<'a> ParserTokenHandler<'a> for JsonSelector<'a> { fn handle(&mut self, token: &ParseToken, parse_value_reader: &F) - where - F: Fn(&StrRange) -> &'a str + where + F: Fn(&StrRange) -> &'a str, { debug!("token: {:?}, stack: {:?}", token, self.tokens); if self.compute_absolute_path_filter(token, parse_value_reader) { + println!("compute_absolute_path_filter"); return; } @@ -450,11 +488,12 @@ impl<'a> ParserTokenHandler<'a> for JsonSelector<'a> { self.visit_key(key); } ParseToken::Keys(keys) => { - let keys: Vec<&str> = keys.iter().map(|s| { parse_value_reader(s) }).collect(); + let keys: Vec<&str> = keys.iter().map(|s| parse_value_reader(s)).collect(); self.visit_keys(&keys) } ParseToken::Number(v) => { - self.selector_filter.push_term(Some(ExprTerm::Number(Number::from_f64(*v).unwrap()))); + self.selector_filter + .push_term(Some(ExprTerm::Number(Number::from_f64(*v).unwrap()))); } ParseToken::Filter(ref ft) => self.visit_filter(ft), ParseToken::Range(from, to, step) => self.visit_range(from, to, step), @@ -466,30 +505,34 @@ impl<'a> ParserTokenHandler<'a> for JsonSelector<'a> { } } -#[derive(Default,Clone)] -pub struct JsonSelectorMut<'a> { +#[derive(Default, Clone)] +pub struct MultiJsonSelectorMut<'a> { value: Option, - parser: Option>>, + parser: Option>>>, } -impl<'a> JsonSelectorMut<'a> { +impl<'a> MultiJsonSelectorMut<'a> { pub fn new(parser: PathParser<'a>) -> Self { - Self::new_ref(Rc::new(parser)) + Self::new_ref(vec![Arc::new(parser)]) + } + + pub fn new_multi_parser(parsers: Vec>>) -> Self { + Self::new_ref(parsers) } - pub fn new_ref(parser: Rc>) -> Self { - JsonSelectorMut { + pub fn new_ref(parser: Vec>>) -> Self { + MultiJsonSelectorMut { value: None, parser: Some(parser), } } pub fn reset_parser(&mut self, parser: PathParser<'a>) -> &mut Self { - self.parser = Some(Rc::new(parser)); + self.parser = Some(vec![Arc::new(parser)]); self } - pub fn reset_parser_ref(&mut self, parser: Rc>) -> &mut Self { + pub fn reset_parser_ref(&mut self, parser: Vec>>) -> &mut Self { self.parser = Some(parser); self } @@ -512,28 +555,34 @@ impl<'a> JsonSelectorMut<'a> { } fn select(&self) -> Result, JsonPathError> { - let mut selector = JsonSelector::default(); - - if let Some(parser) = self.parser.as_ref() { - selector.reset_parser_ref(Rc::clone(parser)); - } else { - return Err(JsonPathError::EmptyPath); - } - - if let Some(value) = self.value.as_ref() { - selector.value(value); - } else { - return Err(JsonPathError::EmptyValue); - } + let res: Vec, JsonPathError>> = + if let Some(parser) = self.parser.as_ref() { + parser + .iter() + .map(|p| { + let mut selector = JsonSelector::default(); + selector.reset_parser_ref(p.clone()); + if let Some(value) = self.value.as_ref() { + selector.value(value); + } else { + return Err(JsonPathError::EmptyValue); + } + Ok(selector.select()?) + }) + .collect() + } else { + return Err(JsonPathError::EmptyPath); + }; - selector.select() + Ok(res.into_iter().flatten().flatten().collect()) } pub fn replace_with(&mut self, fun: &mut F) -> Result<&mut Self, JsonPathError> - where - F: FnMut(Value) -> Result, JsonPathError>, + where + F: FnMut(Value) -> Result, JsonPathError>, { let result = self.select()?; + let result = result.into_iter().filter(|v| !v.is_object() && !v.is_array()).collect(); let paths = self.compute_paths(result); if let Some(ref mut value) = &mut self.value { @@ -545,9 +594,75 @@ impl<'a> JsonSelectorMut<'a> { Ok(self) } - fn replace_value(mut tokens: Vec, value: &mut Value, fun: &mut F) -> Result<(), JsonPathError> - where - F: FnMut(Value) -> Result, JsonPathError> + fn get_json_pointers(&mut self) -> Result, JsonPathError> { + let result = self.select()?; + let paths = self.compute_paths(result); + + let json_pointers = paths + .iter() + .map(|tokens| "/".to_owned() + &tokens.join("/")) + .collect::>(); + + Ok(json_pointers) + } + + pub fn replace_with_async( + &mut self, + fun: F, + ) -> Result>, JsonPathError> + where + F: Fn(Value) -> FutureValue, + { + let mut futures = FuturesOrdered::new(); + + let json_pointers = self.get_json_pointers()?; + + if let Some(ref mut value) = &mut self.value { + for pointer in json_pointers.iter() { + let target = value + .pointer_mut(pointer) + .ok_or(JsonPathError::EmptyValue)?; + let future = fun(std::mem::replace(target, Value::Null)); + futures.push_back(future); + } + }; + + let result_future = Box::pin(async move { + // FuturesOrdered maintains a strict FIFO order, so we can use the index to get the pointer + let mut i = 0; + while let Some(res) = futures.next().await { + // Get the pointer for this value + let pointer = json_pointers.get(i).ok_or(JsonPathError::EmptyValue)?; + + if let Some(v) = res { + if let Some(ref mut value) = &mut self.value { + let target = value + .pointer_mut(pointer) + .ok_or(JsonPathError::EmptyValue)?; + *target = v; + } + } else { + // If None is returned then delete the value + if let Some(ref mut value) = &mut self.value { + value.as_object_mut().unwrap().remove(pointer); + } + } + i += 1; + } + + Ok::<_, JsonPathError>(self) + }); + + Ok(result_future) + } + + fn replace_value( + mut tokens: Vec, + value: &mut Value, + fun: &mut F, + ) -> Result<(), JsonPathError> + where + F: FnMut(Value) -> Result, JsonPathError>, { let mut target = value; diff --git a/src/selector/terms.rs b/src/selector/terms.rs index 6f562b3c..49e57452 100644 --- a/src/selector/terms.rs +++ b/src/selector/terms.rs @@ -11,13 +11,17 @@ pub enum ExprTerm<'a> { String(&'a str), Number(Number), Bool(bool), - Json(Option>, Option>, Vec<&'a Value>), + Json( + Option>, + Option>, + Vec<&'a Value>, + ), } impl<'a> ExprTerm<'a> { fn cmp_string(s1: &str, other: &mut ExprTerm<'a>, cmp_fn: &C) -> ExprTerm<'a> - where - C: Cmp, + where + C: Cmp, { match other { ExprTerm::String(s2) => { @@ -31,19 +35,21 @@ impl<'a> ExprTerm<'a> { } fn cmp_number(n1: &Number, other: &mut ExprTerm<'a>, cmp_fn: &C) -> ExprTerm<'a> - where - C: Cmp, + where + C: Cmp, { match other { - ExprTerm::Number(n2) => ExprTerm::Bool(cmp_fn.cmp_f64(utils::to_f64(n1), utils::to_f64(n2))), + ExprTerm::Number(n2) => { + ExprTerm::Bool(cmp_fn.cmp_f64(utils::to_f64(n1), utils::to_f64(n2))) + } ExprTerm::Json(_, _, _) => unreachable!(), _ => ExprTerm::Bool(cmp_fn.default()), } } fn cmp_bool(b1: &bool, other: &mut ExprTerm<'a>, cmp_fn: &C) -> ExprTerm<'a> - where - C: Cmp, + where + C: Cmp, { match other { ExprTerm::Bool(b2) => ExprTerm::Bool(cmp_fn.cmp_bool(*b1, *b2)), @@ -52,80 +58,95 @@ impl<'a> ExprTerm<'a> { } } - fn cmp_json_string(s2: &str, - fk1: &Option, - vec1: &[&'a Value], - cmp_fn: &C) -> Vec<&'a Value> - where - C: Cmp + fn cmp_json_string( + s2: &str, + fk1: &Option, + vec1: &[&'a Value], + cmp_fn: &C, + ) -> Vec<&'a Value> + where + C: Cmp, { let path_str = utils::to_path_str(s2); - vec1.iter().filter(|v1| match v1 { - Value::String(s1) => { - cmp_fn.cmp_string(s1, path_str.get_key()) - } - Value::Object(map1) => { - if let Some(FilterKey::String(k)) = fk1 { - if let Some(Value::String(s1)) = map1.get(*k) { - return cmp_fn.cmp_string(s1, path_str.get_key()); + vec1.iter() + .filter(|v1| match v1 { + Value::String(s1) => cmp_fn.cmp_string(s1, path_str.get_key()), + Value::Object(map1) => { + if let Some(FilterKey::String(k)) = fk1 { + if let Some(Value::String(s1)) = map1.get(*k) { + return cmp_fn.cmp_string(s1, path_str.get_key()); + } } + cmp_fn.default() } - cmp_fn.default() - } - _ => cmp_fn.default(), - }).copied().collect() - } - - fn cmp_json_number(n2: &Number, - fk1: &Option, - vec1: &[&'a Value], - cmp_fn: &C) -> Vec<&'a Value> - where - C: Cmp + _ => cmp_fn.default(), + }) + .copied() + .collect() + } + + fn cmp_json_number( + n2: &Number, + fk1: &Option, + vec1: &[&'a Value], + cmp_fn: &C, + ) -> Vec<&'a Value> + where + C: Cmp, { let n2 = utils::to_f64(n2); - vec1.iter().filter(|v1| match v1 { - Value::Number(n1) => cmp_fn.cmp_f64(utils::to_f64(n1), n2), - Value::Object(map1) => { - if let Some(FilterKey::String(k)) = fk1 { - if let Some(Value::Number(n1)) = map1.get(*k) { - return cmp_fn.cmp_f64(utils::to_f64(n1), n2); + vec1.iter() + .filter(|v1| match v1 { + Value::Number(n1) => cmp_fn.cmp_f64(utils::to_f64(n1), n2), + Value::Object(map1) => { + if let Some(FilterKey::String(k)) = fk1 { + if let Some(Value::Number(n1)) = map1.get(*k) { + return cmp_fn.cmp_f64(utils::to_f64(n1), n2); + } } + cmp_fn.default() } - cmp_fn.default() - } - _ => cmp_fn.default(), - }).copied().collect() - } - - fn cmp_json_bool(b2: &bool, - fk1: &Option, - vec1: &[&'a Value], - cmp_fn: &C1) -> Vec<&'a Value> - where - C1: Cmp + _ => cmp_fn.default(), + }) + .copied() + .collect() + } + + fn cmp_json_bool( + b2: &bool, + fk1: &Option, + vec1: &[&'a Value], + cmp_fn: &C1, + ) -> Vec<&'a Value> + where + C1: Cmp, { - vec1.iter().filter(|v1| match v1 { - Value::Bool(b1) => cmp_fn.cmp_bool(*b1, *b2), - Value::Object(map1) => { - if let Some(FilterKey::String(k)) = fk1 { - if let Some(Value::Bool(b1)) = map1.get(*k) { - return cmp_fn.cmp_bool(*b1, *b2); + vec1.iter() + .filter(|v1| match v1 { + Value::Bool(b1) => cmp_fn.cmp_bool(*b1, *b2), + Value::Object(map1) => { + if let Some(FilterKey::String(k)) = fk1 { + if let Some(Value::Bool(b1)) = map1.get(*k) { + return cmp_fn.cmp_bool(*b1, *b2); + } } + cmp_fn.default() } - cmp_fn.default() - } - _ => cmp_fn.default(), - }).copied().collect() - } - - fn cmp_json_json(rel: &Option>, - parent: &Option>, - vec1: &[&'a Value], - vec2: &[&'a Value], - cmp_fn: &C1) -> Vec<&'a Value> - where - C1: Cmp + _ => cmp_fn.default(), + }) + .copied() + .collect() + } + + fn cmp_json_json( + rel: &Option>, + parent: &Option>, + vec1: &[&'a Value], + vec2: &[&'a Value], + cmp_fn: &C1, + ) -> Vec<&'a Value> + where + C1: Cmp, { if let Some(vec1) = rel { if let Some(vec2) = parent { @@ -140,13 +161,15 @@ impl<'a> ExprTerm<'a> { } } - fn cmp_json(rel: Option>, - fk1: Option>, - vec1: &mut Vec<&'a Value>, - other: &mut ExprTerm<'a>, - cmp_fn: &C1) -> ExprTerm<'a> - where - C1: Cmp + fn cmp_json( + rel: Option>, + fk1: Option>, + vec1: &mut Vec<&'a Value>, + other: &mut ExprTerm<'a>, + cmp_fn: &C1, + ) -> ExprTerm<'a> + where + C1: Cmp, { let ret: Vec<&Value> = match other { ExprTerm::String(s2) => Self::cmp_json_string(s2, &fk1, vec1, cmp_fn), @@ -186,12 +209,11 @@ impl<'a> ExprTerm<'a> { return ExprTerm::Json(Some(Vec::new()), None, ret); } - let ret_set: HashSet<*const Value> = ret.iter() - .fold(HashSet::new(), |mut acc, v| { - let ptr = *v as *const Value; - acc.insert(ptr); - acc - }); + let ret_set: HashSet<*const Value> = ret.iter().fold(HashSet::new(), |mut acc, v| { + let ptr = *v as *const Value; + acc.insert(ptr); + acc + }); let mut tmp = Vec::new(); for rv in rel { @@ -209,9 +231,9 @@ impl<'a> ExprTerm<'a> { } fn cmp(&mut self, other: &mut Self, cmp_fn: &C1, rev_cmp_fn: &C2) -> ExprTerm<'a> - where - C1: Cmp, - C2: Cmp + where + C1: Cmp, + C2: Cmp, { if let ExprTerm::Json(_, _, _) = other { if let ExprTerm::Json(_, _, _) = &self { @@ -225,8 +247,9 @@ impl<'a> ExprTerm<'a> { ExprTerm::String(s1) => Self::cmp_string(s1, other, cmp_fn), ExprTerm::Number(n1) => Self::cmp_number(n1, other, cmp_fn), ExprTerm::Bool(b1) => Self::cmp_bool(b1, other, cmp_fn), - ExprTerm::Json(rel, fk1, vec1) => + ExprTerm::Json(rel, fk1, vec1) => { Self::cmp_json(rel.take(), fk1.take(), vec1, other, cmp_fn) + } } } @@ -310,7 +333,7 @@ pub enum FilterKey<'a> { struct FilterResult<'a> { key: FilterKey<'a>, - collected: Vec<&'a Value> + collected: Vec<&'a Value>, } #[derive(Debug, Default, Clone)] @@ -336,8 +359,8 @@ impl<'a> FilterTerms<'a> { } fn filter_json_term(&mut self, e: ExprTerm<'a>, fun: F) - where - F: Fn(&Vec<&'a Value>, &mut Option>) -> FilterResult<'a>, + where + F: Fn(&Vec<&'a Value>, &mut Option>) -> FilterResult<'a>, { debug!("filter_json_term: {:?}", e); @@ -353,25 +376,34 @@ impl<'a> FilterTerms<'a> { self.push_term(Some(ExprTerm::Json( rel, Some(filter_result.key), - filter_result.collected))); + filter_result.collected, + ))); } else { let not_matched = not_matched.unwrap(); - let filtered = vec.iter().enumerate() + let filtered = vec + .iter() + .enumerate() .filter(|(idx, _)| !not_matched.contains(idx)) - .map(|(_, v)| *v).collect(); + .map(|(_, v)| *v) + .collect(); self.push_term(Some(ExprTerm::Json( Some(filtered), Some(filter_result.key), - filter_result.collected))); + filter_result.collected, + ))); } } else { unreachable!("unexpected: ExprTerm: {:?}", e); } } - fn push_json_term(&mut self, current: Option>, fun: F) -> Option> - where - F: Fn(&Vec<&'a Value>, &mut Option>) -> FilterResult<'a>, + fn push_json_term( + &mut self, + current: Option>, + fun: F, + ) -> Option> + where + F: Fn(&Vec<&'a Value>, &mut Option>) -> FilterResult<'a>, { debug!("push_json_term: {:?}", ¤t); @@ -380,15 +412,16 @@ impl<'a> FilterTerms<'a> { self.push_term(Some(ExprTerm::Json( None, Some(filter_result.key), - filter_result.collected))); + filter_result.collected, + ))); } current } fn filter(&mut self, current: Option>, fun: F) -> Option> - where - F: Fn(&Vec<&'a Value>, &mut Option>) -> FilterResult<'a>, + where + F: Fn(&Vec<&'a Value>, &mut Option>) -> FilterResult<'a>, { let peek = self.pop_term(); @@ -403,42 +436,49 @@ impl<'a> FilterTerms<'a> { current } - pub fn filter_all_with_str(&mut self, current: Option>, key: &'a str) -> Option> { - let current = self.filter(current, |vec, _| { - FilterResult { - key: FilterKey::All, - collected: ValueWalker::all_with_str(vec, key) - } + pub fn filter_all_with_str( + &mut self, + current: Option>, + key: &'a str, + ) -> Option> { + let current = self.filter(current, |vec, _| FilterResult { + key: FilterKey::All, + collected: ValueWalker::all_with_str(vec, key), }); debug!("filter_all_with_str : {}, {:?}", key, self.0); current } - pub fn filter_next_with_str(&mut self, current: Option>, key: &'a str) -> Option> { + pub fn filter_next_with_str( + &mut self, + current: Option>, + key: &'a str, + ) -> Option> { let current = self.filter(current, |vec, not_matched| { let mut visited = HashSet::new(); let mut acc = Vec::new(); let path_key = &utils::to_path_str(key); - ValueWalker::walk_dedup_all(vec, - path_key.get_key(), - &mut visited, - &mut |v| { - acc.push(v); - }, - &mut |idx| { - if let Some(set) = not_matched { - set.insert(idx); - } - }, - 0 + ValueWalker::walk_dedup_all( + vec, + path_key.get_key(), + &mut visited, + &mut |v| { + acc.push(v); + }, + &mut |idx| { + if let Some(set) = not_matched { + set.insert(idx); + } + }, + 0, ); FilterResult { key: FilterKey::String(path_key.get_origin_key()), - collected: acc + collected: acc, } }); @@ -446,7 +486,11 @@ impl<'a> FilterTerms<'a> { current } - pub fn collect_next_with_num(&mut self, current: Option>, index: f64) -> Option> { + pub fn collect_next_with_num( + &mut self, + current: Option>, + index: f64, + ) -> Option> { if current.is_none() { debug!("collect_next_with_num : {:?}, {:?}", &index, ¤t); return current; @@ -482,12 +526,13 @@ impl<'a> FilterTerms<'a> { Some(acc) } - pub fn collect_next_with_str(&mut self, current: Option>, keys: &[&'a str]) -> Option> { + pub fn collect_next_with_str( + &mut self, + current: Option>, + keys: &[&'a str], + ) -> Option> { if current.is_none() { - debug!( - "collect_next_with_str : {:?}, {:?}", - keys, ¤t - ); + debug!("collect_next_with_str : {:?}, {:?}", keys, ¤t); return current; } @@ -518,7 +563,11 @@ impl<'a> FilterTerms<'a> { Some(ValueWalker::all(current.as_ref().unwrap())) } - pub fn collect_all_with_str(&mut self, current: Option>, key: &'a str) -> Option> { + pub fn collect_all_with_str( + &mut self, + current: Option>, + key: &'a str, + ) -> Option> { if current.is_none() { debug!("collect_all_with_str: {}, {:?}", key, ¤t); return current; @@ -528,7 +577,11 @@ impl<'a> FilterTerms<'a> { Some(ret) } - pub fn collect_all_with_num(&mut self, mut current: Option>, index: f64) -> Option> { + pub fn collect_all_with_num( + &mut self, + mut current: Option>, + index: f64, + ) -> Option> { if let Some(current) = current.take() { let ret = ValueWalker::all_with_num(¤t, index); if !ret.is_empty() { @@ -545,7 +598,7 @@ impl<'a> FilterTerms<'a> { mod expr_term_inner_tests { use serde_json::{Number, Value}; - use selector::terms::ExprTerm; + use crate::selector::terms::ExprTerm; #[test] fn value_vec_into() { diff --git a/src/selector/utils.rs b/src/selector/utils.rs index ec250625..9c224027 100644 --- a/src/selector/utils.rs +++ b/src/selector/utils.rs @@ -20,7 +20,7 @@ pub fn abs_index(n: isize, len: usize) -> usize { pub struct PathKey<'a> { key: &'a str, - special_key: Option + special_key: Option, } impl<'a: 'b, 'b> PathKey<'a> { @@ -40,7 +40,7 @@ impl<'a: 'b, 'b> PathKey<'a> { pub fn to_path_str(key: &str) -> PathKey { let mut path_key = PathKey { key, - special_key: None + special_key: None, }; if key.starts_with('\'') || key.starts_with('"') { @@ -51,4 +51,4 @@ pub fn to_path_str(key: &str) -> PathKey { } } path_key -} \ No newline at end of file +} diff --git a/src/selector/value_walker.rs b/src/selector/value_walker.rs index 2c7c1039..d538e55c 100644 --- a/src/selector/value_walker.rs +++ b/src/selector/value_walker.rs @@ -1,8 +1,8 @@ use std::collections::HashSet; -use serde_json::Value; use super::utils; -use selector::utils::PathKey; +use crate::selector::utils::PathKey; +use serde_json::Value; pub(super) struct ValueWalker; @@ -52,19 +52,23 @@ impl<'a> ValueWalker { pub fn all_with_str(vec: &[&'a Value], key: &'a str) -> Vec<&'a Value> { let path_key = utils::to_path_str(key); - Self::walk(vec, &|v, acc| if let Value::Object(map) = v { - if let Some(v) = map.get(path_key.get_key()) { - acc.push(v); + Self::walk(vec, &|v, acc| { + if let Value::Object(map) = v { + if let Some(v) = map.get(path_key.get_key()) { + acc.push(v); + } } }) } pub fn all_with_strs(vec: &[&'a Value], keys: &[&'a str]) -> Vec<&'a Value> { - let path_keys: &Vec = &keys.iter().map(|key| { utils::to_path_str(key) }).collect(); + let path_keys: &Vec = &keys.iter().map(|key| utils::to_path_str(key)).collect(); vec.iter().fold(Vec::new(), |mut acc, v| { if let Value::Object(map) = v { - path_keys.iter().for_each(|pk| if let Some(v) = map.get(pk.get_key()) { - acc.push(v) + path_keys.iter().for_each(|pk| { + if let Some(v) = map.get(pk.get_key()) { + acc.push(v) + } }); } acc @@ -72,20 +76,18 @@ impl<'a> ValueWalker { } pub fn all(vec: &[&'a Value]) -> Vec<&'a Value> { - Self::walk(vec, &|v, acc| { - match v { - Value::Array(ay) => acc.extend(ay), - Value::Object(map) => { - acc.extend(map.values()); - } - _ => {} + Self::walk(vec, &|v, acc| match v { + Value::Array(ay) => acc.extend(ay), + Value::Object(map) => { + acc.extend(map.values()); } + _ => {} }) } fn walk(vec: &[&'a Value], fun: &F) -> Vec<&'a Value> - where - F: Fn(&'a Value, &mut Vec<&'a Value>), + where + F: Fn(&'a Value, &mut Vec<&'a Value>), { vec.iter().fold(Vec::new(), |mut acc, v| { Self::_walk(v, &mut acc, fun); @@ -94,8 +96,8 @@ impl<'a> ValueWalker { } fn _walk(v: &'a Value, acc: &mut Vec<&'a Value>, fun: &F) - where - F: Fn(&'a Value, &mut Vec<&'a Value>), + where + F: Fn(&'a Value, &mut Vec<&'a Value>), { fun(v, acc); @@ -104,41 +106,41 @@ impl<'a> ValueWalker { vec.iter().for_each(|v| Self::_walk(v, acc, fun)); } Value::Object(map) => { - map.values().into_iter().for_each(|v| Self::_walk(v, acc, fun)); + map.values() + .into_iter() + .for_each(|v| Self::_walk(v, acc, fun)); } _ => {} } } - pub fn walk_dedup_all(vec: &[&'a Value], - key: &str, - visited: &mut HashSet<*const Value>, - is_contain: &mut F1, - is_not_contain: &mut F2, - depth: usize) - where - F1: FnMut(&'a Value), - F2: FnMut(usize), + pub fn walk_dedup_all( + vec: &[&'a Value], + key: &str, + visited: &mut HashSet<*const Value>, + is_contain: &mut F1, + is_not_contain: &mut F2, + depth: usize, + ) where + F1: FnMut(&'a Value), + F2: FnMut(usize), { - vec.iter().enumerate().for_each(|(index, v)| Self::walk_dedup(v, - key, - visited, - index, - is_contain, - is_not_contain, - depth)); + vec.iter().enumerate().for_each(|(index, v)| { + Self::walk_dedup(v, key, visited, index, is_contain, is_not_contain, depth) + }); } - fn walk_dedup(v: &'a Value, - key: &str, - visited: &mut HashSet<*const Value>, - index: usize, - is_contain: &mut F1, - is_not_contain: &mut F2, - depth: usize) - where - F1: FnMut(&'a Value), - F2: FnMut(usize), + fn walk_dedup( + v: &'a Value, + key: &str, + visited: &mut HashSet<*const Value>, + index: usize, + is_contain: &mut F1, + is_not_contain: &mut F2, + depth: usize, + ) where + F1: FnMut(&'a Value), + F2: FnMut(usize), { let ptr = v as *const Value; if visited.contains(&ptr) { @@ -162,7 +164,15 @@ impl<'a> ValueWalker { is_not_contain(index); } vec.iter().for_each(|v| { - Self::walk_dedup(v, key, visited, index, is_contain, is_not_contain, depth + 1); + Self::walk_dedup( + v, + key, + visited, + index, + is_contain, + is_not_contain, + depth + 1, + ); }) } _ => { @@ -173,4 +183,3 @@ impl<'a> ValueWalker { } } } - diff --git a/tests/array_filter.rs b/tests/array_filter.rs index 7f1cf332..cf257d74 100644 --- a/tests/array_filter.rs +++ b/tests/array_filter.rs @@ -256,9 +256,5 @@ fn bugs40_bracket_notation_after_recursive_descent() { fn bugs50() { setup(); - select_and_then_compare( - "$[0]", - json!({"f": [1,2,3]}), - json!([]) - ); -} \ No newline at end of file + select_and_then_compare("$[0]", json!({"f": [1,2,3]}), json!([])); +} diff --git a/tests/async.rs b/tests/async.rs new file mode 100644 index 00000000..346eff9c --- /dev/null +++ b/tests/async.rs @@ -0,0 +1,144 @@ +extern crate jsonpath_lib as jsonpath; +#[macro_use] +extern crate serde_json; + +use std::{ + pin::Pin, + sync::{Arc, Mutex}, + task::{Context, Poll}, +}; + +use common::{read_json, setup}; +use futures::{ + channel::mpsc::{channel, Receiver, Sender}, + task::Waker, + Future, SinkExt, StreamExt, +}; +use jsonpath::{JsonSelector, MultiJsonSelectorMut, PathParser}; +use serde_json::Value; + +mod common; + +#[derive(Clone)] +struct ValueFuture { + inner: Arc>>, +} + +impl ValueFuture { + fn new() -> Self { + ValueFuture { + inner: Arc::new(Mutex::new(None)), + } + } + + fn set_value(&self, value: T) { + let mut inner = self.inner.lock().unwrap(); + *inner = Some(value); + } +} + +impl Future for ValueFuture { + type Output = T; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let inner = self.inner.lock().unwrap(); + if let Some(value) = inner.as_ref() { + Poll::Ready(value.clone()) + } else { + // This future isn't ready yet, so we'll notify the context when it is. + cx.waker().wake_by_ref(); + Poll::Pending + } + } +} + +struct CryptoRequest { + bags: Mutex>, +} + +impl CryptoRequest { + fn new() -> Self { + Self { + bags: Mutex::new(Vec::new()), + } + } + + fn new_field(&self, input: Value) -> CryptoField { + let bag = CryptoField::new(input); + self.bags.lock().unwrap().push(bag.clone()); + bag + } + + async fn send_request(&self) { + let mut bags = self.bags.lock().unwrap(); + let inputs = bags + .iter_mut() + .filter_map(|bag| bag.input.take()) + .collect::>(); + // let _ = reqwest::Client::new() + // .post("https://blackhole.posterior.io/vr5kvy") + // .body(serde_json::to_string(&inputs).unwrap()) + // .send() + // .await + // .unwrap(); + for bag in bags.iter_mut() { + bag.value.set_value(serde_json::json!(42)); + } + } +} + +#[derive(Clone)] +struct CryptoField { + input: Option, + value: ValueFuture, +} + +impl CryptoField { + fn new(input: Value) -> Self { + Self { + input: Some(input), + value: ValueFuture::new(), + } + } + + pub fn value(self) -> ValueFuture { + self.value + } +} + +#[tokio::test] +async fn selector_mut() { + setup(); + + let parser = PathParser::compile("$.store..price").unwrap(); + let parser_two = PathParser::compile("$.store..author").unwrap(); + let mut selector_mut = + MultiJsonSelectorMut::new_multi_parser(vec![parser.into(), parser_two.into()]); + + let crypto_request = Arc::new(CryptoRequest::new()); + + let result_futures = selector_mut + .value(read_json("./benchmark/example.json")) + .replace_with_async(|v| { + let bag: CryptoField = crypto_request.new_field(v); + + Box::pin(async move { + let val = bag.value().await; + Some(val) + }) + }) + .unwrap(); + + crypto_request.send_request().await; + + let result = result_futures.await.unwrap().take().unwrap(); + + let parser = PathParser::compile("$.store..price").unwrap(); + let mut selector = JsonSelector::new(parser); + let result = selector.value(&result).select().unwrap(); + + assert_eq!( + vec![&json!(42), &json!(42), &json!(42), &json!(42), &json!(42)], + result + ); +} diff --git a/tests/common.rs b/tests/common.rs index 6052d62e..eae36803 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -3,14 +3,29 @@ extern crate jsonpath_lib as jsonpath; extern crate serde_json; use std::io::Read; +use std::io::Write; +use log::LevelFilter; use serde_json::Value; use self::jsonpath::{JsonSelector, PathParser}; #[allow(dead_code)] pub fn setup() { - let _ = env_logger::try_init(); + let _ = env_logger::Builder::new() + .format(|buf, record| { + writeln!( + buf, + "{}:{} {} - {}", + record.file().unwrap_or("unknown"), + record.line().unwrap_or(0), + record.level(), + record.args() + ) + }) + .parse_env("RUST_LOG") + // .filter(Some("logger_example"), LevelFilter::Trace) + .init(); } #[allow(dead_code)] diff --git a/tests/filter.rs b/tests/filter.rs index c847dab3..97903bc5 100644 --- a/tests/filter.rs +++ b/tests/filter.rs @@ -133,11 +133,11 @@ fn filter_parent_paths() { select_and_then_compare( "$[?(@.key.subKey == 'subKey2')]", json!([ - {"key": {"seq": 1, "subKey": "subKey1"}}, - {"key": {"seq": 2, "subKey": "subKey2"}}, - {"key": 42}, - {"some": "value"} - ]), + {"key": {"seq": 1, "subKey": "subKey1"}}, + {"key": {"seq": 2, "subKey": "subKey2"}}, + {"key": 42}, + {"some": "value"} + ]), json!([{"key": {"seq": 2, "subKey": "subKey2"}}]), ); } @@ -149,15 +149,15 @@ fn bugs33_exist_in_all() { select_and_then_compare( "$..[?(@.first.second)]", json!({ - "foo": { - "first": { "second": "value" } - }, - "foo2": { - "first": {} - }, - "foo3": { - } - }), + "foo": { + "first": { "second": "value" } + }, + "foo2": { + "first": {} + }, + "foo3": { + } + }), json!([ { "first": { @@ -175,15 +175,15 @@ fn bugs33_exist_left_in_all_with_and_condition() { select_and_then_compare( "$..[?(@.first && @.first.second)]", json!({ - "foo": { - "first": { "second": "value" } - }, - "foo2": { - "first": {} - }, - "foo3": { - } - }), + "foo": { + "first": { "second": "value" } + }, + "foo2": { + "first": {} + }, + "foo3": { + } + }), json!([ { "first": { @@ -232,49 +232,49 @@ fn bugs38_array_notation_in_filter() { select_and_then_compare( "$[?(@['key']==42)]", json!([ - {"key": 0}, - {"key": 42}, - {"key": -1}, - {"key": 41}, - {"key": 43}, - {"key": 42.0001}, - {"key": 41.9999}, - {"key": 100}, - {"some": "value"} - ]), + {"key": 0}, + {"key": 42}, + {"key": -1}, + {"key": 41}, + {"key": 43}, + {"key": 42.0001}, + {"key": 41.9999}, + {"key": 100}, + {"some": "value"} + ]), json!([{"key": 42}]), ); select_and_then_compare( "$[?(@['key'].subKey == 'subKey2')]", json!([ - {"key": {"seq": 1, "subKey": "subKey1"}}, - {"key": {"seq": 2, "subKey": "subKey2"}}, - {"key": 42}, - {"some": "value"} - ]), + {"key": {"seq": 1, "subKey": "subKey1"}}, + {"key": {"seq": 2, "subKey": "subKey2"}}, + {"key": 42}, + {"some": "value"} + ]), json!([{"key": {"seq": 2, "subKey": "subKey2"}}]), ); select_and_then_compare( "$[?(@['key']['subKey'] == 'subKey2')]", json!([ - {"key": {"seq": 1, "subKey": "subKey1"}}, - {"key": {"seq": 2, "subKey": "subKey2"}}, - {"key": 42}, - {"some": "value"} - ]), + {"key": {"seq": 1, "subKey": "subKey1"}}, + {"key": {"seq": 2, "subKey": "subKey2"}}, + {"key": 42}, + {"some": "value"} + ]), json!([{"key": {"seq": 2, "subKey": "subKey2"}}]), ); select_and_then_compare( "$..key[?(@['subKey'] == 'subKey2')]", json!([ - {"key": {"seq": 1, "subKey": "subKey1"}}, - {"key": {"seq": 2, "subKey": "subKey2"}}, - {"key": 42}, - {"some": "value"} - ]), + {"key": {"seq": 1, "subKey": "subKey1"}}, + {"key": {"seq": 2, "subKey": "subKey2"}}, + {"key": 42}, + {"some": "value"} + ]), json!([{"seq": 2, "subKey": "subKey2"}]), ); -} \ No newline at end of file +} diff --git a/tests/op.rs b/tests/op.rs index 35089d8d..c0009c55 100644 --- a/tests/op.rs +++ b/tests/op.rs @@ -174,14 +174,14 @@ fn op_ge_for_number() { select_and_then_compare("$.[?(@.a >= 0)]", json!({ "a": 1 }), json!([{ "a": 1 }])); } - - #[test] fn op_eq_for_string_value() { setup(); select_and_then_compare( - r#"$.[?(@.a == "b")]"#, json!({ "a": "b" }), json!([{ "a": "b" }]), + r#"$.[?(@.a == "b")]"#, + json!({ "a": "b" }), + json!([{ "a": "b" }]), ); } @@ -190,18 +190,17 @@ fn op_ne_for_string_value() { setup(); select_and_then_compare( - r#"$.[?(@.a != "c")]"#, json!({ "a": "b" }), json!([{ "a": "b" }]), + r#"$.[?(@.a != "c")]"#, + json!({ "a": "b" }), + json!([{ "a": "b" }]), ); - } #[test] fn op_lt_for_string_value() { setup(); - select_and_then_compare( - r#"$.[?(@.a < "b")]"#, json!({ "a": "b" }), json!([]), - ); + select_and_then_compare(r#"$.[?(@.a < "b")]"#, json!({ "a": "b" }), json!([])); } #[test] @@ -209,7 +208,9 @@ fn op_le_for_string_value() { setup(); select_and_then_compare( - r#"$.[?(@.a <= "b")]"#, json!({ "a": "b" }), json!([{ "a": "b" }]), + r#"$.[?(@.a <= "b")]"#, + json!({ "a": "b" }), + json!([{ "a": "b" }]), ); } @@ -217,9 +218,7 @@ fn op_le_for_string_value() { fn op_gt_for_string_value() { setup(); - select_and_then_compare( - r#"$.[?(@.a > "b")]"#, json!({ "a": "b" }), json!([]), - ); + select_and_then_compare(r#"$.[?(@.a > "b")]"#, json!({ "a": "b" }), json!([])); } #[test] @@ -227,7 +226,9 @@ fn op_ge_for_string_value() { setup(); select_and_then_compare( - r#"$.[?(@.a >= "b")]"#, json!({ "a": "b" }), json!([{ "a": "b" }]), + r#"$.[?(@.a >= "b")]"#, + json!({ "a": "b" }), + json!([{ "a": "b" }]), ); } @@ -314,21 +315,21 @@ fn cmp_json_rel() { } }), json!([ - { - "a" : { - "a" : [ - true, - "1" - ] - }, - "b" : { - "a" : [ - true, - "1" - ] - } - } - ]) + { + "a" : { + "a" : [ + true, + "1" + ] + }, + "b" : { + "a" : [ + true, + "1" + ] + } + } + ]), ) } @@ -380,11 +381,7 @@ fn op_ge_for_object_value() { fn op_eq_for_complex_value() { setup(); - select_and_then_compare( - r#"$.[?(1 == @.a)]"#, - json!({ "a": { "b": 1 } }), - json!([]), - ); + select_and_then_compare(r#"$.[?(1 == @.a)]"#, json!({ "a": { "b": 1 } }), json!([])); } #[test] @@ -403,7 +400,7 @@ fn op_ne_for_complex_value() { "b" : 1 } } - ]) + ]), ); } @@ -411,22 +408,14 @@ fn op_ne_for_complex_value() { fn op_le_for_complex_value() { setup(); - select_and_then_compare( - r#"$.[?(@.a <= 1)]"#, - json!({ "a": { "b": 1 } }), - json!([]), - ); + select_and_then_compare(r#"$.[?(@.a <= 1)]"#, json!({ "a": { "b": 1 } }), json!([])); } #[test] fn op_gt_for_complex_value() { setup(); - select_and_then_compare( - r#"$.[?(@.a > "1")]"#, - json!({ "a": { "b": 1 } }), - json!([]), - ); + select_and_then_compare(r#"$.[?(@.a > "1")]"#, json!({ "a": { "b": 1 } }), json!([])); } #[test] @@ -439,7 +428,7 @@ fn op_compare_different_types() { r#"$[?(true == 1)]"#, r#"$[?(@ == 1)]"#, ] - .iter() + .iter() { select_and_then_compare(path, json!({}), json!([])); } @@ -461,4 +450,4 @@ fn op_for_same_type() { {"a": 1} ]), ); -} \ No newline at end of file +} diff --git a/tests/paths.rs b/tests/paths.rs index 61da575c..fe2878a2 100644 --- a/tests/paths.rs +++ b/tests/paths.rs @@ -12,104 +12,104 @@ fn dolla_token_in_path() { select_and_then_compare( "$..$ref", json!({ - "Junk1": "This is a test to illustrate use of '$' in the attr for the expression $..['$ref'] ", - "$ref": "Match Root", - "Subset1":[ - {"Junk2": "Data...", - "$ref": "Match Subset1" - } - ], - "hierachy1":{ - "hierachy2.1":{ - "hierachy2.1.1":{ "$ref":"Match 2.1.1"}, - "hierachy2.1.2":{ "ref":"Match 2.1.2"}, - "hierachy2.1.3":{ "ref":"No Match 2.1.3"}, - "hierachy2.1.4":{ "$ref":"Match 2.1.4"}, - "hierachy2.1.5":{ "ref":"No Match 2.1.5"} - }, - "hierachy2.2":{ - "hierachy2.2.1":{ "ref":"No Match 2.2.1"}, - "hierachy2.2.2":{ "$ref":"Match 2.2.2"}, - "hierachy2.2.3":{ "ref":"No Match 2.2.3"}, - "hierachy2.2.4":{ "ref":"No Match 2.2.5"}, - "hierachy2.2.5":{ "$ref":"Match 2.2.5"} - }, - "hierachy2.3":{ - "hierachy2.3.1":{ "ref":"No Match 2.3.1"}, - "hierachy2.3.2":{ "ref":"No Match 2.3.2"}, - "hierachy2.3.3":{ "ref":"No Match 2.3.3"}, - "hierachy2.3.4":{ "ref":"No Match 2.3.4"}, - "hierachy2.3.5":{ "ref":"No Match 2.3.5"}, - "hierachy2.3.6":{ - "hierachy2.3.6.1":{ "$ref":"Match 2.3.6.1"}, - "hierachy2.3.6.2":{ "ref":"No Match 2.3.6.2"}, - "hierachy2.3.6.3":{ "ref":"No Match 2.3.6.3"}, - "hierachy2.3.6.4":{ "ref":"No Match 2.3.6.4"}, - "hierachy2.3.6.5":{ "ref":"No Match 2.3.6.5"} - } - } + "Junk1": "This is a test to illustrate use of '$' in the attr for the expression $..['$ref'] ", + "$ref": "Match Root", + "Subset1":[ + {"Junk2": "Data...", + "$ref": "Match Subset1" } - }), + ], + "hierachy1":{ + "hierachy2.1":{ + "hierachy2.1.1":{ "$ref":"Match 2.1.1"}, + "hierachy2.1.2":{ "ref":"Match 2.1.2"}, + "hierachy2.1.3":{ "ref":"No Match 2.1.3"}, + "hierachy2.1.4":{ "$ref":"Match 2.1.4"}, + "hierachy2.1.5":{ "ref":"No Match 2.1.5"} + }, + "hierachy2.2":{ + "hierachy2.2.1":{ "ref":"No Match 2.2.1"}, + "hierachy2.2.2":{ "$ref":"Match 2.2.2"}, + "hierachy2.2.3":{ "ref":"No Match 2.2.3"}, + "hierachy2.2.4":{ "ref":"No Match 2.2.5"}, + "hierachy2.2.5":{ "$ref":"Match 2.2.5"} + }, + "hierachy2.3":{ + "hierachy2.3.1":{ "ref":"No Match 2.3.1"}, + "hierachy2.3.2":{ "ref":"No Match 2.3.2"}, + "hierachy2.3.3":{ "ref":"No Match 2.3.3"}, + "hierachy2.3.4":{ "ref":"No Match 2.3.4"}, + "hierachy2.3.5":{ "ref":"No Match 2.3.5"}, + "hierachy2.3.6":{ + "hierachy2.3.6.1":{ "$ref":"Match 2.3.6.1"}, + "hierachy2.3.6.2":{ "ref":"No Match 2.3.6.2"}, + "hierachy2.3.6.3":{ "ref":"No Match 2.3.6.3"}, + "hierachy2.3.6.4":{ "ref":"No Match 2.3.6.4"}, + "hierachy2.3.6.5":{ "ref":"No Match 2.3.6.5"} + } + } + } + }), json!([ - "Match Root", - "Match Subset1", - "Match 2.1.1", - "Match 2.1.4", - "Match 2.2.2", - "Match 2.2.5", - "Match 2.3.6.1" + "Match Root", + "Match Subset1", + "Match 2.1.1", + "Match 2.1.4", + "Match 2.2.2", + "Match 2.2.5", + "Match 2.3.6.1" ]), ); select_and_then_compare( "$..['$ref']", json!({ - "Junk1": "This is a test to illustrate use of '$' in the attr for the expression $..['$ref'] ", - "$ref": "Match Root", - "Subset1":[ - {"Junk2": "Data...", - "$ref": "Match Subset1" - } - ], - "hierachy1":{ - "hierachy2.1":{ - "hierachy2.1.1":{ "$ref":"Match 2.1.1"}, - "hierachy2.1.2":{ "ref":"Match 2.1.2"}, - "hierachy2.1.3":{ "ref":"No Match 2.1.3"}, - "hierachy2.1.4":{ "$ref":"Match 2.1.4"}, - "hierachy2.1.5":{ "ref":"No Match 2.1.5"} - }, - "hierachy2.2":{ - "hierachy2.2.1":{ "ref":"No Match 2.2.1"}, - "hierachy2.2.2":{ "$ref":"Match 2.2.2"}, - "hierachy2.2.3":{ "ref":"No Match 2.2.3"}, - "hierachy2.2.4":{ "ref":"No Match 2.2.5"}, - "hierachy2.2.5":{ "$ref":"Match 2.2.5"} - }, - "hierachy2.3":{ - "hierachy2.3.1":{ "ref":"No Match 2.3.1"}, - "hierachy2.3.2":{ "ref":"No Match 2.3.2"}, - "hierachy2.3.3":{ "ref":"No Match 2.3.3"}, - "hierachy2.3.4":{ "ref":"No Match 2.3.4"}, - "hierachy2.3.5":{ "ref":"No Match 2.3.5"}, - "hierachy2.3.6":{ - "hierachy2.3.6.1":{ "$ref":"Match 2.3.6.1"}, - "hierachy2.3.6.2":{ "ref":"No Match 2.3.6.2"}, - "hierachy2.3.6.3":{ "ref":"No Match 2.3.6.3"}, - "hierachy2.3.6.4":{ "ref":"No Match 2.3.6.4"}, - "hierachy2.3.6.5":{ "ref":"No Match 2.3.6.5"} - } - } + "Junk1": "This is a test to illustrate use of '$' in the attr for the expression $..['$ref'] ", + "$ref": "Match Root", + "Subset1":[ + {"Junk2": "Data...", + "$ref": "Match Subset1" } - }), + ], + "hierachy1":{ + "hierachy2.1":{ + "hierachy2.1.1":{ "$ref":"Match 2.1.1"}, + "hierachy2.1.2":{ "ref":"Match 2.1.2"}, + "hierachy2.1.3":{ "ref":"No Match 2.1.3"}, + "hierachy2.1.4":{ "$ref":"Match 2.1.4"}, + "hierachy2.1.5":{ "ref":"No Match 2.1.5"} + }, + "hierachy2.2":{ + "hierachy2.2.1":{ "ref":"No Match 2.2.1"}, + "hierachy2.2.2":{ "$ref":"Match 2.2.2"}, + "hierachy2.2.3":{ "ref":"No Match 2.2.3"}, + "hierachy2.2.4":{ "ref":"No Match 2.2.5"}, + "hierachy2.2.5":{ "$ref":"Match 2.2.5"} + }, + "hierachy2.3":{ + "hierachy2.3.1":{ "ref":"No Match 2.3.1"}, + "hierachy2.3.2":{ "ref":"No Match 2.3.2"}, + "hierachy2.3.3":{ "ref":"No Match 2.3.3"}, + "hierachy2.3.4":{ "ref":"No Match 2.3.4"}, + "hierachy2.3.5":{ "ref":"No Match 2.3.5"}, + "hierachy2.3.6":{ + "hierachy2.3.6.1":{ "$ref":"Match 2.3.6.1"}, + "hierachy2.3.6.2":{ "ref":"No Match 2.3.6.2"}, + "hierachy2.3.6.3":{ "ref":"No Match 2.3.6.3"}, + "hierachy2.3.6.4":{ "ref":"No Match 2.3.6.4"}, + "hierachy2.3.6.5":{ "ref":"No Match 2.3.6.5"} + } + } + } + }), json!([ - "Match Root", - "Match Subset1", - "Match 2.1.1", - "Match 2.1.4", - "Match 2.2.2", - "Match 2.2.5", - "Match 2.3.6.1" + "Match Root", + "Match Subset1", + "Match 2.1.1", + "Match 2.1.4", + "Match 2.2.2", + "Match 2.2.5", + "Match 2.3.6.1" ]), ); -} \ No newline at end of file +} diff --git a/tests/precompile.rs b/tests/precompile.rs index 6793a12b..4a4c0a8d 100644 --- a/tests/precompile.rs +++ b/tests/precompile.rs @@ -2,7 +2,7 @@ extern crate serde_json; extern crate jsonpath_lib; -use common::{setup}; +use common::setup; use jsonpath_lib::PathCompiled; use serde_json::Value; @@ -27,8 +27,14 @@ fn precompile_test() { // re-use //let result = compiled(&json).unwrap(); - assert_eq!(compiled.select(&json).unwrap().clone(), vec![&Value::String("baz".into())]); - assert_eq!(compiled.select(&json).unwrap().clone(), vec![&Value::String("baz".into())]); + assert_eq!( + compiled.select(&json).unwrap().clone(), + vec![&Value::String("baz".into())] + ); + assert_eq!( + compiled.select(&json).unwrap().clone(), + vec![&Value::String("baz".into())] + ); } #[test] @@ -38,4 +44,4 @@ fn precompile_failure() { let compiled = PathCompiled::compile(""); assert!(compiled.is_err()); -} \ No newline at end of file +} diff --git a/tests/readme.rs b/tests/readme.rs index b8b6db8d..175310ff 100644 --- a/tests/readme.rs +++ b/tests/readme.rs @@ -6,7 +6,7 @@ extern crate serde_json; use serde::Deserialize; use serde_json::Value; -use jsonpath::{JsonSelector, JsonSelectorMut, PathParser}; +use jsonpath::{JsonSelector, MultiJsonSelectorMut, PathParser}; mod common; @@ -176,9 +176,7 @@ fn readme_selector() { let parser = PathParser::compile("$..[?(@.age >= 30)]").unwrap(); let mut selector = JsonSelector::new(parser); - let result = selector.value(&json_obj) - .select() - .unwrap(); + let result = selector.value(&json_obj).select().unwrap(); assert_eq!(vec![&json!({"name": "친구3", "age": 30})], result); @@ -210,9 +208,10 @@ fn readme_selector_mut() { ]}); let parser = PathParser::compile("$..[?(@.age == 20)].age").unwrap(); - let mut selector_mut = JsonSelectorMut::new(parser); + let mut selector_mut = MultiJsonSelectorMut::new(parser); - let result = selector_mut.value(json_obj) + let result = selector_mut + .value(json_obj) .replace_with(&mut |v| { let age = if let Value::Number(n) = v { n.as_u64().unwrap() * 2 @@ -286,7 +285,7 @@ fn readme_select_as_str() { "#, "$..friends[0]", ) - .unwrap(); + .unwrap(); assert_eq!( ret, @@ -317,7 +316,7 @@ fn readme_select_as() { }"#, "$.person", ) - .unwrap(); + .unwrap(); let person = Person { name: "Doe John".to_string(), @@ -520,7 +519,7 @@ fn readme_replace_with() { Ok(Some(json!(age))) }) - .unwrap(); + .unwrap(); assert_eq!( result, diff --git a/tests/return_type.rs b/tests/return_type.rs index 0139dda0..e260b27c 100644 --- a/tests/return_type.rs +++ b/tests/return_type.rs @@ -100,9 +100,5 @@ fn return_type_for_array_filter_true() { fn return_type_empty() { setup(); - select_and_then_compare( - "$[?(@.key==43)]", - json!([{"key": 42}]), - json!([]), - ); -} \ No newline at end of file + select_and_then_compare("$[?(@.key==43)]", json!([{"key": 42}]), json!([])); +} diff --git a/tests/selector.rs b/tests/selector.rs index 770501be..84c18b17 100644 --- a/tests/selector.rs +++ b/tests/selector.rs @@ -3,9 +3,9 @@ extern crate jsonpath_lib as jsonpath; extern crate serde_json; use common::{read_json, setup}; -use jsonpath::{Parser, Selector, SelectorMut, JsonPathError}; +use jsonpath::{JsonPathError, Parser, Selector, SelectorMut}; +use jsonpath::{JsonSelector, MultiJsonSelectorMut, PathParser}; use serde_json::Value; -use jsonpath::{PathParser, JsonSelector, JsonSelectorMut}; mod common; @@ -14,10 +14,11 @@ fn selector_mut() { setup(); let parser = PathParser::compile("$.store..price").unwrap(); - let mut selector_mut = JsonSelectorMut::new(parser); + let mut selector_mut = MultiJsonSelectorMut::new(parser); let mut nums = Vec::new(); - let result = selector_mut.value(read_json("./benchmark/example.json")) + let result = selector_mut + .value(read_json("./benchmark/example.json")) .replace_with(&mut |v| { if let Value::Number(n) = v { nums.push(n.as_f64().unwrap()); @@ -35,9 +36,7 @@ fn selector_mut() { let parser = PathParser::compile("$.store..price").unwrap(); let mut selector = JsonSelector::new(parser); - let result = selector.value(&result) - .select() - .unwrap(); + let result = selector.value(&result).select().unwrap(); assert_eq!( vec![ @@ -60,14 +59,9 @@ fn selector_mut_err() { .str_path(r#"$.store..price"#) .unwrap() .value(read_json("./benchmark/example.json")) - .replace_with(&mut |_| { - Err(JsonPathError::EmptyValue) - }); + .replace_with(&mut |_| Err(JsonPathError::EmptyValue)); - assert_eq!( - result.is_err(), - true - ); + assert_eq!(result.is_err(), true); } #[test] @@ -75,17 +69,12 @@ fn jsonselector_mut_err() { setup(); let parser = PathParser::compile("$.store..price[?(@>13)]").unwrap(); - let mut selector_mut = JsonSelectorMut::new(parser); + let mut selector_mut = MultiJsonSelectorMut::new(parser); let result = selector_mut .value(read_json("./benchmark/example.json")) - .replace_with(&mut |_| { - Err(JsonPathError::EmptyValue) - }); + .replace_with(&mut |_| Err(JsonPathError::EmptyValue)); - assert_eq!( - result.is_err(), - true - ); + assert_eq!(result.is_err(), true); } #[test] @@ -100,9 +89,10 @@ fn selector_delete_multi_elements_from_array() { setup(); let parser = PathParser::compile("$[0,2]").unwrap(); - let mut selector_mut = JsonSelectorMut::new(parser); + let mut selector_mut = MultiJsonSelectorMut::new(parser); - let result = selector_mut.value(serde_json::from_str("[1,2,3]").unwrap()) + let result = selector_mut + .value(serde_json::from_str("[1,2,3]").unwrap()) .remove() .unwrap() .take() @@ -119,9 +109,10 @@ fn selector_delete() { setup(); let parser = PathParser::compile("$.store..price[?(@>13)]").unwrap(); - let mut selector_mut = JsonSelectorMut::new(parser); + let mut selector_mut = MultiJsonSelectorMut::new(parser); - let result = selector_mut.value(read_json("./benchmark/example.json")) + let result = selector_mut + .value(read_json("./benchmark/example.json")) .delete() .unwrap() .take() @@ -129,9 +120,7 @@ fn selector_delete() { let parser = PathParser::compile("$.store..price").unwrap(); let mut selector = JsonSelector::new(parser); - let result = selector.value(&result) - .select() - .unwrap(); + let result = selector.value(&result).select().unwrap(); assert_eq!( result, @@ -149,9 +138,10 @@ fn selector_delete() { fn selector_remove() { setup(); let parser = PathParser::compile("$.store..price[?(@>13)]").unwrap(); - let mut selector_mut = JsonSelectorMut::new(parser); + let mut selector_mut = MultiJsonSelectorMut::new(parser); - let result = selector_mut.value(read_json("./benchmark/example.json")) + let result = selector_mut + .value(read_json("./benchmark/example.json")) .remove() .unwrap() .take() @@ -159,16 +149,7 @@ fn selector_remove() { let parser = PathParser::compile("$.store..price").unwrap(); let mut selector = JsonSelector::new(parser); - let result = selector.value(&result) - .select() - .unwrap(); + let result = selector.value(&result).select().unwrap(); - assert_eq!( - result, - vec![ - &json!(8.95), - &json!(12.99), - &json!(8.99) - ] - ); + assert_eq!(result, vec![&json!(8.95), &json!(12.99), &json!(8.99)]); } From b93d68fe0a3c54149191815c7bacf3a3433be028 Mon Sep 17 00:00:00 2001 From: Eoin Power-Moran Date: Thu, 19 Oct 2023 14:34:30 +0100 Subject: [PATCH 12/20] make changes --- Cargo.toml | 10 +- benches/async.rs | 82 +++++-- benches/common.rs | 4 +- benches/sync_mut.rs | 113 ++++++++++ lua/.gitignore | 5 - lua/Cargo.toml | 14 -- lua/bench_lua_vs_rust/example.lua | 22 -- lua/bench_lua_vs_rust/example.rs | 46 ---- lua/bench_lua_vs_rust/run.sh | 27 --- lua/docker_example/default.conf | 107 --------- lua/docker_example/init.lua | 3 - lua/docker_example/run.sh | 25 --- lua/jsonpath.lua | 60 ----- src/lib.rs | 10 +- src/parser/mod.rs | 16 +- src/paths/mod.rs | 1 + src/paths/parser_node_visitor.rs | 16 +- src/paths/path_parser.rs | 54 ++++- src/paths/tokenizer.rs | 12 +- src/select/mod.rs | 6 +- src/selector/async_selector_impl.rs | 326 ++++++++++++++++++++++++++++ src/selector/cmp.rs | 2 +- src/selector/mod.rs | 4 +- src/selector/selector_impl.rs | 129 ++--------- src/selector/terms.rs | 2 +- src/selector/value_walker.rs | 1 - tests/async.rs | 60 +++-- tests/common.rs | 4 +- tests/filter.rs | 2 +- tests/readme.rs | 4 +- tests/selector.rs | 19 +- 31 files changed, 663 insertions(+), 523 deletions(-) create mode 100644 benches/sync_mut.rs delete mode 100644 lua/.gitignore delete mode 100644 lua/Cargo.toml delete mode 100644 lua/bench_lua_vs_rust/example.lua delete mode 100644 lua/bench_lua_vs_rust/example.rs delete mode 100755 lua/bench_lua_vs_rust/run.sh delete mode 100644 lua/docker_example/default.conf delete mode 100644 lua/docker_example/init.lua delete mode 100755 lua/docker_example/run.sh delete mode 100644 lua/jsonpath.lua create mode 100644 src/selector/async_selector_impl.rs diff --git a/Cargo.toml b/Cargo.toml index 7bf50888..a886f7c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["preserve_order"] } [dev-dependencies] -criterion = { version = "0.5.1", features = ["html_reports"] } +criterion = { version = "0.5.1", features = ["html_reports", "async_tokio"] } env_logger = "0.8" tokio = { version = "1.33.0", features = ["macros"] } tokio-test = "0.4.3" @@ -38,3 +38,11 @@ crate-type = ["cdylib", "rlib"] #[profile.release] #debug = true #lto = false + +[[bench]] +name = "async" +harness = false + +[[bench]] +name = "sync_mut" +harness = false diff --git a/benches/async.rs b/benches/async.rs index b2e65055..72477021 100644 --- a/benches/async.rs +++ b/benches/async.rs @@ -8,10 +8,10 @@ use std::{ task::{Context, Poll}, }; -use common::{read_json, setup}; -use criterion::{criterion_group, criterion_main}; +use common::{read_json}; +use criterion::{criterion_group, criterion_main, BenchmarkId}; use futures::Future; -use jsonpath::{JsonSelector, MultiJsonSelectorMut, PathParser}; +use jsonpath::{MultiJsonSelectorMutWithMetadata, PathParserWithMetadata}; use serde_json::Value; mod common; @@ -93,19 +93,11 @@ impl CryptoField { } } -async fn selector_mut() { - setup(); - - let parser = PathParser::compile("$.store..price").unwrap(); - let parser_two = PathParser::compile("$.store..author").unwrap(); - let mut selector_mut = - MultiJsonSelectorMut::new_multi_parser(vec![parser.into(), parser_two.into()]); - +async fn async_run(mut selector_mut: MultiJsonSelectorMutWithMetadata<'_, &str>, json: Value) { let crypto_request = Arc::new(CryptoRequest::new()); let result_futures = selector_mut - .value(read_json("./benchmark/example.json")) - .replace_with_async(|v| { + .replace_with_async(json, |v, _| { let bag: CryptoField = crypto_request.new_field(v); Box::pin(async move { @@ -117,21 +109,63 @@ async fn selector_mut() { crypto_request.send_request().await; - let result = result_futures.await.unwrap().take().unwrap(); + let _result = result_futures.await.unwrap(); +} - let parser = PathParser::compile("$.store..price").unwrap(); - let mut selector = JsonSelector::new(parser); - let result = selector.value(&result).select().unwrap(); +fn setup_async_benchmark(c: &mut criterion::Criterion) { + let t1_json = read_json("./benchmark/example.json"); + let t1_parser = PathParserWithMetadata::compile("$.store..price", "one").unwrap(); + let t1_parser_two = PathParserWithMetadata::compile("$.store..author", "two").unwrap(); + let t1_selector_mut = MultiJsonSelectorMutWithMetadata::new_multi_parser(vec![ + t1_parser, + t1_parser_two, + ]); + + // let big_array = read_json("./benchmark/big_array.json"); + let t2_json = read_json("./benchmark/big_example.json"); + let t2_parser = PathParserWithMetadata::compile("$.store.book[*].author", "one").unwrap(); + let t2_parser_two = PathParserWithMetadata::compile("$.store.author", "two").unwrap(); + let t2_selector_mut = MultiJsonSelectorMutWithMetadata::new_multi_parser(vec![ + t2_parser, + t2_parser_two, + ]); + + let runtime = tokio::runtime::Builder::new_current_thread() + .build() + .unwrap(); - assert_eq!( - vec![&json!(42), &json!(42), &json!(42), &json!(42), &json!(42)], - result + c.bench_with_input( + BenchmarkId::new("async_selector_mut", "Json"), + &(t1_selector_mut, t1_json), + |b, (s, v)| { + // Insert a call to `to_async` to convert the bencher to async mode. + // The timing loops are the same as with the normal bencher. + b.to_async(&runtime).iter_batched( + || (s.clone(), v.clone()), + |(s, v)| async { + async_run(s, v).await; + }, + criterion::BatchSize::SmallInput, + ); + }, ); -} -fn setup_async_benchmark(c: &mut criterion::Criterion) { - c.bench_function("selector_mut", |b| b.iter(|| selector_mut())); + c.bench_with_input( + BenchmarkId::new("async_selector_mut", "BigJson"), + &(t2_selector_mut, t2_json), + |b, (s, v)| { + // Insert a call to `to_async` to convert the bencher to async mode. + // The timing loops are the same as with the normal bencher. + b.to_async(&runtime).iter_batched( + || (s.clone(), v.clone()), + |(s, v)| async { + async_run(s, v).await; + }, + criterion::BatchSize::LargeInput, + ); + }, + ); } criterion_group!(benches, setup_async_benchmark); -criterion_main!(benches); \ No newline at end of file +criterion_main!(benches); diff --git a/benches/common.rs b/benches/common.rs index eae36803..35a59d07 100644 --- a/benches/common.rs +++ b/benches/common.rs @@ -5,14 +5,14 @@ extern crate serde_json; use std::io::Read; use std::io::Write; -use log::LevelFilter; + use serde_json::Value; use self::jsonpath::{JsonSelector, PathParser}; #[allow(dead_code)] pub fn setup() { - let _ = env_logger::Builder::new() + env_logger::Builder::new() .format(|buf, record| { writeln!( buf, diff --git a/benches/sync_mut.rs b/benches/sync_mut.rs new file mode 100644 index 00000000..821b5eb7 --- /dev/null +++ b/benches/sync_mut.rs @@ -0,0 +1,113 @@ +extern crate jsonpath_lib as jsonpath; +extern crate serde_json; + +use common::{read_json}; +use criterion::{criterion_group, criterion_main, BenchmarkId}; + +use jsonpath::{PathParser, JsonSelectorMut}; +use serde_json::Value; + +mod common; + +fn selector_mut(mut selector_mut: JsonSelectorMut, json: Value) -> Value { + let mut nums = Vec::new(); + let result = selector_mut + .value(json) + .replace_with(&mut |v| { + if let Value::Number(n) = v { + nums.push(n.as_f64().unwrap()); + } + Ok(Some(Value::String("a".to_string()))) + }) + .unwrap() + .take() + .unwrap(); + + result +} + +fn setup_async_benchmark(c: &mut criterion::Criterion) { + let t1_json = read_json("./benchmark/example.json"); + let t1_parser = PathParser::compile("$.store..price").unwrap(); + let t1_selector_mut = JsonSelectorMut::new(t1_parser.clone()); + let t1_selector_mut_two = JsonSelectorMut::new(t1_parser); + + let t2_json = read_json("./benchmark/big_example.json"); + let t2_parser = PathParser::compile("$.store.book[*].author").unwrap(); + let t2_parser_two = PathParser::compile("$.store.author").unwrap(); + let t2_selector_mut = JsonSelectorMut::new(t2_parser); + let t2_selector_mut_two = JsonSelectorMut::new(t2_parser_two); + + let runtime = tokio::runtime::Builder::new_current_thread() + .build() + .unwrap(); + + c.bench_with_input( + BenchmarkId::new("selector_mut", "Json"), + &(t1_selector_mut.clone(), t1_json.clone()), + |b, (s, v)| { + // Insert a call to `to_async` to convert the bencher to async mode. + // The timing loops are the same as with the normal bencher. + b.to_async(&runtime).iter_batched( + || (s.clone(), v.clone()), + |(s, v)| async { + selector_mut(s, v); + }, + criterion::BatchSize::SmallInput, + ); + }, + ); + + c.bench_with_input( + BenchmarkId::new("selector_mut", "BigJson"), + &(t2_selector_mut.clone(), t2_json.clone()), + |b, (s, v)| { + // Insert a call to `to_async` to convert the bencher to async mode. + // The timing loops are the same as with the normal bencher. + b.to_async(&runtime).iter_batched( + || (s.clone(), v.clone()), + |(s, v)| async { + selector_mut(s, v); + }, + criterion::BatchSize::LargeInput, + ); + }, + ); + + c.bench_with_input( + BenchmarkId::new("double_selector_mut", "Json"), + &(t1_selector_mut, t1_selector_mut_two, t1_json), + |b, (s, s2, v)| { + // Insert a call to `to_async` to convert the bencher to async mode. + // The timing loops are the same as with the normal bencher. + b.to_async(&runtime).iter_batched( + || (s.clone(), s2.clone(), v.clone()), + |(s, s2, v)| async { + let v = selector_mut(s, v); + let _ = selector_mut(s2, v); + }, + criterion::BatchSize::SmallInput, + ); + }, +); + + c.bench_with_input( + BenchmarkId::new("double_selector_mut", "BigJson"), + &(t2_selector_mut, t2_selector_mut_two, t2_json), + |b, (s, s2, v)| { + // Insert a call to `to_async` to convert the bencher to async mode. + // The timing loops are the same as with the normal bencher. + b.to_async(&runtime).iter_batched( + || (s.clone(), s2.clone(), v.clone()), + |(s, s2, v)| async { + let v = selector_mut(s, v); + let _ = selector_mut(s2, v); + }, + criterion::BatchSize::LargeInput, + ); + }, + ); +} + +criterion_group!(benches, setup_async_benchmark); +criterion_main!(benches); diff --git a/lua/.gitignore b/lua/.gitignore deleted file mode 100644 index 7a7d6214..00000000 --- a/lua/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -.idea/* -.vscode -/target/ -Cargo.lock -docker_example/ab_results/** \ No newline at end of file diff --git a/lua/Cargo.toml b/lua/Cargo.toml deleted file mode 100644 index fe05a109..00000000 --- a/lua/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "jsonpath_lua" -version = "0.1.0" -authors = ["Changseok Han "] -license = "MIT" -[dependencies] -serde = { version = "1.0", features = ["derive"] } -serde_json = { version = "1.0", features = ["preserve_order"] } -jsonpath_lib = { path = "../" } - -[[bin]] -name = "bench" -path = "bench_lua_vs_rust/example.rs" - diff --git a/lua/bench_lua_vs_rust/example.lua b/lua/bench_lua_vs_rust/example.lua deleted file mode 100644 index 0c59a37d..00000000 --- a/lua/bench_lua_vs_rust/example.lua +++ /dev/null @@ -1,22 +0,0 @@ -local jsonpath = require("jsonpath") - -local iter; -if arg[1] == nil or arg[1] == '' then - iter = 5000; -else - iter = tonumber(arg[1]); -end - -print(string.format("%s - %u", "lua iter", iter)); - -local file = io.open("../../benchmark/example.json", "r"); -io.input(file) -local data = io.read("*a"); -io.close(file); - -jsonpath.init('../target/release/deps/libjsonpath_lib.so') -local template = jsonpath.compile("$..book[?(@.price<30 && @.category==\"fiction\")]"); -for i = 0, iter do - local r = template(data); --- print(r); -end diff --git a/lua/bench_lua_vs_rust/example.rs b/lua/bench_lua_vs_rust/example.rs deleted file mode 100644 index 68f63b2d..00000000 --- a/lua/bench_lua_vs_rust/example.rs +++ /dev/null @@ -1,46 +0,0 @@ -extern crate jsonpath_lib as jsonpath; -extern crate serde; -extern crate serde_json; - -use std::io::Read; - -use serde_json::Value; - -fn read_json(path: &str) -> String { - let mut f = std::fs::File::open(path).unwrap(); - let mut contents = String::new(); - f.read_to_string(&mut contents).unwrap(); - contents -} - -fn get_string() -> String { - read_json("../../benchmark/example.json") -} - -fn get_json() -> Value { - let string = get_string(); - serde_json::from_str(string.as_str()).unwrap() -} - -fn get_path() -> &'static str { - r#"$..book[?(@.price<30 && @.category=="fiction")]"# -} - -fn main() { - let args: Vec = std::env::args().collect(); - let iter = if args.len() < 2 { 5000_usize } else { args[1].as_str().parse::().unwrap() }; - - println!("rust iter - {}", iter); - - let json = get_json(); - for _ in 0..iter { - let mut selector = jsonpath::Selector::default(); - let _ = selector.str_path(get_path()); - selector.value(&json); - let r = selector.select(); - if r.is_err() { - panic!(); - } -// println!("{:?}", serde_json::to_string(&r.expect("")).unwrap()); - } -} \ No newline at end of file diff --git a/lua/bench_lua_vs_rust/run.sh b/lua/bench_lua_vs_rust/run.sh deleted file mode 100755 index e9e0863a..00000000 --- a/lua/bench_lua_vs_rust/run.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash - -# cd lua/bench_lua_vs_rust && ./run.sh - -set -e - -# http://luajit.org/index.html - -# cargo clean && \ -cargo build --release - -export JSONPATH_LIB_PATH="${PWD}/../target/release/deps" -export LUA_PATH="${PWD}/../?.lua;" - -echo -time cargo run --release --bin bench -- 1000 -echo -time luajit example.lua 1000 -echo -time cargo run --release --bin bench -- 5000 -echo -time luajit example.lua 5000 -echo -time cargo run --release --bin bench -- 10000 -echo -time luajit example.lua 10000 - diff --git a/lua/docker_example/default.conf b/lua/docker_example/default.conf deleted file mode 100644 index 4b04b076..00000000 --- a/lua/docker_example/default.conf +++ /dev/null @@ -1,107 +0,0 @@ -lua_package_path '/etc/jsonpath/?.lua;;'; - -access_log /var/log/access.log; -error_log /var/log/error.log info; - -lua_shared_dict jsonpaths 1m; - -init_by_lua_block { - local pathStrings = { - "$.store.book[*].author", - "$..author", - "$.store.*", - "$.store..price", - "$..book[2]", - "$..book[-2]", - "$..book[0,1]", - "$..book[:2]", - "$..book[1:2]", - "$..book[-2:]", - "$..book[2:]", - "$..book[?(@.isbn)]", - "$.store.book[?(@.price == 10)]", - "$..*", - "$..book[ ?( (@.price < 13 || $.store.bicycle.price < @.price) && @.price <=10 ) ]", - "$.store.book[?( (@.price < 10 || @.price > 10) && @.price > 10 )]", - "$..[?(@.originPrice > 1)]", - "$.pickBanner[?(@.originPrice > 1)]" - } - - local jp = require("jsonpath") - jp.init("/etc/jsonpath/libjsonpath_lib.so") - local jsonpaths = ngx.shared.jsonpaths - - for i, path in ipairs(pathStrings) do - jsonpaths:set(i, path) - jp.compile(path) - end - -} - -server { - listen 80; - server_name localhost; - - gzip on; - gzip_types text/plain application/json; - #gzip_comp_level 6; - #gzip_vary on; - - location / { - add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; - expires off; - - default_type 'text/plain'; - root /etc/jsonpath/example; - } - - location /filter { - # https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/Accept-Encoding - proxy_set_header Accept-Encoding "*"; - - default_type 'text/plain'; - - rewrite /filter/(.*) /$1 break; - proxy_pass http://localhost; - - header_filter_by_lua_block { - ngx.header["content-length"] = nil - - local args = ngx.req.get_uri_args() - local jsonpaths = ngx.shared.jsonpaths - local path = jsonpaths:get(args['path']) - - if path == nil then - ngx.exit(ngx.HTTP_BAD_REQUEST) - end - } - - body_filter_by_lua_block { - local chunk, eof = ngx.arg[1], ngx.arg[2] - local buf = ngx.ctx.buf - - if eof then - if buf then - local args = ngx.req.get_uri_args() - local path = ngx.shared.jsonpaths:get(args['path']) - local jsonpath = require("jsonpath") - local template = jsonpath.exec(path) - local json = buf .. chunk - local result = template(json) - ngx.arg[1] = result - return - end - - return - end - - if buf then - ngx.ctx.buf = buf .. chunk - else - ngx.ctx.buf = chunk - end - - ngx.arg[1] = nil - } - } -} \ No newline at end of file diff --git a/lua/docker_example/init.lua b/lua/docker_example/init.lua deleted file mode 100644 index 38ac53fd..00000000 --- a/lua/docker_example/init.lua +++ /dev/null @@ -1,3 +0,0 @@ -local jsonpath = require("jsonpath") -jsonpath.init("/etc/jsonpath/libjsonpath_lib.so") -ngx.log(ngx.INFO, "loaded libjsonpath_lib.so") \ No newline at end of file diff --git a/lua/docker_example/run.sh b/lua/docker_example/run.sh deleted file mode 100755 index f55bd9d8..00000000 --- a/lua/docker_example/run.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash - -# cd lua && cargo build --release && cd docker_example && ./run.sh - -set -v - -[ "$(docker ps -a | grep jsonpath)" ] && docker kill jsonpath - -docker run -d --rm --name jsonpath \ - -v "${PWD}/../../benchmark/example.json":/etc/jsonpath/example/example.json:ro \ - -v "${PWD}/../../benchmark/big_example.json":/etc/jsonpath/example/big_example.json:ro \ - -v "${PWD}/../jsonpath.lua":/etc/jsonpath/jsonpath.lua:ro \ - -v "${PWD}/init.lua":/etc/jsonpath/init.lua:ro \ - -v "${PWD}/../target/release/deps/libjsonpath_lib.so":/etc/jsonpath/libjsonpath_lib.so:ro \ - -v "${PWD}/default.conf":/etc/nginx/conf.d/default.conf \ - -p 8080:80 \ - openresty/openresty:bionic - -#for i in {1..16}; do -# curl http://localhost:8080/filter/example.json?path=${i} -# echo -#done - -#ab -n 1000 -c 10 http://localhost:8080/filter/big_example.json?path=17 -#ab -n 1000 -c 10 http://localhost:8080/filter/big_example.json?path=18 \ No newline at end of file diff --git a/lua/jsonpath.lua b/lua/jsonpath.lua deleted file mode 100644 index ba5fc289..00000000 --- a/lua/jsonpath.lua +++ /dev/null @@ -1,60 +0,0 @@ -local ffi = require('ffi') - -ffi.cdef [[ -const char* ffi_select(const char *json_str, const char *path); -void *ffi_path_compile(const char *path); -const char* ffi_select_with_compiled_path(void *ptr, const char *json_str); -]] - -local jsonpath -local cache = {} -local module = {} - -local function existsVaiable(var) - for k, _ in pairs(_G) do - if k == var then - return true - end - end -end - -local _ngx -if existsVaiable('ngx') then - _ngx = ngx -else - _ngx = {} - _ngx.log = function(level, msg) - print('['..level..'] ' .. msg) - end -end - -function module.compile(path) - assert(jsonpath, '"libjsonpath_lib" is not loaded') - - if(cache[path] == nil) then - cache[path] = jsonpath.ffi_path_compile(path) - _ngx.log(_ngx.INFO, 'compile : [' .. path .. ']') - end -end - -function module.exec(path) - local compiledPath = cache[path] - - if(cache[path] == nil) then - assert(jsonpath, path .. ": is not compiled") - end - - return function(jsonStr) - local result = jsonpath.ffi_select_with_compiled_path(compiledPath, jsonStr) - return ffi.string(result); - end -end - -function module.init(path) - if jsonpath == nil then - jsonpath = ffi.load(path) - _ngx.log(_ngx.INFO, '"' .. path .. '" initialized') - end -end - -return module \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 591d94b4..024da0fe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -140,9 +140,9 @@ pub use select::{Selector, SelectorMut}; #[deprecated(since = "0.4.0", note = "It will be move to common module. since 0.5")] pub use select::JsonPathError; -pub use paths::PathParser; -pub use selector::{JsonSelector, MultiJsonSelectorMut}; -use std::{rc::Rc, sync::Arc}; +pub use paths::{PathParser, PathParserWithMetadata}; +pub use selector::{JsonSelector, JsonSelectorMut, MultiJsonSelectorMutWithMetadata}; +use std::sync::Arc; #[doc(hidden)] mod parser; @@ -454,7 +454,7 @@ pub fn select_as( /// ``` pub fn delete(value: Value, path: &str) -> Result { let parser = PathParser::compile(path).map_err(|e| JsonPathError::from(&e))?; - let mut selector = MultiJsonSelectorMut::new(parser); + let mut selector = JsonSelectorMut::new(parser); let value = selector.value(value).delete()?; Ok(value.take().unwrap_or(Value::Null)) } @@ -506,7 +506,7 @@ where F: FnMut(Value) -> Result, JsonPathError>, { let parser = PathParser::compile(path).map_err(|e| JsonPathError::from(&e))?; - let mut selector = MultiJsonSelectorMut::new(parser); + let mut selector = JsonSelectorMut::new(parser); let value = selector.value(value).replace_with(fun)?; Ok(value.take().unwrap_or(Value::Null)) } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 160b7243..6d1ccd49 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -623,48 +623,48 @@ pub trait NodeVisitor { } ParseToken::In | ParseToken::Leaves => { if let Some(n) = &node.left { - self.visit(&*n); + self.visit(n); } self.visit_token(&node.token); if let Some(n) = &node.right { - self.visit(&*n); + self.visit(n); } } ParseToken::Array => { if let Some(n) = &node.left { - self.visit(&*n); + self.visit(n); } self.visit_token(&node.token); if let Some(n) = &node.right { - self.visit(&*n); + self.visit(n); } self.visit_token(&ParseToken::ArrayEof); } ParseToken::Filter(FilterToken::And) | ParseToken::Filter(FilterToken::Or) => { if let Some(n) = &node.left { - self.visit(&*n); + self.visit(n); } if let Some(n) = &node.right { - self.visit(&*n); + self.visit(n); } self.visit_token(&node.token); } ParseToken::Filter(_) => { if let Some(n) = &node.left { - self.visit(&*n); + self.visit(n); } self.end_term(); if let Some(n) = &node.right { - self.visit(&*n); + self.visit(n); } self.end_term(); diff --git a/src/paths/mod.rs b/src/paths/mod.rs index 186008c5..72c3cf48 100644 --- a/src/paths/mod.rs +++ b/src/paths/mod.rs @@ -1,6 +1,7 @@ pub use self::parser_node_visitor::ParserNodeVisitor; pub use self::parser_token_handler::ParserTokenHandler; pub use self::path_parser::PathParser; +pub use self::path_parser::PathParserWithMetadata; pub use self::str_reader::StrRange; pub use self::tokenizer::TokenError; diff --git a/src/paths/parser_node_visitor.rs b/src/paths/parser_node_visitor.rs index 9df56822..e84cb2e2 100644 --- a/src/paths/parser_node_visitor.rs +++ b/src/paths/parser_node_visitor.rs @@ -23,46 +23,46 @@ pub trait ParserNodeVisitor<'a> { } ParseToken::In | ParseToken::Leaves => { if let Some(n) = &parse_node.left { - self.visit(&*n, token_handler, parse_value_reader); + self.visit(n, token_handler, parse_value_reader); } token_handler.handle(&parse_node.token, parse_value_reader); if let Some(n) = &parse_node.right { - self.visit(&*n, token_handler, parse_value_reader); + self.visit(n, token_handler, parse_value_reader); } } ParseToken::Array => { if let Some(n) = &parse_node.left { - self.visit(&*n, token_handler, parse_value_reader); + self.visit(n, token_handler, parse_value_reader); } token_handler.handle(&parse_node.token, parse_value_reader); if let Some(n) = &parse_node.right { - self.visit(&*n, token_handler, parse_value_reader); + self.visit(n, token_handler, parse_value_reader); } token_handler.handle(&ParseToken::ArrayEof, parse_value_reader); } ParseToken::Filter(FilterToken::And) | ParseToken::Filter(FilterToken::Or) => { if let Some(n) = &parse_node.left { - self.visit(&*n, token_handler, parse_value_reader); + self.visit(n, token_handler, parse_value_reader); } if let Some(n) = &parse_node.right { - self.visit(&*n, token_handler, parse_value_reader); + self.visit(n, token_handler, parse_value_reader); } token_handler.handle(&parse_node.token, parse_value_reader); } ParseToken::Filter(_) => { if let Some(n) = &parse_node.left { - self.visit(&*n, token_handler, parse_value_reader); + self.visit(n, token_handler, parse_value_reader); } if let Some(n) = &parse_node.right { - self.visit(&*n, token_handler, parse_value_reader); + self.visit(n, token_handler, parse_value_reader); } token_handler.handle(&parse_node.token, parse_value_reader); diff --git a/src/paths/path_parser.rs b/src/paths/path_parser.rs index 1957d5f5..6a175eb1 100644 --- a/src/paths/path_parser.rs +++ b/src/paths/path_parser.rs @@ -1,4 +1,7 @@ +use std::fmt::Debug; +use std::ops::Deref; use std::str::FromStr; +use std::sync::Arc; use super::parser_node_visitor::ParserNodeVisitor; use super::parser_token_handler::ParserTokenHandler; @@ -11,6 +14,53 @@ pub struct PathParser<'a> { parser: ParserImpl<'a>, } +/// PathParserWithMetadata is a wrapper around PathParser that allows you to +/// associate metadata with the parser. This is useful when you are using a +/// multi selector and want to associate metadata with each parser. +/// +/// For example, if you have a multi selector that is parsing two paths, you +/// can use PathParserWithMetadata to associate metadata with each parser. +/// +/// ``` +/// use jsonpath_lib::paths::PathParserWithMetadata; +/// +/// let parser = PathParserWithMetadata::compile("$.store..price", 1).unwrap(); +/// assert_eq!(parser.metadata(), &1); +/// ``` +#[derive(Clone, Debug)] +pub struct PathParserWithMetadata<'a, T: Debug> { + /// The underlying parser + /// + /// It is wrapped in an `Arc<>` so that it can be shared between threads. + parser: Arc>, + /// The metadata associated with the parser + metadata: T, +} + +impl<'a, T: Debug> Deref for PathParserWithMetadata<'a, T> { + type Target = PathParser<'a>; + + fn deref(&self) -> &Self::Target { + &self.parser + } +} + +impl<'a, T: Debug> PathParserWithMetadata<'a, T> { + /// Compile a JsonPath with metadata + pub fn compile(input: &'a str, metadata: T) -> Result { + let parser = Arc::new(PathParser::compile(input)?); + Ok(PathParserWithMetadata { parser, metadata }) + } + + pub(crate) fn parser(&self) -> Arc> { + self.parser.clone() + } + + pub(crate) fn metadata(&self) -> &T { + &self.metadata + } +} + impl<'a> PathParser<'a> { pub fn compile(input: &'a str) -> Result { let mut parser = ParserImpl::new(input); @@ -1125,11 +1175,11 @@ mod path_parser_tests { ); assert_eq!( - run(r#"$['single\'quote']"#), + run(r"$['single\'quote']"), Ok(vec![ ParseToken::Absolute, ParseToken::Array, - ParseToken::Key(StrRange::new(2, r#"'single\'quote'"#.len())), + ParseToken::Key(StrRange::new(2, r"'single\'quote'".len())), ParseToken::ArrayEof ]) ); diff --git a/src/paths/tokenizer.rs b/src/paths/tokenizer.rs index b4f12d69..d7c8f1ee 100644 --- a/src/paths/tokenizer.rs +++ b/src/paths/tokenizer.rs @@ -223,7 +223,6 @@ impl<'a> Tokenizer<'a> { pub(super) struct TokenReader<'a> { tokenizer: Tokenizer<'a>, curr_pos: usize, - err: Option, peeked: Option>, } @@ -232,7 +231,6 @@ impl<'a> TokenReader<'a> { TokenReader { tokenizer: Tokenizer::new(input), curr_pos: 0, - err: None, peeked: None, } } @@ -523,12 +521,12 @@ mod tokenizer_tests { ); run( - r#"$['single\'quote']"#, + r"$['single\'quote']", ( vec![ Token::Absolute(StrRange::new(0, 1)), Token::OpenArray(StrRange::new(1, 1)), - Token::SingleQuoted(StrRange::new(2, r#"'single\'quote'"#.len())), + Token::SingleQuoted(StrRange::new(2, r"'single\'quote'".len())), Token::CloseArray(StrRange::new(17, 1)), ], Some(TokenError::Eof), @@ -536,14 +534,14 @@ mod tokenizer_tests { ); run( - r#"$['single\'1','single\'2']"#, + r"$['single\'1','single\'2']", ( vec![ Token::Absolute(StrRange::new(0, 1)), Token::OpenArray(StrRange::new(1, 1)), - Token::SingleQuoted(StrRange::new(2, r#"'single\'1'"#.len())), + Token::SingleQuoted(StrRange::new(2, r"'single\'1'".len())), Token::Comma(StrRange::new(13, 1)), - Token::SingleQuoted(StrRange::new(14, r#"'single\'2'"#.len())), + Token::SingleQuoted(StrRange::new(14, r"'single\'2'".len())), Token::CloseArray(StrRange::new(25, 1)), ], Some(TokenError::Eof), diff --git a/src/select/mod.rs b/src/select/mod.rs index b538a782..3e597c96 100644 --- a/src/select/mod.rs +++ b/src/select/mod.rs @@ -1101,7 +1101,7 @@ mod select_inner_tests { let number = 0_i64; let v: Value = serde_json::from_str(&format!("{}", number)).unwrap(); if let Value::Number(n) = v { - assert_eq!((super::to_f64(&n) - number as f64).abs() == 0_f64, true); + assert!((super::to_f64(&n) - number as f64).abs() == 0_f64); } else { panic!(); } @@ -1112,7 +1112,7 @@ mod select_inner_tests { let number = 0.1_f64; let v: Value = serde_json::from_str(&format!("{}", number)).unwrap(); if let Value::Number(n) = v { - assert_eq!((super::to_f64(&n) - number).abs() == 0_f64, true); + assert!((super::to_f64(&n) - number).abs() == 0_f64); } else { panic!(); } @@ -1123,7 +1123,7 @@ mod select_inner_tests { let number = u64::max_value(); let v: Value = serde_json::from_str(&format!("{}", number)).unwrap(); if let Value::Number(n) = v { - assert_eq!((super::to_f64(&n) - number as f64).abs() == 0_f64, true); + assert!((super::to_f64(&n) - number as f64).abs() == 0_f64); } else { panic!(); } diff --git a/src/selector/async_selector_impl.rs b/src/selector/async_selector_impl.rs new file mode 100644 index 00000000..e989e9fd --- /dev/null +++ b/src/selector/async_selector_impl.rs @@ -0,0 +1,326 @@ +use std::collections::HashSet; +use std::fmt::Debug; +use std::future::Future; +use std::pin::Pin; + +use futures::future::BoxFuture; +use futures::stream::FuturesOrdered; +use futures::StreamExt; +use serde_json::map::Entry; +use serde_json::Value; + +use crate::paths::PathParserWithMetadata; +use crate::{JsonPathError, JsonSelector}; + +type FutureValue = Pin> + Send>>; + +struct JsonPointerWithMetadata<'a, T: Debug + Send + Sync> { + pointer: String, + metadata: &'a T, +} + +impl<'a, T: Debug + Send + Sync> From<(Vec, &'a T)> for JsonPointerWithMetadata<'a, T> { + fn from((pointer, metadata): (Vec, &'a T)) -> Self { + let pointer = "/".to_owned() + &pointer.join("/"); + + JsonPointerWithMetadata { + pointer, + metadata, + } + } +} + +#[derive(Default, Clone)] +pub struct MultiJsonSelectorMutWithMetadata<'a, T: Debug + Send + Sync> { + parser: Option>>, +} + +impl<'a, T: Debug + Send + Sync + 'a> MultiJsonSelectorMutWithMetadata<'a, T> { + pub fn new(parser: PathParserWithMetadata<'a, T>) -> Self { + Self::new_ref(vec![parser]) + } + + pub fn new_multi_parser(parsers: Vec>) -> Self { + Self::new_ref(parsers) + } + + pub fn new_ref(parser: Vec>) -> Self { + MultiJsonSelectorMutWithMetadata { + parser: Some(parser), + } + } + + pub fn reset_parser(&mut self, parser: PathParserWithMetadata<'a, T>) -> &mut Self { + self.parser = Some(vec![parser]); + self + } + + pub fn reset_parser_ref(&mut self, parser: Vec>) -> &mut Self { + self.parser = Some(parser); + self + } + + pub fn delete(&mut self, value: Value) -> Result<&mut Self, JsonPathError> { + self.replace_with(value, &mut |_| Ok(Some(Value::Null))) + } + + pub fn remove(&mut self, value: Value) -> Result<&mut Self, JsonPathError> { + self.replace_with(value, &mut |_| Ok(None)) + } + + fn select_with_parser<'b>( + &self, + value: &'b Value, + parser: &PathParserWithMetadata<'b, T>, + ) -> Result, JsonPathError> { + let mut selector = JsonSelector::default(); + + selector.reset_parser_ref(parser.parser()); + + selector.value(value); + + selector.select() + } + + fn select<'b>(&'b self, value: &'b Value) -> Result, JsonPathError> { + let res: Vec, JsonPathError>> = + if let Some(parser) = self.parser.as_ref() { + parser + .iter() + .map(|p| { + let mut selector = JsonSelector::default(); + selector.reset_parser_ref(p.parser()); + selector.value(value); + selector.select() + }) + .collect() + } else { + return Err(JsonPathError::EmptyPath); + }; + + Ok(res.into_iter().flatten().flatten().collect()) + } + + pub fn replace_with( + &mut self, + mut value: Value, + fun: &mut F, + ) -> Result<&mut Self, JsonPathError> + where + F: FnMut(Value) -> Result, JsonPathError>, + { + let result = self.select(&value)?; + let result = result + .into_iter() + .filter(|v| !v.is_object() && !v.is_array()) + .collect(); + let paths = self.compute_paths(&value, result); + + for tokens in paths { + Self::replace_value(tokens, &mut value, fun)?; + } + + Ok(self) + } + + fn get_json_pointers( + &'a self, + value: &Value, + ) -> Result>, JsonPathError> { + let Some(parsers) = &self.parser else { + return Err(JsonPathError::EmptyPath); + }; + + let paths = parsers + .iter() + .map(|p| { + let selections = self.select_with_parser(value, p)?; + let selections = selections + .into_iter() + .filter(|v| !v.is_object() && !v.is_array()) + .collect(); + let paths = self.compute_paths(value, selections); + + let paths_with_metadata: Vec> = paths + .into_iter() + .map(|pointer| JsonPointerWithMetadata::from((pointer, p.metadata()))) + .collect(); + + Ok(paths_with_metadata) + }) + .collect::>>, JsonPathError>>()?; + + let pointers: Vec> = paths.into_iter().flatten().collect(); + + Ok(pointers) + } + + pub fn replace_with_async( + &mut self, + mut value: Value, + fun: F, + ) -> Result>, JsonPathError> + where + F: Fn(Value, &T) -> FutureValue, + { + let mut futures = FuturesOrdered::new(); + + let json_pointers = self.get_json_pointers(&value)?; + + for pointer in json_pointers.iter() { + let target = value + .pointer_mut(&pointer.pointer) + .ok_or(JsonPathError::EmptyValue)?; + let future = fun(std::mem::replace(target, Value::Null), pointer.metadata); + futures.push_back(future); + } + + let result_future = Box::pin(async move { + // FuturesOrdered maintains a strict FIFO order, so we can use the index to get the pointer + let mut i = 0; + while let Some(res) = futures.next().await { + // Get the pointer for this value + let pointer = json_pointers.get(i).ok_or(JsonPathError::EmptyValue)?; + + if let Some(v) = res { + let target = value + .pointer_mut(&pointer.pointer) + .ok_or(JsonPathError::EmptyValue)?; + *target = v; + } else { + // If None is returned then delete the value + value.as_object_mut().unwrap().remove(&pointer.pointer); + } + i += 1; + } + + Ok::<_, JsonPathError>(value) + }); + + Ok(result_future) + } + + fn replace_value( + mut tokens: Vec, + value: &mut Value, + fun: &mut F, + ) -> Result<(), JsonPathError> + where + F: FnMut(Value) -> Result, JsonPathError>, + { + let mut target = value; + + let last_index = tokens.len().saturating_sub(1); + for (i, token) in tokens.drain(..).enumerate() { + let target_once = target; + let is_last = i == last_index; + let target_opt = match *target_once { + Value::Object(ref mut map) => { + if is_last { + if let Entry::Occupied(mut e) = map.entry(token) { + let v = e.insert(Value::Null); + if let Some(res) = fun(v)? { + e.insert(res); + } else { + e.remove(); + } + } + return Ok(()); + } + map.get_mut(&token) + } + Value::Array(ref mut vec) => { + if let Ok(x) = token.parse::() { + if is_last { + if x < vec.len() { + let v = std::mem::replace(&mut vec[x], Value::Null); + if let Some(res) = fun(v)? { + vec[x] = res; + } else { + vec.remove(x); + } + } + return Ok(()); + } + vec.get_mut(x) + } else { + None + } + } + _ => None, + }; + + if let Some(t) = target_opt { + target = t; + } else { + break; + } + } + Ok(()) + } + + fn compute_paths(&self, origin: &Value, mut result: Vec<&Value>) -> Vec> { + let mut visited = HashSet::new(); + let mut visited_order = Vec::new(); + + let mut tokens = Vec::new(); + Self::walk( + origin, + &mut result, + &mut tokens, + &mut visited, + &mut visited_order, + ); + + visited_order + } + + fn walk( + origin: &Value, + target: &mut Vec<&Value>, + tokens: &mut Vec, + visited: &mut HashSet<*const Value>, + visited_order: &mut Vec>, + ) -> bool { + trace!("{:?}, {:?}", target, tokens); + + if target.is_empty() { + return true; + } + + target.retain(|t| { + if std::ptr::eq(origin, *t) { + if visited.insert(*t) { + visited_order.push(tokens.to_vec()); + } + false + } else { + true + } + }); + + match origin { + Value::Array(vec) => { + for (i, v) in vec.iter().enumerate() { + tokens.push(i.to_string()); + if Self::walk(v, target, tokens, visited, visited_order) { + return true; + } + tokens.pop(); + } + } + Value::Object(map) => { + for (k, v) in map { + tokens.push(k.clone()); + if Self::walk(v, target, tokens, visited, visited_order) { + return true; + } + tokens.pop(); + } + } + _ => {} + } + + false + } +} diff --git a/src/selector/cmp.rs b/src/selector/cmp.rs index 40cd89bc..e1c6417d 100644 --- a/src/selector/cmp.rs +++ b/src/selector/cmp.rs @@ -60,7 +60,7 @@ impl Cmp for CmpNe { let mut ret = v1.to_vec(); for v in v2 { for i in 0..ret.len() { - if std::ptr::eq(*v, &*ret[i]) { + if std::ptr::eq(*v, ret[i]) { ret.remove(i); break; } diff --git a/src/selector/mod.rs b/src/selector/mod.rs index a0f4acf6..0cd0d449 100644 --- a/src/selector/mod.rs +++ b/src/selector/mod.rs @@ -1,5 +1,7 @@ -pub use self::selector_impl::{JsonSelector, MultiJsonSelectorMut}; +pub use self::async_selector_impl::MultiJsonSelectorMutWithMetadata; +pub use self::selector_impl::{JsonSelector, JsonSelectorMut}; +mod async_selector_impl; mod cmp; mod selector_impl; mod terms; diff --git a/src/selector/selector_impl.rs b/src/selector/selector_impl.rs index f75feca4..03853dbc 100644 --- a/src/selector/selector_impl.rs +++ b/src/selector/selector_impl.rs @@ -1,19 +1,12 @@ use std::collections::HashSet; -use std::future::Future; -use std::pin::Pin; -use std::rc::Rc; use std::sync::Arc; -use futures::future::BoxFuture; -use futures::stream::FuturesOrdered; -use futures::StreamExt; -use rayon::prelude::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; use serde_json::map::Entry; use serde_json::{Number, Value}; use super::utils; use crate::paths::{tokens::*, ParserTokenHandler, PathParser, StrRange}; -use crate::{parser, JsonPathError}; +use crate::JsonPathError; use super::terms::*; @@ -27,8 +20,6 @@ pub struct JsonSelector<'a> { selector_filter: FilterTerms<'a>, } -type FutureValue = Pin> + Send>>; - impl<'a> JsonSelector<'a> { pub fn new(parser: PathParser<'a>) -> Self { JsonSelector { @@ -488,7 +479,7 @@ impl<'a> ParserTokenHandler<'a> for JsonSelector<'a> { self.visit_key(key); } ParseToken::Keys(keys) => { - let keys: Vec<&str> = keys.iter().map(|s| parse_value_reader(s)).collect(); + let keys: Vec<&str> = keys.iter().map(parse_value_reader).collect(); self.visit_keys(&keys) } ParseToken::Number(v) => { @@ -506,33 +497,29 @@ impl<'a> ParserTokenHandler<'a> for JsonSelector<'a> { } #[derive(Default, Clone)] -pub struct MultiJsonSelectorMut<'a> { +pub struct JsonSelectorMut<'a> { value: Option, - parser: Option>>>, + parser: Option>>, } -impl<'a> MultiJsonSelectorMut<'a> { +impl<'a> JsonSelectorMut<'a> { pub fn new(parser: PathParser<'a>) -> Self { - Self::new_ref(vec![Arc::new(parser)]) - } - - pub fn new_multi_parser(parsers: Vec>>) -> Self { - Self::new_ref(parsers) + Self::new_ref(Arc::new(parser)) } - pub fn new_ref(parser: Vec>>) -> Self { - MultiJsonSelectorMut { + pub fn new_ref(parser: Arc>) -> Self { + JsonSelectorMut { value: None, parser: Some(parser), } } pub fn reset_parser(&mut self, parser: PathParser<'a>) -> &mut Self { - self.parser = Some(vec![Arc::new(parser)]); + self.parser = Some(Arc::new(parser)); self } - pub fn reset_parser_ref(&mut self, parser: Vec>>) -> &mut Self { + pub fn reset_parser_ref(&mut self, parser: Arc>) -> &mut Self { self.parser = Some(parser); self } @@ -555,26 +542,21 @@ impl<'a> MultiJsonSelectorMut<'a> { } fn select(&self) -> Result, JsonPathError> { - let res: Vec, JsonPathError>> = - if let Some(parser) = self.parser.as_ref() { - parser - .iter() - .map(|p| { - let mut selector = JsonSelector::default(); - selector.reset_parser_ref(p.clone()); - if let Some(value) = self.value.as_ref() { - selector.value(value); - } else { - return Err(JsonPathError::EmptyValue); - } - Ok(selector.select()?) - }) - .collect() - } else { - return Err(JsonPathError::EmptyPath); - }; + let mut selector = JsonSelector::default(); - Ok(res.into_iter().flatten().flatten().collect()) + if let Some(parser) = self.parser.as_ref() { + selector.reset_parser_ref(Arc::clone(parser)); + } else { + return Err(JsonPathError::EmptyPath); + } + + if let Some(value) = self.value.as_ref() { + selector.value(value); + } else { + return Err(JsonPathError::EmptyValue); + } + + selector.select() } pub fn replace_with(&mut self, fun: &mut F) -> Result<&mut Self, JsonPathError> @@ -582,7 +564,6 @@ impl<'a> MultiJsonSelectorMut<'a> { F: FnMut(Value) -> Result, JsonPathError>, { let result = self.select()?; - let result = result.into_iter().filter(|v| !v.is_object() && !v.is_array()).collect(); let paths = self.compute_paths(result); if let Some(ref mut value) = &mut self.value { @@ -594,68 +575,6 @@ impl<'a> MultiJsonSelectorMut<'a> { Ok(self) } - fn get_json_pointers(&mut self) -> Result, JsonPathError> { - let result = self.select()?; - let paths = self.compute_paths(result); - - let json_pointers = paths - .iter() - .map(|tokens| "/".to_owned() + &tokens.join("/")) - .collect::>(); - - Ok(json_pointers) - } - - pub fn replace_with_async( - &mut self, - fun: F, - ) -> Result>, JsonPathError> - where - F: Fn(Value) -> FutureValue, - { - let mut futures = FuturesOrdered::new(); - - let json_pointers = self.get_json_pointers()?; - - if let Some(ref mut value) = &mut self.value { - for pointer in json_pointers.iter() { - let target = value - .pointer_mut(pointer) - .ok_or(JsonPathError::EmptyValue)?; - let future = fun(std::mem::replace(target, Value::Null)); - futures.push_back(future); - } - }; - - let result_future = Box::pin(async move { - // FuturesOrdered maintains a strict FIFO order, so we can use the index to get the pointer - let mut i = 0; - while let Some(res) = futures.next().await { - // Get the pointer for this value - let pointer = json_pointers.get(i).ok_or(JsonPathError::EmptyValue)?; - - if let Some(v) = res { - if let Some(ref mut value) = &mut self.value { - let target = value - .pointer_mut(pointer) - .ok_or(JsonPathError::EmptyValue)?; - *target = v; - } - } else { - // If None is returned then delete the value - if let Some(ref mut value) = &mut self.value { - value.as_object_mut().unwrap().remove(pointer); - } - } - i += 1; - } - - Ok::<_, JsonPathError>(self) - }); - - Ok(result_future) - } - fn replace_value( mut tokens: Vec, value: &mut Value, diff --git a/src/selector/terms.rs b/src/selector/terms.rs index 49e57452..ff6747e4 100644 --- a/src/selector/terms.rs +++ b/src/selector/terms.rs @@ -164,7 +164,7 @@ impl<'a> ExprTerm<'a> { fn cmp_json( rel: Option>, fk1: Option>, - vec1: &mut Vec<&'a Value>, + vec1: &mut [&'a Value], other: &mut ExprTerm<'a>, cmp_fn: &C1, ) -> ExprTerm<'a> diff --git a/src/selector/value_walker.rs b/src/selector/value_walker.rs index d538e55c..7cc03baa 100644 --- a/src/selector/value_walker.rs +++ b/src/selector/value_walker.rs @@ -107,7 +107,6 @@ impl<'a> ValueWalker { } Value::Object(map) => { map.values() - .into_iter() .for_each(|v| Self::_walk(v, acc, fun)); } _ => {} diff --git a/tests/async.rs b/tests/async.rs index 346eff9c..d25a0580 100644 --- a/tests/async.rs +++ b/tests/async.rs @@ -9,12 +9,10 @@ use std::{ }; use common::{read_json, setup}; -use futures::{ - channel::mpsc::{channel, Receiver, Sender}, - task::Waker, - Future, SinkExt, StreamExt, +use futures::Future; +use jsonpath::{ + JsonSelector, MultiJsonSelectorMutWithMetadata, PathParser, PathParserWithMetadata, }; -use jsonpath::{JsonSelector, MultiJsonSelectorMut, PathParser}; use serde_json::Value; mod common; @@ -63,40 +61,30 @@ impl CryptoRequest { } } - fn new_field(&self, input: Value) -> CryptoField { - let bag = CryptoField::new(input); + fn new_field(&self, metadata: String) -> CryptoField { + let bag = CryptoField::new(metadata); self.bags.lock().unwrap().push(bag.clone()); bag } async fn send_request(&self) { let mut bags = self.bags.lock().unwrap(); - let inputs = bags - .iter_mut() - .filter_map(|bag| bag.input.take()) - .collect::>(); - // let _ = reqwest::Client::new() - // .post("https://blackhole.posterior.io/vr5kvy") - // .body(serde_json::to_string(&inputs).unwrap()) - // .send() - // .await - // .unwrap(); for bag in bags.iter_mut() { - bag.value.set_value(serde_json::json!(42)); + bag.value.set_value(serde_json::Value::String(bag.metadata.clone())); } } } #[derive(Clone)] struct CryptoField { - input: Option, + metadata: String, value: ValueFuture, } impl CryptoField { - fn new(input: Value) -> Self { + fn new(metadata: String) -> Self { Self { - input: Some(input), + metadata: metadata, value: ValueFuture::new(), } } @@ -107,20 +95,19 @@ impl CryptoField { } #[tokio::test] -async fn selector_mut() { +async fn async_selector_mut() { setup(); - let parser = PathParser::compile("$.store..price").unwrap(); - let parser_two = PathParser::compile("$.store..author").unwrap(); + let parser = PathParserWithMetadata::compile("$.store..price", "price-metadata").unwrap(); + let parser_two = PathParserWithMetadata::compile("$.store..author", "author-metadata").unwrap(); let mut selector_mut = - MultiJsonSelectorMut::new_multi_parser(vec![parser.into(), parser_two.into()]); + MultiJsonSelectorMutWithMetadata::new_multi_parser(vec![parser, parser_two]); let crypto_request = Arc::new(CryptoRequest::new()); let result_futures = selector_mut - .value(read_json("./benchmark/example.json")) - .replace_with_async(|v| { - let bag: CryptoField = crypto_request.new_field(v); + .replace_with_async(read_json("./benchmark/example.json"), |_, m| { + let bag: CryptoField = crypto_request.new_field(m.to_string()); Box::pin(async move { let val = bag.value().await; @@ -131,14 +118,25 @@ async fn selector_mut() { crypto_request.send_request().await; - let result = result_futures.await.unwrap().take().unwrap(); + let root_result = result_futures.await.unwrap(); + // Check that it replaced $.store..price with 42 let parser = PathParser::compile("$.store..price").unwrap(); let mut selector = JsonSelector::new(parser); - let result = selector.value(&result).select().unwrap(); + let result = selector.value(&root_result).select().unwrap(); + + assert_eq!( + vec![&json!("price-metadata"), &json!("price-metadata"), &json!("price-metadata"), &json!("price-metadata"), &json!("price-metadata")], + result + ); + + // Check that it replaced $.store..author with 42 + let parser = PathParser::compile("$.store..author").unwrap(); + let mut selector = JsonSelector::new(parser); + let result = selector.value(&root_result).select().unwrap(); assert_eq!( - vec![&json!(42), &json!(42), &json!(42), &json!(42), &json!(42)], + vec![&json!("author-metadata"), &json!("author-metadata"), &json!("author-metadata"), &json!("author-metadata")], result ); } diff --git a/tests/common.rs b/tests/common.rs index eae36803..35a59d07 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -5,14 +5,14 @@ extern crate serde_json; use std::io::Read; use std::io::Write; -use log::LevelFilter; + use serde_json::Value; use self::jsonpath::{JsonSelector, PathParser}; #[allow(dead_code)] pub fn setup() { - let _ = env_logger::Builder::new() + env_logger::Builder::new() .format(|buf, record| { writeln!( buf, diff --git a/tests/filter.rs b/tests/filter.rs index 97903bc5..77c5f681 100644 --- a/tests/filter.rs +++ b/tests/filter.rs @@ -10,7 +10,7 @@ fn quote() { setup(); select_and_then_compare( - r#"$['single\'quote']"#, + r"$['single\'quote']", json!({"single'quote":"value"}), json!(["value"]), ); diff --git a/tests/readme.rs b/tests/readme.rs index 175310ff..0dfd093b 100644 --- a/tests/readme.rs +++ b/tests/readme.rs @@ -6,7 +6,7 @@ extern crate serde_json; use serde::Deserialize; use serde_json::Value; -use jsonpath::{JsonSelector, MultiJsonSelectorMut, PathParser}; +use jsonpath::{JsonSelector, JsonSelectorMut, PathParser}; mod common; @@ -208,7 +208,7 @@ fn readme_selector_mut() { ]}); let parser = PathParser::compile("$..[?(@.age == 20)].age").unwrap(); - let mut selector_mut = MultiJsonSelectorMut::new(parser); + let mut selector_mut = JsonSelectorMut::new(parser); let result = selector_mut .value(json_obj) diff --git a/tests/selector.rs b/tests/selector.rs index 84c18b17..06ac266d 100644 --- a/tests/selector.rs +++ b/tests/selector.rs @@ -3,8 +3,8 @@ extern crate jsonpath_lib as jsonpath; extern crate serde_json; use common::{read_json, setup}; -use jsonpath::{JsonPathError, Parser, Selector, SelectorMut}; -use jsonpath::{JsonSelector, MultiJsonSelectorMut, PathParser}; +use jsonpath::{JsonPathError, JsonSelectorMut, Parser, Selector, SelectorMut}; +use jsonpath::{JsonSelector, PathParser}; use serde_json::Value; mod common; @@ -14,7 +14,7 @@ fn selector_mut() { setup(); let parser = PathParser::compile("$.store..price").unwrap(); - let mut selector_mut = MultiJsonSelectorMut::new(parser); + let mut selector_mut = JsonSelectorMut::new(parser); let mut nums = Vec::new(); let result = selector_mut @@ -61,7 +61,7 @@ fn selector_mut_err() { .value(read_json("./benchmark/example.json")) .replace_with(&mut |_| Err(JsonPathError::EmptyValue)); - assert_eq!(result.is_err(), true); + assert!(result.is_err()); } #[test] @@ -69,12 +69,12 @@ fn jsonselector_mut_err() { setup(); let parser = PathParser::compile("$.store..price[?(@>13)]").unwrap(); - let mut selector_mut = MultiJsonSelectorMut::new(parser); + let mut selector_mut = JsonSelectorMut::new(parser); let result = selector_mut .value(read_json("./benchmark/example.json")) .replace_with(&mut |_| Err(JsonPathError::EmptyValue)); - assert_eq!(result.is_err(), true); + assert!(result.is_err()); } #[test] @@ -85,11 +85,12 @@ fn selector_node_ref() { assert!(std::ptr::eq(selector.node_ref().unwrap(), &node)); } +#[test] fn selector_delete_multi_elements_from_array() { setup(); let parser = PathParser::compile("$[0,2]").unwrap(); - let mut selector_mut = MultiJsonSelectorMut::new(parser); + let mut selector_mut = JsonSelectorMut::new(parser); let result = selector_mut .value(serde_json::from_str("[1,2,3]").unwrap()) @@ -109,7 +110,7 @@ fn selector_delete() { setup(); let parser = PathParser::compile("$.store..price[?(@>13)]").unwrap(); - let mut selector_mut = MultiJsonSelectorMut::new(parser); + let mut selector_mut = JsonSelectorMut::new(parser); let result = selector_mut .value(read_json("./benchmark/example.json")) @@ -138,7 +139,7 @@ fn selector_delete() { fn selector_remove() { setup(); let parser = PathParser::compile("$.store..price[?(@>13)]").unwrap(); - let mut selector_mut = MultiJsonSelectorMut::new(parser); + let mut selector_mut = JsonSelectorMut::new(parser); let result = selector_mut .value(read_json("./benchmark/example.json")) From abcd313bc15aae8b22c200bbfe460584e054aa01 Mon Sep 17 00:00:00 2001 From: Eoin Power-Moran Date: Thu, 19 Oct 2023 14:38:35 +0100 Subject: [PATCH 13/20] fmt and upgrade edition --- Cargo.toml | 1 + src/ffi/mod.rs | 2 +- src/lib.rs | 65 ++++--- src/parser/mod.rs | 37 ++-- src/parser/tokenizer.rs | 41 +--- src/paths/mod.rs | 6 +- src/paths/parser_node_visitor.rs | 14 +- src/paths/parser_token_handler.rs | 6 +- src/paths/path_parser.rs | 62 +++--- src/paths/str_reader.rs | 13 +- src/paths/tokenizer.rs | 68 +++---- src/paths/tokens.rs | 2 +- src/select/cmp.rs | 3 +- src/select/expr_term.rs | 4 +- src/select/mod.rs | 132 ++++++++----- src/select/value_walker.rs | 31 +-- src/selector/cmp.rs | 3 +- src/selector/mod.rs | 4 +- src/selector/selector_impl.rs | 86 +++++--- src/selector/terms.rs | 313 +++++++++++++++++------------- src/selector/utils.rs | 6 +- src/selector/value_walker.rs | 105 +++++----- tests/array_filter.rs | 10 +- tests/filter.rs | 100 +++++----- tests/jsonpath_examples.rs | 2 +- tests/lib.rs | 4 +- tests/op.rs | 83 ++++---- tests/paths.rs | 180 ++++++++--------- tests/precompile.rs | 14 +- tests/readme.rs | 15 +- tests/return_type.rs | 10 +- tests/selector.rs | 57 ++---- 32 files changed, 772 insertions(+), 707 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 3905cfb0..60cce38a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "jsonpath_lib" version = "0.3.0" +edition = "2021" authors = ["Changseok Han "] description = "It is JsonPath engine written in Rust. it provide a similar API interface in Webassembly and Javascript too. - Webassembly Demo: https://freestrings.github.io/jsonpath" diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs index 813486cd..383951ab 100644 --- a/src/ffi/mod.rs +++ b/src/ffi/mod.rs @@ -1,7 +1,7 @@ use std::ffi::{CStr, CString}; use std::os::raw::{c_char, c_void}; -use {parser, select, select_as_str}; +use crate::{parser, select, select_as_str}; const INVALID_PATH: &str = "invalid path"; const INVALID_JSON: &str = "invalud json"; diff --git a/src/lib.rs b/src/lib.rs index 664e69a0..4d9f603c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -131,26 +131,23 @@ extern crate serde_json; use serde_json::Value; #[allow(deprecated)] -use parser::Node; +use crate::parser::Node; #[allow(deprecated)] -pub use parser::Parser; +pub use crate::parser::Parser; #[allow(deprecated)] -pub use select::{Selector, SelectorMut}; +pub use crate::select::{Selector, SelectorMut}; -#[deprecated( -since = "0.4.0", -note = "It will be move to common module. since 0.5" -)] -pub use select::JsonPathError; +#[deprecated(since = "0.4.0", note = "It will be move to common module. since 0.5")] +pub use crate::select::JsonPathError; -pub use selector::{JsonSelector, JsonSelectorMut}; -pub use paths::PathParser; +pub use crate::paths::PathParser; +pub use crate::selector::{JsonSelector, JsonSelectorMut}; use std::rc::Rc; #[doc(hidden)] #[deprecated( -since = "0.4.0", -note = "'ffi' is moved to another location like 'wasm' from version 0.5.x" + since = "0.4.0", + note = "'ffi' is moved to another location like 'wasm' from version 0.5.x" )] mod ffi; #[doc(hidden)] @@ -165,7 +162,9 @@ impl From<&paths::TokenError> for JsonPathError { fn from(e: &paths::TokenError) -> Self { match e { paths::TokenError::Eof => JsonPathError::Path("Eof".to_string()), - paths::TokenError::Position(pos) => JsonPathError::Path(["Position:", &pos.to_string()].concat()) + paths::TokenError::Position(pos) => { + JsonPathError::Path(["Position:", &pos.to_string()].concat()) + } } } } @@ -198,8 +197,8 @@ impl From<&paths::TokenError> for JsonPathError { /// ]); /// ``` #[deprecated( -since = "0.2.5", -note = "Please use the PathCompiled::compile function instead. It will be removed from 0.4.1" + since = "0.2.5", + note = "Please use the PathCompiled::compile function instead. It will be removed from 0.4.1" )] pub fn compile(path: &str) -> impl FnMut(&Value) -> Result, JsonPathError> { #[allow(deprecated)] @@ -249,11 +248,17 @@ pub fn compile(path: &str) -> impl FnMut(&Value) -> Result, JsonPath /// ]); /// ``` #[allow(clippy::needless_lifetimes)] -pub fn selector<'a>(json: &'a Value) -> impl FnMut(&'a str) -> Result, JsonPathError> { +pub fn selector<'a>( + json: &'a Value, +) -> impl FnMut(&'a str) -> Result, JsonPathError> { let mut selector = JsonSelector::default(); move |path| { let parser = PathParser::compile(path).map_err(|e| JsonPathError::from(&e))?; - selector.reset_parser(parser).value(json).reset_value().select() + selector + .reset_parser(parser) + .value(json) + .reset_value() + .select() } } @@ -303,9 +308,9 @@ pub fn selector<'a>(json: &'a Value) -> impl FnMut(&'a str) -> Result(json: &'a Value) - -> impl FnMut(&'a str) -> Result, JsonPathError> + '_ -{ +pub fn selector_as<'a, T: serde::de::DeserializeOwned>( + json: &'a Value, +) -> impl FnMut(&'a str) -> Result, JsonPathError> + '_ { let mut selector = JsonSelector::default(); let _ = selector.value(json); move |path: &str| { @@ -555,11 +560,14 @@ where /// ]})); /// ``` pub fn replace_with_tokens(value: Value, path: &str, fun: &mut F) -> Result - where - F: FnMut(Value, &[String]) -> Result, JsonPathError>, +where + F: FnMut(Value, &[String]) -> Result, JsonPathError>, { let mut selector = SelectorMut::default(); - let value = selector.str_path(path)?.value(value).replace_with_tokens(fun)?; + let value = selector + .str_path(path)? + .value(value) + .replace_with_tokens(fun)?; Ok(value.take().unwrap_or(Value::Null)) } @@ -606,10 +614,7 @@ pub fn replace_with_tokens(value: Value, path: &str, fun: &mut F) -> Result Result { let node = parser::Parser::compile(path)?; - Ok(Self { - node - }) + Ok(Self { node }) } /// Execute the select operation on the pre-compiled path. @@ -688,7 +691,7 @@ impl<'a> PathCompiled<'a> { pub fn compile(path: &str) -> Result { let parser = PathParser::compile(path).map_err(|e| JsonPathError::from(&e))?; Ok(PathCompiled { - parser: Rc::new(parser) + parser: Rc::new(parser), }) } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index affb887a..160b7243 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -13,8 +13,8 @@ mod utils { use std::str::FromStr; pub fn string_to_num(string: &str, msg_handler: F) -> Result - where - F: Fn() -> String, + where + F: Fn() -> String, { match string.parse() { Ok(n) => Ok(n), @@ -481,14 +481,15 @@ impl Parser { let node = Self::term(tokenizer)?; Self::eat_whitespace(tokenizer); - if matches!(tokenizer.peek_token(), + if matches!( + tokenizer.peek_token(), Ok(Token::Equal(_)) - | Ok(Token::NotEqual(_)) - | Ok(Token::Little(_)) - | Ok(Token::LittleOrEqual(_)) - | Ok(Token::Greater(_)) - | Ok(Token::GreaterOrEqual(_))) - { + | Ok(Token::NotEqual(_)) + | Ok(Token::Little(_)) + | Ok(Token::LittleOrEqual(_)) + | Ok(Token::Greater(_)) + | Ok(Token::GreaterOrEqual(_)) + ) { Self::op(node, tokenizer) } else if has_prop_candidate { Ok(node) @@ -543,21 +544,15 @@ impl Parser { _ => Self::paths(node, tokenizer), } } - Ok(Token::Absolute(_)) => { - Self::json_path(tokenizer) - } + Ok(Token::Absolute(_)) => Self::json_path(tokenizer), Ok(Token::DoubleQuoted(_, _)) | Ok(Token::SingleQuoted(_, _)) => { Self::array_quote_value(tokenizer) } - Ok(Token::Key(_, key)) => { - match key.as_bytes()[0] { - b'-' | b'0'..=b'9' => Self::term_num(tokenizer), - _ => Self::boolean(tokenizer), - } - } - _ => { - Err(tokenizer.err_msg()) - } + Ok(Token::Key(_, key)) => match key.as_bytes()[0] { + b'-' | b'0'..=b'9' => Self::term_num(tokenizer), + _ => Self::boolean(tokenizer), + }, + _ => Err(tokenizer.err_msg()), } } diff --git a/src/parser/tokenizer.rs b/src/parser/tokenizer.rs index 3e079b9d..67d7d1e8 100644 --- a/src/parser/tokenizer.rs +++ b/src/parser/tokenizer.rs @@ -105,23 +105,9 @@ impl<'a> Tokenizer<'a> { fn dolla(&mut self, pos: usize, ch: char) -> Result { let fun = |c: &char| match c { - &CH_DOT - | &CH_ASTERISK - | &CH_LARRAY - | &CH_RARRAY - | &CH_LPAREN - | &CH_RPAREN - | &CH_AT - | &CH_QUESTION - | &CH_COMMA - | &CH_SEMICOLON - | &CH_LITTLE - | &CH_GREATER - | &CH_EQUAL - | &CH_AMPERSAND - | &CH_PIPE - | &CH_EXCLAMATION - => false, + &CH_DOT | &CH_ASTERISK | &CH_LARRAY | &CH_RARRAY | &CH_LPAREN | &CH_RPAREN | &CH_AT + | &CH_QUESTION | &CH_COMMA | &CH_SEMICOLON | &CH_LITTLE | &CH_GREATER | &CH_EQUAL + | &CH_AMPERSAND | &CH_PIPE | &CH_EXCLAMATION => false, _ => !c.is_whitespace(), }; let (_, mut vec) = self.input.take_while(fun).map_err(to_token_error)?; @@ -243,24 +229,9 @@ impl<'a> Tokenizer<'a> { fn other(&mut self, pos: usize, ch: char) -> Result { let fun = |c: &char| match c { - &CH_DOLLA - | &CH_DOT - | &CH_ASTERISK - | &CH_LARRAY - | &CH_RARRAY - | &CH_LPAREN - | &CH_RPAREN - | &CH_AT - | &CH_QUESTION - | &CH_COMMA - | &CH_SEMICOLON - | &CH_LITTLE - | &CH_GREATER - | &CH_EQUAL - | &CH_AMPERSAND - | &CH_PIPE - | &CH_EXCLAMATION - => false, + &CH_DOLLA | &CH_DOT | &CH_ASTERISK | &CH_LARRAY | &CH_RARRAY | &CH_LPAREN + | &CH_RPAREN | &CH_AT | &CH_QUESTION | &CH_COMMA | &CH_SEMICOLON | &CH_LITTLE + | &CH_GREATER | &CH_EQUAL | &CH_AMPERSAND | &CH_PIPE | &CH_EXCLAMATION => false, _ => !c.is_whitespace(), }; let (_, mut vec) = self.input.take_while(fun).map_err(to_token_error)?; diff --git a/src/paths/mod.rs b/src/paths/mod.rs index 59474c47..186008c5 100644 --- a/src/paths/mod.rs +++ b/src/paths/mod.rs @@ -4,9 +4,9 @@ pub use self::path_parser::PathParser; pub use self::str_reader::StrRange; pub use self::tokenizer::TokenError; +mod parser_node_visitor; +mod parser_token_handler; +mod path_parser; mod str_reader; mod tokenizer; pub mod tokens; -mod parser_token_handler; -mod parser_node_visitor; -mod path_parser; diff --git a/src/paths/parser_node_visitor.rs b/src/paths/parser_node_visitor.rs index ab35c1e3..9df56822 100644 --- a/src/paths/parser_node_visitor.rs +++ b/src/paths/parser_node_visitor.rs @@ -1,12 +1,12 @@ -use paths::{ParserTokenHandler, StrRange}; -use paths::path_parser::ParserNode; -use paths::tokens::{FilterToken, ParseToken}; +use crate::paths::path_parser::ParserNode; +use crate::paths::tokens::{FilterToken, ParseToken}; +use crate::paths::{ParserTokenHandler, StrRange}; pub trait ParserNodeVisitor<'a> { fn visit(&self, parse_node: &ParserNode, token_handler: &mut F, parse_value_reader: &F1) - where - F: ParserTokenHandler<'a>, - F1: Fn(&StrRange) -> &'a str + where + F: ParserTokenHandler<'a>, + F1: Fn(&StrRange) -> &'a str, { trace!("visit {:?}", parse_node); match &parse_node.token { @@ -70,4 +70,4 @@ pub trait ParserNodeVisitor<'a> { _ => {} } } -} \ No newline at end of file +} diff --git a/src/paths/parser_token_handler.rs b/src/paths/parser_token_handler.rs index ec06d410..65fcd24f 100644 --- a/src/paths/parser_token_handler.rs +++ b/src/paths/parser_token_handler.rs @@ -3,6 +3,6 @@ use super::tokens::ParseToken; pub trait ParserTokenHandler<'a> { fn handle(&mut self, token: &ParseToken, parse_value_reader: &F) - where - F: Fn(&StrRange) -> &'a str; -} \ No newline at end of file + where + F: Fn(&StrRange) -> &'a str; +} diff --git a/src/paths/path_parser.rs b/src/paths/path_parser.rs index 178e105d..1ceef84a 100644 --- a/src/paths/path_parser.rs +++ b/src/paths/path_parser.rs @@ -1,7 +1,7 @@ use std::str::FromStr; -use super::parser_token_handler::ParserTokenHandler; use super::parser_node_visitor::ParserNodeVisitor; +use super::parser_token_handler::ParserTokenHandler; use super::str_reader::StrRange; use super::tokenizer::{TokenError, TokenReader}; use super::tokens::{FilterToken, ParseToken, Token}; @@ -19,8 +19,8 @@ impl<'a> PathParser<'a> { } pub(crate) fn parse(&self, parse_token_handler: &mut F) -> Result<(), String> - where - F: ParserTokenHandler<'a>, + where + F: ParserTokenHandler<'a>, { if self.parser.parse_node.is_none() { unreachable!() @@ -54,8 +54,8 @@ impl<'a> ParserImpl<'a> { } fn string_to_num(string: &str, msg_handler: F) -> Result - where - F: Fn() -> TokenError, + where + F: Fn() -> TokenError, { match string.parse() { Ok(n) => Ok(n), @@ -293,9 +293,7 @@ impl<'a> ParserImpl<'a> { self.eat_token(); self.range_to() } - Ok(Token::DoubleQuoted(_)) | Ok(Token::SingleQuoted(_)) => { - self.array_quote_value() - } + Ok(Token::DoubleQuoted(_)) | Ok(Token::SingleQuoted(_)) => self.array_quote_value(), Err(TokenError::Eof) => Ok(self.create_node(ParseToken::Eof)), _ => { self.eat_token(); @@ -472,14 +470,15 @@ impl<'a> ParserImpl<'a> { let node = self.term()?; self.eat_whitespace(); - if matches!(self.token_reader.peek_token(), + if matches!( + self.token_reader.peek_token(), Ok(Token::Equal(_)) - | Ok(Token::NotEqual(_)) - | Ok(Token::Little(_)) - | Ok(Token::LittleOrEqual(_)) - | Ok(Token::Greater(_)) - | Ok(Token::GreaterOrEqual(_))) - { + | Ok(Token::NotEqual(_)) + | Ok(Token::Little(_)) + | Ok(Token::LittleOrEqual(_)) + | Ok(Token::Greater(_)) + | Ok(Token::GreaterOrEqual(_)) + ) { self.op(node) } else if has_prop_candidate { Ok(node) @@ -511,7 +510,9 @@ impl<'a> ParserImpl<'a> { match self.token_reader.next_token() { Ok(Token::Key(s)) => { let frac = self.token_reader.read_value(&s); - let number = Self::string_to_num(&[num, ".", frac].concat(), || self.token_reader.to_error())?; + let number = Self::string_to_num(&[num, ".", frac].concat(), || { + self.token_reader.to_error() + })?; Ok(self.create_node(ParseToken::Number(number))) } _ => Err(self.token_reader.to_error()), @@ -552,15 +553,9 @@ impl<'a> ParserImpl<'a> { _ => self.paths(node), } } - Ok(Token::Absolute(_)) => { - self.json_path() - } - Ok(Token::DoubleQuoted(_)) | Ok(Token::SingleQuoted(_)) => { - self.array_quote_value() - } - _ => { - Err(self.token_reader.to_error()) - } + Ok(Token::Absolute(_)) => self.json_path(), + Ok(Token::DoubleQuoted(_)) | Ok(Token::SingleQuoted(_)) => self.array_quote_value(), + _ => Err(self.token_reader.to_error()), } } @@ -623,10 +618,10 @@ pub struct ParserNode { #[cfg(test)] mod path_parser_tests { - use paths::ParserTokenHandler; - use paths::path_parser::PathParser; - use paths::str_reader::StrRange; - use paths::tokens::{FilterToken, ParseToken}; + use crate::paths::path_parser::PathParser; + use crate::paths::str_reader::StrRange; + use crate::paths::tokens::{FilterToken, ParseToken}; + use crate::paths::ParserTokenHandler; struct NodeVisitorTestImpl<'a> { input: &'a str, @@ -650,8 +645,8 @@ mod path_parser_tests { impl<'a> ParserTokenHandler<'a> for NodeVisitorTestImpl<'a> { fn handle(&mut self, token: &ParseToken, _: &F) - where - F: Fn(&StrRange) -> &'a str + where + F: Fn(&StrRange) -> &'a str, { trace!("handle {:?}", token); self.stack.push(token.clone()); @@ -964,7 +959,10 @@ mod path_parser_tests { Ok(vec![ ParseToken::Absolute, ParseToken::Array, - ParseToken::Keys(vec![StrRange::new(2, "\"a\"".len()), StrRange::new(7, "'b'".len())]), + ParseToken::Keys(vec![ + StrRange::new(2, "\"a\"".len()), + StrRange::new(7, "'b'".len()) + ]), ParseToken::ArrayEof ]) ); diff --git a/src/paths/str_reader.rs b/src/paths/str_reader.rs index 11b74a70..1e5aca29 100644 --- a/src/paths/str_reader.rs +++ b/src/paths/str_reader.rs @@ -28,7 +28,12 @@ pub(crate) struct StrReader<'a> { impl<'a> StrReader<'a> { pub fn new(input: &'a str) -> Self { - StrReader { input, pos: 0, chars: input.chars(), peeked: None } + StrReader { + input, + pos: 0, + chars: input.chars(), + peeked: None, + } } pub fn peek_char(&mut self) -> Result { @@ -37,8 +42,8 @@ impl<'a> StrReader<'a> { } pub fn take_while(&mut self, fun: F) -> Result - where - F: Fn(&char) -> bool, + where + F: Fn(&char) -> bool, { let mut char_len: usize = 0; while let Some(c) = self.peek() { @@ -47,7 +52,7 @@ impl<'a> StrReader<'a> { } match self.next() { Some(ch) => char_len += ch.len_utf8(), - _ => return Err(ReaderError::Eof) + _ => return Err(ReaderError::Eof), } } diff --git a/src/paths/tokenizer.rs b/src/paths/tokenizer.rs index fd61c7e5..b4f12d69 100644 --- a/src/paths/tokenizer.rs +++ b/src/paths/tokenizer.rs @@ -50,23 +50,9 @@ impl<'a> Tokenizer<'a> { fn dolla(&mut self) -> Result { let fun = |c: &char| match c { - &CH_DOT - | &CH_ASTERISK - | &CH_LARRAY - | &CH_RARRAY - | &CH_LPAREN - | &CH_RPAREN - | &CH_AT - | &CH_QUESTION - | &CH_COMMA - | &CH_SEMICOLON - | &CH_LITTLE - | &CH_GREATER - | &CH_EQUAL - | &CH_AMPERSAND - | &CH_PIPE - | &CH_EXCLAMATION - => false, + &CH_DOT | &CH_ASTERISK | &CH_LARRAY | &CH_RARRAY | &CH_LPAREN | &CH_RPAREN | &CH_AT + | &CH_QUESTION | &CH_COMMA | &CH_SEMICOLON | &CH_LITTLE | &CH_GREATER | &CH_EQUAL + | &CH_AMPERSAND | &CH_PIPE | &CH_EXCLAMATION => false, _ => !c.is_whitespace(), }; let read = self.input.take_while(fun).map_err(to_token_error)?; @@ -78,11 +64,17 @@ impl<'a> Tokenizer<'a> { } fn quote(&mut self, ch: char) -> Result { - let span = self.input.take_while(|c| *c != ch).map_err(to_token_error)?; + let span = self + .input + .take_while(|c| *c != ch) + .map_err(to_token_error)?; let val = self.input.read(&span); if let Some('\\') = val.chars().last() { self.input.next_char().map_err(to_token_error)?; - let remain_span = self.input.take_while(|c| *c != ch).map_err(to_token_error)?; + let remain_span = self + .input + .take_while(|c| *c != ch) + .map_err(to_token_error)?; self.input.next_char().map_err(to_token_error)?; Ok(StrRange::new(span.pos, remain_span.offset)) } else { @@ -175,24 +167,9 @@ impl<'a> Tokenizer<'a> { fn other(&mut self) -> Result { let fun = |c: &char| match c { - &CH_DOLLA - | &CH_DOT - | &CH_ASTERISK - | &CH_LARRAY - | &CH_RARRAY - | &CH_LPAREN - | &CH_RPAREN - | &CH_AT - | &CH_QUESTION - | &CH_COMMA - | &CH_SEMICOLON - | &CH_LITTLE - | &CH_GREATER - | &CH_EQUAL - | &CH_AMPERSAND - | &CH_PIPE - | &CH_EXCLAMATION - => false, + &CH_DOLLA | &CH_DOT | &CH_ASTERISK | &CH_LARRAY | &CH_RARRAY | &CH_LPAREN + | &CH_RPAREN | &CH_AT | &CH_QUESTION | &CH_COMMA | &CH_SEMICOLON | &CH_LITTLE + | &CH_GREATER | &CH_EQUAL | &CH_AMPERSAND | &CH_PIPE | &CH_EXCLAMATION => false, _ => !c.is_whitespace(), }; let span = self.input.take_while(fun).map_err(to_token_error)?; @@ -270,7 +247,8 @@ impl<'a> TokenReader<'a> { let peeked = self.peeked.get_or_insert_with(|| { let mut token = tokenizer.next_token(); if let Ok(token) = &mut token { - let token = token.reset_span(StrRange::new(prev_pos, tokenizer.current_pos() - prev_pos)); + let token = + token.reset_span(StrRange::new(prev_pos, tokenizer.current_pos() - prev_pos)); return Ok(token); } token @@ -310,9 +288,9 @@ impl<'a> TokenReader<'a> { #[cfg(test)] mod tokenizer_tests { - use paths::str_reader::StrRange; - use paths::tokenizer::{TokenError, TokenReader}; - use paths::tokens::Token; + use crate::paths::str_reader::StrRange; + use crate::paths::tokenizer::{TokenError, TokenReader}; + use crate::paths::tokens::Token; fn setup() { let _ = env_logger::try_init(); @@ -393,7 +371,11 @@ mod tokenizer_tests { run( "$..", ( - vec![Token::Absolute(StrRange::new(0, 1)), Token::Dot(StrRange::new(1, 1)), Token::Dot(StrRange::new(2, 1))], + vec![ + Token::Absolute(StrRange::new(0, 1)), + Token::Dot(StrRange::new(1, 1)), + Token::Dot(StrRange::new(2, 1)), + ], Some(TokenError::Eof), ), ); @@ -581,4 +563,4 @@ mod tokenizer_tests { ), ); } -} \ No newline at end of file +} diff --git a/src/paths/tokens.rs b/src/paths/tokens.rs index 0d4cc919..f7cf272f 100644 --- a/src/paths/tokens.rs +++ b/src/paths/tokens.rs @@ -128,4 +128,4 @@ pub enum FilterToken { GreaterOrEqual, And, Or, -} \ No newline at end of file +} diff --git a/src/select/cmp.rs b/src/select/cmp.rs index 74834d34..37b23147 100644 --- a/src/select/cmp.rs +++ b/src/select/cmp.rs @@ -193,7 +193,7 @@ impl Cmp for CmpOr { let mut ret = [v1, v2].concat(); for x in (0..ret.len()).rev() { - for y in (x+1..ret.len()).rev() { + for y in (x + 1..ret.len()).rev() { if ret[x] == ret[y] { ret.remove(y); } @@ -204,7 +204,6 @@ impl Cmp for CmpOr { } } - // #[cfg(test)] // mod cmp_inner_tests { // use serde_json::Value; diff --git a/src/select/expr_term.rs b/src/select/expr_term.rs index f128a7cc..fbc41418 100644 --- a/src/select/expr_term.rs +++ b/src/select/expr_term.rs @@ -1,6 +1,6 @@ +use crate::select::cmp::*; +use crate::select::{to_f64, FilterKey}; use serde_json::{Number, Value}; -use select::cmp::*; -use select::{FilterKey, to_f64}; #[derive(Debug, PartialEq, Clone)] pub(super) enum ExprTerm<'a> { diff --git a/src/select/mod.rs b/src/select/mod.rs index 3c13a90d..b538a782 100644 --- a/src/select/mod.rs +++ b/src/select/mod.rs @@ -1,10 +1,10 @@ use std::collections::HashSet; use std::fmt; -use serde_json::{Number, Value}; use serde_json::map::Entry; +use serde_json::{Number, Value}; -use parser::*; +use crate::parser::*; use self::expr_term::*; use self::value_walker::ValueWalker; @@ -42,7 +42,7 @@ pub enum JsonPathError { EmptyValue, Path(String), Serde(String), - Replacement(String) + Replacement(String), } impl std::error::Error for JsonPathError {} @@ -60,7 +60,10 @@ impl fmt::Display for JsonPathError { JsonPathError::EmptyValue => f.write_str("json value not set"), JsonPathError::Path(msg) => f.write_str(&format!("path error: \n{}\n", msg)), JsonPathError::Serde(msg) => f.write_str(&format!("serde error: \n{}\n", msg)), - JsonPathError::Replacement(msg) => f.write_str(&format!("error occurred during jsonpath replacement: \n{}\n", msg)) + JsonPathError::Replacement(msg) => f.write_str(&format!( + "error occurred during jsonpath replacement: \n{}\n", + msg + )), } } } @@ -87,7 +90,9 @@ impl<'a> FilterTerms<'a> { self.0.pop() } - fn filter_json_term, &mut Vec<&'a Value>, &mut HashSet) -> FilterKey>( + fn filter_json_term< + F: Fn(&Vec<&'a Value>, &mut Vec<&'a Value>, &mut HashSet) -> FilterKey, + >( &mut self, e: ExprTerm<'a>, fun: F, @@ -98,33 +103,40 @@ impl<'a> FilterTerms<'a> { let mut tmp = Vec::new(); let mut not_matched = HashSet::new(); let filter_key = if let Some(FilterKey::String(key)) = fk { - let key_contained = &vec.iter().map(|v| match v { - Value::Object(map) if map.contains_key(&key) => map.get(&key).unwrap(), - _ => v, - }).collect(); + let key_contained = &vec + .iter() + .map(|v| match v { + Value::Object(map) if map.contains_key(&key) => map.get(&key).unwrap(), + _ => v, + }) + .collect(); fun(key_contained, &mut tmp, &mut not_matched) } else { fun(&vec, &mut tmp, &mut not_matched) }; if rel.is_some() { - self.0.push(Some(ExprTerm::Json(rel, Some(filter_key), tmp))); + self.0 + .push(Some(ExprTerm::Json(rel, Some(filter_key), tmp))); } else { - let filtered: Vec<&Value> = vec.iter().enumerate() - .filter( - |(idx, _)| !not_matched.contains(idx) - ) + let filtered: Vec<&Value> = vec + .iter() + .enumerate() + .filter(|(idx, _)| !not_matched.contains(idx)) .map(|(_, v)| *v) .collect(); - self.0.push(Some(ExprTerm::Json(Some(filtered), Some(filter_key), tmp))); + self.0 + .push(Some(ExprTerm::Json(Some(filtered), Some(filter_key), tmp))); } } else { unreachable!("unexpected: ExprTerm: {:?}", e); } } - fn push_json_term, &mut Vec<&'a Value>, &mut HashSet) -> FilterKey>( + fn push_json_term< + F: Fn(&Vec<&'a Value>, &mut Vec<&'a Value>, &mut HashSet) -> FilterKey, + >( &mut self, current: &Option>, fun: F, @@ -135,7 +147,8 @@ impl<'a> FilterTerms<'a> { let mut tmp = Vec::new(); let mut not_matched = HashSet::new(); let filter_key = fun(current, &mut tmp, &mut not_matched); - self.0.push(Some(ExprTerm::Json(None, Some(filter_key), tmp))); + self.0 + .push(Some(ExprTerm::Json(None, Some(filter_key), tmp))); } } @@ -196,7 +209,11 @@ impl<'a> FilterTerms<'a> { debug!("filter_next_with_str : {}, {:?}", key, self.0); } - fn collect_next_with_num(&mut self, current: &Option>, index: f64) -> Option> { + fn collect_next_with_num( + &mut self, + current: &Option>, + index: f64, + ) -> Option> { fn _collect<'a>(tmp: &mut Vec<&'a Value>, vec: &'a [Value], index: f64) { let index = abs_index(index as isize, vec.len()); if let Some(v) = vec.get(index) { @@ -230,10 +247,7 @@ impl<'a> FilterTerms<'a> { } } - debug!( - "collect_next_with_num : {:?}, {:?}", - &index, ¤t - ); + debug!("collect_next_with_num : {:?}, {:?}", &index, ¤t); None } @@ -264,7 +278,11 @@ impl<'a> FilterTerms<'a> { None } - fn collect_next_with_str(&mut self, current: &Option>, keys: &[String]) -> Option> { + fn collect_next_with_str( + &mut self, + current: &Option>, + keys: &[String], + ) -> Option> { if let Some(current) = current { let mut tmp = Vec::new(); for c in current { @@ -285,10 +303,7 @@ impl<'a> FilterTerms<'a> { } } - debug!( - "collect_next_with_str : {:?}, {:?}", - keys, ¤t - ); + debug!("collect_next_with_str : {:?}, {:?}", keys, ¤t); None } @@ -304,7 +319,11 @@ impl<'a> FilterTerms<'a> { None } - fn collect_all_with_str(&mut self, current: &Option>, key: &str) -> Option> { + fn collect_all_with_str( + &mut self, + current: &Option>, + key: &str, + ) -> Option> { if let Some(current) = current { let mut tmp = Vec::new(); ValueWalker::all_with_str(current, &mut tmp, key, false); @@ -316,7 +335,11 @@ impl<'a> FilterTerms<'a> { None } - fn collect_all_with_num(&mut self, current: &Option>, index: f64) -> Option> { + fn collect_all_with_num( + &mut self, + current: &Option>, + index: f64, + ) -> Option> { if let Some(current) = current { let mut tmp = Vec::new(); ValueWalker::all_with_num(current, &mut tmp, index); @@ -506,7 +529,8 @@ impl<'a, 'b> Selector<'a, 'b> { if self.is_last_before_token_match(ParseToken::Array) { if let Some(Some(e)) = self.selector_filter.pop_term() { if let ExprTerm::String(key) = e { - self.selector_filter.filter_next_with_str(&self.current, &key); + self.selector_filter + .filter_next_with_str(&self.current, &key); self.tokens.pop(); return; } @@ -521,12 +545,16 @@ impl<'a, 'b> Selector<'a, 'b> { if let Some(Some(e)) = self.selector_filter.pop_term() { let selector_filter_consumed = match &e { ExprTerm::Number(n) => { - self.current = self.selector_filter.collect_all_with_num(&self.current, to_f64(n)); + self.current = self + .selector_filter + .collect_all_with_num(&self.current, to_f64(n)); self.selector_filter.pop_term(); true } ExprTerm::String(key) => { - self.current = self.selector_filter.collect_all_with_str(&self.current, key); + self.current = self + .selector_filter + .collect_all_with_str(&self.current, key); self.selector_filter.pop_term(); true } @@ -545,10 +573,14 @@ impl<'a, 'b> Selector<'a, 'b> { if let Some(Some(e)) = self.selector_filter.pop_term() { match e { ExprTerm::Number(n) => { - self.current = self.selector_filter.collect_next_with_num(&self.current, to_f64(&n)); + self.current = self + .selector_filter + .collect_next_with_num(&self.current, to_f64(&n)); } ExprTerm::String(key) => { - self.current = self.selector_filter.collect_next_with_str(&self.current, &[key]); + self.current = self + .selector_filter + .collect_next_with_str(&self.current, &[key]); } ExprTerm::Json(rel, _, v) => { if v.is_empty() { @@ -599,7 +631,8 @@ impl<'a, 'b> Selector<'a, 'b> { fn visit_key(&mut self, key: &str) { if let Some(ParseToken::Array) = self.tokens.last() { - self.selector_filter.push_term(Some(ExprTerm::String(key.to_string()))); + self.selector_filter + .push_term(Some(ExprTerm::String(key.to_string()))); return; } @@ -607,10 +640,14 @@ impl<'a, 'b> Selector<'a, 'b> { if self.selector_filter.is_term_empty() { match t { ParseToken::Leaves => { - self.current = self.selector_filter.collect_all_with_str(&self.current, key) + self.current = self + .selector_filter + .collect_all_with_str(&self.current, key) } ParseToken::In => { - self.current = self.selector_filter.collect_next_with_str(&self.current, &[key.to_string()]) + self.current = self + .selector_filter + .collect_next_with_str(&self.current, &[key.to_string()]) } _ => {} } @@ -620,7 +657,8 @@ impl<'a, 'b> Selector<'a, 'b> { self.selector_filter.filter_all_with_str(&self.current, key); } ParseToken::In => { - self.selector_filter.filter_next_with_str(&self.current, key); + self.selector_filter + .filter_next_with_str(&self.current, key); } _ => {} } @@ -634,7 +672,9 @@ impl<'a, 'b> Selector<'a, 'b> { } if let Some(ParseToken::Array) = self.tokens.pop() { - self.current = self.selector_filter.collect_next_with_str(&self.current, keys); + self.current = self + .selector_filter + .collect_next_with_str(&self.current, keys); } else { unreachable!(); } @@ -772,7 +812,8 @@ impl<'a, 'b> NodeVisitor for Selector<'a, 'b> { ParseToken::Key(key) => self.visit_key(key), ParseToken::Keys(keys) => self.visit_keys(keys), ParseToken::Number(v) => { - self.selector_filter.push_term(Some(ExprTerm::Number(Number::from_f64(*v).unwrap()))); + self.selector_filter + .push_term(Some(ExprTerm::Number(Number::from_f64(*v).unwrap()))); } ParseToken::Filter(ref ft) => self.visit_filter(ft), ParseToken::Range(from, to, step) => self.visit_range(from, to, step), @@ -808,7 +849,7 @@ fn replace_value_with_tokens Result, if is_last { if let Entry::Occupied(mut e) = map.entry(token) { let v = e.insert(Value::Null); - if let Some(res) = fun(v,&tokens_clone)? { + if let Some(res) = fun(v, &tokens_clone)? { e.insert(res); } else { e.remove(); @@ -822,7 +863,7 @@ fn replace_value_with_tokens Result, if let Ok(x) = token.parse::() { if is_last { let v = std::mem::replace(&mut vec[x], Value::Null); - if let Some(res) = fun(v,&tokens_clone)? { + if let Some(res) = fun(v, &tokens_clone)? { vec[x] = res; } else { vec.remove(x); @@ -1030,9 +1071,11 @@ impl SelectorMut { Ok(self) } - pub fn replace_with_tokens Result, JsonPathError>>( + pub fn replace_with_tokens< + F: FnMut(Value, &[String]) -> Result, JsonPathError>, + >( &mut self, - fun: &mut F + fun: &mut F, ) -> Result<&mut Self, JsonPathError> { let paths = { let result = self.select()?; @@ -1049,7 +1092,6 @@ impl SelectorMut { } } - #[cfg(test)] mod select_inner_tests { use serde_json::Value; diff --git a/src/select/value_walker.rs b/src/select/value_walker.rs index 63be27df..a56aed8a 100644 --- a/src/select/value_walker.rs +++ b/src/select/value_walker.rs @@ -5,10 +5,12 @@ pub(super) struct ValueWalker; impl<'a> ValueWalker { pub fn all_with_num(vec: &[&'a Value], tmp: &mut Vec<&'a Value>, index: f64) { - Self::walk(vec, tmp, &|v| if v.is_array() { - v.get(index as usize).map(|item| vec![item]) - } else { - None + Self::walk(vec, tmp, &|v| { + if v.is_array() { + v.get(index as usize).map(|item| vec![item]) + } else { + None + } }); } @@ -40,13 +42,19 @@ impl<'a> ValueWalker { }); } - fn walk(vec: &[&'a Value], tmp: &mut Vec<&'a Value>, fun: &F) where F: Fn(&Value) -> Option> { + fn walk(vec: &[&'a Value], tmp: &mut Vec<&'a Value>, fun: &F) + where + F: Fn(&Value) -> Option>, + { for v in vec { Self::_walk(v, tmp, fun); } } - fn _walk(v: &'a Value, tmp: &mut Vec<&'a Value>, fun: &F) where F: Fn(&Value) -> Option> { + fn _walk(v: &'a Value, tmp: &mut Vec<&'a Value>, fun: &F) + where + F: Fn(&Value) -> Option>, + { if let Some(mut ret) = fun(v) { tmp.append(&mut ret); } @@ -66,10 +74,12 @@ impl<'a> ValueWalker { } } - pub fn walk_dedup(v: &'a Value, - tmp: &mut Vec<&'a Value>, - key: &str, - visited: &mut HashSet<*const Value>, ) { + pub fn walk_dedup( + v: &'a Value, + tmp: &mut Vec<&'a Value>, + key: &str, + visited: &mut HashSet<*const Value>, + ) { match v { Value::Object(map) => { if map.contains_key(key) { @@ -89,4 +99,3 @@ impl<'a> ValueWalker { } } } - diff --git a/src/selector/cmp.rs b/src/selector/cmp.rs index 951cfae2..40cd89bc 100644 --- a/src/selector/cmp.rs +++ b/src/selector/cmp.rs @@ -209,12 +209,11 @@ impl Cmp for CmpOr { } } - #[cfg(test)] mod cmp_inner_tests { use serde_json::Value; - use selector::cmp::*; + use crate::selector::cmp::*; #[test] fn cmp_eq() { diff --git a/src/selector/mod.rs b/src/selector/mod.rs index a188668c..30aae43e 100644 --- a/src/selector/mod.rs +++ b/src/selector/mod.rs @@ -1,7 +1,7 @@ pub use self::selector_impl::{JsonSelector, JsonSelectorMut}; mod cmp; -mod terms; mod selector_impl; +mod terms; +mod utils; mod value_walker; -mod utils; \ No newline at end of file diff --git a/src/selector/selector_impl.rs b/src/selector/selector_impl.rs index 2a22c454..aac1c71e 100644 --- a/src/selector/selector_impl.rs +++ b/src/selector/selector_impl.rs @@ -1,12 +1,12 @@ use std::collections::HashSet; use std::rc::Rc; -use serde_json::{Number, Value}; use serde_json::map::Entry; +use serde_json::{Number, Value}; -use JsonPathError; -use paths::{ParserTokenHandler, PathParser, StrRange, tokens::*}; use super::utils; +use crate::paths::{tokens::*, ParserTokenHandler, PathParser, StrRange}; +use crate::JsonPathError; use super::terms::*; @@ -111,9 +111,13 @@ impl<'a> JsonSelector<'a> { } } - fn compute_absolute_path_filter(&mut self, token: &ParseToken, parse_value_reader: &F) -> bool - where - F: Fn(&StrRange) -> &'a str + fn compute_absolute_path_filter( + &mut self, + token: &ParseToken, + parse_value_reader: &F, + ) -> bool + where + F: Fn(&StrRange) -> &'a str, { if !self.selectors.is_empty() { match token { @@ -140,7 +144,10 @@ impl<'a> JsonSelector<'a> { return false; } - self.selectors.last_mut().unwrap().handle(token, parse_value_reader); + self.selectors + .last_mut() + .unwrap() + .handle(token, parse_value_reader); true } } @@ -183,7 +190,9 @@ impl<'a> JsonSelector<'a> { if self.is_last_before_token_match(ParseToken::Array) { if let Some(Some(e)) = self.selector_filter.pop_term() { if let ExprTerm::String(key) = e { - self.current = self.selector_filter.filter_next_with_str(self.current.take(), key); + self.current = self + .selector_filter + .filter_next_with_str(self.current.take(), key); self.tokens.pop(); return; } @@ -198,12 +207,16 @@ impl<'a> JsonSelector<'a> { if let Some(Some(e)) = self.selector_filter.pop_term() { let selector_filter_consumed = match e { ExprTerm::Number(n) => { - self.current = self.selector_filter.collect_all_with_num(self.current.take(), utils::to_f64(&n)); + self.current = self + .selector_filter + .collect_all_with_num(self.current.take(), utils::to_f64(&n)); self.selector_filter.pop_term(); true } ExprTerm::String(key) => { - self.current = self.selector_filter.collect_all_with_str(self.current.take(), key); + self.current = self + .selector_filter + .collect_all_with_str(self.current.take(), key); self.selector_filter.pop_term(); true } @@ -222,10 +235,14 @@ impl<'a> JsonSelector<'a> { if let Some(Some(e)) = self.selector_filter.pop_term() { match e { ExprTerm::Number(n) => { - self.current = self.selector_filter.collect_next_with_num(self.current.take(), utils::to_f64(&n)); + self.current = self + .selector_filter + .collect_next_with_num(self.current.take(), utils::to_f64(&n)); } ExprTerm::String(key) => { - self.current = self.selector_filter.collect_next_with_str(self.current.take(), &[key]); + self.current = self + .selector_filter + .collect_next_with_str(self.current.take(), &[key]); } ExprTerm::Json(rel, _, v) => { if v.is_empty() { @@ -284,20 +301,28 @@ impl<'a> JsonSelector<'a> { if self.selector_filter.is_term_empty() { match t { ParseToken::Leaves => { - self.current = self.selector_filter.collect_all_with_str(self.current.take(), key) + self.current = self + .selector_filter + .collect_all_with_str(self.current.take(), key) } ParseToken::In => { - self.current = self.selector_filter.collect_next_with_str(self.current.take(), &[key]) + self.current = self + .selector_filter + .collect_next_with_str(self.current.take(), &[key]) } _ => {} } } else { match t { ParseToken::Leaves => { - self.current = self.selector_filter.filter_all_with_str(self.current.take(), key); + self.current = self + .selector_filter + .filter_all_with_str(self.current.take(), key); } ParseToken::In => { - self.current = self.selector_filter.filter_next_with_str(self.current.take(), key); + self.current = self + .selector_filter + .filter_next_with_str(self.current.take(), key); } _ => {} } @@ -311,7 +336,9 @@ impl<'a> JsonSelector<'a> { } if let Some(ParseToken::Array) = self.tokens.pop() { - self.current = self.selector_filter.collect_next_with_str(self.current.take(), keys); + self.current = self + .selector_filter + .collect_next_with_str(self.current.take(), keys); } else { unreachable!(); } @@ -425,8 +452,8 @@ impl<'a> JsonSelector<'a> { impl<'a> ParserTokenHandler<'a> for JsonSelector<'a> { fn handle(&mut self, token: &ParseToken, parse_value_reader: &F) - where - F: Fn(&StrRange) -> &'a str + where + F: Fn(&StrRange) -> &'a str, { debug!("token: {:?}, stack: {:?}", token, self.tokens); @@ -450,11 +477,12 @@ impl<'a> ParserTokenHandler<'a> for JsonSelector<'a> { self.visit_key(key); } ParseToken::Keys(keys) => { - let keys: Vec<&str> = keys.iter().map(|s| { parse_value_reader(s) }).collect(); + let keys: Vec<&str> = keys.iter().map(|s| parse_value_reader(s)).collect(); self.visit_keys(&keys) } ParseToken::Number(v) => { - self.selector_filter.push_term(Some(ExprTerm::Number(Number::from_f64(*v).unwrap()))); + self.selector_filter + .push_term(Some(ExprTerm::Number(Number::from_f64(*v).unwrap()))); } ParseToken::Filter(ref ft) => self.visit_filter(ft), ParseToken::Range(from, to, step) => self.visit_range(from, to, step), @@ -466,7 +494,7 @@ impl<'a> ParserTokenHandler<'a> for JsonSelector<'a> { } } -#[derive(Default,Clone)] +#[derive(Default, Clone)] pub struct JsonSelectorMut<'a> { value: Option, parser: Option>>, @@ -530,8 +558,8 @@ impl<'a> JsonSelectorMut<'a> { } pub fn replace_with(&mut self, fun: &mut F) -> Result<&mut Self, JsonPathError> - where - F: FnMut(Value) -> Result, JsonPathError>, + where + F: FnMut(Value) -> Result, JsonPathError>, { let result = self.select()?; let paths = self.compute_paths(result); @@ -545,9 +573,13 @@ impl<'a> JsonSelectorMut<'a> { Ok(self) } - fn replace_value(mut tokens: Vec, value: &mut Value, fun: &mut F) -> Result<(), JsonPathError> - where - F: FnMut(Value) -> Result, JsonPathError> + fn replace_value( + mut tokens: Vec, + value: &mut Value, + fun: &mut F, + ) -> Result<(), JsonPathError> + where + F: FnMut(Value) -> Result, JsonPathError>, { let mut target = value; diff --git a/src/selector/terms.rs b/src/selector/terms.rs index 6f562b3c..49e57452 100644 --- a/src/selector/terms.rs +++ b/src/selector/terms.rs @@ -11,13 +11,17 @@ pub enum ExprTerm<'a> { String(&'a str), Number(Number), Bool(bool), - Json(Option>, Option>, Vec<&'a Value>), + Json( + Option>, + Option>, + Vec<&'a Value>, + ), } impl<'a> ExprTerm<'a> { fn cmp_string(s1: &str, other: &mut ExprTerm<'a>, cmp_fn: &C) -> ExprTerm<'a> - where - C: Cmp, + where + C: Cmp, { match other { ExprTerm::String(s2) => { @@ -31,19 +35,21 @@ impl<'a> ExprTerm<'a> { } fn cmp_number(n1: &Number, other: &mut ExprTerm<'a>, cmp_fn: &C) -> ExprTerm<'a> - where - C: Cmp, + where + C: Cmp, { match other { - ExprTerm::Number(n2) => ExprTerm::Bool(cmp_fn.cmp_f64(utils::to_f64(n1), utils::to_f64(n2))), + ExprTerm::Number(n2) => { + ExprTerm::Bool(cmp_fn.cmp_f64(utils::to_f64(n1), utils::to_f64(n2))) + } ExprTerm::Json(_, _, _) => unreachable!(), _ => ExprTerm::Bool(cmp_fn.default()), } } fn cmp_bool(b1: &bool, other: &mut ExprTerm<'a>, cmp_fn: &C) -> ExprTerm<'a> - where - C: Cmp, + where + C: Cmp, { match other { ExprTerm::Bool(b2) => ExprTerm::Bool(cmp_fn.cmp_bool(*b1, *b2)), @@ -52,80 +58,95 @@ impl<'a> ExprTerm<'a> { } } - fn cmp_json_string(s2: &str, - fk1: &Option, - vec1: &[&'a Value], - cmp_fn: &C) -> Vec<&'a Value> - where - C: Cmp + fn cmp_json_string( + s2: &str, + fk1: &Option, + vec1: &[&'a Value], + cmp_fn: &C, + ) -> Vec<&'a Value> + where + C: Cmp, { let path_str = utils::to_path_str(s2); - vec1.iter().filter(|v1| match v1 { - Value::String(s1) => { - cmp_fn.cmp_string(s1, path_str.get_key()) - } - Value::Object(map1) => { - if let Some(FilterKey::String(k)) = fk1 { - if let Some(Value::String(s1)) = map1.get(*k) { - return cmp_fn.cmp_string(s1, path_str.get_key()); + vec1.iter() + .filter(|v1| match v1 { + Value::String(s1) => cmp_fn.cmp_string(s1, path_str.get_key()), + Value::Object(map1) => { + if let Some(FilterKey::String(k)) = fk1 { + if let Some(Value::String(s1)) = map1.get(*k) { + return cmp_fn.cmp_string(s1, path_str.get_key()); + } } + cmp_fn.default() } - cmp_fn.default() - } - _ => cmp_fn.default(), - }).copied().collect() - } - - fn cmp_json_number(n2: &Number, - fk1: &Option, - vec1: &[&'a Value], - cmp_fn: &C) -> Vec<&'a Value> - where - C: Cmp + _ => cmp_fn.default(), + }) + .copied() + .collect() + } + + fn cmp_json_number( + n2: &Number, + fk1: &Option, + vec1: &[&'a Value], + cmp_fn: &C, + ) -> Vec<&'a Value> + where + C: Cmp, { let n2 = utils::to_f64(n2); - vec1.iter().filter(|v1| match v1 { - Value::Number(n1) => cmp_fn.cmp_f64(utils::to_f64(n1), n2), - Value::Object(map1) => { - if let Some(FilterKey::String(k)) = fk1 { - if let Some(Value::Number(n1)) = map1.get(*k) { - return cmp_fn.cmp_f64(utils::to_f64(n1), n2); + vec1.iter() + .filter(|v1| match v1 { + Value::Number(n1) => cmp_fn.cmp_f64(utils::to_f64(n1), n2), + Value::Object(map1) => { + if let Some(FilterKey::String(k)) = fk1 { + if let Some(Value::Number(n1)) = map1.get(*k) { + return cmp_fn.cmp_f64(utils::to_f64(n1), n2); + } } + cmp_fn.default() } - cmp_fn.default() - } - _ => cmp_fn.default(), - }).copied().collect() - } - - fn cmp_json_bool(b2: &bool, - fk1: &Option, - vec1: &[&'a Value], - cmp_fn: &C1) -> Vec<&'a Value> - where - C1: Cmp + _ => cmp_fn.default(), + }) + .copied() + .collect() + } + + fn cmp_json_bool( + b2: &bool, + fk1: &Option, + vec1: &[&'a Value], + cmp_fn: &C1, + ) -> Vec<&'a Value> + where + C1: Cmp, { - vec1.iter().filter(|v1| match v1 { - Value::Bool(b1) => cmp_fn.cmp_bool(*b1, *b2), - Value::Object(map1) => { - if let Some(FilterKey::String(k)) = fk1 { - if let Some(Value::Bool(b1)) = map1.get(*k) { - return cmp_fn.cmp_bool(*b1, *b2); + vec1.iter() + .filter(|v1| match v1 { + Value::Bool(b1) => cmp_fn.cmp_bool(*b1, *b2), + Value::Object(map1) => { + if let Some(FilterKey::String(k)) = fk1 { + if let Some(Value::Bool(b1)) = map1.get(*k) { + return cmp_fn.cmp_bool(*b1, *b2); + } } + cmp_fn.default() } - cmp_fn.default() - } - _ => cmp_fn.default(), - }).copied().collect() - } - - fn cmp_json_json(rel: &Option>, - parent: &Option>, - vec1: &[&'a Value], - vec2: &[&'a Value], - cmp_fn: &C1) -> Vec<&'a Value> - where - C1: Cmp + _ => cmp_fn.default(), + }) + .copied() + .collect() + } + + fn cmp_json_json( + rel: &Option>, + parent: &Option>, + vec1: &[&'a Value], + vec2: &[&'a Value], + cmp_fn: &C1, + ) -> Vec<&'a Value> + where + C1: Cmp, { if let Some(vec1) = rel { if let Some(vec2) = parent { @@ -140,13 +161,15 @@ impl<'a> ExprTerm<'a> { } } - fn cmp_json(rel: Option>, - fk1: Option>, - vec1: &mut Vec<&'a Value>, - other: &mut ExprTerm<'a>, - cmp_fn: &C1) -> ExprTerm<'a> - where - C1: Cmp + fn cmp_json( + rel: Option>, + fk1: Option>, + vec1: &mut Vec<&'a Value>, + other: &mut ExprTerm<'a>, + cmp_fn: &C1, + ) -> ExprTerm<'a> + where + C1: Cmp, { let ret: Vec<&Value> = match other { ExprTerm::String(s2) => Self::cmp_json_string(s2, &fk1, vec1, cmp_fn), @@ -186,12 +209,11 @@ impl<'a> ExprTerm<'a> { return ExprTerm::Json(Some(Vec::new()), None, ret); } - let ret_set: HashSet<*const Value> = ret.iter() - .fold(HashSet::new(), |mut acc, v| { - let ptr = *v as *const Value; - acc.insert(ptr); - acc - }); + let ret_set: HashSet<*const Value> = ret.iter().fold(HashSet::new(), |mut acc, v| { + let ptr = *v as *const Value; + acc.insert(ptr); + acc + }); let mut tmp = Vec::new(); for rv in rel { @@ -209,9 +231,9 @@ impl<'a> ExprTerm<'a> { } fn cmp(&mut self, other: &mut Self, cmp_fn: &C1, rev_cmp_fn: &C2) -> ExprTerm<'a> - where - C1: Cmp, - C2: Cmp + where + C1: Cmp, + C2: Cmp, { if let ExprTerm::Json(_, _, _) = other { if let ExprTerm::Json(_, _, _) = &self { @@ -225,8 +247,9 @@ impl<'a> ExprTerm<'a> { ExprTerm::String(s1) => Self::cmp_string(s1, other, cmp_fn), ExprTerm::Number(n1) => Self::cmp_number(n1, other, cmp_fn), ExprTerm::Bool(b1) => Self::cmp_bool(b1, other, cmp_fn), - ExprTerm::Json(rel, fk1, vec1) => + ExprTerm::Json(rel, fk1, vec1) => { Self::cmp_json(rel.take(), fk1.take(), vec1, other, cmp_fn) + } } } @@ -310,7 +333,7 @@ pub enum FilterKey<'a> { struct FilterResult<'a> { key: FilterKey<'a>, - collected: Vec<&'a Value> + collected: Vec<&'a Value>, } #[derive(Debug, Default, Clone)] @@ -336,8 +359,8 @@ impl<'a> FilterTerms<'a> { } fn filter_json_term(&mut self, e: ExprTerm<'a>, fun: F) - where - F: Fn(&Vec<&'a Value>, &mut Option>) -> FilterResult<'a>, + where + F: Fn(&Vec<&'a Value>, &mut Option>) -> FilterResult<'a>, { debug!("filter_json_term: {:?}", e); @@ -353,25 +376,34 @@ impl<'a> FilterTerms<'a> { self.push_term(Some(ExprTerm::Json( rel, Some(filter_result.key), - filter_result.collected))); + filter_result.collected, + ))); } else { let not_matched = not_matched.unwrap(); - let filtered = vec.iter().enumerate() + let filtered = vec + .iter() + .enumerate() .filter(|(idx, _)| !not_matched.contains(idx)) - .map(|(_, v)| *v).collect(); + .map(|(_, v)| *v) + .collect(); self.push_term(Some(ExprTerm::Json( Some(filtered), Some(filter_result.key), - filter_result.collected))); + filter_result.collected, + ))); } } else { unreachable!("unexpected: ExprTerm: {:?}", e); } } - fn push_json_term(&mut self, current: Option>, fun: F) -> Option> - where - F: Fn(&Vec<&'a Value>, &mut Option>) -> FilterResult<'a>, + fn push_json_term( + &mut self, + current: Option>, + fun: F, + ) -> Option> + where + F: Fn(&Vec<&'a Value>, &mut Option>) -> FilterResult<'a>, { debug!("push_json_term: {:?}", ¤t); @@ -380,15 +412,16 @@ impl<'a> FilterTerms<'a> { self.push_term(Some(ExprTerm::Json( None, Some(filter_result.key), - filter_result.collected))); + filter_result.collected, + ))); } current } fn filter(&mut self, current: Option>, fun: F) -> Option> - where - F: Fn(&Vec<&'a Value>, &mut Option>) -> FilterResult<'a>, + where + F: Fn(&Vec<&'a Value>, &mut Option>) -> FilterResult<'a>, { let peek = self.pop_term(); @@ -403,42 +436,49 @@ impl<'a> FilterTerms<'a> { current } - pub fn filter_all_with_str(&mut self, current: Option>, key: &'a str) -> Option> { - let current = self.filter(current, |vec, _| { - FilterResult { - key: FilterKey::All, - collected: ValueWalker::all_with_str(vec, key) - } + pub fn filter_all_with_str( + &mut self, + current: Option>, + key: &'a str, + ) -> Option> { + let current = self.filter(current, |vec, _| FilterResult { + key: FilterKey::All, + collected: ValueWalker::all_with_str(vec, key), }); debug!("filter_all_with_str : {}, {:?}", key, self.0); current } - pub fn filter_next_with_str(&mut self, current: Option>, key: &'a str) -> Option> { + pub fn filter_next_with_str( + &mut self, + current: Option>, + key: &'a str, + ) -> Option> { let current = self.filter(current, |vec, not_matched| { let mut visited = HashSet::new(); let mut acc = Vec::new(); let path_key = &utils::to_path_str(key); - ValueWalker::walk_dedup_all(vec, - path_key.get_key(), - &mut visited, - &mut |v| { - acc.push(v); - }, - &mut |idx| { - if let Some(set) = not_matched { - set.insert(idx); - } - }, - 0 + ValueWalker::walk_dedup_all( + vec, + path_key.get_key(), + &mut visited, + &mut |v| { + acc.push(v); + }, + &mut |idx| { + if let Some(set) = not_matched { + set.insert(idx); + } + }, + 0, ); FilterResult { key: FilterKey::String(path_key.get_origin_key()), - collected: acc + collected: acc, } }); @@ -446,7 +486,11 @@ impl<'a> FilterTerms<'a> { current } - pub fn collect_next_with_num(&mut self, current: Option>, index: f64) -> Option> { + pub fn collect_next_with_num( + &mut self, + current: Option>, + index: f64, + ) -> Option> { if current.is_none() { debug!("collect_next_with_num : {:?}, {:?}", &index, ¤t); return current; @@ -482,12 +526,13 @@ impl<'a> FilterTerms<'a> { Some(acc) } - pub fn collect_next_with_str(&mut self, current: Option>, keys: &[&'a str]) -> Option> { + pub fn collect_next_with_str( + &mut self, + current: Option>, + keys: &[&'a str], + ) -> Option> { if current.is_none() { - debug!( - "collect_next_with_str : {:?}, {:?}", - keys, ¤t - ); + debug!("collect_next_with_str : {:?}, {:?}", keys, ¤t); return current; } @@ -518,7 +563,11 @@ impl<'a> FilterTerms<'a> { Some(ValueWalker::all(current.as_ref().unwrap())) } - pub fn collect_all_with_str(&mut self, current: Option>, key: &'a str) -> Option> { + pub fn collect_all_with_str( + &mut self, + current: Option>, + key: &'a str, + ) -> Option> { if current.is_none() { debug!("collect_all_with_str: {}, {:?}", key, ¤t); return current; @@ -528,7 +577,11 @@ impl<'a> FilterTerms<'a> { Some(ret) } - pub fn collect_all_with_num(&mut self, mut current: Option>, index: f64) -> Option> { + pub fn collect_all_with_num( + &mut self, + mut current: Option>, + index: f64, + ) -> Option> { if let Some(current) = current.take() { let ret = ValueWalker::all_with_num(¤t, index); if !ret.is_empty() { @@ -545,7 +598,7 @@ impl<'a> FilterTerms<'a> { mod expr_term_inner_tests { use serde_json::{Number, Value}; - use selector::terms::ExprTerm; + use crate::selector::terms::ExprTerm; #[test] fn value_vec_into() { diff --git a/src/selector/utils.rs b/src/selector/utils.rs index ec250625..9c224027 100644 --- a/src/selector/utils.rs +++ b/src/selector/utils.rs @@ -20,7 +20,7 @@ pub fn abs_index(n: isize, len: usize) -> usize { pub struct PathKey<'a> { key: &'a str, - special_key: Option + special_key: Option, } impl<'a: 'b, 'b> PathKey<'a> { @@ -40,7 +40,7 @@ impl<'a: 'b, 'b> PathKey<'a> { pub fn to_path_str(key: &str) -> PathKey { let mut path_key = PathKey { key, - special_key: None + special_key: None, }; if key.starts_with('\'') || key.starts_with('"') { @@ -51,4 +51,4 @@ pub fn to_path_str(key: &str) -> PathKey { } } path_key -} \ No newline at end of file +} diff --git a/src/selector/value_walker.rs b/src/selector/value_walker.rs index 2c7c1039..d538e55c 100644 --- a/src/selector/value_walker.rs +++ b/src/selector/value_walker.rs @@ -1,8 +1,8 @@ use std::collections::HashSet; -use serde_json::Value; use super::utils; -use selector::utils::PathKey; +use crate::selector::utils::PathKey; +use serde_json::Value; pub(super) struct ValueWalker; @@ -52,19 +52,23 @@ impl<'a> ValueWalker { pub fn all_with_str(vec: &[&'a Value], key: &'a str) -> Vec<&'a Value> { let path_key = utils::to_path_str(key); - Self::walk(vec, &|v, acc| if let Value::Object(map) = v { - if let Some(v) = map.get(path_key.get_key()) { - acc.push(v); + Self::walk(vec, &|v, acc| { + if let Value::Object(map) = v { + if let Some(v) = map.get(path_key.get_key()) { + acc.push(v); + } } }) } pub fn all_with_strs(vec: &[&'a Value], keys: &[&'a str]) -> Vec<&'a Value> { - let path_keys: &Vec = &keys.iter().map(|key| { utils::to_path_str(key) }).collect(); + let path_keys: &Vec = &keys.iter().map(|key| utils::to_path_str(key)).collect(); vec.iter().fold(Vec::new(), |mut acc, v| { if let Value::Object(map) = v { - path_keys.iter().for_each(|pk| if let Some(v) = map.get(pk.get_key()) { - acc.push(v) + path_keys.iter().for_each(|pk| { + if let Some(v) = map.get(pk.get_key()) { + acc.push(v) + } }); } acc @@ -72,20 +76,18 @@ impl<'a> ValueWalker { } pub fn all(vec: &[&'a Value]) -> Vec<&'a Value> { - Self::walk(vec, &|v, acc| { - match v { - Value::Array(ay) => acc.extend(ay), - Value::Object(map) => { - acc.extend(map.values()); - } - _ => {} + Self::walk(vec, &|v, acc| match v { + Value::Array(ay) => acc.extend(ay), + Value::Object(map) => { + acc.extend(map.values()); } + _ => {} }) } fn walk(vec: &[&'a Value], fun: &F) -> Vec<&'a Value> - where - F: Fn(&'a Value, &mut Vec<&'a Value>), + where + F: Fn(&'a Value, &mut Vec<&'a Value>), { vec.iter().fold(Vec::new(), |mut acc, v| { Self::_walk(v, &mut acc, fun); @@ -94,8 +96,8 @@ impl<'a> ValueWalker { } fn _walk(v: &'a Value, acc: &mut Vec<&'a Value>, fun: &F) - where - F: Fn(&'a Value, &mut Vec<&'a Value>), + where + F: Fn(&'a Value, &mut Vec<&'a Value>), { fun(v, acc); @@ -104,41 +106,41 @@ impl<'a> ValueWalker { vec.iter().for_each(|v| Self::_walk(v, acc, fun)); } Value::Object(map) => { - map.values().into_iter().for_each(|v| Self::_walk(v, acc, fun)); + map.values() + .into_iter() + .for_each(|v| Self::_walk(v, acc, fun)); } _ => {} } } - pub fn walk_dedup_all(vec: &[&'a Value], - key: &str, - visited: &mut HashSet<*const Value>, - is_contain: &mut F1, - is_not_contain: &mut F2, - depth: usize) - where - F1: FnMut(&'a Value), - F2: FnMut(usize), + pub fn walk_dedup_all( + vec: &[&'a Value], + key: &str, + visited: &mut HashSet<*const Value>, + is_contain: &mut F1, + is_not_contain: &mut F2, + depth: usize, + ) where + F1: FnMut(&'a Value), + F2: FnMut(usize), { - vec.iter().enumerate().for_each(|(index, v)| Self::walk_dedup(v, - key, - visited, - index, - is_contain, - is_not_contain, - depth)); + vec.iter().enumerate().for_each(|(index, v)| { + Self::walk_dedup(v, key, visited, index, is_contain, is_not_contain, depth) + }); } - fn walk_dedup(v: &'a Value, - key: &str, - visited: &mut HashSet<*const Value>, - index: usize, - is_contain: &mut F1, - is_not_contain: &mut F2, - depth: usize) - where - F1: FnMut(&'a Value), - F2: FnMut(usize), + fn walk_dedup( + v: &'a Value, + key: &str, + visited: &mut HashSet<*const Value>, + index: usize, + is_contain: &mut F1, + is_not_contain: &mut F2, + depth: usize, + ) where + F1: FnMut(&'a Value), + F2: FnMut(usize), { let ptr = v as *const Value; if visited.contains(&ptr) { @@ -162,7 +164,15 @@ impl<'a> ValueWalker { is_not_contain(index); } vec.iter().for_each(|v| { - Self::walk_dedup(v, key, visited, index, is_contain, is_not_contain, depth + 1); + Self::walk_dedup( + v, + key, + visited, + index, + is_contain, + is_not_contain, + depth + 1, + ); }) } _ => { @@ -173,4 +183,3 @@ impl<'a> ValueWalker { } } } - diff --git a/tests/array_filter.rs b/tests/array_filter.rs index 7f1cf332..41ffa2c7 100644 --- a/tests/array_filter.rs +++ b/tests/array_filter.rs @@ -1,7 +1,7 @@ #[macro_use] extern crate serde_json; -use common::{read_json, select_and_then_compare, setup}; +use crate::common::{read_json, select_and_then_compare, setup}; mod common; @@ -256,9 +256,5 @@ fn bugs40_bracket_notation_after_recursive_descent() { fn bugs50() { setup(); - select_and_then_compare( - "$[0]", - json!({"f": [1,2,3]}), - json!([]) - ); -} \ No newline at end of file + select_and_then_compare("$[0]", json!({"f": [1,2,3]}), json!([])); +} diff --git a/tests/filter.rs b/tests/filter.rs index c847dab3..8097723e 100644 --- a/tests/filter.rs +++ b/tests/filter.rs @@ -1,7 +1,7 @@ #[macro_use] extern crate serde_json; -use common::{read_json, select_and_then_compare, setup}; +use crate::common::{read_json, select_and_then_compare, setup}; mod common; @@ -133,11 +133,11 @@ fn filter_parent_paths() { select_and_then_compare( "$[?(@.key.subKey == 'subKey2')]", json!([ - {"key": {"seq": 1, "subKey": "subKey1"}}, - {"key": {"seq": 2, "subKey": "subKey2"}}, - {"key": 42}, - {"some": "value"} - ]), + {"key": {"seq": 1, "subKey": "subKey1"}}, + {"key": {"seq": 2, "subKey": "subKey2"}}, + {"key": 42}, + {"some": "value"} + ]), json!([{"key": {"seq": 2, "subKey": "subKey2"}}]), ); } @@ -149,15 +149,15 @@ fn bugs33_exist_in_all() { select_and_then_compare( "$..[?(@.first.second)]", json!({ - "foo": { - "first": { "second": "value" } - }, - "foo2": { - "first": {} - }, - "foo3": { - } - }), + "foo": { + "first": { "second": "value" } + }, + "foo2": { + "first": {} + }, + "foo3": { + } + }), json!([ { "first": { @@ -175,15 +175,15 @@ fn bugs33_exist_left_in_all_with_and_condition() { select_and_then_compare( "$..[?(@.first && @.first.second)]", json!({ - "foo": { - "first": { "second": "value" } - }, - "foo2": { - "first": {} - }, - "foo3": { - } - }), + "foo": { + "first": { "second": "value" } + }, + "foo2": { + "first": {} + }, + "foo3": { + } + }), json!([ { "first": { @@ -232,49 +232,49 @@ fn bugs38_array_notation_in_filter() { select_and_then_compare( "$[?(@['key']==42)]", json!([ - {"key": 0}, - {"key": 42}, - {"key": -1}, - {"key": 41}, - {"key": 43}, - {"key": 42.0001}, - {"key": 41.9999}, - {"key": 100}, - {"some": "value"} - ]), + {"key": 0}, + {"key": 42}, + {"key": -1}, + {"key": 41}, + {"key": 43}, + {"key": 42.0001}, + {"key": 41.9999}, + {"key": 100}, + {"some": "value"} + ]), json!([{"key": 42}]), ); select_and_then_compare( "$[?(@['key'].subKey == 'subKey2')]", json!([ - {"key": {"seq": 1, "subKey": "subKey1"}}, - {"key": {"seq": 2, "subKey": "subKey2"}}, - {"key": 42}, - {"some": "value"} - ]), + {"key": {"seq": 1, "subKey": "subKey1"}}, + {"key": {"seq": 2, "subKey": "subKey2"}}, + {"key": 42}, + {"some": "value"} + ]), json!([{"key": {"seq": 2, "subKey": "subKey2"}}]), ); select_and_then_compare( "$[?(@['key']['subKey'] == 'subKey2')]", json!([ - {"key": {"seq": 1, "subKey": "subKey1"}}, - {"key": {"seq": 2, "subKey": "subKey2"}}, - {"key": 42}, - {"some": "value"} - ]), + {"key": {"seq": 1, "subKey": "subKey1"}}, + {"key": {"seq": 2, "subKey": "subKey2"}}, + {"key": 42}, + {"some": "value"} + ]), json!([{"key": {"seq": 2, "subKey": "subKey2"}}]), ); select_and_then_compare( "$..key[?(@['subKey'] == 'subKey2')]", json!([ - {"key": {"seq": 1, "subKey": "subKey1"}}, - {"key": {"seq": 2, "subKey": "subKey2"}}, - {"key": 42}, - {"some": "value"} - ]), + {"key": {"seq": 1, "subKey": "subKey1"}}, + {"key": {"seq": 2, "subKey": "subKey2"}}, + {"key": 42}, + {"some": "value"} + ]), json!([{"seq": 2, "subKey": "subKey2"}]), ); -} \ No newline at end of file +} diff --git a/tests/jsonpath_examples.rs b/tests/jsonpath_examples.rs index 35e84a3d..f17c027c 100644 --- a/tests/jsonpath_examples.rs +++ b/tests/jsonpath_examples.rs @@ -1,7 +1,7 @@ #[macro_use] extern crate serde_json; -use common::{read_json, select_and_then_compare, setup}; +use crate::common::{read_json, select_and_then_compare, setup}; mod common; diff --git a/tests/lib.rs b/tests/lib.rs index d2c48cb2..c0dc5418 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -6,8 +6,8 @@ extern crate serde_json; use serde::Deserialize; use serde_json::Value; -use common::{compare_result, read_contents, read_json, setup}; -use jsonpath::JsonPathError; +use crate::common::{compare_result, read_contents, read_json, setup}; +use crate::jsonpath::JsonPathError; mod common; diff --git a/tests/op.rs b/tests/op.rs index 35089d8d..d328a182 100644 --- a/tests/op.rs +++ b/tests/op.rs @@ -1,7 +1,7 @@ #[macro_use] extern crate serde_json; -use common::{read_json, select_and_then_compare, setup}; +use crate::common::{read_json, select_and_then_compare, setup}; mod common; @@ -174,14 +174,14 @@ fn op_ge_for_number() { select_and_then_compare("$.[?(@.a >= 0)]", json!({ "a": 1 }), json!([{ "a": 1 }])); } - - #[test] fn op_eq_for_string_value() { setup(); select_and_then_compare( - r#"$.[?(@.a == "b")]"#, json!({ "a": "b" }), json!([{ "a": "b" }]), + r#"$.[?(@.a == "b")]"#, + json!({ "a": "b" }), + json!([{ "a": "b" }]), ); } @@ -190,18 +190,17 @@ fn op_ne_for_string_value() { setup(); select_and_then_compare( - r#"$.[?(@.a != "c")]"#, json!({ "a": "b" }), json!([{ "a": "b" }]), + r#"$.[?(@.a != "c")]"#, + json!({ "a": "b" }), + json!([{ "a": "b" }]), ); - } #[test] fn op_lt_for_string_value() { setup(); - select_and_then_compare( - r#"$.[?(@.a < "b")]"#, json!({ "a": "b" }), json!([]), - ); + select_and_then_compare(r#"$.[?(@.a < "b")]"#, json!({ "a": "b" }), json!([])); } #[test] @@ -209,7 +208,9 @@ fn op_le_for_string_value() { setup(); select_and_then_compare( - r#"$.[?(@.a <= "b")]"#, json!({ "a": "b" }), json!([{ "a": "b" }]), + r#"$.[?(@.a <= "b")]"#, + json!({ "a": "b" }), + json!([{ "a": "b" }]), ); } @@ -217,9 +218,7 @@ fn op_le_for_string_value() { fn op_gt_for_string_value() { setup(); - select_and_then_compare( - r#"$.[?(@.a > "b")]"#, json!({ "a": "b" }), json!([]), - ); + select_and_then_compare(r#"$.[?(@.a > "b")]"#, json!({ "a": "b" }), json!([])); } #[test] @@ -227,7 +226,9 @@ fn op_ge_for_string_value() { setup(); select_and_then_compare( - r#"$.[?(@.a >= "b")]"#, json!({ "a": "b" }), json!([{ "a": "b" }]), + r#"$.[?(@.a >= "b")]"#, + json!({ "a": "b" }), + json!([{ "a": "b" }]), ); } @@ -314,21 +315,21 @@ fn cmp_json_rel() { } }), json!([ - { - "a" : { - "a" : [ - true, - "1" - ] - }, - "b" : { - "a" : [ - true, - "1" - ] - } - } - ]) + { + "a" : { + "a" : [ + true, + "1" + ] + }, + "b" : { + "a" : [ + true, + "1" + ] + } + } + ]), ) } @@ -380,11 +381,7 @@ fn op_ge_for_object_value() { fn op_eq_for_complex_value() { setup(); - select_and_then_compare( - r#"$.[?(1 == @.a)]"#, - json!({ "a": { "b": 1 } }), - json!([]), - ); + select_and_then_compare(r#"$.[?(1 == @.a)]"#, json!({ "a": { "b": 1 } }), json!([])); } #[test] @@ -403,7 +400,7 @@ fn op_ne_for_complex_value() { "b" : 1 } } - ]) + ]), ); } @@ -411,22 +408,14 @@ fn op_ne_for_complex_value() { fn op_le_for_complex_value() { setup(); - select_and_then_compare( - r#"$.[?(@.a <= 1)]"#, - json!({ "a": { "b": 1 } }), - json!([]), - ); + select_and_then_compare(r#"$.[?(@.a <= 1)]"#, json!({ "a": { "b": 1 } }), json!([])); } #[test] fn op_gt_for_complex_value() { setup(); - select_and_then_compare( - r#"$.[?(@.a > "1")]"#, - json!({ "a": { "b": 1 } }), - json!([]), - ); + select_and_then_compare(r#"$.[?(@.a > "1")]"#, json!({ "a": { "b": 1 } }), json!([])); } #[test] @@ -439,7 +428,7 @@ fn op_compare_different_types() { r#"$[?(true == 1)]"#, r#"$[?(@ == 1)]"#, ] - .iter() + .iter() { select_and_then_compare(path, json!({}), json!([])); } @@ -461,4 +450,4 @@ fn op_for_same_type() { {"a": 1} ]), ); -} \ No newline at end of file +} diff --git a/tests/paths.rs b/tests/paths.rs index 61da575c..7dbb51bf 100644 --- a/tests/paths.rs +++ b/tests/paths.rs @@ -1,7 +1,7 @@ #[macro_use] extern crate serde_json; -use common::{select_and_then_compare, setup}; +use crate::common::{select_and_then_compare, setup}; mod common; @@ -12,104 +12,104 @@ fn dolla_token_in_path() { select_and_then_compare( "$..$ref", json!({ - "Junk1": "This is a test to illustrate use of '$' in the attr for the expression $..['$ref'] ", - "$ref": "Match Root", - "Subset1":[ - {"Junk2": "Data...", - "$ref": "Match Subset1" - } - ], - "hierachy1":{ - "hierachy2.1":{ - "hierachy2.1.1":{ "$ref":"Match 2.1.1"}, - "hierachy2.1.2":{ "ref":"Match 2.1.2"}, - "hierachy2.1.3":{ "ref":"No Match 2.1.3"}, - "hierachy2.1.4":{ "$ref":"Match 2.1.4"}, - "hierachy2.1.5":{ "ref":"No Match 2.1.5"} - }, - "hierachy2.2":{ - "hierachy2.2.1":{ "ref":"No Match 2.2.1"}, - "hierachy2.2.2":{ "$ref":"Match 2.2.2"}, - "hierachy2.2.3":{ "ref":"No Match 2.2.3"}, - "hierachy2.2.4":{ "ref":"No Match 2.2.5"}, - "hierachy2.2.5":{ "$ref":"Match 2.2.5"} - }, - "hierachy2.3":{ - "hierachy2.3.1":{ "ref":"No Match 2.3.1"}, - "hierachy2.3.2":{ "ref":"No Match 2.3.2"}, - "hierachy2.3.3":{ "ref":"No Match 2.3.3"}, - "hierachy2.3.4":{ "ref":"No Match 2.3.4"}, - "hierachy2.3.5":{ "ref":"No Match 2.3.5"}, - "hierachy2.3.6":{ - "hierachy2.3.6.1":{ "$ref":"Match 2.3.6.1"}, - "hierachy2.3.6.2":{ "ref":"No Match 2.3.6.2"}, - "hierachy2.3.6.3":{ "ref":"No Match 2.3.6.3"}, - "hierachy2.3.6.4":{ "ref":"No Match 2.3.6.4"}, - "hierachy2.3.6.5":{ "ref":"No Match 2.3.6.5"} - } - } + "Junk1": "This is a test to illustrate use of '$' in the attr for the expression $..['$ref'] ", + "$ref": "Match Root", + "Subset1":[ + {"Junk2": "Data...", + "$ref": "Match Subset1" } - }), + ], + "hierachy1":{ + "hierachy2.1":{ + "hierachy2.1.1":{ "$ref":"Match 2.1.1"}, + "hierachy2.1.2":{ "ref":"Match 2.1.2"}, + "hierachy2.1.3":{ "ref":"No Match 2.1.3"}, + "hierachy2.1.4":{ "$ref":"Match 2.1.4"}, + "hierachy2.1.5":{ "ref":"No Match 2.1.5"} + }, + "hierachy2.2":{ + "hierachy2.2.1":{ "ref":"No Match 2.2.1"}, + "hierachy2.2.2":{ "$ref":"Match 2.2.2"}, + "hierachy2.2.3":{ "ref":"No Match 2.2.3"}, + "hierachy2.2.4":{ "ref":"No Match 2.2.5"}, + "hierachy2.2.5":{ "$ref":"Match 2.2.5"} + }, + "hierachy2.3":{ + "hierachy2.3.1":{ "ref":"No Match 2.3.1"}, + "hierachy2.3.2":{ "ref":"No Match 2.3.2"}, + "hierachy2.3.3":{ "ref":"No Match 2.3.3"}, + "hierachy2.3.4":{ "ref":"No Match 2.3.4"}, + "hierachy2.3.5":{ "ref":"No Match 2.3.5"}, + "hierachy2.3.6":{ + "hierachy2.3.6.1":{ "$ref":"Match 2.3.6.1"}, + "hierachy2.3.6.2":{ "ref":"No Match 2.3.6.2"}, + "hierachy2.3.6.3":{ "ref":"No Match 2.3.6.3"}, + "hierachy2.3.6.4":{ "ref":"No Match 2.3.6.4"}, + "hierachy2.3.6.5":{ "ref":"No Match 2.3.6.5"} + } + } + } + }), json!([ - "Match Root", - "Match Subset1", - "Match 2.1.1", - "Match 2.1.4", - "Match 2.2.2", - "Match 2.2.5", - "Match 2.3.6.1" + "Match Root", + "Match Subset1", + "Match 2.1.1", + "Match 2.1.4", + "Match 2.2.2", + "Match 2.2.5", + "Match 2.3.6.1" ]), ); select_and_then_compare( "$..['$ref']", json!({ - "Junk1": "This is a test to illustrate use of '$' in the attr for the expression $..['$ref'] ", - "$ref": "Match Root", - "Subset1":[ - {"Junk2": "Data...", - "$ref": "Match Subset1" - } - ], - "hierachy1":{ - "hierachy2.1":{ - "hierachy2.1.1":{ "$ref":"Match 2.1.1"}, - "hierachy2.1.2":{ "ref":"Match 2.1.2"}, - "hierachy2.1.3":{ "ref":"No Match 2.1.3"}, - "hierachy2.1.4":{ "$ref":"Match 2.1.4"}, - "hierachy2.1.5":{ "ref":"No Match 2.1.5"} - }, - "hierachy2.2":{ - "hierachy2.2.1":{ "ref":"No Match 2.2.1"}, - "hierachy2.2.2":{ "$ref":"Match 2.2.2"}, - "hierachy2.2.3":{ "ref":"No Match 2.2.3"}, - "hierachy2.2.4":{ "ref":"No Match 2.2.5"}, - "hierachy2.2.5":{ "$ref":"Match 2.2.5"} - }, - "hierachy2.3":{ - "hierachy2.3.1":{ "ref":"No Match 2.3.1"}, - "hierachy2.3.2":{ "ref":"No Match 2.3.2"}, - "hierachy2.3.3":{ "ref":"No Match 2.3.3"}, - "hierachy2.3.4":{ "ref":"No Match 2.3.4"}, - "hierachy2.3.5":{ "ref":"No Match 2.3.5"}, - "hierachy2.3.6":{ - "hierachy2.3.6.1":{ "$ref":"Match 2.3.6.1"}, - "hierachy2.3.6.2":{ "ref":"No Match 2.3.6.2"}, - "hierachy2.3.6.3":{ "ref":"No Match 2.3.6.3"}, - "hierachy2.3.6.4":{ "ref":"No Match 2.3.6.4"}, - "hierachy2.3.6.5":{ "ref":"No Match 2.3.6.5"} - } - } + "Junk1": "This is a test to illustrate use of '$' in the attr for the expression $..['$ref'] ", + "$ref": "Match Root", + "Subset1":[ + {"Junk2": "Data...", + "$ref": "Match Subset1" } - }), + ], + "hierachy1":{ + "hierachy2.1":{ + "hierachy2.1.1":{ "$ref":"Match 2.1.1"}, + "hierachy2.1.2":{ "ref":"Match 2.1.2"}, + "hierachy2.1.3":{ "ref":"No Match 2.1.3"}, + "hierachy2.1.4":{ "$ref":"Match 2.1.4"}, + "hierachy2.1.5":{ "ref":"No Match 2.1.5"} + }, + "hierachy2.2":{ + "hierachy2.2.1":{ "ref":"No Match 2.2.1"}, + "hierachy2.2.2":{ "$ref":"Match 2.2.2"}, + "hierachy2.2.3":{ "ref":"No Match 2.2.3"}, + "hierachy2.2.4":{ "ref":"No Match 2.2.5"}, + "hierachy2.2.5":{ "$ref":"Match 2.2.5"} + }, + "hierachy2.3":{ + "hierachy2.3.1":{ "ref":"No Match 2.3.1"}, + "hierachy2.3.2":{ "ref":"No Match 2.3.2"}, + "hierachy2.3.3":{ "ref":"No Match 2.3.3"}, + "hierachy2.3.4":{ "ref":"No Match 2.3.4"}, + "hierachy2.3.5":{ "ref":"No Match 2.3.5"}, + "hierachy2.3.6":{ + "hierachy2.3.6.1":{ "$ref":"Match 2.3.6.1"}, + "hierachy2.3.6.2":{ "ref":"No Match 2.3.6.2"}, + "hierachy2.3.6.3":{ "ref":"No Match 2.3.6.3"}, + "hierachy2.3.6.4":{ "ref":"No Match 2.3.6.4"}, + "hierachy2.3.6.5":{ "ref":"No Match 2.3.6.5"} + } + } + } + }), json!([ - "Match Root", - "Match Subset1", - "Match 2.1.1", - "Match 2.1.4", - "Match 2.2.2", - "Match 2.2.5", - "Match 2.3.6.1" + "Match Root", + "Match Subset1", + "Match 2.1.1", + "Match 2.1.4", + "Match 2.2.2", + "Match 2.2.5", + "Match 2.3.6.1" ]), ); -} \ No newline at end of file +} diff --git a/tests/precompile.rs b/tests/precompile.rs index 6793a12b..be4668b0 100644 --- a/tests/precompile.rs +++ b/tests/precompile.rs @@ -2,7 +2,7 @@ extern crate serde_json; extern crate jsonpath_lib; -use common::{setup}; +use crate::common::setup; use jsonpath_lib::PathCompiled; use serde_json::Value; @@ -27,8 +27,14 @@ fn precompile_test() { // re-use //let result = compiled(&json).unwrap(); - assert_eq!(compiled.select(&json).unwrap().clone(), vec![&Value::String("baz".into())]); - assert_eq!(compiled.select(&json).unwrap().clone(), vec![&Value::String("baz".into())]); + assert_eq!( + compiled.select(&json).unwrap().clone(), + vec![&Value::String("baz".into())] + ); + assert_eq!( + compiled.select(&json).unwrap().clone(), + vec![&Value::String("baz".into())] + ); } #[test] @@ -38,4 +44,4 @@ fn precompile_failure() { let compiled = PathCompiled::compile(""); assert!(compiled.is_err()); -} \ No newline at end of file +} diff --git a/tests/readme.rs b/tests/readme.rs index b8b6db8d..26798d2d 100644 --- a/tests/readme.rs +++ b/tests/readme.rs @@ -6,7 +6,7 @@ extern crate serde_json; use serde::Deserialize; use serde_json::Value; -use jsonpath::{JsonSelector, JsonSelectorMut, PathParser}; +use crate::jsonpath::{JsonSelector, JsonSelectorMut, PathParser}; mod common; @@ -176,9 +176,7 @@ fn readme_selector() { let parser = PathParser::compile("$..[?(@.age >= 30)]").unwrap(); let mut selector = JsonSelector::new(parser); - let result = selector.value(&json_obj) - .select() - .unwrap(); + let result = selector.value(&json_obj).select().unwrap(); assert_eq!(vec![&json!({"name": "친구3", "age": 30})], result); @@ -212,7 +210,8 @@ fn readme_selector_mut() { let parser = PathParser::compile("$..[?(@.age == 20)].age").unwrap(); let mut selector_mut = JsonSelectorMut::new(parser); - let result = selector_mut.value(json_obj) + let result = selector_mut + .value(json_obj) .replace_with(&mut |v| { let age = if let Value::Number(n) = v { n.as_u64().unwrap() * 2 @@ -286,7 +285,7 @@ fn readme_select_as_str() { "#, "$..friends[0]", ) - .unwrap(); + .unwrap(); assert_eq!( ret, @@ -317,7 +316,7 @@ fn readme_select_as() { }"#, "$.person", ) - .unwrap(); + .unwrap(); let person = Person { name: "Doe John".to_string(), @@ -520,7 +519,7 @@ fn readme_replace_with() { Ok(Some(json!(age))) }) - .unwrap(); + .unwrap(); assert_eq!( result, diff --git a/tests/return_type.rs b/tests/return_type.rs index 0139dda0..0fca344e 100644 --- a/tests/return_type.rs +++ b/tests/return_type.rs @@ -1,7 +1,7 @@ #[macro_use] extern crate serde_json; -use common::{read_json, select_and_then_compare, setup}; +use crate::common::{read_json, select_and_then_compare, setup}; mod common; @@ -100,9 +100,5 @@ fn return_type_for_array_filter_true() { fn return_type_empty() { setup(); - select_and_then_compare( - "$[?(@.key==43)]", - json!([{"key": 42}]), - json!([]), - ); -} \ No newline at end of file + select_and_then_compare("$[?(@.key==43)]", json!([{"key": 42}]), json!([])); +} diff --git a/tests/selector.rs b/tests/selector.rs index 770501be..8e6c5b55 100644 --- a/tests/selector.rs +++ b/tests/selector.rs @@ -2,10 +2,10 @@ extern crate jsonpath_lib as jsonpath; #[macro_use] extern crate serde_json; -use common::{read_json, setup}; -use jsonpath::{Parser, Selector, SelectorMut, JsonPathError}; +use crate::common::{read_json, setup}; +use crate::jsonpath::{JsonPathError, Parser, Selector, SelectorMut}; +use crate::jsonpath::{JsonSelector, JsonSelectorMut, PathParser}; use serde_json::Value; -use jsonpath::{PathParser, JsonSelector, JsonSelectorMut}; mod common; @@ -17,7 +17,8 @@ fn selector_mut() { let mut selector_mut = JsonSelectorMut::new(parser); let mut nums = Vec::new(); - let result = selector_mut.value(read_json("./benchmark/example.json")) + let result = selector_mut + .value(read_json("./benchmark/example.json")) .replace_with(&mut |v| { if let Value::Number(n) = v { nums.push(n.as_f64().unwrap()); @@ -35,9 +36,7 @@ fn selector_mut() { let parser = PathParser::compile("$.store..price").unwrap(); let mut selector = JsonSelector::new(parser); - let result = selector.value(&result) - .select() - .unwrap(); + let result = selector.value(&result).select().unwrap(); assert_eq!( vec![ @@ -60,14 +59,9 @@ fn selector_mut_err() { .str_path(r#"$.store..price"#) .unwrap() .value(read_json("./benchmark/example.json")) - .replace_with(&mut |_| { - Err(JsonPathError::EmptyValue) - }); + .replace_with(&mut |_| Err(JsonPathError::EmptyValue)); - assert_eq!( - result.is_err(), - true - ); + assert_eq!(result.is_err(), true); } #[test] @@ -78,14 +72,9 @@ fn jsonselector_mut_err() { let mut selector_mut = JsonSelectorMut::new(parser); let result = selector_mut .value(read_json("./benchmark/example.json")) - .replace_with(&mut |_| { - Err(JsonPathError::EmptyValue) - }); + .replace_with(&mut |_| Err(JsonPathError::EmptyValue)); - assert_eq!( - result.is_err(), - true - ); + assert_eq!(result.is_err(), true); } #[test] @@ -102,7 +91,8 @@ fn selector_delete_multi_elements_from_array() { let parser = PathParser::compile("$[0,2]").unwrap(); let mut selector_mut = JsonSelectorMut::new(parser); - let result = selector_mut.value(serde_json::from_str("[1,2,3]").unwrap()) + let result = selector_mut + .value(serde_json::from_str("[1,2,3]").unwrap()) .remove() .unwrap() .take() @@ -121,7 +111,8 @@ fn selector_delete() { let parser = PathParser::compile("$.store..price[?(@>13)]").unwrap(); let mut selector_mut = JsonSelectorMut::new(parser); - let result = selector_mut.value(read_json("./benchmark/example.json")) + let result = selector_mut + .value(read_json("./benchmark/example.json")) .delete() .unwrap() .take() @@ -129,9 +120,7 @@ fn selector_delete() { let parser = PathParser::compile("$.store..price").unwrap(); let mut selector = JsonSelector::new(parser); - let result = selector.value(&result) - .select() - .unwrap(); + let result = selector.value(&result).select().unwrap(); assert_eq!( result, @@ -151,7 +140,8 @@ fn selector_remove() { let parser = PathParser::compile("$.store..price[?(@>13)]").unwrap(); let mut selector_mut = JsonSelectorMut::new(parser); - let result = selector_mut.value(read_json("./benchmark/example.json")) + let result = selector_mut + .value(read_json("./benchmark/example.json")) .remove() .unwrap() .take() @@ -159,16 +149,7 @@ fn selector_remove() { let parser = PathParser::compile("$.store..price").unwrap(); let mut selector = JsonSelector::new(parser); - let result = selector.value(&result) - .select() - .unwrap(); + let result = selector.value(&result).select().unwrap(); - assert_eq!( - result, - vec![ - &json!(8.95), - &json!(12.99), - &json!(8.99) - ] - ); + assert_eq!(result, vec![&json!(8.95), &json!(12.99), &json!(8.99)]); } From ebc5f103b45ef8b03c8b211bd2281baf0937aee2 Mon Sep 17 00:00:00 2001 From: Eoin Power-Moran Date: Thu, 19 Oct 2023 14:40:54 +0100 Subject: [PATCH 14/20] clean up repo --- lua/.gitignore | 5 -- lua/Cargo.toml | 14 ---- lua/bench_lua_vs_rust/example.lua | 22 ------ lua/bench_lua_vs_rust/example.rs | 46 ------------- lua/bench_lua_vs_rust/run.sh | 27 -------- lua/docker_example/default.conf | 107 ------------------------------ lua/docker_example/init.lua | 3 - lua/docker_example/run.sh | 25 ------- lua/jsonpath.lua | 60 ----------------- src/ffi/mod.rs | 62 ----------------- src/lib.rs | 6 -- src/parser/mod.rs | 16 ++--- src/paths/parser_node_visitor.rs | 16 ++--- src/paths/path_parser.rs | 4 +- src/paths/tokenizer.rs | 10 +-- src/select/mod.rs | 6 +- src/selector/cmp.rs | 2 +- src/selector/selector_impl.rs | 2 +- src/selector/value_walker.rs | 4 +- tests/filter.rs | 2 +- tests/selector.rs | 4 +- 21 files changed, 32 insertions(+), 411 deletions(-) delete mode 100644 lua/.gitignore delete mode 100644 lua/Cargo.toml delete mode 100644 lua/bench_lua_vs_rust/example.lua delete mode 100644 lua/bench_lua_vs_rust/example.rs delete mode 100755 lua/bench_lua_vs_rust/run.sh delete mode 100644 lua/docker_example/default.conf delete mode 100644 lua/docker_example/init.lua delete mode 100755 lua/docker_example/run.sh delete mode 100644 lua/jsonpath.lua delete mode 100644 src/ffi/mod.rs diff --git a/lua/.gitignore b/lua/.gitignore deleted file mode 100644 index 7a7d6214..00000000 --- a/lua/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -.idea/* -.vscode -/target/ -Cargo.lock -docker_example/ab_results/** \ No newline at end of file diff --git a/lua/Cargo.toml b/lua/Cargo.toml deleted file mode 100644 index fe05a109..00000000 --- a/lua/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "jsonpath_lua" -version = "0.1.0" -authors = ["Changseok Han "] -license = "MIT" -[dependencies] -serde = { version = "1.0", features = ["derive"] } -serde_json = { version = "1.0", features = ["preserve_order"] } -jsonpath_lib = { path = "../" } - -[[bin]] -name = "bench" -path = "bench_lua_vs_rust/example.rs" - diff --git a/lua/bench_lua_vs_rust/example.lua b/lua/bench_lua_vs_rust/example.lua deleted file mode 100644 index 0c59a37d..00000000 --- a/lua/bench_lua_vs_rust/example.lua +++ /dev/null @@ -1,22 +0,0 @@ -local jsonpath = require("jsonpath") - -local iter; -if arg[1] == nil or arg[1] == '' then - iter = 5000; -else - iter = tonumber(arg[1]); -end - -print(string.format("%s - %u", "lua iter", iter)); - -local file = io.open("../../benchmark/example.json", "r"); -io.input(file) -local data = io.read("*a"); -io.close(file); - -jsonpath.init('../target/release/deps/libjsonpath_lib.so') -local template = jsonpath.compile("$..book[?(@.price<30 && @.category==\"fiction\")]"); -for i = 0, iter do - local r = template(data); --- print(r); -end diff --git a/lua/bench_lua_vs_rust/example.rs b/lua/bench_lua_vs_rust/example.rs deleted file mode 100644 index 68f63b2d..00000000 --- a/lua/bench_lua_vs_rust/example.rs +++ /dev/null @@ -1,46 +0,0 @@ -extern crate jsonpath_lib as jsonpath; -extern crate serde; -extern crate serde_json; - -use std::io::Read; - -use serde_json::Value; - -fn read_json(path: &str) -> String { - let mut f = std::fs::File::open(path).unwrap(); - let mut contents = String::new(); - f.read_to_string(&mut contents).unwrap(); - contents -} - -fn get_string() -> String { - read_json("../../benchmark/example.json") -} - -fn get_json() -> Value { - let string = get_string(); - serde_json::from_str(string.as_str()).unwrap() -} - -fn get_path() -> &'static str { - r#"$..book[?(@.price<30 && @.category=="fiction")]"# -} - -fn main() { - let args: Vec = std::env::args().collect(); - let iter = if args.len() < 2 { 5000_usize } else { args[1].as_str().parse::().unwrap() }; - - println!("rust iter - {}", iter); - - let json = get_json(); - for _ in 0..iter { - let mut selector = jsonpath::Selector::default(); - let _ = selector.str_path(get_path()); - selector.value(&json); - let r = selector.select(); - if r.is_err() { - panic!(); - } -// println!("{:?}", serde_json::to_string(&r.expect("")).unwrap()); - } -} \ No newline at end of file diff --git a/lua/bench_lua_vs_rust/run.sh b/lua/bench_lua_vs_rust/run.sh deleted file mode 100755 index e9e0863a..00000000 --- a/lua/bench_lua_vs_rust/run.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash - -# cd lua/bench_lua_vs_rust && ./run.sh - -set -e - -# http://luajit.org/index.html - -# cargo clean && \ -cargo build --release - -export JSONPATH_LIB_PATH="${PWD}/../target/release/deps" -export LUA_PATH="${PWD}/../?.lua;" - -echo -time cargo run --release --bin bench -- 1000 -echo -time luajit example.lua 1000 -echo -time cargo run --release --bin bench -- 5000 -echo -time luajit example.lua 5000 -echo -time cargo run --release --bin bench -- 10000 -echo -time luajit example.lua 10000 - diff --git a/lua/docker_example/default.conf b/lua/docker_example/default.conf deleted file mode 100644 index 4b04b076..00000000 --- a/lua/docker_example/default.conf +++ /dev/null @@ -1,107 +0,0 @@ -lua_package_path '/etc/jsonpath/?.lua;;'; - -access_log /var/log/access.log; -error_log /var/log/error.log info; - -lua_shared_dict jsonpaths 1m; - -init_by_lua_block { - local pathStrings = { - "$.store.book[*].author", - "$..author", - "$.store.*", - "$.store..price", - "$..book[2]", - "$..book[-2]", - "$..book[0,1]", - "$..book[:2]", - "$..book[1:2]", - "$..book[-2:]", - "$..book[2:]", - "$..book[?(@.isbn)]", - "$.store.book[?(@.price == 10)]", - "$..*", - "$..book[ ?( (@.price < 13 || $.store.bicycle.price < @.price) && @.price <=10 ) ]", - "$.store.book[?( (@.price < 10 || @.price > 10) && @.price > 10 )]", - "$..[?(@.originPrice > 1)]", - "$.pickBanner[?(@.originPrice > 1)]" - } - - local jp = require("jsonpath") - jp.init("/etc/jsonpath/libjsonpath_lib.so") - local jsonpaths = ngx.shared.jsonpaths - - for i, path in ipairs(pathStrings) do - jsonpaths:set(i, path) - jp.compile(path) - end - -} - -server { - listen 80; - server_name localhost; - - gzip on; - gzip_types text/plain application/json; - #gzip_comp_level 6; - #gzip_vary on; - - location / { - add_header 'Cache-Control' 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; - expires off; - - default_type 'text/plain'; - root /etc/jsonpath/example; - } - - location /filter { - # https://developer.mozilla.org/ko/docs/Web/HTTP/Headers/Accept-Encoding - proxy_set_header Accept-Encoding "*"; - - default_type 'text/plain'; - - rewrite /filter/(.*) /$1 break; - proxy_pass http://localhost; - - header_filter_by_lua_block { - ngx.header["content-length"] = nil - - local args = ngx.req.get_uri_args() - local jsonpaths = ngx.shared.jsonpaths - local path = jsonpaths:get(args['path']) - - if path == nil then - ngx.exit(ngx.HTTP_BAD_REQUEST) - end - } - - body_filter_by_lua_block { - local chunk, eof = ngx.arg[1], ngx.arg[2] - local buf = ngx.ctx.buf - - if eof then - if buf then - local args = ngx.req.get_uri_args() - local path = ngx.shared.jsonpaths:get(args['path']) - local jsonpath = require("jsonpath") - local template = jsonpath.exec(path) - local json = buf .. chunk - local result = template(json) - ngx.arg[1] = result - return - end - - return - end - - if buf then - ngx.ctx.buf = buf .. chunk - else - ngx.ctx.buf = chunk - end - - ngx.arg[1] = nil - } - } -} \ No newline at end of file diff --git a/lua/docker_example/init.lua b/lua/docker_example/init.lua deleted file mode 100644 index 38ac53fd..00000000 --- a/lua/docker_example/init.lua +++ /dev/null @@ -1,3 +0,0 @@ -local jsonpath = require("jsonpath") -jsonpath.init("/etc/jsonpath/libjsonpath_lib.so") -ngx.log(ngx.INFO, "loaded libjsonpath_lib.so") \ No newline at end of file diff --git a/lua/docker_example/run.sh b/lua/docker_example/run.sh deleted file mode 100755 index f55bd9d8..00000000 --- a/lua/docker_example/run.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env bash - -# cd lua && cargo build --release && cd docker_example && ./run.sh - -set -v - -[ "$(docker ps -a | grep jsonpath)" ] && docker kill jsonpath - -docker run -d --rm --name jsonpath \ - -v "${PWD}/../../benchmark/example.json":/etc/jsonpath/example/example.json:ro \ - -v "${PWD}/../../benchmark/big_example.json":/etc/jsonpath/example/big_example.json:ro \ - -v "${PWD}/../jsonpath.lua":/etc/jsonpath/jsonpath.lua:ro \ - -v "${PWD}/init.lua":/etc/jsonpath/init.lua:ro \ - -v "${PWD}/../target/release/deps/libjsonpath_lib.so":/etc/jsonpath/libjsonpath_lib.so:ro \ - -v "${PWD}/default.conf":/etc/nginx/conf.d/default.conf \ - -p 8080:80 \ - openresty/openresty:bionic - -#for i in {1..16}; do -# curl http://localhost:8080/filter/example.json?path=${i} -# echo -#done - -#ab -n 1000 -c 10 http://localhost:8080/filter/big_example.json?path=17 -#ab -n 1000 -c 10 http://localhost:8080/filter/big_example.json?path=18 \ No newline at end of file diff --git a/lua/jsonpath.lua b/lua/jsonpath.lua deleted file mode 100644 index ba5fc289..00000000 --- a/lua/jsonpath.lua +++ /dev/null @@ -1,60 +0,0 @@ -local ffi = require('ffi') - -ffi.cdef [[ -const char* ffi_select(const char *json_str, const char *path); -void *ffi_path_compile(const char *path); -const char* ffi_select_with_compiled_path(void *ptr, const char *json_str); -]] - -local jsonpath -local cache = {} -local module = {} - -local function existsVaiable(var) - for k, _ in pairs(_G) do - if k == var then - return true - end - end -end - -local _ngx -if existsVaiable('ngx') then - _ngx = ngx -else - _ngx = {} - _ngx.log = function(level, msg) - print('['..level..'] ' .. msg) - end -end - -function module.compile(path) - assert(jsonpath, '"libjsonpath_lib" is not loaded') - - if(cache[path] == nil) then - cache[path] = jsonpath.ffi_path_compile(path) - _ngx.log(_ngx.INFO, 'compile : [' .. path .. ']') - end -end - -function module.exec(path) - local compiledPath = cache[path] - - if(cache[path] == nil) then - assert(jsonpath, path .. ": is not compiled") - end - - return function(jsonStr) - local result = jsonpath.ffi_select_with_compiled_path(compiledPath, jsonStr) - return ffi.string(result); - end -end - -function module.init(path) - if jsonpath == nil then - jsonpath = ffi.load(path) - _ngx.log(_ngx.INFO, '"' .. path .. '" initialized') - end -end - -return module \ No newline at end of file diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs deleted file mode 100644 index 383951ab..00000000 --- a/src/ffi/mod.rs +++ /dev/null @@ -1,62 +0,0 @@ -use std::ffi::{CStr, CString}; -use std::os::raw::{c_char, c_void}; - -use crate::{parser, select, select_as_str}; - -const INVALID_PATH: &str = "invalid path"; -const INVALID_JSON: &str = "invalud json"; - -fn to_str(v: *const c_char, err_msg: &str) -> &str { - unsafe { CStr::from_ptr(v) }.to_str().expect(err_msg) -} - -fn to_char_ptr(v: &str) -> *const c_char { - let s = CString::new(v).unwrap_or_else(|_| panic!("invalid string: {}", v)); - let ptr = s.as_ptr(); - std::mem::forget(s); - ptr -} - -#[no_mangle] -pub extern "C" fn ffi_select(json_str: *const c_char, path: *const c_char) -> *const c_char { - let json_str = to_str(json_str, INVALID_JSON); - let path = to_str(path, INVALID_PATH); - match select_as_str(json_str, path) { - Ok(v) => to_char_ptr(v.as_str()), - Err(e) => { - panic!("{:?}", e); - } - } -} - -#[no_mangle] -#[allow(clippy::forget_copy)] -pub extern "C" fn ffi_path_compile(path: *const c_char) -> *mut c_void { - let path = to_str(path, INVALID_PATH); - #[allow(deprecated)] - let ref_node = Box::into_raw(Box::new(parser::Parser::compile(path).unwrap())); - let ptr = ref_node as *mut c_void; - std::mem::forget(ref_node); - ptr -} - -#[no_mangle] -pub extern "C" fn ffi_select_with_compiled_path( - path_ptr: *mut c_void, - json_ptr: *const c_char, -) -> *const c_char { - #[allow(deprecated)] - let node = unsafe { Box::from_raw(path_ptr as *mut parser::Node) }; - let json_str = to_str(json_ptr, INVALID_JSON); - let json = serde_json::from_str(json_str) - .unwrap_or_else(|_| panic!("invalid json string: {}", json_str)); - - #[allow(deprecated)] - let mut selector = select::Selector::default(); - let found = selector.compiled_path(&node).value(&json).select().unwrap(); - std::mem::forget(node); - - let result = serde_json::to_string(&found) - .unwrap_or_else(|_| panic!("json serialize error: {:?}", found)); - to_char_ptr(result.as_str()) -} diff --git a/src/lib.rs b/src/lib.rs index 4d9f603c..d2856a34 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -144,12 +144,6 @@ pub use crate::paths::PathParser; pub use crate::selector::{JsonSelector, JsonSelectorMut}; use std::rc::Rc; -#[doc(hidden)] -#[deprecated( - since = "0.4.0", - note = "'ffi' is moved to another location like 'wasm' from version 0.5.x" -)] -mod ffi; #[doc(hidden)] mod parser; #[doc(hidden)] diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 160b7243..6d1ccd49 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -623,48 +623,48 @@ pub trait NodeVisitor { } ParseToken::In | ParseToken::Leaves => { if let Some(n) = &node.left { - self.visit(&*n); + self.visit(n); } self.visit_token(&node.token); if let Some(n) = &node.right { - self.visit(&*n); + self.visit(n); } } ParseToken::Array => { if let Some(n) = &node.left { - self.visit(&*n); + self.visit(n); } self.visit_token(&node.token); if let Some(n) = &node.right { - self.visit(&*n); + self.visit(n); } self.visit_token(&ParseToken::ArrayEof); } ParseToken::Filter(FilterToken::And) | ParseToken::Filter(FilterToken::Or) => { if let Some(n) = &node.left { - self.visit(&*n); + self.visit(n); } if let Some(n) = &node.right { - self.visit(&*n); + self.visit(n); } self.visit_token(&node.token); } ParseToken::Filter(_) => { if let Some(n) = &node.left { - self.visit(&*n); + self.visit(n); } self.end_term(); if let Some(n) = &node.right { - self.visit(&*n); + self.visit(n); } self.end_term(); diff --git a/src/paths/parser_node_visitor.rs b/src/paths/parser_node_visitor.rs index 9df56822..e84cb2e2 100644 --- a/src/paths/parser_node_visitor.rs +++ b/src/paths/parser_node_visitor.rs @@ -23,46 +23,46 @@ pub trait ParserNodeVisitor<'a> { } ParseToken::In | ParseToken::Leaves => { if let Some(n) = &parse_node.left { - self.visit(&*n, token_handler, parse_value_reader); + self.visit(n, token_handler, parse_value_reader); } token_handler.handle(&parse_node.token, parse_value_reader); if let Some(n) = &parse_node.right { - self.visit(&*n, token_handler, parse_value_reader); + self.visit(n, token_handler, parse_value_reader); } } ParseToken::Array => { if let Some(n) = &parse_node.left { - self.visit(&*n, token_handler, parse_value_reader); + self.visit(n, token_handler, parse_value_reader); } token_handler.handle(&parse_node.token, parse_value_reader); if let Some(n) = &parse_node.right { - self.visit(&*n, token_handler, parse_value_reader); + self.visit(n, token_handler, parse_value_reader); } token_handler.handle(&ParseToken::ArrayEof, parse_value_reader); } ParseToken::Filter(FilterToken::And) | ParseToken::Filter(FilterToken::Or) => { if let Some(n) = &parse_node.left { - self.visit(&*n, token_handler, parse_value_reader); + self.visit(n, token_handler, parse_value_reader); } if let Some(n) = &parse_node.right { - self.visit(&*n, token_handler, parse_value_reader); + self.visit(n, token_handler, parse_value_reader); } token_handler.handle(&parse_node.token, parse_value_reader); } ParseToken::Filter(_) => { if let Some(n) = &parse_node.left { - self.visit(&*n, token_handler, parse_value_reader); + self.visit(n, token_handler, parse_value_reader); } if let Some(n) = &parse_node.right { - self.visit(&*n, token_handler, parse_value_reader); + self.visit(n, token_handler, parse_value_reader); } token_handler.handle(&parse_node.token, parse_value_reader); diff --git a/src/paths/path_parser.rs b/src/paths/path_parser.rs index 1ceef84a..816b1ec6 100644 --- a/src/paths/path_parser.rs +++ b/src/paths/path_parser.rs @@ -1125,11 +1125,11 @@ mod path_parser_tests { ); assert_eq!( - run(r#"$['single\'quote']"#), + run(r"$['single\'quote']"), Ok(vec![ ParseToken::Absolute, ParseToken::Array, - ParseToken::Key(StrRange::new(2, r#"'single\'quote'"#.len())), + ParseToken::Key(StrRange::new(2, r"'single\'quote'".len())), ParseToken::ArrayEof ]) ); diff --git a/src/paths/tokenizer.rs b/src/paths/tokenizer.rs index b4f12d69..764118e5 100644 --- a/src/paths/tokenizer.rs +++ b/src/paths/tokenizer.rs @@ -523,12 +523,12 @@ mod tokenizer_tests { ); run( - r#"$['single\'quote']"#, + r"$['single\'quote']", ( vec![ Token::Absolute(StrRange::new(0, 1)), Token::OpenArray(StrRange::new(1, 1)), - Token::SingleQuoted(StrRange::new(2, r#"'single\'quote'"#.len())), + Token::SingleQuoted(StrRange::new(2, r"'single\'quote'".len())), Token::CloseArray(StrRange::new(17, 1)), ], Some(TokenError::Eof), @@ -536,14 +536,14 @@ mod tokenizer_tests { ); run( - r#"$['single\'1','single\'2']"#, + r"$['single\'1','single\'2']", ( vec![ Token::Absolute(StrRange::new(0, 1)), Token::OpenArray(StrRange::new(1, 1)), - Token::SingleQuoted(StrRange::new(2, r#"'single\'1'"#.len())), + Token::SingleQuoted(StrRange::new(2, r"'single\'1'".len())), Token::Comma(StrRange::new(13, 1)), - Token::SingleQuoted(StrRange::new(14, r#"'single\'2'"#.len())), + Token::SingleQuoted(StrRange::new(14, r"'single\'2'".len())), Token::CloseArray(StrRange::new(25, 1)), ], Some(TokenError::Eof), diff --git a/src/select/mod.rs b/src/select/mod.rs index b538a782..3e597c96 100644 --- a/src/select/mod.rs +++ b/src/select/mod.rs @@ -1101,7 +1101,7 @@ mod select_inner_tests { let number = 0_i64; let v: Value = serde_json::from_str(&format!("{}", number)).unwrap(); if let Value::Number(n) = v { - assert_eq!((super::to_f64(&n) - number as f64).abs() == 0_f64, true); + assert!((super::to_f64(&n) - number as f64).abs() == 0_f64); } else { panic!(); } @@ -1112,7 +1112,7 @@ mod select_inner_tests { let number = 0.1_f64; let v: Value = serde_json::from_str(&format!("{}", number)).unwrap(); if let Value::Number(n) = v { - assert_eq!((super::to_f64(&n) - number).abs() == 0_f64, true); + assert!((super::to_f64(&n) - number).abs() == 0_f64); } else { panic!(); } @@ -1123,7 +1123,7 @@ mod select_inner_tests { let number = u64::max_value(); let v: Value = serde_json::from_str(&format!("{}", number)).unwrap(); if let Value::Number(n) = v { - assert_eq!((super::to_f64(&n) - number as f64).abs() == 0_f64, true); + assert!((super::to_f64(&n) - number as f64).abs() == 0_f64); } else { panic!(); } diff --git a/src/selector/cmp.rs b/src/selector/cmp.rs index 40cd89bc..e1c6417d 100644 --- a/src/selector/cmp.rs +++ b/src/selector/cmp.rs @@ -60,7 +60,7 @@ impl Cmp for CmpNe { let mut ret = v1.to_vec(); for v in v2 { for i in 0..ret.len() { - if std::ptr::eq(*v, &*ret[i]) { + if std::ptr::eq(*v, ret[i]) { ret.remove(i); break; } diff --git a/src/selector/selector_impl.rs b/src/selector/selector_impl.rs index aac1c71e..e31e5ee9 100644 --- a/src/selector/selector_impl.rs +++ b/src/selector/selector_impl.rs @@ -477,7 +477,7 @@ impl<'a> ParserTokenHandler<'a> for JsonSelector<'a> { self.visit_key(key); } ParseToken::Keys(keys) => { - let keys: Vec<&str> = keys.iter().map(|s| parse_value_reader(s)).collect(); + let keys: Vec<&str> = keys.iter().map(parse_value_reader).collect(); self.visit_keys(&keys) } ParseToken::Number(v) => { diff --git a/src/selector/value_walker.rs b/src/selector/value_walker.rs index d538e55c..a78409dd 100644 --- a/src/selector/value_walker.rs +++ b/src/selector/value_walker.rs @@ -106,9 +106,7 @@ impl<'a> ValueWalker { vec.iter().for_each(|v| Self::_walk(v, acc, fun)); } Value::Object(map) => { - map.values() - .into_iter() - .for_each(|v| Self::_walk(v, acc, fun)); + map.values().for_each(|v| Self::_walk(v, acc, fun)); } _ => {} } diff --git a/tests/filter.rs b/tests/filter.rs index 8097723e..069d8113 100644 --- a/tests/filter.rs +++ b/tests/filter.rs @@ -10,7 +10,7 @@ fn quote() { setup(); select_and_then_compare( - r#"$['single\'quote']"#, + r"$['single\'quote']", json!({"single'quote":"value"}), json!(["value"]), ); diff --git a/tests/selector.rs b/tests/selector.rs index 8e6c5b55..091f9d62 100644 --- a/tests/selector.rs +++ b/tests/selector.rs @@ -61,7 +61,7 @@ fn selector_mut_err() { .value(read_json("./benchmark/example.json")) .replace_with(&mut |_| Err(JsonPathError::EmptyValue)); - assert_eq!(result.is_err(), true); + assert!(result.is_err()); } #[test] @@ -74,7 +74,7 @@ fn jsonselector_mut_err() { .value(read_json("./benchmark/example.json")) .replace_with(&mut |_| Err(JsonPathError::EmptyValue)); - assert_eq!(result.is_err(), true); + assert!(result.is_err()); } #[test] From 15ef316c4fda819d3e61190dc2438df6fe363db6 Mon Sep 17 00:00:00 2001 From: Eoin Power-Moran Date: Thu, 19 Oct 2023 15:10:15 +0100 Subject: [PATCH 15/20] merge --- src/paths/path_parser.rs | 3 +-- src/selector/terms.rs | 5 ----- src/selector/value_walker.rs | 2 -- tests/common.rs | 32 ++++++++++++++++++-------------- 4 files changed, 19 insertions(+), 23 deletions(-) diff --git a/src/paths/path_parser.rs b/src/paths/path_parser.rs index 6a175eb1..86cb5335 100644 --- a/src/paths/path_parser.rs +++ b/src/paths/path_parser.rs @@ -22,10 +22,9 @@ pub struct PathParser<'a> { /// can use PathParserWithMetadata to associate metadata with each parser. /// /// ``` -/// use jsonpath_lib::paths::PathParserWithMetadata; +/// use jsonpath_lib::PathParserWithMetadata; /// /// let parser = PathParserWithMetadata::compile("$.store..price", 1).unwrap(); -/// assert_eq!(parser.metadata(), &1); /// ``` #[derive(Clone, Debug)] pub struct PathParserWithMetadata<'a, T: Debug> { diff --git a/src/selector/terms.rs b/src/selector/terms.rs index 07353577..ff6747e4 100644 --- a/src/selector/terms.rs +++ b/src/selector/terms.rs @@ -16,11 +16,6 @@ pub enum ExprTerm<'a> { Option>, Vec<&'a Value>, ), - Json( - Option>, - Option>, - Vec<&'a Value>, - ), } impl<'a> ExprTerm<'a> { diff --git a/src/selector/value_walker.rs b/src/selector/value_walker.rs index fca0c608..7cc03baa 100644 --- a/src/selector/value_walker.rs +++ b/src/selector/value_walker.rs @@ -1,7 +1,5 @@ use std::collections::HashSet; -use super::utils; -use crate::selector::utils::PathKey; use super::utils; use crate::selector::utils::PathKey; use serde_json::Value; diff --git a/tests/common.rs b/tests/common.rs index 35a59d07..68b5edb5 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -10,22 +10,26 @@ use serde_json::Value; use self::jsonpath::{JsonSelector, PathParser}; +static SETUP_LOGS_ONCE: std::sync::Once = std::sync::Once::new(); + #[allow(dead_code)] pub fn setup() { - env_logger::Builder::new() - .format(|buf, record| { - writeln!( - buf, - "{}:{} {} - {}", - record.file().unwrap_or("unknown"), - record.line().unwrap_or(0), - record.level(), - record.args() - ) - }) - .parse_env("RUST_LOG") - // .filter(Some("logger_example"), LevelFilter::Trace) - .init(); + SETUP_LOGS_ONCE.call_once(|| { + env_logger::Builder::new() + .format(|buf, record| { + writeln!( + buf, + "{}:{} {} - {}", + record.file().unwrap_or("unknown"), + record.line().unwrap_or(0), + record.level(), + record.args() + ) + }) + .parse_env("RUST_LOG") + // .filter(Some("logger_example"), LevelFilter::Trace) + .init(); + }) } #[allow(dead_code)] From 163a3e693ad17ba0e9806df27be854c2f3815f80 Mon Sep 17 00:00:00 2001 From: Eoin Power-Moran Date: Thu, 19 Oct 2023 15:12:36 +0100 Subject: [PATCH 16/20] unintended change --- src/selector/selector_impl.rs | 2 -- tests/common.rs | 21 +-------------------- 2 files changed, 1 insertion(+), 22 deletions(-) diff --git a/src/selector/selector_impl.rs b/src/selector/selector_impl.rs index 03853dbc..233b3069 100644 --- a/src/selector/selector_impl.rs +++ b/src/selector/selector_impl.rs @@ -175,7 +175,6 @@ impl<'a> JsonSelector<'a> { } fn visit_relative(&mut self) { - println!("visit_relative"); if let Some(ParseToken::Array) = self.tokens.last() { let array_token = self.tokens.pop(); if let Some(ParseToken::Leaves) = self.tokens.last() { @@ -459,7 +458,6 @@ impl<'a> ParserTokenHandler<'a> for JsonSelector<'a> { debug!("token: {:?}, stack: {:?}", token, self.tokens); if self.compute_absolute_path_filter(token, parse_value_reader) { - println!("compute_absolute_path_filter"); return; } diff --git a/tests/common.rs b/tests/common.rs index 68b5edb5..6052d62e 100644 --- a/tests/common.rs +++ b/tests/common.rs @@ -3,33 +3,14 @@ extern crate jsonpath_lib as jsonpath; extern crate serde_json; use std::io::Read; -use std::io::Write; - use serde_json::Value; use self::jsonpath::{JsonSelector, PathParser}; -static SETUP_LOGS_ONCE: std::sync::Once = std::sync::Once::new(); - #[allow(dead_code)] pub fn setup() { - SETUP_LOGS_ONCE.call_once(|| { - env_logger::Builder::new() - .format(|buf, record| { - writeln!( - buf, - "{}:{} {} - {}", - record.file().unwrap_or("unknown"), - record.line().unwrap_or(0), - record.level(), - record.args() - ) - }) - .parse_env("RUST_LOG") - // .filter(Some("logger_example"), LevelFilter::Trace) - .init(); - }) + let _ = env_logger::try_init(); } #[allow(dead_code)] From fd039337f9c39562d2840c4971d7263eee3ad379 Mon Sep 17 00:00:00 2001 From: Eoin Power-Moran Date: Thu, 19 Oct 2023 15:15:33 +0100 Subject: [PATCH 17/20] add comment to replace with async --- src/selector/async_selector_impl.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/selector/async_selector_impl.rs b/src/selector/async_selector_impl.rs index e989e9fd..ede3834c 100644 --- a/src/selector/async_selector_impl.rs +++ b/src/selector/async_selector_impl.rs @@ -155,6 +155,9 @@ impl<'a, T: Debug + Send + Sync + 'a> MultiJsonSelectorMutWithMetadata<'a, T> { Ok(pointers) } + /// Replace the value at the given path with the result of some asynchronous computation. + /// The function provided is called with the current value and the metadata associated with the path, + /// and should return a Future that resolves to an Option. This value will replace the current value. pub fn replace_with_async( &mut self, mut value: Value, From e0c0df38c67949b5c6e7d568b5d37d8575ad8fee Mon Sep 17 00:00:00 2001 From: Eoin Power-Moran Date: Thu, 19 Oct 2023 15:17:20 +0100 Subject: [PATCH 18/20] fix deps --- Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a886f7c0..9ffb6e89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,9 +18,8 @@ license = "MIT" travis-ci = { repository = "freestrings/jsonpath", branch = "master" } [dependencies] -futures = "0.3.28" +futures = "0.3" log = "0.4" -reqwest = "0.11.22" serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0", features = ["preserve_order"] } From b8efb10b04ec537771e7225741a1bcf5e5ba8bb4 Mon Sep 17 00:00:00 2001 From: Eoin Power-Moran Date: Thu, 19 Oct 2023 15:21:13 +0100 Subject: [PATCH 19/20] improve trait bounds for select --- benches/async.rs | 14 ++--- benches/common.rs | 1 - benches/sync_mut.rs | 98 ++++++++++++++--------------- src/lib.rs | 1 - src/selector/async_selector_impl.rs | 10 +-- src/selector/value_walker.rs | 3 +- tests/async.rs | 18 +++++- 7 files changed, 75 insertions(+), 70 deletions(-) diff --git a/benches/async.rs b/benches/async.rs index 72477021..ae50a081 100644 --- a/benches/async.rs +++ b/benches/async.rs @@ -8,7 +8,7 @@ use std::{ task::{Context, Poll}, }; -use common::{read_json}; +use common::read_json; use criterion::{criterion_group, criterion_main, BenchmarkId}; use futures::Future; use jsonpath::{MultiJsonSelectorMutWithMetadata, PathParserWithMetadata}; @@ -116,19 +116,15 @@ fn setup_async_benchmark(c: &mut criterion::Criterion) { let t1_json = read_json("./benchmark/example.json"); let t1_parser = PathParserWithMetadata::compile("$.store..price", "one").unwrap(); let t1_parser_two = PathParserWithMetadata::compile("$.store..author", "two").unwrap(); - let t1_selector_mut = MultiJsonSelectorMutWithMetadata::new_multi_parser(vec![ - t1_parser, - t1_parser_two, - ]); + let t1_selector_mut = + MultiJsonSelectorMutWithMetadata::new_multi_parser(vec![t1_parser, t1_parser_two]); // let big_array = read_json("./benchmark/big_array.json"); let t2_json = read_json("./benchmark/big_example.json"); let t2_parser = PathParserWithMetadata::compile("$.store.book[*].author", "one").unwrap(); let t2_parser_two = PathParserWithMetadata::compile("$.store.author", "two").unwrap(); - let t2_selector_mut = MultiJsonSelectorMutWithMetadata::new_multi_parser(vec![ - t2_parser, - t2_parser_two, - ]); + let t2_selector_mut = + MultiJsonSelectorMutWithMetadata::new_multi_parser(vec![t2_parser, t2_parser_two]); let runtime = tokio::runtime::Builder::new_current_thread() .build() diff --git a/benches/common.rs b/benches/common.rs index 35a59d07..62ae459b 100644 --- a/benches/common.rs +++ b/benches/common.rs @@ -5,7 +5,6 @@ extern crate serde_json; use std::io::Read; use std::io::Write; - use serde_json::Value; use self::jsonpath::{JsonSelector, PathParser}; diff --git a/benches/sync_mut.rs b/benches/sync_mut.rs index 821b5eb7..714034e1 100644 --- a/benches/sync_mut.rs +++ b/benches/sync_mut.rs @@ -1,10 +1,10 @@ extern crate jsonpath_lib as jsonpath; extern crate serde_json; -use common::{read_json}; +use common::read_json; use criterion::{criterion_group, criterion_main, BenchmarkId}; -use jsonpath::{PathParser, JsonSelectorMut}; +use jsonpath::{JsonSelectorMut, PathParser}; use serde_json::Value; mod common; @@ -31,7 +31,7 @@ fn setup_async_benchmark(c: &mut criterion::Criterion) { let t1_parser = PathParser::compile("$.store..price").unwrap(); let t1_selector_mut = JsonSelectorMut::new(t1_parser.clone()); let t1_selector_mut_two = JsonSelectorMut::new(t1_parser); - + let t2_json = read_json("./benchmark/big_example.json"); let t2_parser = PathParser::compile("$.store.book[*].author").unwrap(); let t2_parser_two = PathParser::compile("$.store.author").unwrap(); @@ -51,7 +51,7 @@ fn setup_async_benchmark(c: &mut criterion::Criterion) { b.to_async(&runtime).iter_batched( || (s.clone(), v.clone()), |(s, v)| async { - selector_mut(s, v); + selector_mut(s, v); }, criterion::BatchSize::SmallInput, ); @@ -59,54 +59,54 @@ fn setup_async_benchmark(c: &mut criterion::Criterion) { ); c.bench_with_input( - BenchmarkId::new("selector_mut", "BigJson"), - &(t2_selector_mut.clone(), t2_json.clone()), - |b, (s, v)| { - // Insert a call to `to_async` to convert the bencher to async mode. - // The timing loops are the same as with the normal bencher. - b.to_async(&runtime).iter_batched( - || (s.clone(), v.clone()), - |(s, v)| async { - selector_mut(s, v); - }, - criterion::BatchSize::LargeInput, - ); - }, - ); + BenchmarkId::new("selector_mut", "BigJson"), + &(t2_selector_mut.clone(), t2_json.clone()), + |b, (s, v)| { + // Insert a call to `to_async` to convert the bencher to async mode. + // The timing loops are the same as with the normal bencher. + b.to_async(&runtime).iter_batched( + || (s.clone(), v.clone()), + |(s, v)| async { + selector_mut(s, v); + }, + criterion::BatchSize::LargeInput, + ); + }, + ); - c.bench_with_input( - BenchmarkId::new("double_selector_mut", "Json"), - &(t1_selector_mut, t1_selector_mut_two, t1_json), - |b, (s, s2, v)| { - // Insert a call to `to_async` to convert the bencher to async mode. - // The timing loops are the same as with the normal bencher. - b.to_async(&runtime).iter_batched( - || (s.clone(), s2.clone(), v.clone()), - |(s, s2, v)| async { - let v = selector_mut(s, v); - let _ = selector_mut(s2, v); - }, - criterion::BatchSize::SmallInput, - ); - }, -); + c.bench_with_input( + BenchmarkId::new("double_selector_mut", "Json"), + &(t1_selector_mut, t1_selector_mut_two, t1_json), + |b, (s, s2, v)| { + // Insert a call to `to_async` to convert the bencher to async mode. + // The timing loops are the same as with the normal bencher. + b.to_async(&runtime).iter_batched( + || (s.clone(), s2.clone(), v.clone()), + |(s, s2, v)| async { + let v = selector_mut(s, v); + let _ = selector_mut(s2, v); + }, + criterion::BatchSize::SmallInput, + ); + }, + ); c.bench_with_input( - BenchmarkId::new("double_selector_mut", "BigJson"), - &(t2_selector_mut, t2_selector_mut_two, t2_json), - |b, (s, s2, v)| { - // Insert a call to `to_async` to convert the bencher to async mode. - // The timing loops are the same as with the normal bencher. - b.to_async(&runtime).iter_batched( - || (s.clone(), s2.clone(), v.clone()), - |(s, s2, v)| async { - let v = selector_mut(s, v); - let _ = selector_mut(s2, v); - }, - criterion::BatchSize::LargeInput, - ); - }, - ); + BenchmarkId::new("double_selector_mut", "BigJson"), + &(t2_selector_mut, t2_selector_mut_two, t2_json), + |b, (s, s2, v)| { + // Insert a call to `to_async` to convert the bencher to async mode. + // The timing loops are the same as with the normal bencher. + b.to_async(&runtime).iter_batched( + || (s.clone(), s2.clone(), v.clone()), + |(s, s2, v)| async { + let v = selector_mut(s, v); + let _ = selector_mut(s2, v); + }, + criterion::BatchSize::LargeInput, + ); + }, + ); } criterion_group!(benches, setup_async_benchmark); diff --git a/src/lib.rs b/src/lib.rs index 818e9797..655e6665 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -140,7 +140,6 @@ pub use crate::select::{Selector, SelectorMut}; #[deprecated(since = "0.4.0", note = "It will be move to common module. since 0.5")] pub use crate::select::JsonPathError; - pub use paths::{PathParser, PathParserWithMetadata}; pub use selector::{JsonSelector, JsonSelectorMut, MultiJsonSelectorMutWithMetadata}; use std::sync::Arc; diff --git a/src/selector/async_selector_impl.rs b/src/selector/async_selector_impl.rs index ede3834c..b696e6a2 100644 --- a/src/selector/async_selector_impl.rs +++ b/src/selector/async_selector_impl.rs @@ -23,10 +23,7 @@ impl<'a, T: Debug + Send + Sync> From<(Vec, &'a T)> for JsonPointerWithM fn from((pointer, metadata): (Vec, &'a T)) -> Self { let pointer = "/".to_owned() + &pointer.join("/"); - JsonPointerWithMetadata { - pointer, - metadata, - } + JsonPointerWithMetadata { pointer, metadata } } } @@ -82,7 +79,10 @@ impl<'a, T: Debug + Send + Sync + 'a> MultiJsonSelectorMutWithMetadata<'a, T> { selector.select() } - fn select<'b>(&'b self, value: &'b Value) -> Result, JsonPathError> { + fn select<'b>(&self, value: &'b Value) -> Result, JsonPathError> + where + 'a: 'b, + { let res: Vec, JsonPathError>> = if let Some(parser) = self.parser.as_ref() { parser diff --git a/src/selector/value_walker.rs b/src/selector/value_walker.rs index 7cc03baa..a78409dd 100644 --- a/src/selector/value_walker.rs +++ b/src/selector/value_walker.rs @@ -106,8 +106,7 @@ impl<'a> ValueWalker { vec.iter().for_each(|v| Self::_walk(v, acc, fun)); } Value::Object(map) => { - map.values() - .for_each(|v| Self::_walk(v, acc, fun)); + map.values().for_each(|v| Self::_walk(v, acc, fun)); } _ => {} } diff --git a/tests/async.rs b/tests/async.rs index d25a0580..56d2e23c 100644 --- a/tests/async.rs +++ b/tests/async.rs @@ -70,7 +70,8 @@ impl CryptoRequest { async fn send_request(&self) { let mut bags = self.bags.lock().unwrap(); for bag in bags.iter_mut() { - bag.value.set_value(serde_json::Value::String(bag.metadata.clone())); + bag.value + .set_value(serde_json::Value::String(bag.metadata.clone())); } } } @@ -126,7 +127,13 @@ async fn async_selector_mut() { let result = selector.value(&root_result).select().unwrap(); assert_eq!( - vec![&json!("price-metadata"), &json!("price-metadata"), &json!("price-metadata"), &json!("price-metadata"), &json!("price-metadata")], + vec![ + &json!("price-metadata"), + &json!("price-metadata"), + &json!("price-metadata"), + &json!("price-metadata"), + &json!("price-metadata") + ], result ); @@ -136,7 +143,12 @@ async fn async_selector_mut() { let result = selector.value(&root_result).select().unwrap(); assert_eq!( - vec![&json!("author-metadata"), &json!("author-metadata"), &json!("author-metadata"), &json!("author-metadata")], + vec![ + &json!("author-metadata"), + &json!("author-metadata"), + &json!("author-metadata"), + &json!("author-metadata") + ], result ); } From a4b5f893726eae506df8152327276912cdd176e0 Mon Sep 17 00:00:00 2001 From: Eoin Power-Moran Date: Thu, 19 Oct 2023 15:54:25 +0100 Subject: [PATCH 20/20] rename --- benches/async.rs | 20 ++++++++++---------- benches/common.rs | 16 +--------------- tests/async.rs | 20 ++++++++++---------- 3 files changed, 21 insertions(+), 35 deletions(-) diff --git a/benches/async.rs b/benches/async.rs index ae50a081..e93a753a 100644 --- a/benches/async.rs +++ b/benches/async.rs @@ -49,19 +49,19 @@ impl Future for ValueFuture { } } -struct CryptoRequest { - bags: Mutex>, +struct MutationRequest { + bags: Mutex>, } -impl CryptoRequest { +impl MutationRequest { fn new() -> Self { Self { bags: Mutex::new(Vec::new()), } } - fn new_field(&self, input: Value) -> CryptoField { - let bag = CryptoField::new(input); + fn new_field(&self, input: Value) -> Field { + let bag = Field::new(input); self.bags.lock().unwrap().push(bag.clone()); bag } @@ -75,12 +75,12 @@ impl CryptoRequest { } #[derive(Clone)] -struct CryptoField { +struct Field { input: Option, value: ValueFuture, } -impl CryptoField { +impl Field { fn new(input: Value) -> Self { Self { input: Some(input), @@ -94,11 +94,11 @@ impl CryptoField { } async fn async_run(mut selector_mut: MultiJsonSelectorMutWithMetadata<'_, &str>, json: Value) { - let crypto_request = Arc::new(CryptoRequest::new()); + let mut_request = Arc::new(MutationRequest::new()); let result_futures = selector_mut .replace_with_async(json, |v, _| { - let bag: CryptoField = crypto_request.new_field(v); + let bag: Field = mut_request.new_field(v); Box::pin(async move { let val = bag.value().await; @@ -107,7 +107,7 @@ async fn async_run(mut selector_mut: MultiJsonSelectorMutWithMetadata<'_, &str>, }) .unwrap(); - crypto_request.send_request().await; + mut_request.send_request().await; let _result = result_futures.await.unwrap(); } diff --git a/benches/common.rs b/benches/common.rs index 62ae459b..6052d62e 100644 --- a/benches/common.rs +++ b/benches/common.rs @@ -3,7 +3,6 @@ extern crate jsonpath_lib as jsonpath; extern crate serde_json; use std::io::Read; -use std::io::Write; use serde_json::Value; @@ -11,20 +10,7 @@ use self::jsonpath::{JsonSelector, PathParser}; #[allow(dead_code)] pub fn setup() { - env_logger::Builder::new() - .format(|buf, record| { - writeln!( - buf, - "{}:{} {} - {}", - record.file().unwrap_or("unknown"), - record.line().unwrap_or(0), - record.level(), - record.args() - ) - }) - .parse_env("RUST_LOG") - // .filter(Some("logger_example"), LevelFilter::Trace) - .init(); + let _ = env_logger::try_init(); } #[allow(dead_code)] diff --git a/tests/async.rs b/tests/async.rs index 56d2e23c..3eb53bd2 100644 --- a/tests/async.rs +++ b/tests/async.rs @@ -50,19 +50,19 @@ impl Future for ValueFuture { } } -struct CryptoRequest { - bags: Mutex>, +struct MutationRequest { + bags: Mutex>, } -impl CryptoRequest { +impl MutationRequest { fn new() -> Self { Self { bags: Mutex::new(Vec::new()), } } - fn new_field(&self, metadata: String) -> CryptoField { - let bag = CryptoField::new(metadata); + fn new_field(&self, metadata: String) -> Field { + let bag = Field::new(metadata); self.bags.lock().unwrap().push(bag.clone()); bag } @@ -77,12 +77,12 @@ impl CryptoRequest { } #[derive(Clone)] -struct CryptoField { +struct Field { metadata: String, value: ValueFuture, } -impl CryptoField { +impl Field { fn new(metadata: String) -> Self { Self { metadata: metadata, @@ -104,11 +104,11 @@ async fn async_selector_mut() { let mut selector_mut = MultiJsonSelectorMutWithMetadata::new_multi_parser(vec![parser, parser_two]); - let crypto_request = Arc::new(CryptoRequest::new()); + let mut_request = Arc::new(MutationRequest::new()); let result_futures = selector_mut .replace_with_async(read_json("./benchmark/example.json"), |_, m| { - let bag: CryptoField = crypto_request.new_field(m.to_string()); + let bag: Field = mut_request.new_field(m.to_string()); Box::pin(async move { let val = bag.value().await; @@ -117,7 +117,7 @@ async fn async_selector_mut() { }) .unwrap(); - crypto_request.send_request().await; + mut_request.send_request().await; let root_result = result_futures.await.unwrap();