From fc17949683236e294acf965cbdc2c419e3487817 Mon Sep 17 00:00:00 2001 From: Boris Zhguchev Date: Fri, 3 Nov 2023 10:05:17 +0100 Subject: [PATCH] Issue 31 not stale (#49) add a new api to handle the return of the path with value as well --- CHANGELOG.md | 2 + Cargo.toml | 2 +- README.md | 52 +++++++-- src/lib.rs | 285 +++++++++++++++++++++++++++++++++------------- src/path/index.rs | 227 ++++++++++++++++++++++++------------ src/path/top.rs | 192 +++++++++++++++---------------- 6 files changed, 506 insertions(+), 254 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fd5f6b3..0ff4dda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,3 +33,5 @@ * make jsonpath inst cloneable. * **`0.3.3`** * fix a bug with the logical operators +* **`0.3.4`** + * add a result as a path diff --git a/Cargo.toml b/Cargo.toml index 4d2f620..0d89dfd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsonpath-rust" description = "The library provides the basic functionality to find the set of the data according to the filtering query." -version = "0.3.3" +version = "0.3.4" authors = ["BorisZhguchev "] edition = "2018" license-file = "LICENSE" diff --git a/README.md b/README.md index 10b0b5a..cf4224e 100644 --- a/README.md +++ b/README.md @@ -223,11 +223,11 @@ Given the json | `$..book[?(@.author ~= /.*REES/i)]` | All books matching regex (ignore case) | | `$..*` | Give me every thing | -## The library +### The library The library intends to provide the basic functionality for ability to find the slices of data using the syntax, saying above. The dependency can be found as following: -``` jsonpath-rust = 0.3.0``` +``` jsonpath-rust = *``` The basic example is the following one: @@ -255,7 +255,7 @@ fn main() { let finder = JsonPathFinder::from_str(r#"{"first":{"second":[{"active":1},{"passive":1}]}}"#, "$.first.second[?(@.active)]").unwrap(); let slice_of_data: Vec<&Value> = finder.find_slice(); let js = json!({"active":1}); - assert_eq!(slice_of_data, vec![JsonPathValue::Slice(&js)]); + assert_eq!(slice_of_data, vec![JsonPathValue::Slice(&js,"$.first.second[0]".to_string())]); } ``` @@ -282,7 +282,7 @@ fn test() { let v = finder.find_slice(); let js = json!("Sayings of the Century"); - assert_eq!(v, vec![JsonPathValue::Slice(&js)]); + assert_eq!(v, vec![JsonPathValue::Slice(&js,"$.book[0].title".to_string())]); } ``` @@ -345,7 +345,45 @@ fn test() { } ``` -### The structure +The library can return a path describing the value instead of the value itself. +To do that, the method `find_as_path` can be used: + +```rust +use jsonpath_rust::JsonPathFinder; +use serde_json::{json, Value, JsonPathValue}; + +fn main() { + let finder = JsonPathFinder::from_str(r#"{"first":{"second":[{"active":1},{"passive":1}]}}"#, "$.first.second[?(@.active)]").unwrap(); + let slice_of_data: Value = finder.find_as_path(); + assert_eq!(slice_of_data, Value::Array(vec!["$.first.second[0]".to_string()])); +} +``` + +or it can be taken from the `JsonPathValue` instance: +```rust + use serde_json::{json, Value}; +use crate::jsonpath_rust::{JsonPathFinder, JsonPathQuery, JsonPathInst, JsonPathValue}; +use std::str::FromStr; + +fn test() { + let json: Box = serde_json::from_str("{}").unwrap(); + let path: Box = Box::from(JsonPathInst::from_str("$..book[?(@.author size 10)].title").unwrap()); + let finder = JsonPathFinder::new(json, path); + + let v = finder.find_slice(); + let js = json!("Sayings of the Century"); + + // Slice has a path of its value as well + assert_eq!(v, vec![JsonPathValue::Slice(&js,"$.book[0].title".to_string())]); +} +``` + +** If the value has been modified during the search, there is no way to find a path of a new value. +It can happen if we try to find a length() of array, for in stance.** + + + +## The structure ```rust pub enum JsonPath { @@ -380,11 +418,11 @@ pub enum JsonPathIndex { ``` -### How to contribute +## How to contribute TBD -### How to update version +## How to update version - update files - add tag `git tag -a v -m "message"` - git push origin \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 41b8b53..786a5ae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -80,16 +80,17 @@ //! - `[?()]`the logical expression to filter elements in the list.It is used with arrays preliminary. //! //! # Examples -//!``` +//!```rust //! use serde_json::{json,Value}; -//! use jsonpath_rust::json_path_value; +//! use jsonpath_rust::jp_v; //! use self::jsonpath_rust::JsonPathFinder; //! use self::jsonpath_rust::JsonPathValue; +//! //! fn test(){ //! let finder = JsonPathFinder::from_str(r#"{"first":{"second":[{"active":1},{"passive":1}]}}"#, "$.first.second[?(@.active)]").unwrap(); //! let slice_of_data:Vec> = finder.find_slice(); -//! let js = json!({"active":1}); -//! assert_eq!(slice_of_data, json_path_value![&js,]); +//! let js = json!({"active":2}); +//! assert_eq!(slice_of_data, jp_v![&js;"$.first.second[0]",]); //! } //! ``` //! or even simpler: @@ -137,7 +138,7 @@ extern crate pest; /// ``` /// use std::str::FromStr; /// use serde_json::{json,Value}; -/// use jsonpath_rust::json_path_value; +/// use jsonpath_rust::jp_v; /// use crate::jsonpath_rust::{JsonPathFinder,JsonPathQuery,JsonPathInst,JsonPathValue}; ///fn test(){ /// let json: Value = serde_json::from_str("{}").unwrap(); @@ -155,7 +156,7 @@ extern crate pest; /// /// let v = finder.find_slice(); /// let js = json!("Sayings of the Century"); -/// assert_eq!(v, json_path_value![&js,]); +/// assert_eq!(v, jp_v![&js;"",]); /// } /// /// ``` @@ -183,11 +184,11 @@ impl FromStr for JsonPathInst { impl JsonPathInst { pub fn find_slice<'a>(&'a self, value: &'a Value) -> Vec> { json_path_instance(&self.inner, value) - .find(value.into()) + .find(JsonPathValue::from_root(value)) .into_iter() .filter(|v| v.has_value()) .map(|v| match v { - JsonPathValue::Slice(v) => JsonPtr::Slice(v), + JsonPathValue::Slice(v, _) => JsonPtr::Slice(v), JsonPathValue::NewValue(v) => JsonPtr::NewValue(v), JsonPathValue::NoValue => unreachable!("has_value was already checked"), }) @@ -239,7 +240,7 @@ impl JsonPathQuery for Value { /// ``` /// use std::str::FromStr; /// use serde_json::{json,Value}; -/// use jsonpath_rust::json_path_value; +/// use jsonpath_rust::jp_v; /// use crate::jsonpath_rust::{JsonPathFinder,JsonPathQuery,JsonPathInst,JsonPathValue}; ///fn test(){ /// let json: Box = serde_json::from_str("{}").unwrap(); @@ -248,36 +249,61 @@ impl JsonPathQuery for Value { /// /// let v = finder.find_slice(); /// let js = json!("Sayings of the Century"); -/// assert_eq!(v, json_path_value![&js,]); +/// assert_eq!(v, jp_v![&js;"",]); /// } /// ``` #[macro_export] -macro_rules! json_path_value { +macro_rules! jp_v { (&$v:expr) =>{ - JsonPathValue::Slice(&$v) + JsonPathValue::Slice(&$v, String::new()) + }; + + (&$v:expr ; $s:expr) =>{ + JsonPathValue::Slice(&$v, $s.to_string()) + }; + + ($(&$v:expr;$s:expr),+ $(,)?) =>{ + { + let mut res = Vec::new(); + $( + res.push(jp_v!(&$v ; $s)); + )+ + res + } }; - ($(&$v:expr),+ $(,)?) =>{ + ($(&$v:expr),+ $(,)?) => { { let mut res = Vec::new(); $( - res.push(json_path_value!(&$v)); + res.push(jp_v!(&$v)); )+ res } }; + ($v:expr) =>{ JsonPathValue::NewValue($v) }; } +/// Represents the path of the found json data +type JsPathStr = String; + +pub(crate) fn jsp_idx(prefix: &str, idx: usize) -> String { + format!("{}[{}]", prefix, idx) +} +pub(crate) fn jsp_obj(prefix: &str, key: &str) -> String { + format!("{}.['{}']", prefix, key) +} + /// A result of json path /// Can be either a slice of initial data or a new generated value(like length of array) -#[derive(Debug, PartialEq, Copy, Clone)] +#[derive(Debug, PartialEq, Clone)] pub enum JsonPathValue<'a, Data> { /// The slice of the initial json data - Slice(&'a Data), + Slice(&'a Data, JsPathStr), /// The new data that was generated from the input data (like length operator) NewValue(Data), /// The absent value that indicates the input data is not matched to the given json path (like the absent fields) @@ -288,16 +314,25 @@ impl<'a, Data: Clone + Debug + Default> JsonPathValue<'a, Data> { /// Transforms given value into data either by moving value out or by cloning pub fn to_data(self) -> Data { match self { - Slice(r) => r.clone(), + Slice(r, _) => r.clone(), NewValue(val) => val, NoValue => Data::default(), } } -} -impl<'a, Data> From<&'a Data> for JsonPathValue<'a, Data> { - fn from(data: &'a Data) -> Self { - Slice(data) + /// Transforms given value into path + pub fn to_path(self) -> Option { + match self { + Slice(_, path) => Some(path), + _ => None, + } + } + + pub fn from_root(data: &'a Data) -> Self { + Slice(data, String::from("$")) + } + pub fn new_slice(data: &'a Data, path: String) -> Self { + Slice(data, path.to_string()) } } @@ -305,17 +340,22 @@ impl<'a, Data> JsonPathValue<'a, Data> { fn only_no_value(input: &Vec>) -> bool { !input.is_empty() && input.iter().filter(|v| v.has_value()).count() == 0 } - - fn map_vec(data: Vec<&'a Data>) -> Vec> { - data.into_iter().map(|v| v.into()).collect() + fn map_vec(data: Vec<(&'a Data, JsPathStr)>) -> Vec> { + data.into_iter() + .map(|(data, pref)| Slice(data, pref)) + .collect() } fn map_slice(self, mapper: F) -> Vec> where - F: FnOnce(&'a Data) -> Vec<&'a Data>, + F: FnOnce(&'a Data, JsPathStr) -> Vec<(&'a Data, JsPathStr)>, { match self { - Slice(r) => mapper(r).into_iter().map(Slice).collect(), + Slice(r, pref) => mapper(r, pref) + .into_iter() + .map(|(d, s)| Slice(d, s)) + .collect(), + NewValue(_) => vec![], no_v => vec![no_v], } @@ -323,10 +363,10 @@ impl<'a, Data> JsonPathValue<'a, Data> { fn flat_map_slice(self, mapper: F) -> Vec> where - F: FnOnce(&'a Data) -> Vec>, + F: FnOnce(&'a Data, JsPathStr) -> Vec>, { match self { - Slice(r) => mapper(r), + Slice(r, pref) => mapper(r, pref), _ => vec![NoValue], } } @@ -335,11 +375,20 @@ impl<'a, Data> JsonPathValue<'a, Data> { !matches!(self, NoValue) } - pub fn into_data(input: Vec>) -> Vec<&'a Data> { + pub fn vec_as_data(input: Vec>) -> Vec<&'a Data> { input .into_iter() .filter_map(|v| match v { - Slice(el) => Some(el), + Slice(el, _) => Some(el), + _ => None, + }) + .collect() + } + pub fn vec_as_pair(input: Vec>) -> Vec<(&'a Data, JsPathStr)> { + input + .into_iter() + .filter_map(|v| match v { + Slice(el, v) => Some((el, v)), _ => None, }) .collect() @@ -348,7 +397,7 @@ impl<'a, Data> JsonPathValue<'a, Data> { /// moves a pointer (from slice) out or provides a default value when the value was generated pub fn slice_or(self, default: &'a Data) -> &'a Data { match self { - Slice(r) => r, + Slice(r, _) => r, NewValue(_) | NoValue => default, } } @@ -399,7 +448,7 @@ impl JsonPathFinder { /// finds a slice of data in the set json. /// The result is a vector of references to the incoming structure. pub fn find_slice(&self) -> Vec> { - let res = self.instance().find((&(*self.json)).into()); + let res = self.instance().find(JsonPathValue::from_root(&self.json)); let has_v: Vec> = res.into_iter().filter(|v| v.has_value()).collect(); @@ -430,13 +479,24 @@ impl JsonPathFinder { Value::Array(vec![]) } } + /// finds a path of the values. + /// If the values has been obtained by moving the data out of the initial json the path is absent. + pub fn find_as_path(&self) -> Value { + Value::Array( + self.find_slice() + .into_iter() + .flat_map(|v| v.to_path()) + .map(|v| v.into()) + .collect(), + ) + } } #[cfg(test)] mod tests { use crate::JsonPathQuery; use crate::JsonPathValue::{NoValue, Slice}; - use crate::{json_path_value, JsonPathFinder, JsonPathInst, JsonPathValue}; + use crate::{jp_v, JsonPathFinder, JsonPathInst, JsonPathValue}; use serde_json::{json, Value}; use std::ops::Deref; use std::str::FromStr; @@ -506,13 +566,13 @@ mod tests { #[test] fn simple_test() { let j1 = json!(2); - test("[1,2,3]", "$[1]", json_path_value![&j1,]); + test("[1,2,3]", "$[1]", jp_v![&j1;"$[1]",]); } #[test] fn root_test() { let js = serde_json::from_str(template_json()).unwrap(); - test(template_json(), "$", json_path_value![&js,]); + test(template_json(), "$", jp_v![&js;"$",]); } #[test] @@ -522,7 +582,11 @@ mod tests { test( template_json(), "$..category", - json_path_value![&v1, &v2, &v2, &v2,], + jp_v![ + &v1;"$.['store'].['book'][0].['category']", + &v2;"$.['store'].['book'][1].['category']", + &v2;"$.['store'].['book'][2].['category']", + &v2;"$.['store'].['book'][3].['category']",], ); let js1 = json!(19.95); let js2 = json!(8.95); @@ -532,7 +596,13 @@ mod tests { test( template_json(), "$.store..price", - json_path_value![&js1, &js2, &js3, &js4, &js5,], + jp_v![ + &js1;"$.['store'].['bicycle'].['price']", + &js2;"$.['store'].['book'][0].['price']", + &js3;"$.['store'].['book'][1].['price']", + &js4;"$.['store'].['book'][2].['price']", + &js5;"$.['store'].['book'][3].['price']", + ], ); let js1 = json!("Nigel Rees"); let js2 = json!("Evelyn Waugh"); @@ -541,7 +611,11 @@ mod tests { test( template_json(), "$..author", - json_path_value![&js1, &js2, &js3, &js4,], + jp_v![ + &js1;"$.['store'].['book'][0].['author']", + &js2;"$.['store'].['book'][1].['author']", + &js3;"$.['store'].['book'][2].['author']", + &js4;"$.['store'].['book'][3].['author']",], ); } @@ -552,7 +626,11 @@ mod tests { test( template_json(), "$..book.[*].category", - json_path_value![&js1, &js2, &js2, &js2,], + jp_v![ + &js1;"$.['store'].['book'][0].['category']", + &js2;"$.['store'].['book'][1].['category']", + &js2;"$.['store'].['book'][2].['category']", + &js2;"$.['store'].['book'][3].['category']",], ); let js1 = json!("Nigel Rees"); let js2 = json!("Evelyn Waugh"); @@ -561,7 +639,11 @@ mod tests { test( template_json(), "$.store.book[*].author", - json_path_value![&js1, &js2, &js3, &js4,], + jp_v![ + &js1;"$.['store'].['book'][0].['author']", + &js2;"$.['store'].['book'][1].['author']", + &js3;"$.['store'].['book'][2].['author']", + &js4;"$.['store'].['book'][3].['author']",], ); } @@ -572,7 +654,11 @@ mod tests { test( template_json(), "$..*.[?(@.isbn)].title", - json_path_value![&js1, &js2, &js1, &js2], + jp_v![ + &js1;"$.['store'].['book'][2].['title']", + &js2;"$.['store'].['book'][3].['title']", + &js1;"$.['store'].['book'][2].['title']", + &js2;"$.['store'].['book'][3].['title']"], ); } @@ -582,7 +668,7 @@ mod tests { test( r#"{"field":{"field":[{"active":1},{"passive":1}]}}"#, "$.field.field[?(@.active)]", - json_path_value![&value,], + jp_v![&value;"$.['field'].['field'][0]",], ); } @@ -592,7 +678,7 @@ mod tests { test( template_json(), "$..book[2].isbn", - json_path_value![&value,], + jp_v![&value;"$.['store'].['book'][2].['isbn']",], ); } @@ -602,13 +688,13 @@ mod tests { test( template_json(), "$..book[2,4].isbn", - json_path_value![&value,], + jp_v![&value;"$.['store'].['book'][2].['isbn']",], ); let value1 = json!("0-395-19395-8"); test( template_json(), "$..book[2,3].isbn", - json_path_value![&value, &value1,], + jp_v![&value;"$.['store'].['book'][2].['isbn']", &value1;"$.['store'].['book'][3].['isbn']",], ); } @@ -621,12 +707,27 @@ mod tests { test( template_json(), "$..book[2,3]['title','price']", - json_path_value![&js1, &js2, &js3, &js4,], + jp_v![ + &js1;"$.['store'].['book'][2].['title']", + &js2;"$.['store'].['book'][2].['price']", + &js3;"$.['store'].['book'][3].['title']", + &js4;"$.['store'].['book'][3].['price']",], ); } #[test] fn index_slice_test() { + let i0 = "$.['array'][0]"; + let i1 = "$.['array'][1]"; + let i2 = "$.['array'][2]"; + let i3 = "$.['array'][3]"; + let i4 = "$.['array'][4]"; + let i5 = "$.['array'][5]"; + let i6 = "$.['array'][6]"; + let i7 = "$.['array'][7]"; + let i8 = "$.['array'][8]"; + let i9 = "$.['array'][9]"; + let j0 = json!(0); let j1 = json!(1); let j2 = json!(2); @@ -640,20 +741,26 @@ mod tests { test( template_json(), "$.array[:]", - json_path_value![&j0, &j1, &j2, &j3, &j4, &j5, &j6, &j7, &j8, &j9,], - ); - test( - template_json(), - "$.array[1:4:2]", - json_path_value![&j1, &j3,], + jp_v![ + &j0;&i0, + &j1;&i1, + &j2;&i2, + &j3;&i3, + &j4;&i4, + &j5;&i5, + &j6;&i6, + &j7;&i7, + &j8;&i8, + &j9;&i9,], ); + test(template_json(), "$.array[1:4:2]", jp_v![&j1;&i1, &j3;&i3,]); test( template_json(), "$.array[::3]", - json_path_value![&j0, &j3, &j6, &j9,], + jp_v![&j0;&i0, &j3;&i3, &j6;&i6, &j9;&i9,], ); - test(template_json(), "$.array[-1:]", json_path_value![&j9,]); - test(template_json(), "$.array[-2:-1]", json_path_value![&j8,]); + test(template_json(), "$.array[-1:]", jp_v![&j9;&i9,]); + test(template_json(), "$.array[-2:-1]", jp_v![&j8;&i8,]); } #[test] @@ -663,25 +770,30 @@ mod tests { test( template_json(), "$..book[?(@.isbn)].title", - json_path_value![&moby, &rings,], + jp_v![ + &moby;"$.['store'].['book'][2].['title']", + &rings;"$.['store'].['book'][3].['title']",], ); let sword = json!("Sword of Honour"); test( template_json(), "$..book[?(@.price != 8.95)].title", - json_path_value![&sword, &moby, &rings,], + jp_v![ + &sword;"$.['store'].['book'][1].['title']", + &moby;"$.['store'].['book'][2].['title']", + &rings;"$.['store'].['book'][3].['title']",], ); let sayings = json!("Sayings of the Century"); test( template_json(), "$..book[?(@.price == 8.95)].title", - json_path_value![&sayings,], + jp_v![&sayings;"$.['store'].['book'][0].['title']",], ); let js895 = json!(8.95); test( template_json(), "$..book[?(@.author ~= '.*Rees')].price", - json_path_value![&js895,], + jp_v![&js895;"$.['store'].['book'][0].['price']",], ); let js12 = json!(12.99); let js899 = json!(8.99); @@ -689,59 +801,77 @@ mod tests { test( template_json(), "$..book[?(@.price >= 8.99)].price", - json_path_value![&js12, &js899, &js2299,], + jp_v![ + &js12;"$.['store'].['book'][1].['price']", + &js899;"$.['store'].['book'][2].['price']", + &js2299;"$.['store'].['book'][3].['price']", + ], ); test( template_json(), "$..book[?(@.price > 8.99)].price", - json_path_value![&js12, &js2299,], + jp_v![ + &js12;"$.['store'].['book'][1].['price']", + &js2299;"$.['store'].['book'][3].['price']",], ); test( template_json(), "$..book[?(@.price < 8.99)].price", - json_path_value![&js895,], + jp_v![&js895;"$.['store'].['book'][0].['price']",], ); test( template_json(), "$..book[?(@.price <= 8.99)].price", - json_path_value![&js895, &js899,], + jp_v![ + &js895;"$.['store'].['book'][0].['price']", + &js899;"$.['store'].['book'][2].['price']", + ], ); test( template_json(), "$..book[?(@.price <= $.expensive)].price", - json_path_value![&js895, &js899,], + jp_v![ + &js895;"$.['store'].['book'][0].['price']", + &js899;"$.['store'].['book'][2].['price']", + ], ); test( template_json(), "$..book[?(@.price >= $.expensive)].price", - json_path_value![&js12, &js2299,], + jp_v![ + &js12;"$.['store'].['book'][1].['price']", + &js2299;"$.['store'].['book'][3].['price']", + ], ); test( template_json(), "$..book[?(@.title in ['Moby Dick','Shmoby Dick','Big Dick','Dicks'])].price", - json_path_value![&js899,], + jp_v![&js899;"$.['store'].['book'][2].['price']",], ); test( template_json(), "$..book[?(@.title nin ['Moby Dick','Shmoby Dick','Big Dick','Dicks'])].title", - json_path_value![&sayings, &sword, &rings,], + jp_v![ + &sayings;"$.['store'].['book'][0].['title']", + &sword;"$.['store'].['book'][1].['title']", + &rings;"$.['store'].['book'][3].['title']",], ); test( template_json(), "$..book[?(@.author size 10)].title", - json_path_value![&sayings,], + jp_v![&sayings;"$.['store'].['book'][0].['title']",], ); let filled_true = json!(1); test( template_json(), "$.orders[?(@.filled == true)].id", - json_path_value![&filled_true,], + jp_v![&filled_true;"$.['orders'][0].['id']",], ); let filled_null = json!(3); test( template_json(), "$.orders[?(@.filled == null)].id", - json_path_value![&filled_null,], + jp_v![&filled_null;"$.['orders'][2].['id']",], ); } @@ -751,19 +881,19 @@ mod tests { test( template_json(), "$.orders[?(@.ref subsetOf [1,2,3,4])].id", - json_path_value![&j1,], + jp_v![&j1;"$.['orders'][0].['id']",], ); let j2 = json!(2); test( template_json(), "$.orders[?(@.ref anyOf [1,4])].id", - json_path_value![&j1, &j2,], + jp_v![&j1;"$.['orders'][0].['id']", &j2;"$.['orders'][1].['id']",], ); let j3 = json!(3); test( template_json(), "$.orders[?(@.ref noneOf [3,6])].id", - json_path_value![&j3,], + jp_v![&j3;"$.['orders'][2].['id']",], ); } @@ -794,7 +924,7 @@ mod tests { let v = finder.find_slice(); let js = json!("Sayings of the Century"); - assert_eq!(v, json_path_value![&js,]); + assert_eq!(v, jp_v![&js;"$.['store'].['book'][0].['title']",]); } #[test] @@ -807,7 +937,7 @@ mod tests { let v = finder.find_slice(); let js = json!({"verb":"TEST"}); - assert_eq!(v, json_path_value![&js,]); + assert_eq!(v, jp_v![&js;"$[0]",]); } #[test] @@ -989,7 +1119,10 @@ mod tests { Box::from(JsonPathInst::from_str("$..f_").expect("the path is correct")); let finder = JsonPathFinder::new(json, path); let v = finder.find_slice(); - assert_eq!(v, vec![Slice(&json!(1))]); + assert_eq!( + v, + vec![Slice(&json!(1), "$.['field'][1].['f'].['f_']".to_string())] + ); } #[test] diff --git a/src/path/index.rs b/src/path/index.rs index 6ec7262..f371788 100644 --- a/src/path/index.rs +++ b/src/path/index.rs @@ -1,3 +1,4 @@ +use crate::jsp_idx; use crate::parser::model::{FilterExpression, FilterSign, JsonPath}; use crate::path::json::*; use crate::path::top::ObjectField; @@ -51,9 +52,9 @@ impl ArraySlice { } } - fn process<'a, T>(&self, elements: &'a [T]) -> Vec<&'a T> { + fn process<'a, T>(&self, elements: &'a [T]) -> Vec<(&'a T, usize)> { let len = elements.len() as i32; - let mut filtered_elems: Vec<&T> = vec![]; + let mut filtered_elems: Vec<(&'a T, usize)> = vec![]; match (self.start(len), self.end(len)) { (Some(start_idx), Some(end_idx)) => { let end_idx = if end_idx == 0 { @@ -63,7 +64,7 @@ impl ArraySlice { }; for idx in (start_idx..end_idx).step_by(self.step) { if let Some(v) = elements.get(idx) { - filtered_elems.push(v) + filtered_elems.push((v, idx)) } } filtered_elems @@ -77,13 +78,14 @@ impl<'a> Path<'a> for ArraySlice { type Data = Value; fn find(&self, input: JsonPathValue<'a, Self::Data>) -> Vec> { - input.flat_map_slice(|data| { + input.flat_map_slice(|data, pref| { data.as_array() .map(|elems| self.process(elems)) .and_then(|v| { if v.is_empty() { None } else { + let v = v.into_iter().map(|(e, i)| (e, jsp_idx(&pref, i))).collect(); Some(JsonPathValue::map_vec(v)) } }) @@ -107,10 +109,10 @@ impl<'a> Path<'a> for ArrayIndex { type Data = Value; fn find(&self, input: JsonPathValue<'a, Self::Data>) -> Vec> { - input.flat_map_slice(|data| { + input.flat_map_slice(|data, pref| { data.as_array() .and_then(|elems| elems.get(self.index)) - .map(|e| vec![e.into()]) + .map(|e| vec![JsonPathValue::new_slice(e, jsp_idx(&pref, self.index))]) .unwrap_or_else(|| vec![NoValue]) }) } @@ -239,67 +241,70 @@ impl<'a> FilterPath<'a> { ) -> bool { match op { FilterSign::Equal => eq( - JsonPathValue::into_data(left), - JsonPathValue::into_data(right), + JsonPathValue::vec_as_data(left), + JsonPathValue::vec_as_data(right), ), FilterSign::Unequal => !FilterPath::process_atom(&FilterSign::Equal, left, right), FilterSign::Less => less( - JsonPathValue::into_data(left), - JsonPathValue::into_data(right), + JsonPathValue::vec_as_data(left), + JsonPathValue::vec_as_data(right), ), FilterSign::LeOrEq => { FilterPath::compound(&FilterSign::Less, &FilterSign::Equal, left, right) } FilterSign::Greater => less( - JsonPathValue::into_data(right), - JsonPathValue::into_data(left), + JsonPathValue::vec_as_data(right), + JsonPathValue::vec_as_data(left), ), FilterSign::GrOrEq => { FilterPath::compound(&FilterSign::Greater, &FilterSign::Equal, left, right) } FilterSign::Regex => regex( - JsonPathValue::into_data(left), - JsonPathValue::into_data(right), + JsonPathValue::vec_as_data(left), + JsonPathValue::vec_as_data(right), ), FilterSign::In => inside( - JsonPathValue::into_data(left), - JsonPathValue::into_data(right), + JsonPathValue::vec_as_data(left), + JsonPathValue::vec_as_data(right), ), FilterSign::Nin => !FilterPath::process_atom(&FilterSign::In, left, right), FilterSign::NoneOf => !FilterPath::process_atom(&FilterSign::AnyOf, left, right), FilterSign::AnyOf => any_of( - JsonPathValue::into_data(left), - JsonPathValue::into_data(right), + JsonPathValue::vec_as_data(left), + JsonPathValue::vec_as_data(right), ), FilterSign::SubSetOf => sub_set_of( - JsonPathValue::into_data(left), - JsonPathValue::into_data(right), + JsonPathValue::vec_as_data(left), + JsonPathValue::vec_as_data(right), ), - FilterSign::Exists => !JsonPathValue::into_data(left).is_empty(), + FilterSign::Exists => !JsonPathValue::vec_as_data(left).is_empty(), FilterSign::Size => size( - JsonPathValue::into_data(left), - JsonPathValue::into_data(right), + JsonPathValue::vec_as_data(left), + JsonPathValue::vec_as_data(right), ), } } fn process(&self, curr_el: &'a Value) -> bool { + let pref = String::new(); match self { - FilterPath::Filter { left, right, op } => { - FilterPath::process_atom(op, left.find(Slice(curr_el)), right.find(Slice(curr_el))) - } + FilterPath::Filter { left, right, op } => FilterPath::process_atom( + op, + left.find(Slice(curr_el, pref.clone())), + right.find(Slice(curr_el, pref)), + ), FilterPath::Or { left, right } => { - if !JsonPathValue::into_data(left.find(Slice(curr_el))).is_empty() { + if !JsonPathValue::vec_as_data(left.find(Slice(curr_el, pref.clone()))).is_empty() { true } else { - !JsonPathValue::into_data(right.find(Slice(curr_el))).is_empty() + !JsonPathValue::vec_as_data(right.find(Slice(curr_el, pref))).is_empty() } } FilterPath::And { left, right } => { - if JsonPathValue::into_data(left.find(Slice(curr_el))).is_empty() { + if JsonPathValue::vec_as_data(left.find(Slice(curr_el, pref.clone()))).is_empty() { false } else { - !JsonPathValue::into_data(right.find(Slice(curr_el))).is_empty() + !JsonPathValue::vec_as_data(right.find(Slice(curr_el, pref))).is_empty() } } } @@ -310,19 +315,19 @@ impl<'a> Path<'a> for FilterPath<'a> { type Data = Value; fn find(&self, input: JsonPathValue<'a, Self::Data>) -> Vec> { - input.flat_map_slice(|data| { + input.flat_map_slice(|data, pref| { let mut res = vec![]; match data { Array(elems) => { - for el in elems.iter() { + for (i, el) in elems.iter().enumerate() { if self.process(el) { - res.push(Slice(el)) + res.push(Slice(el, jsp_idx(&pref, i))) } } } el => { if self.process(el) { - res.push(Slice(el)) + res.push(Slice(el, pref)) } } } @@ -342,7 +347,7 @@ mod tests { use crate::path::JsonPathValue; use crate::path::{json_path_instance, Path}; use crate::JsonPathValue::NoValue; - use crate::{chain, filter, idx, json_path_value, op, path}; + use crate::{chain, filter, idx, jp_v, op, path}; use serde_json::json; #[test] @@ -392,20 +397,23 @@ mod tests { let j1 = json!(0); let j2 = json!(2); let j4 = json!(4); - assert_eq!(slice.find((&array).into()), json_path_value![&j1, &j2, &j4]); + assert_eq!( + slice.find(JsonPathValue::new_slice(&array, "a".to_string())), + jp_v![&j1;"a[0]", &j2;"a[2]", &j4;"a[4]"] + ); slice.step = 3; let j0 = json!(0); let j3 = json!(3); - assert_eq!( - slice.find(json_path_value!(&array)), - json_path_value![&j0, &j3] - ); + assert_eq!(slice.find(jp_v!(&array)), jp_v![&j0;"[0]", &j3;"[3]"]); slice.start_index = -1; slice.end_index = 1; - assert_eq!(slice.find((&array).into()), vec![NoValue]); + assert_eq!( + slice.find(JsonPathValue::new_slice(&array, "a".to_string())), + vec![NoValue] + ); slice.start_index = -10; slice.end_index = 10; @@ -414,7 +422,10 @@ mod tests { let j4 = json!(4); let j7 = json!(7); - assert_eq!(slice.find((&array).into()), json_path_value![&j1, &j4, &j7]); + assert_eq!( + slice.find(JsonPathValue::new_slice(&array, "a".to_string())), + jp_v![&j1;"a[1]", &j4;"a[4]", &j7;"a[7]"] + ); } #[test] @@ -424,11 +435,20 @@ mod tests { let mut index = ArrayIndex::new(0); let j0 = json!(0); let j10 = json!(10); - assert_eq!(index.find((&array).into()), json_path_value![&j0,]); + assert_eq!( + index.find(JsonPathValue::new_slice(&array, "a".to_string())), + jp_v![&j0;"a[0]",] + ); index.index = 10; - assert_eq!(index.find((&array).into()), json_path_value![&j10,]); + assert_eq!( + index.find(JsonPathValue::new_slice(&array, "a".to_string())), + jp_v![&j10;"a[10]",] + ); index.index = 100; - assert_eq!(index.find((&array).into()), vec![NoValue]); + assert_eq!( + index.find(JsonPathValue::new_slice(&array, "a".to_string())), + vec![NoValue] + ); } #[test] @@ -452,8 +472,8 @@ mod tests { "field_3":{"a":"b"} }); - let expected_res = vec![json_path_value!(&res)]; - assert_eq!(path_inst.find(json_path_value!(&json)), expected_res); + let expected_res = jp_v!(&res;"$.['object']",); + assert_eq!(path_inst.find(jp_v!(&json)), expected_res); let cur = path!(@,path!("field_3"),path!("a")); let chain = chain!(path!($), path!("object"), cur); @@ -461,8 +481,11 @@ mod tests { let path_inst = json_path_instance(&chain, &json); let res1 = json!("b"); - let expected_res = vec![(&res1).into()]; - assert_eq!(path_inst.find(json_path_value!(&json)), expected_res); + let expected_res = vec![JsonPathValue::new_slice( + &res1, + "$.['object'].['field_3'].['a']".to_string(), + )]; + assert_eq!(path_inst.find(jp_v!(&json)), expected_res); } #[test] @@ -479,8 +502,8 @@ mod tests { let exp1 = json!([1, 2, 3, 4, 5]); let exp2 = json!(42); - let expected_res = vec![json_path_value!(&exp1), json_path_value!(&exp2)]; - assert_eq!(path_inst.find(json_path_value!(&json)), expected_res) + let expected_res = jp_v!(&exp1;"$.['key'][0].['field']",&exp2;"$.['key'][1].['field']"); + assert_eq!(path_inst.find(jp_v!(&json)), expected_res) } #[test] @@ -495,42 +518,68 @@ mod tests { {"field":1}, ] }); - let exp1 = json!( {"field":10}); - let exp2 = json!( {"field":5}); + let _exp1 = json!( {"field":10}); + let _exp2 = json!( {"field":5}); let exp3 = json!( {"field":4}); let exp4 = json!( {"field":1}); let index = path!( idx!(?filter!(op!(path!(@, path!("field"))), ">", op!(chain!(path!($), path!("threshold"))))) ); + let chain = chain!(path!($), path!("key"), index); + let path_inst = json_path_instance(&chain, &json); - let expected_res = json_path_value![&exp1, &exp2]; - assert_eq!(path_inst.find((&json).into()), expected_res); + + let exp1 = json!( {"field":10}); + let exp2 = json!( {"field":5}); + let expected_res = jp_v![&exp1;"$.['key'][1]", &exp2;"$.['key'][3]"]; + assert_eq!( + path_inst.find(JsonPathValue::from_root(&json)), + expected_res + ); + let expected_res = jp_v![&exp1;"$.['key'][1]", &exp2;"$.['key'][3]"]; + assert_eq!( + path_inst.find(JsonPathValue::from_root(&json)), + expected_res + ); let index = path!( idx!(?filter!(op!(path!(@, path!("field"))), ">=", op!(chain!(path!($), path!("threshold"))))) ); let chain = chain!(path!($), path!("key"), index); let path_inst = json_path_instance(&chain, &json); - let expected_res = json_path_value![&exp1, &exp3, &exp2]; - assert_eq!(path_inst.find((&json).into()), expected_res); + let expected_res = jp_v![ + &exp1;"$.['key'][1]", &exp3;"$.['key'][2]", &exp2;"$.['key'][3]"]; + assert_eq!( + path_inst.find(JsonPathValue::from_root(&json)), + expected_res + ); let index = path!( idx!(?filter!(op!(path!(@, path!("field"))), "<", op!(chain!(path!($), path!("threshold"))))) ); let chain = chain!(path!($), path!("key"), index); let path_inst = json_path_instance(&chain, &json); - let expected_res = json_path_value![&exp4, &exp4]; - assert_eq!(path_inst.find((&json).into()), expected_res); + let expected_res = jp_v![&exp4;"$.['key'][0]", &exp4;"$.['key'][4]"]; + assert_eq!( + path_inst.find(JsonPathValue::from_root(&json)), + expected_res + ); let index = path!( idx!(?filter!(op!(path!(@, path!("field"))), "<=", op!(chain!(path!($), path!("threshold"))))) ); let chain = chain!(path!($), path!("key"), index); let path_inst = json_path_instance(&chain, &json); - let expected_res = json_path_value![&exp4, &exp3, &exp4]; - assert_eq!(path_inst.find((&json).into()), expected_res); + let expected_res = jp_v![ + &exp4;"$.['key'][0]", + &exp3;"$.['key'][2]", + &exp4;"$.['key'][4]"]; + assert_eq!( + path_inst.find(JsonPathValue::from_root(&json)), + expected_res + ); } #[test] @@ -550,8 +599,11 @@ mod tests { let path_inst = json_path_instance(&chain, &json); let exp2 = json!( {"field":"a1#1"}); - let expected_res = json_path_value![&exp2,]; - assert_eq!(path_inst.find((&json).into()), expected_res) + let expected_res = jp_v![&exp2;"$.['key'][1]",]; + assert_eq!( + path_inst.find(JsonPathValue::from_root(&json)), + expected_res + ) } #[test] @@ -576,8 +628,11 @@ mod tests { let path_inst = json_path_instance(&chain, &json); let exp2 = json!( {"field":"a11#"}); - let expected_res = json_path_value![&exp2,]; - assert_eq!(path_inst.find((&json).into()), expected_res) + let expected_res = jp_v![&exp2;"$.['key'][0]",]; + assert_eq!( + path_inst.find(JsonPathValue::from_root(&json)), + expected_res + ) } #[test] @@ -600,8 +655,11 @@ mod tests { let f2 = json!( {"field":"dddd"}); let f3 = json!( {"field":[1,1,1,1]}); - let expected_res = json_path_value![&f1, &f2, &f3]; - assert_eq!(path_inst.find((&json).into()), expected_res) + let expected_res = jp_v![&f1;"$.['key'][0]", &f2;"$.['key'][3]", &f3;"$.['key'][4]"]; + assert_eq!( + path_inst.find(JsonPathValue::from_root(&json)), + expected_res + ) } #[test] @@ -623,7 +681,10 @@ mod tests { "not_id": 2, "more_then_id" :3 }); - assert_eq!(path_inst.find((&json).into()), json_path_value![&js,]) + assert_eq!( + path_inst.find(JsonPathValue::from_root(&json)), + jp_v![&js;"$.['obj']",] + ) } #[test] @@ -651,8 +712,11 @@ mod tests { let d = json!("Dortmund"); let dd = json!("Dublin"); assert_eq!( - path_inst.find((&json).into()), - json_path_value![&a, &d, &dd] + path_inst.find(JsonPathValue::from_root(&json)), + jp_v![ + &a;"$.['key'][4].['city']", + &d;"$.['key'][5].['city']", + ⅆ"$.['key'][6].['city']"] ) } @@ -674,7 +738,10 @@ mod tests { let chain = chain!(path!($), path!("key"), path!(index), path!("id")); let path_inst = json_path_instance(&chain, &json); let j1 = json!(1); - assert_eq!(path_inst.find((&json).into()), json_path_value![&j1,]) + assert_eq!( + path_inst.find(JsonPathValue::from_root(&json)), + jp_v![&j1;"$.['key'].['id']",] + ) } #[test] @@ -695,7 +762,10 @@ mod tests { let chain = chain!(path!($), path!("key"), path!(index), path!("id")); let path_inst = json_path_instance(&chain, &json); let j1 = json!(1); - assert_eq!(path_inst.find((&json).into()), json_path_value![&j1,]) + assert_eq!( + path_inst.find(JsonPathValue::from_root(&json)), + jp_v![&j1;"$.['key'].['id']",] + ) } #[test] @@ -720,7 +790,8 @@ mod tests { let chain = chain!(path!($), path!("key"), path!(index), path!("city")); let path_inst = json_path_instance(&chain, &json); let a = json!("Athlon"); - assert_eq!(path_inst.find((&json).into()), json_path_value![&a,]) + let value = jp_v!( &a;"$.['key'][4].['city']",); + assert_eq!(path_inst.find(JsonPathValue::from_root(&json)), value) } #[test] @@ -741,7 +812,10 @@ mod tests { let chain = chain!(path!($), path!("key"), path!(index), path!("id")); let path_inst = json_path_instance(&chain, &json); let j1 = json!(1); - assert_eq!(path_inst.find((&json).into()), json_path_value![&j1,]) + assert_eq!( + path_inst.find(JsonPathValue::from_root(&json)), + jp_v![&j1; "$.['key'].['id']",] + ) } #[test] @@ -761,6 +835,9 @@ mod tests { ); let chain = chain!(path!($), path!("key"), path!(index), path!("id")); let path_inst = json_path_instance(&chain, &json); - assert_eq!(path_inst.find((&json).into()), vec![NoValue]) + assert_eq!( + path_inst.find(JsonPathValue::from_root(&json)), + vec![NoValue] + ) } } diff --git a/src/path/top.rs b/src/path/top.rs index 4981a64..090ae38 100644 --- a/src/path/top.rs +++ b/src/path/top.rs @@ -1,6 +1,7 @@ use crate::parser::model::*; use crate::path::{json_path_instance, JsonPathValue, Path, PathInstance}; use crate::JsonPathValue::{NewValue, NoValue, Slice}; +use crate::{jsp_idx, jsp_obj, JsPathStr}; use serde_json::value::Value::{Array, Object}; use serde_json::{json, Value}; @@ -11,20 +12,20 @@ impl<'a> Path<'a> for Wildcard { type Data = Value; fn find(&self, data: JsonPathValue<'a, Self::Data>) -> Vec> { - data.flat_map_slice(|data| { + data.flat_map_slice(|data, pref| { let res = match data { Array(elems) => { let mut res = vec![]; - for el in elems.iter() { - res.push(Slice(el)); + for (idx, el) in elems.iter().enumerate() { + res.push(Slice(el, jsp_idx(&pref, idx))); } res } Object(elems) => { let mut res = vec![]; - for el in elems.values() { - res.push(Slice(el)); + for (key, el) in elems.into_iter() { + res.push(Slice(el, jsp_obj(&pref, key))); } res } @@ -75,7 +76,7 @@ impl<'a> Path<'a> for RootPointer<'a, Value> { type Data = Value; fn find(&self, _data: JsonPathValue<'a, Self::Data>) -> Vec> { - vec![Slice(self.root)] + vec![JsonPathValue::from_root(self.root)] } } @@ -108,7 +109,6 @@ impl<'a> Path<'a> for FnPath { if JsonPathValue::only_no_value(&input) { return vec![NoValue]; } - let res = if is_search_length { NewValue(json!(input.iter().filter(|v| v.has_value()).count())) } else { @@ -120,7 +120,7 @@ impl<'a> Path<'a> for FnPath { match input.get(0) { Some(v) => match v { NewValue(d) => take_len(d), - Slice(s) => take_len(s), + Slice(s, _) => take_len(s), NoValue => NoValue, }, None => NoValue, @@ -148,7 +148,9 @@ impl<'a> Path<'a> for ObjectField<'a> { }; let res = match data { - Slice(js) => take_field(js).map(Slice).unwrap_or_else(|| NoValue), + Slice(js, p) => take_field(js) + .map(|v| JsonPathValue::new_slice(v, jsp_obj(&p, self.key))) + .unwrap_or_else(|| NoValue), _ => NoValue, }; vec![res] @@ -166,19 +168,21 @@ impl<'a> Path<'a> for DescentWildcard { } // todo rewrite to tail rec -fn deep_flatten(data: &Value) -> Vec<&Value> { +fn deep_flatten(data: &Value, pref: JsPathStr) -> Vec<(&Value, JsPathStr)> { let mut acc = vec![]; match data { Object(elems) => { - for v in elems.values() { - acc.push(v); - acc.append(&mut deep_flatten(v)); + for (f, v) in elems.into_iter() { + let pref = jsp_obj(&pref, f); + acc.push((v, pref.clone())); + acc.append(&mut deep_flatten(v, pref)); } } Array(elems) => { - for v in elems.iter() { - acc.push(v); - acc.append(&mut deep_flatten(v)); + for (i, v) in elems.iter().enumerate() { + let pref = jsp_idx(&pref, i); + acc.push((v, pref.clone())); + acc.append(&mut deep_flatten(v, pref)); } } _ => (), @@ -187,26 +191,32 @@ fn deep_flatten(data: &Value) -> Vec<&Value> { } // todo rewrite to tail rec -fn deep_path_by_key<'a>(data: &'a Value, key: ObjectField<'a>) -> Vec<&'a Value> { - let mut level: Vec<&Value> = JsonPathValue::into_data(key.find(data.into())); +fn deep_path_by_key<'a>( + data: &'a Value, + key: ObjectField<'a>, + pref: JsPathStr, +) -> Vec<(&'a Value, JsPathStr)> { + let mut result: Vec<(&'a Value, JsPathStr)> = + JsonPathValue::vec_as_pair(key.find(JsonPathValue::new_slice(data, pref.clone()))); match data { Object(elems) => { - let mut next_levels: Vec<&Value> = elems - .values() - .flat_map(|v| deep_path_by_key(v, key.clone())) + let mut next_levels: Vec<(&'a Value, JsPathStr)> = elems + .into_iter() + .flat_map(|(k, v)| deep_path_by_key(v, key.clone(), jsp_obj(&pref, k))) .collect(); - level.append(&mut next_levels); - level + result.append(&mut next_levels); + result } Array(elems) => { - let mut next_levels: Vec<&Value> = elems + let mut next_levels: Vec<(&'a Value, JsPathStr)> = elems .iter() - .flat_map(|v| deep_path_by_key(v, key.clone())) + .enumerate() + .flat_map(|(i, v)| deep_path_by_key(v, key.clone(), jsp_idx(&pref, i))) .collect(); - level.append(&mut next_levels); - level + result.append(&mut next_levels); + result } - _ => level, + _ => result, } } @@ -219,8 +229,8 @@ impl<'a> Path<'a> for DescentObject<'a> { type Data = Value; fn find(&self, data: JsonPathValue<'a, Self::Data>) -> Vec> { - data.flat_map_slice(|data| { - let res_col = deep_path_by_key(data, ObjectField::new(self.key)); + data.flat_map_slice(|data, pref| { + let res_col = deep_path_by_key(data, ObjectField::new(self.key), pref.clone()); if res_col.is_empty() { vec![NoValue] } else { @@ -318,23 +328,26 @@ mod tests { use crate::path::top::{deep_flatten, json_path_instance, Function, ObjectField, RootPointer}; use crate::path::{JsonPathValue, Path}; use crate::JsonPathValue::NoValue; - use crate::{chain, function, idx, json_path_value, path}; + use crate::{chain, function, idx, jp_v, path}; use serde_json::json; use serde_json::Value; #[test] fn object_test() { let js = json!({"product": {"key":42}}); - let res_income = json_path_value!(&js); + let res_income = jp_v!(&js); let key = String::from("product"); let mut field = ObjectField::new(&key); let js = json!({"key":42}); - assert_eq!(field.find(res_income.clone()), vec![json_path_value!(&js)]); + assert_eq!( + field.find(res_income.clone()), + vec![jp_v!(&js;".['product']")] + ); let key = String::from("fake"); field.key = &key; - assert!(field.find(res_income) == vec![NoValue]); + assert_eq!(field.find(res_income), vec![NoValue]); } #[test] @@ -343,10 +356,7 @@ mod tests { let root = RootPointer::::new(&res_income); - assert_eq!( - root.find(json_path_value!(&res_income)), - vec![json_path_value!(&res_income)] - ) + assert_eq!(root.find(jp_v!(&res_income)), jp_v!(&res_income;"$",)) } #[test] @@ -359,26 +369,20 @@ mod tests { let field5 = path!("object"); let path_inst = json_path_instance(&path!($), &json); - assert_eq!( - path_inst.find(json_path_value!(&json)), - vec![json_path_value!(&json)] - ); + assert_eq!(path_inst.find(jp_v!(&json)), jp_v!(&json;"$",)); let path_inst = json_path_instance(&field1, &json); let exp_json = json!({"k":{"f":42,"array":[0,1,2,3,4,5],"object":{"field1":"val1","field2":"val2"}}}); - assert_eq!( - path_inst.find(json_path_value!(&json)), - vec![json_path_value!(&exp_json)] - ); + assert_eq!(path_inst.find(jp_v!(&json)), jp_v!(&exp_json;".['v']",)); let chain = chain!(path!($), field1.clone(), field2.clone(), field3); let path_inst = json_path_instance(&chain, &json); let exp_json = json!(42); assert_eq!( - path_inst.find(json_path_value!(&json)), - vec![json_path_value!(&exp_json)] + path_inst.find(jp_v!(&json)), + jp_v!(&exp_json;"$.['v'].['k'].['f']",) ); let chain = chain!( @@ -391,8 +395,8 @@ mod tests { let path_inst = json_path_instance(&chain, &json); let exp_json = json!(3); assert_eq!( - path_inst.find(json_path_value!(&json)), - vec![json_path_value!(&exp_json)] + path_inst.find(jp_v!(&json)), + jp_v!(&exp_json;"$.['v'].['k'].['array'][3]",) ); let index = idx!([1;-1;2]); @@ -407,8 +411,8 @@ mod tests { let one = json!(1); let tree = json!(3); assert_eq!( - path_inst.find(json_path_value!(&json)), - vec![json_path_value!(&one), json_path_value!(&tree)] + path_inst.find(jp_v!(&json)), + jp_v!(&one;"$.['v'].['k'].['array'][1]", &tree;"$.['v'].['k'].['array'][3]") ); let union = idx!(idx 1,2 ); @@ -423,8 +427,8 @@ mod tests { let tree = json!(1); let two = json!(2); assert_eq!( - path_inst.find(json_path_value!(&json)), - vec![json_path_value!(&tree), json_path_value!(&two)] + path_inst.find(jp_v!(&json)), + jp_v!(&tree;"$.['v'].['k'].['array'][1]",&two;"$.['v'].['k'].['array'][2]") ); let union = idx!("field1", "field2"); @@ -433,8 +437,10 @@ mod tests { let one = json!("val1"); let two = json!("val2"); assert_eq!( - path_inst.find(json_path_value!(&json)), - vec![json_path_value!(&one), json_path_value!(&two)] + path_inst.find(jp_v!(&json)), + jp_v!( + &one;"$.['v'].['k'].['object'].['field1']", + &two;"$.['v'].['k'].['object'].['field2']") ); } #[test] @@ -444,14 +450,14 @@ mod tests { let path_inst = json_path_instance(&chain, &json); let one = json!(1); - let expected_res = vec![json_path_value!(&one)]; - assert_eq!(path_inst.find(json_path_value!(&json)), expected_res) + let expected_res = jp_v!(&one;"$[0].['a']",); + assert_eq!(path_inst.find(jp_v!(&json)), expected_res) } #[test] fn deep_path_test() { let value = json!([1]); - let r = deep_flatten(&value); - assert_eq!(r, vec![&json!(1)]) + let r = deep_flatten(&value, "".to_string()); + assert_eq!(r, vec![(&json!(1), "[0]".to_string())]) } #[test] @@ -466,8 +472,8 @@ mod tests { let arr = json!([1]); let one = json!(1); - let expected_res = vec![json_path_value!(&arr), json_path_value!(&one)]; - assert_eq!(path_inst.find(json_path_value!(&json)), expected_res) + let expected_res = jp_v!(&arr;"$.['key1']",&one;"$.['key1'][0]"); + assert_eq!(path_inst.find(jp_v!(&json)), expected_res) } #[test] fn path_descent_w_nested_array_test() { @@ -484,13 +490,13 @@ mod tests { let one = json!(1); - let expected_res = vec![ - json_path_value!(&arr2), - json_path_value!(&obj), - json_path_value!(&one), - json_path_value!(&empty), - ]; - assert_eq!(path_inst.find(json_path_value!(&json)), expected_res) + let expected_res = jp_v!( + &arr2;"$.['key2']", + &obj;"$.['key2'][0]", + &one;"$.['key2'][0].['a']", + ∅"$.['key2'][1]" + ); + assert_eq!(path_inst.find(jp_v!(&json)), expected_res) } #[test] @@ -535,16 +541,16 @@ mod tests { }); let expected_res = vec![ - json_path_value!(&key1), - json_path_value!(&one), - json_path_value!(&key), - json_path_value!(&key_3), - json_path_value!(&key1_s), - json_path_value!(&key_sec), - json_path_value!(&key_th), - json_path_value!(&zero), + jp_v!(&key1;"$.['key1']"), + jp_v!(&one;"$.['key1'][0]"), + jp_v!(&key;"$.['key2']"), + jp_v!(&key_3;"$.['key3']"), + jp_v!(&key1_s;"$.['key3'].['key1']"), + jp_v!(&key_sec;"$.['key3'].['key2']"), + jp_v!(&key_th;"$.['key3'].['key2'].['key1']"), + jp_v!(&zero;"$.['key3'].['key2'].['key1'].['key1']"), ]; - assert_eq!(path_inst.find(json_path_value!(&json)), expected_res) + assert_eq!(path_inst.find(jp_v!(&json)), expected_res) } #[test] fn path_descent_test() { @@ -569,13 +575,13 @@ mod tests { let res3 = json!({"key1":0}); let res4 = json!(0); - let expected_res = vec![ - json_path_value!(&res1), - json_path_value!(&res2), - json_path_value!(&res3), - json_path_value!(&res4), - ]; - assert_eq!(path_inst.find(json_path_value!(&json)), expected_res) + let expected_res = jp_v!( + &res1;"$.['key1']", + &res2;"$.['key3'].['key1']", + &res3;"$.['key3'].['key2'].['key1']", + &res4;"$.['key3'].['key2'].['key1'].['key1']", + ); + assert_eq!(path_inst.find(jp_v!(&json)), expected_res) } #[test] @@ -593,12 +599,8 @@ mod tests { let res2 = json!("key"); let res3 = json!({}); - let expected_res = vec![ - json_path_value!(&res1), - json_path_value!(&res2), - json_path_value!(&res3), - ]; - assert_eq!(path_inst.find(json_path_value!(&json)), expected_res) + let expected_res = jp_v!(&res1;"$.['key1']", &res2;"$.['key2']", &res3;"$.['key3']"); + assert_eq!(path_inst.find(jp_v!(&json)), expected_res) } #[test] @@ -613,15 +615,15 @@ mod tests { let path_inst = json_path_instance(&chain, &json); assert_eq!( - path_inst.flat_find(vec![json_path_value!(&json)], true), - vec![json_path_value!(json!(3))] + path_inst.flat_find(vec![jp_v!(&json)], true), + vec![jp_v!(json!(3))] ); let chain = chain!(path!($), path!("key1"), function!(length)); let path_inst = json_path_instance(&chain, &json); assert_eq!( - path_inst.flat_find(vec![json_path_value!(&json)], false), - vec![json_path_value!(json!(3))] + path_inst.flat_find(vec![jp_v!(&json)], false), + vec![jp_v!(json!(3))] ); } }