support iterator

This commit is contained in:
vms 2020-11-23 07:45:26 +03:00
parent f919a5305b
commit 538b433ae2
13 changed files with 677 additions and 396 deletions

View File

@ -305,6 +305,18 @@ pub fn select<'a>(json: &'a Value, path: &str) -> Result<Vec<&'a Value>, JsonPat
Selector::default().str_path(path)?.value(json).select()
}
pub fn select_with_iter<'a>(
json: impl ExactSizeIterator<Item = &'a Value> + 'a,
path: &str,
) -> Result<(Vec<&'a Value>, Vec<usize>), 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<T: serde::de::DeserializeOwned>(
path: &str,
) -> Result<Vec<T>, 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.

View File

@ -13,8 +13,8 @@ mod utils {
use std::str::FromStr;
pub fn string_to_num<F, S: FromStr>(string: &str, msg_handler: F) -> Result<S, String>
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()),
}
}

View File

@ -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<Token, TokenError> {
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<Token, TokenError> {
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)?;

View File

@ -175,7 +175,6 @@ impl Cmp for CmpOr {
}
}
#[cfg(test)]
mod cmp_inner_tests {
use serde_json::Value;

View File

@ -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<C1: Cmp, C2: Cmp>(
&self,
other: &Self,
@ -201,11 +203,10 @@ impl<'a> Into<ExprTerm<'a>> 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() {

View File

@ -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<F: Fn(&Vec<&'a Value>, &mut Vec<&'a Value>, &mut HashSet<usize>) -> FilterKey>(
fn filter_json_term<
F: Fn(&Vec<&'a Value>, &mut Vec<&'a Value>, &mut HashSet<usize>) -> 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<F: Fn(&Vec<&'a Value>, &mut Vec<&'a Value>, &mut HashSet<usize>) -> FilterKey>(
fn push_json_term<
F: Fn(&Vec<&'a Value>, &mut Vec<&'a Value>, &mut HashSet<usize>) -> FilterKey,
FF: Fn(&Vec<&'a Value>, &mut Vec<&'a Value>, &mut HashSet<usize>) -> FilterKey,
>(
&mut self,
current: &Option<Vec<&'a Value>>,
values: Option<Box<dyn ExactSizeIterator<Item = &'a Value> + 'a>>,
fun: F,
fun_flattened: FF,
) {
debug!("push_json_term: {:?}", &current);
@ -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<F: Fn(&Vec<&'a Value>, &mut Vec<&'a Value>, &mut HashSet<usize>) -> FilterKey>(
fn filter<
F: Fn(&Vec<&'a Value>, &mut Vec<&'a Value>, &mut HashSet<usize>) -> FilterKey,
FF: Fn(&Vec<&'a Value>, &mut Vec<&'a Value>, &mut HashSet<usize>) -> FilterKey,
>(
&mut self,
current: &Option<Vec<&'a Value>>,
values: Option<Box<dyn ExactSizeIterator<Item = &'a Value> + '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<Vec<&'a Value>>, 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<Vec<&'a Value>>,
values: Option<Box<dyn ExactSizeIterator<Item = &'a Value> + '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<Vec<&'a Value>>, 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<Vec<&'a Value>>,
values: Option<Box<dyn ExactSizeIterator<Item = &'a Value> + '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<Vec<&'a Value>>, index: f64) -> Option<Vec<&'a Value>> {
fn collect_next_with_num(
&mut self,
current: &Option<Vec<&'a Value>>,
values: Option<Box<dyn ExactSizeIterator<Item = &'a Value> + 'a>>,
index: f64,
) -> (Option<Vec<&'a Value>>, Option<Vec<usize>>) {
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, &current
);
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, &current);
(None, None)
}
fn collect_next_all(&mut self, current: &Option<Vec<&'a Value>>) -> Option<Vec<&'a Value>> {
fn collect_next_all(
&mut self,
current: &Option<Vec<&'a Value>>,
values: Option<Box<dyn ExactSizeIterator<Item = &'a Value> + 'a>>,
) -> (Option<Vec<&'a Value>>, Option<Vec<usize>>) {
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::<Vec<_>>();
let values_len = values.len();
return (Some(values), Some((0..values_len).collect()));
}
debug!("collect_next_all : {:?}", &current);
None
(None, None)
}
fn collect_next_with_str(&mut self, current: &Option<Vec<&'a Value>>, keys: &[String]) -> Option<Vec<&'a Value>> {
fn collect_next_with_str(
&mut self,
current: &Option<Vec<&'a Value>>,
values: Option<Box<dyn ExactSizeIterator<Item = &'a Value> + 'a>>,
keys: &[String],
) -> (Option<Vec<&'a Value>>, Option<Vec<usize>>) {
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, &current
);
if values.is_some() {
// values has array-like structure
self.0.pop();
return (Some(vec![]), None);
}
None
debug!("collect_next_with_str : {:?}, {:?}", keys, &current);
(None, None)
}
fn collect_all(&mut self, current: &Option<Vec<&'a Value>>) -> Option<Vec<&'a Value>> {
fn collect_all(
&mut self,
current: &Option<Vec<&'a Value>>,
values: Option<Box<dyn ExactSizeIterator<Item = &'a Value> + 'a>>,
) -> (Option<Vec<&'a Value>>, Option<Vec<usize>>) {
if let Some(current) = current {
let mut tmp = Vec::new();
ValueWalker::all(&current, &mut tmp);
return Some(tmp);
JValueWalker::all(&current, &mut tmp);
return (Some(tmp), None);
}
if let Some(values) = values {
let values = values.collect::<Vec<_>>();
let values_len = values.len();
return (Some(values), Some((0..values_len).collect()));
}
debug!("collect_all: {:?}", &current);
None
(None, None)
}
fn collect_all_with_str(&mut self, current: &Option<Vec<&'a Value>>, key: &str) -> Option<Vec<&'a Value>> {
fn collect_all_with_str(
&mut self,
current: &Option<Vec<&'a Value>>,
values: Option<Box<dyn ExactSizeIterator<Item = &'a Value> + 'a>>,
key: &str,
) -> (Option<Vec<&'a Value>>, Option<Vec<usize>>) {
if let Some(current) = current {
let mut tmp = Vec::new();
ValueWalker::all_with_str(&current, &mut tmp, key, false);
return Some(tmp);
JValueWalker::all_with_str(&current, &mut tmp, key, false);
return (Some(tmp), None);
}
if values.is_some() {
return (Some(vec![]), None);
}
debug!("collect_all_with_str: {}, {:?}", key, &current);
None
(None, None)
}
fn collect_all_with_num(&mut self, current: &Option<Vec<&'a Value>>, index: f64) -> Option<Vec<&'a Value>> {
fn collect_all_with_num(
&mut self,
current: &Option<Vec<&'a Value>>,
values: Option<Box<dyn ExactSizeIterator<Item = &'a Value> + 'a>>,
index: f64,
) -> (Option<Vec<&'a Value>>, Option<Vec<usize>>) {
if let Some(current) = current {
let mut tmp = Vec::new();
ValueWalker::all_with_num(&current, &mut tmp, index);
return Some(tmp);
JValueWalker::all_with_num(&current, &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, &current);
None
(None, None)
}
}
#[derive(Debug, Default)]
#[derive(Default)]
pub struct Selector<'a, 'b> {
node: Option<Node>,
node_ref: Option<&'b Node>,
value: Option<&'a Value>,
tokens: Vec<ParseToken>,
current: Option<Vec<&'a Value>>,
values: Option<Box<dyn ExactSizeIterator<Item = &'a Value> + 'a>>,
chose_indices: Vec<usize>,
selectors: Vec<Selector<'a, 'b>>,
selector_filter: FilterTerms<'a>,
}
@ -376,6 +497,14 @@ impl<'a, 'b> Selector<'a, 'b> {
self
}
pub fn values_iter(
&mut self,
values: impl Iterator<Item = &'a Value> + 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<Vec<&'a Value>, 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::<Vec<_>>())
}
}
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::<HashSet<_>>();
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::<Vec<_>>();
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<usize> {
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!();
}
}
}
}

View File

@ -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<F>(vec: &[&'a Value], tmp: &mut Vec<&'a Value>, fun: &F) where F: Fn(&Value) -> Option<Vec<&Value>> {
fn walk<F>(vec: &[&'a Value], tmp: &mut Vec<&'a Value>, fun: &F)
where
F: Fn(&Value) -> Option<Vec<&Value>>,
{
for v in vec {
Self::_walk(v, tmp, fun);
}
}
fn _walk<F>(v: &'a Value, tmp: &mut Vec<&'a Value>, fun: &F) where F: Fn(&Value) -> Option<Vec<&Value>> {
fn _walk<F>(v: &'a Value, tmp: &mut Vec<&'a Value>, fun: &F)
where
F: Fn(&Value) -> Option<Vec<&Value>>,
{
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 {
}
}
}

View File

@ -250,4 +250,4 @@ fn bugs40_bracket_notation_after_recursive_descent() {
"more"
]),
);
}
}

View File

@ -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"}]),
);
}
}

View File

@ -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}
]),
);
}
}

View File

@ -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"
]),
);
}
}

View File

@ -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!([]),
);
}
select_and_then_compare("$[?(@.key==43)]", json!([{"key": 42}]), json!([]));
}

View File

@ -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<Value>,
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])]);
}