From 538b433ae2ccbd882ff02fcff22ea16be779fb6f Mon Sep 17 00:00:00 2001 From: vms Date: Mon, 23 Nov 2020 07:45:26 +0300 Subject: [PATCH] support iterator --- src/lib.rs | 16 +- src/parser/mod.rs | 22 +- src/parser/tokenizer.rs | 87 +++---- src/select/cmp.rs | 1 - src/select/expr_term.rs | 9 +- src/select/mod.rs | 504 ++++++++++++++++++++++++++++--------- src/select/value_walker.rs | 37 +-- tests/array_filter.rs | 2 +- tests/filter.rs | 98 ++++---- tests/op.rs | 49 ++-- tests/paths.rs | 178 ++++++------- tests/return_type.rs | 8 +- tests/selector.rs | 62 ++++- 13 files changed, 677 insertions(+), 396 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index cb2e9ef..3cc7d6d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -305,6 +305,18 @@ pub fn select<'a>(json: &'a Value, path: &str) -> Result, JsonPat Selector::default().str_path(path)?.value(json).select() } +pub fn select_with_iter<'a>( + json: impl ExactSizeIterator + 'a, + path: &str, +) -> Result<(Vec<&'a Value>, Vec), JsonPathError> { + let mut selector = Selector::default(); + let json = selector.str_path(path)?.values_iter(json).select()?; + + let chose_indices = selector.chose_indices(); + + Ok((json, chose_indices)) +} + /// It is the same to `select` function but it return the result as string. /// /// ```rust @@ -377,7 +389,9 @@ pub fn select_as( path: &str, ) -> Result, JsonPathError> { let json = serde_json::from_str(json_str).map_err(|e| JsonPathError::Serde(e.to_string()))?; - Selector::default().str_path(path)?.value(&json).select_as() + let t = Selector::default().str_path(path)?.value(&json).select_as(); + + t } /// Delete(= replace with null) the JSON property using the jsonpath. diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 51db86d..b242e31 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), @@ -543,21 +543,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), }, - 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()) - } + _ => Err(tokenizer.err_msg()), } } diff --git a/src/parser/tokenizer.rs b/src/parser/tokenizer.rs index 39ce14d..d08e579 100644 --- a/src/parser/tokenizer.rs +++ b/src/parser/tokenizer.rs @@ -66,95 +66,95 @@ impl Token { match self { Token::Absolute(_) => match other { Token::Absolute(_) => true, - _ => false + _ => false, }, Token::Dot(_) => match other { Token::Dot(_) => true, - _ => false + _ => false, }, Token::At(_) => match other { Token::At(_) => true, - _ => false + _ => false, }, Token::OpenArray(_) => match other { Token::OpenArray(_) => true, - _ => false + _ => false, }, Token::CloseArray(_) => match other { Token::CloseArray(_) => true, - _ => false + _ => false, }, Token::Asterisk(_) => match other { Token::Asterisk(_) => true, - _ => false + _ => false, }, Token::Question(_) => match other { Token::Question(_) => true, - _ => false + _ => false, }, Token::Comma(_) => match other { Token::Comma(_) => true, - _ => false + _ => false, }, Token::Split(_) => match other { Token::Split(_) => true, - _ => false + _ => false, }, Token::OpenParenthesis(_) => match other { Token::OpenParenthesis(_) => true, - _ => false + _ => false, }, Token::CloseParenthesis(_) => match other { Token::CloseParenthesis(_) => true, - _ => false + _ => false, }, Token::Key(_, _) => match other { Token::Key(_, _) => true, - _ => false + _ => false, }, Token::DoubleQuoted(_, _) => match other { Token::DoubleQuoted(_, _) => true, - _ => false + _ => false, }, Token::SingleQuoted(_, _) => match other { Token::SingleQuoted(_, _) => true, - _ => false + _ => false, }, Token::Equal(_) => match other { Token::Equal(_) => true, - _ => false + _ => false, }, Token::GreaterOrEqual(_) => match other { Token::GreaterOrEqual(_) => true, - _ => false + _ => false, }, Token::Greater(_) => match other { Token::Greater(_) => true, - _ => false + _ => false, }, Token::Little(_) => match other { Token::Little(_) => true, - _ => false + _ => false, }, Token::LittleOrEqual(_) => match other { Token::LittleOrEqual(_) => true, - _ => false + _ => false, }, Token::NotEqual(_) => match other { Token::NotEqual(_) => true, - _ => false + _ => false, }, Token::And(_) => match other { Token::And(_) => true, - _ => false + _ => false, }, Token::Or(_) => match other { Token::Or(_) => true, - _ => false + _ => false, }, Token::Whitespace(_, _) => match other { Token::Whitespace(_, _) => true, - _ => false + _ => false, }, } } @@ -174,23 +174,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)?; @@ -312,24 +298,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/select/cmp.rs b/src/select/cmp.rs index 209e672..c4e3dd7 100644 --- a/src/select/cmp.rs +++ b/src/select/cmp.rs @@ -175,7 +175,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 ddbf64e..0c12a33 100644 --- a/src/select/expr_term.rs +++ b/src/select/expr_term.rs @@ -1,7 +1,8 @@ -use serde_json::{Number, Value}; use select::cmp::*; -use select::{FilterKey, to_f64}; +use select::{to_f64, FilterKey}; +use serde_json::{Number, Value}; +// Parsed form of a json path. #[derive(Debug, PartialEq)] pub(super) enum ExprTerm<'a> { String(String), @@ -11,6 +12,7 @@ pub(super) enum ExprTerm<'a> { } impl<'a> ExprTerm<'a> { + // tries to match current json path with provided fn cmp( &self, other: &Self, @@ -201,11 +203,10 @@ impl<'a> Into> for &Vec<&'a Value> { } } - #[cfg(test)] mod expr_term_inner_tests { - use serde_json::{Number, Value}; use select::expr_term::ExprTerm; + use serde_json::{Number, Value}; #[test] fn value_vec_into() { diff --git a/src/select/mod.rs b/src/select/mod.rs index a3d0ec4..20542ef 100644 --- a/src/select/mod.rs +++ b/src/select/mod.rs @@ -1,13 +1,13 @@ 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 self::expr_term::*; -use self::value_walker::ValueWalker; +use self::value_walker::JValueWalker; mod cmp; mod expr_term; @@ -83,7 +83,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, @@ -94,36 +96,46 @@ 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, + FF: Fn(&Vec<&'a Value>, &mut Vec<&'a Value>, &mut HashSet) -> FilterKey, + >( &mut self, current: &Option>, + values: Option + 'a>>, fun: F, + fun_flattened: FF, ) { debug!("push_json_term: {:?}", ¤t); @@ -131,68 +143,116 @@ 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))); + } else { + if let Some(values) = values { + let mut tmp = Vec::new(); + let mut not_matched = HashSet::new(); + let values = values.collect(); + let filter_key = fun_flattened(&values, &mut tmp, &mut not_matched); + self.0 + .push(Some(ExprTerm::Json(None, Some(filter_key), tmp))); + } } } - fn filter, &mut Vec<&'a Value>, &mut HashSet) -> FilterKey>( + fn filter< + F: Fn(&Vec<&'a Value>, &mut Vec<&'a Value>, &mut HashSet) -> FilterKey, + FF: Fn(&Vec<&'a Value>, &mut Vec<&'a Value>, &mut HashSet) -> FilterKey, + >( &mut self, current: &Option>, + values: Option + 'a>>, fun: F, + fun_flattened: FF, ) { if let Some(peek) = self.0.pop() { if let Some(e) = peek { self.filter_json_term(e, fun); } else { - self.push_json_term(current, fun); + self.push_json_term(current, values, fun, fun_flattened); } } } - fn filter_all_with_str(&mut self, current: &Option>, key: &str) { - self.filter(current, |vec, tmp, _| { - ValueWalker::all_with_str(&vec, tmp, key, true); - FilterKey::All - }); + fn filter_all_with_str( + &mut self, + current: &Option>, + values: Option + 'a>>, + key: &str, + ) { + self.filter( + current, + values, + |vec, tmp, _| { + JValueWalker::all_with_str(&vec, tmp, key, true); + FilterKey::All + }, + |_, _, _| FilterKey::All, + ); debug!("filter_all_with_str : {}, {:?}", key, self.0); } - fn filter_next_with_str(&mut self, current: &Option>, key: &str) { - self.filter(current, |vec, tmp, not_matched| { - let mut visited = HashSet::new(); - for (idx, v) in vec.iter().enumerate() { - match v { - Value::Object(map) => { - if map.contains_key(key) { - let ptr = *v as *const Value; - if !visited.contains(&ptr) { - visited.insert(ptr); - tmp.push(v) + fn filter_next_with_str( + &mut self, + current: &Option>, + values: Option + 'a>>, + key: &str, + ) { + self.filter( + current, + values, + |vec, tmp, not_matched| { + let mut visited = HashSet::new(); + for (idx, v) in vec.iter().enumerate() { + match v { + Value::Object(map) => { + if map.contains_key(key) { + let ptr = *v as *const Value; + if !visited.contains(&ptr) { + visited.insert(ptr); + tmp.push(v) + } + } else { + not_matched.insert(idx); } - } else { + } + Value::Array(vec) => { + not_matched.insert(idx); + for v in vec { + JValueWalker::walk_dedup(v, tmp, key, &mut visited); + } + } + _ => { not_matched.insert(idx); } } - Value::Array(vec) => { - not_matched.insert(idx); - for v in vec { - ValueWalker::walk_dedup(v, tmp, key, &mut visited); - } - } - _ => { - not_matched.insert(idx); - } } - } - FilterKey::String(key.to_owned()) - }); + FilterKey::String(key.to_owned()) + }, + |vec, tmp, not_matched| { + let mut visited = HashSet::new(); + for (idx, v) in vec.iter().enumerate() { + not_matched.insert(idx); + JValueWalker::walk_dedup(v, tmp, key, &mut visited); + } + + FilterKey::String(key.to_owned()) + }, + ); 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>, + values: Option + 'a>>, + index: f64, + ) -> (Option>, 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) { @@ -220,21 +280,34 @@ impl<'a> FilterTerms<'a> { if tmp.is_empty() { self.0.pop(); - return Some(vec![]); + return (Some(vec![]), None); } else { - return Some(tmp); + return (Some(tmp), None); } } - debug!( - "collect_next_with_num : {:?}, {:?}", - &index, ¤t - ); + if let Some(mut values) = values { + let index = abs_index(index as isize, values.len()); - None + match values.nth(index) { + Some(value) => return (Some(vec![value]), Some(vec![index])), + None => { + self.0.pop(); + return (Some(vec![]), None); + } + } + } + + debug!("collect_next_with_num : {:?}, {:?}", &index, ¤t); + + (None, None) } - fn collect_next_all(&mut self, current: &Option>) -> Option> { + fn collect_next_all( + &mut self, + current: &Option>, + values: Option + 'a>>, + ) -> (Option>, Option>) { if let Some(current) = current { let mut tmp = Vec::new(); for c in current { @@ -252,15 +325,27 @@ impl<'a> FilterTerms<'a> { _ => {} } } - return Some(tmp); + return (Some(tmp), None); + } + + if let Some(current) = values { + let values = current.collect::>(); + + let values_len = values.len(); + return (Some(values), Some((0..values_len).collect())); } debug!("collect_next_all : {:?}", ¤t); - None + (None, None) } - fn collect_next_with_str(&mut self, current: &Option>, keys: &[String]) -> Option> { + fn collect_next_with_str( + &mut self, + current: &Option>, + values: Option + 'a>>, + keys: &[String], + ) -> (Option>, Option>) { if let Some(current) = current { let mut tmp = Vec::new(); for c in current { @@ -275,63 +360,99 @@ impl<'a> FilterTerms<'a> { if tmp.is_empty() { self.0.pop(); - return Some(vec![]); + return (Some(vec![]), None); } else { - return Some(tmp); + return (Some(tmp), None); } } - debug!( - "collect_next_with_str : {:?}, {:?}", - keys, ¤t - ); + if values.is_some() { + // values has array-like structure + self.0.pop(); + return (Some(vec![]), None); + } - None + debug!("collect_next_with_str : {:?}, {:?}", keys, ¤t); + + (None, None) } - fn collect_all(&mut self, current: &Option>) -> Option> { + fn collect_all( + &mut self, + current: &Option>, + values: Option + 'a>>, + ) -> (Option>, Option>) { if let Some(current) = current { let mut tmp = Vec::new(); - ValueWalker::all(¤t, &mut tmp); - return Some(tmp); + JValueWalker::all(¤t, &mut tmp); + return (Some(tmp), None); + } + + if let Some(values) = values { + let values = values.collect::>(); + let values_len = values.len(); + return (Some(values), Some((0..values_len).collect())); } debug!("collect_all: {:?}", ¤t); - None + (None, None) } - fn collect_all_with_str(&mut self, current: &Option>, key: &str) -> Option> { + fn collect_all_with_str( + &mut self, + current: &Option>, + values: Option + 'a>>, + key: &str, + ) -> (Option>, Option>) { if let Some(current) = current { let mut tmp = Vec::new(); - ValueWalker::all_with_str(¤t, &mut tmp, key, false); - return Some(tmp); + JValueWalker::all_with_str(¤t, &mut tmp, key, false); + return (Some(tmp), None); + } + + if values.is_some() { + return (Some(vec![]), None); } debug!("collect_all_with_str: {}, {:?}", key, ¤t); - None + (None, None) } - fn collect_all_with_num(&mut self, current: &Option>, index: f64) -> Option> { + fn collect_all_with_num( + &mut self, + current: &Option>, + values: Option + 'a>>, + index: f64, + ) -> (Option>, Option>) { if let Some(current) = current { let mut tmp = Vec::new(); - ValueWalker::all_with_num(¤t, &mut tmp, index); - return Some(tmp); + JValueWalker::all_with_num(¤t, &mut tmp, index); + return (Some(tmp), None); + } + + if let Some(mut values) = values { + match values.nth(index as usize) { + Some(value) => return (Some(vec![value]), Some(vec![index as usize])), + None => return (Some(vec![]), None), + } } debug!("collect_all_with_num: {}, {:?}", index, ¤t); - None + (None, None) } } -#[derive(Debug, Default)] +#[derive(Default)] pub struct Selector<'a, 'b> { node: Option, node_ref: Option<&'b Node>, value: Option<&'a Value>, tokens: Vec, current: Option>, + values: Option + 'a>>, + chose_indices: Vec, selectors: Vec>, selector_filter: FilterTerms<'a>, } @@ -376,6 +497,14 @@ impl<'a, 'b> Selector<'a, 'b> { self } + pub fn values_iter( + &mut self, + values: impl Iterator + ExactSizeIterator + 'a, + ) -> &mut Self { + self.values = Some(Box::new(values)); + self + } + fn _select(&mut self) -> Result<(), JsonPathError> { if self.node_ref.is_some() { let node_ref = self.node_ref.take().unwrap(); @@ -432,6 +561,15 @@ impl<'a, 'b> Selector<'a, 'b> { } } + pub fn select_(&mut self) -> Result, JsonPathError> { + self._select()?; + + match &self.current { + Some(r) => Ok(r.to_vec()), + _ => Err(JsonPathError::EmptyValue), + } + } + fn compute_absolute_path_filter(&mut self, token: &ParseToken) -> bool { if !self.selectors.is_empty() { match token { @@ -471,11 +609,17 @@ impl<'a, 'b> Selector<'a, 'b> { if let Some(value) = self.value { selector.value = Some(value); selector.current = Some(vec![value]); + + println!("current.is_some: {:?}", selector.current); self.selectors.push(selector); } return; } + if self.values.is_some() { + return; + } + if let Some(v) = &self.value { self.current = Some(vec![v]); } @@ -485,8 +629,13 @@ impl<'a, 'b> Selector<'a, 'b> { if let Some(ParseToken::Array) = self.tokens.last() { let array_token = self.tokens.pop(); if let Some(ParseToken::Leaves) = self.tokens.last() { + let values = std::mem::replace(&mut self.values, None); self.tokens.pop(); - self.current = self.selector_filter.collect_all(&self.current); + let (current, chose_indices) = + self.selector_filter.collect_all(&self.current, values); + + self.current = current; + self.chose_indices.extend(chose_indices.unwrap_or_default()); } self.tokens.push(array_token.unwrap()); } @@ -496,8 +645,10 @@ impl<'a, 'b> Selector<'a, 'b> { fn visit_array_eof(&mut self) { if self.is_last_before_token_match(ParseToken::Array) { if let Some(Some(e)) = self.selector_filter.pop_term() { + let values = std::mem::replace(&mut self.values, None); 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, values, &key); self.tokens.pop(); return; } @@ -510,22 +661,36 @@ impl<'a, 'b> Selector<'a, 'b> { self.tokens.pop(); self.tokens.pop(); 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.selector_filter.pop_term(); - true - } - ExprTerm::String(key) => { - self.current = self.selector_filter.collect_all_with_str(&self.current, key); - self.selector_filter.pop_term(); - true - } - _ => { - self.selector_filter.push_term(Some(e)); - false - } - }; + let selector_filter_consumed = + match &e { + ExprTerm::Number(n) => { + let values = std::mem::replace(&mut self.values, None); + let (current, chose_indices) = self + .selector_filter + .collect_all_with_num(&self.current, values, to_f64(n)); + + self.selector_filter.pop_term(); + + self.current = current; + self.chose_indices.extend(chose_indices.unwrap_or_default()); + true + } + ExprTerm::String(key) => { + let values = std::mem::replace(&mut self.values, None); + let (current, chose_indices) = self + .selector_filter + .collect_all_with_str(&self.current, values, key); + + self.selector_filter.pop_term(); + self.current = current; + self.chose_indices.extend(chose_indices.unwrap_or_default()); + true + } + _ => { + self.selector_filter.push_term(Some(e)); + false + } + }; if selector_filter_consumed { return; @@ -536,10 +701,25 @@ 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)); + let values = std::mem::replace(&mut self.values, None); + let (current, chose_indices) = self.selector_filter.collect_next_with_num( + &self.current, + values, + to_f64(&n), + ); + + self.current = current; + self.chose_indices.extend(chose_indices.unwrap_or_default()); } ExprTerm::String(key) => { - self.current = self.selector_filter.collect_next_with_str(&self.current, &[key]); + let values = std::mem::replace(&mut self.values, None); + + let (current, chose_indices) = + self.selector_filter + .collect_next_with_str(&self.current, values, &[key]); + + self.current = current; + self.chose_indices.extend(chose_indices.unwrap_or_default()); } ExprTerm::Json(rel, _, v) => { if v.is_empty() { @@ -573,24 +753,38 @@ impl<'a, 'b> Selector<'a, 'b> { self.tokens.pop(); } + let values = std::mem::replace(&mut self.values, None); match self.tokens.last() { Some(ParseToken::Leaves) => { self.tokens.pop(); - self.current = self.selector_filter.collect_all(&self.current); + let (current, chose_indices) = + self.selector_filter.collect_all(&self.current, values); + + self.current = current; + self.chose_indices.extend(chose_indices.unwrap_or_default()); } Some(ParseToken::In) => { self.tokens.pop(); - self.current = self.selector_filter.collect_next_all(&self.current); + let (current, chose_indices) = + self.selector_filter.collect_next_all(&self.current, values); + + self.current = current; + self.chose_indices.extend(chose_indices.unwrap_or_default()); } _ => { - self.current = self.selector_filter.collect_next_all(&self.current); + let (current, chose_indices) = + self.selector_filter.collect_next_all(&self.current, values); + + self.current = current; + self.chose_indices.extend(chose_indices.unwrap_or_default()); } } } 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; } @@ -598,20 +792,38 @@ 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) + let values = std::mem::replace(&mut self.values, None); + let (current, chose_indices) = + self.selector_filter + .collect_all_with_str(&self.current, values, key); + + self.current = current; + self.chose_indices.extend(chose_indices.unwrap_or_default()); } ParseToken::In => { - self.current = self.selector_filter.collect_next_with_str(&self.current, &[key.to_string()]) + let values = std::mem::replace(&mut self.values, None); + let (current, chose_indices) = self.selector_filter.collect_next_with_str( + &self.current, + values, + &[key.to_string()], + ); + + self.current = current; + self.chose_indices.extend(chose_indices.unwrap_or_default()); } _ => {} } } else { match t { ParseToken::Leaves => { - self.selector_filter.filter_all_with_str(&self.current, key); + let values = std::mem::replace(&mut self.values, None); + self.selector_filter + .filter_all_with_str(&self.current, values, key); } ParseToken::In => { - self.selector_filter.filter_next_with_str(&self.current, key); + let values = std::mem::replace(&mut self.values, None); + self.selector_filter + .filter_next_with_str(&self.current, values, key); } _ => {} } @@ -625,7 +837,12 @@ 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); + let values = std::mem::replace(&mut self.values, None); + let (current, chose_indices) = + self.selector_filter + .collect_next_with_str(&self.current, values, keys); + self.current = current; + self.chose_indices.extend(chose_indices.unwrap_or_default()); } else { unreachable!(); } @@ -685,28 +902,54 @@ impl<'a, 'b> Selector<'a, 'b> { if let Some(current) = &self.current { for v in current { if let Value::Array(vec) = v { - let from = if let Some(from) = from { - abs_index(*from, vec.len()) - } else { - 0 + let from = match from { + Some(from) => abs_index(*from, vec.len()), + None => 0, }; - let to = if let Some(to) = to { - abs_index(*to, vec.len()) - } else { - vec.len() + let to = match to { + Some(to) => abs_index(*to, vec.len()), + None => vec.len(), }; - for i in (from..to).step_by(match step { + let step = match step { Some(step) => *step, - _ => 1, - }) { + None => 1, + }; + + for i in (from..to).step_by(step) { if let Some(v) = vec.get(i) { tmp.push(v); } } } } + } else { + let values = std::mem::replace(&mut self.values, None); + if let Some(values) = values { + let from = match from { + Some(from) => abs_index(*from, values.len()), + None => 0, + }; + + let to = match to { + Some(to) => abs_index(*to, values.len()), + None => values.len(), + }; + + let step = match step { + Some(step) => *step, + None => 1, + }; + + let take_count = if from > to { 0 } else { to - from }; + + let matched_values = values.skip(from).step_by(step).take(take_count); + tmp.extend(matched_values); + + self.chose_indices + .extend((from..to).step_by(step).collect::>()) + } } self.current = Some(tmp); } else { @@ -731,6 +974,21 @@ impl<'a, 'b> Selector<'a, 'b> { } } } + } else { + let indices = indices.iter().map(|&v| v as usize).collect::>(); + let values = std::mem::replace(&mut self.values, None); + if let Some(values) = values { + let new_values = values + .enumerate() + .filter(|(id, _)| indices.contains(id)) + .map(|(id, value)| { + self.chose_indices.push(id); + value + }) + .collect::>(); + + tmp.extend(new_values); + } } self.current = Some(tmp); @@ -738,6 +996,10 @@ impl<'a, 'b> Selector<'a, 'b> { unreachable!(); } } + + pub fn chose_indices(self) -> Vec { + self.chose_indices + } } impl<'a, 'b> NodeVisitor for Selector<'a, 'b> { @@ -762,7 +1024,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), @@ -961,7 +1224,6 @@ impl SelectorMut { } } - #[cfg(test)] mod select_inner_tests { use serde_json::Value; @@ -998,4 +1260,4 @@ mod select_inner_tests { panic!(); } } -} \ No newline at end of file +} diff --git a/src/select/value_walker.rs b/src/select/value_walker.rs index e7b4de0..c338ffb 100644 --- a/src/select/value_walker.rs +++ b/src/select/value_walker.rs @@ -1,18 +1,20 @@ use serde_json::Value; use std::collections::HashSet; -pub(super) struct ValueWalker; +pub(super) struct JValueWalker; -impl<'a> ValueWalker { +impl<'a> JValueWalker { pub fn all_with_num(vec: &[&'a Value], tmp: &mut Vec<&'a Value>, index: f64) { - Self::walk(vec, tmp, &|v| if v.is_array() { - if let Some(item) = v.get(index as usize) { - Some(vec![item]) + Self::walk(vec, tmp, &|v| { + if v.is_array() { + if let Some(item) = v.get(index as usize) { + Some(vec![item]) + } else { + None + } } else { None } - } else { - None }); } @@ -47,13 +49,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); } @@ -73,10 +81,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) { @@ -96,4 +106,3 @@ impl<'a> ValueWalker { } } } - diff --git a/tests/array_filter.rs b/tests/array_filter.rs index 9ffa79e..a224de6 100644 --- a/tests/array_filter.rs +++ b/tests/array_filter.rs @@ -250,4 +250,4 @@ fn bugs40_bracket_notation_after_recursive_descent() { "more" ]), ); -} \ No newline at end of file +} diff --git a/tests/filter.rs b/tests/filter.rs index c847dab..97903bc 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 1ade0dd..2aa7589 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" }]), ); } @@ -301,11 +302,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] @@ -323,22 +320,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] @@ -351,7 +340,7 @@ fn op_compare_different_types() { r#"$[?(true == 1)]"#, r#"$[?(@ == 1)]"#, ] - .iter() + .iter() { select_and_then_compare(path, json!({}), json!([])); } @@ -373,4 +362,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 61da575..fe2878a 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/return_type.rs b/tests/return_type.rs index 0139dda..e260b27 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 113f66b..975a277 100644 --- a/tests/selector.rs +++ b/tests/selector.rs @@ -120,12 +120,58 @@ fn selector_remove() { .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)]); +} + +#[test] +fn iter_test() { + use std::rc::Rc; + + struct T { + pub value: Rc, + pub tt: i32, + }; + + let t = Value::Array(vec![ + Value::String(String::from("vv")), + Value::Array(vec![]), + ]); + let t1 = T { + value: Rc::new(t.clone()), + tt: 1, + }; + let t2 = T { + value: Rc::new(t.clone()), + tt: 1, + }; + + let values = vec![t1, t2]; + + let mut selector = Selector::default(); + + let path = "$.[0]"; + let result = selector + .str_path(path) + .unwrap() + .values_iter(values.iter().map(|v| v.value.as_ref())) + .select() + .unwrap(); + + assert_eq!(result, vec![&json!(["vv", []])]); + + let result = jsonpath::select_with_iter(values.iter().map(|v| v.value.as_ref()), path).unwrap(); + assert_eq!(result.0, vec![&json!(["vv", []])]); + assert_eq!(result.1, vec![0]); + + let values = vec![json!([1, 2, 3, 4]), json!([2, 2]), json!(3)]; + + let mut selector = Selector::default(); + let result = selector + .str_path(path) + .unwrap() + .values_iter(values.iter()) + .select() + .unwrap(); + + assert_eq!(result, vec![&json!([1, 2, 3, 4])]); }