From 0534dad755e778cbbda7d4cebc6b54f6df55618b Mon Sep 17 00:00:00 2001 From: freestrings Date: Mon, 25 Feb 2019 16:43:46 +0900 Subject: [PATCH] =?UTF-8?q?JsonPath=20Example=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 1 + benches/giveme_every_thing_result.json | 116 ++ src/jsonpath/json_filter.rs | 1216 --------------------- src/jsonpath/json_filter/cmp.rs | 189 ++++ src/jsonpath/json_filter/mod.rs | 330 ++++++ src/jsonpath/json_filter/term.rs | 130 +++ src/jsonpath/json_filter/value_filter.rs | 568 ++++++++++ src/jsonpath/json_filter/value_wrapper.rs | 256 +++++ src/lib.rs | 2 +- 9 files changed, 1591 insertions(+), 1217 deletions(-) create mode 100644 benches/giveme_every_thing_result.json delete mode 100644 src/jsonpath/json_filter.rs create mode 100644 src/jsonpath/json_filter/cmp.rs create mode 100644 src/jsonpath/json_filter/mod.rs create mode 100644 src/jsonpath/json_filter/term.rs create mode 100644 src/jsonpath/json_filter/value_filter.rs create mode 100644 src/jsonpath/json_filter/value_wrapper.rs diff --git a/Cargo.toml b/Cargo.toml index 0450cd5..c2a4bd6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ log = "0.4" env_logger = "0.6.0" serde = "1.0" serde_json = { version = "1.0", features = ["preserve_order"] } +indexmap = "1.0.2" [dev-dependencies] bencher = "0.1.5" diff --git a/benches/giveme_every_thing_result.json b/benches/giveme_every_thing_result.json new file mode 100644 index 0000000..ad44768 --- /dev/null +++ b/benches/giveme_every_thing_result.json @@ -0,0 +1,116 @@ +[ + { + "book": [ + { + "category": "reference", + "author": "Nigel Rees", + "title": "Sayings of the Century", + "price": 8.95 + }, + { + "category": "fiction", + "author": "Evelyn Waugh", + "title": "Sword of Honour", + "price": 12.99 + }, + { + "category": "fiction", + "author": "Herman Melville", + "title": "Moby Dick", + "isbn": "0-553-21311-3", + "price": 8.99 + }, + { + "category": "fiction", + "author": "J. R. R. Tolkien", + "title": "The Lord of the Rings", + "isbn": "0-395-19395-8", + "price": 22.99 + } + ], + "bicycle": { + "color": "red", + "price": 19.95 + } + }, + 10, + [ + { + "category": "reference", + "author": "Nigel Rees", + "title": "Sayings of the Century", + "price": 8.95 + }, + { + "category": "fiction", + "author": "Evelyn Waugh", + "title": "Sword of Honour", + "price": 12.99 + }, + { + "category": "fiction", + "author": "Herman Melville", + "title": "Moby Dick", + "isbn": "0-553-21311-3", + "price": 8.99 + }, + { + "category": "fiction", + "author": "J. R. R. Tolkien", + "title": "The Lord of the Rings", + "isbn": "0-395-19395-8", + "price": 22.99 + } + ], + { + "color": "red", + "price": 19.95 + }, + { + "category": "reference", + "author": "Nigel Rees", + "title": "Sayings of the Century", + "price": 8.95 + }, + { + "category": "fiction", + "author": "Evelyn Waugh", + "title": "Sword of Honour", + "price": 12.99 + }, + { + "category": "fiction", + "author": "Herman Melville", + "title": "Moby Dick", + "isbn": "0-553-21311-3", + "price": 8.99 + }, + { + "category": "fiction", + "author": "J. R. R. Tolkien", + "title": "The Lord of the Rings", + "isbn": "0-395-19395-8", + "price": 22.99 + }, + "reference", + "Nigel Rees", + "Sayings of the Century", + 8.95, + "fiction", + "Evelyn Waugh", + "Sword of Honour", + 12.99, + "fiction", + "Herman Melville", + "Moby Dick", + "0-553-21311-3", + 8.99, + "fiction", + "J. R. R. Tolkien", + "The Lord of the Rings", + "0-395-19395-8", + 22.99, + "red", + 19.95 +] + diff --git a/src/jsonpath/json_filter.rs b/src/jsonpath/json_filter.rs deleted file mode 100644 index 06e7c13..0000000 --- a/src/jsonpath/json_filter.rs +++ /dev/null @@ -1,1216 +0,0 @@ -use core::borrow::Borrow; -use std::collections::HashMap; -use std::error::Error; -use std::rc::Rc; -use std::result; - -use serde_json::Value; - -use jsonpath::parser::{ - FilterToken, - NodeVisitor, - ParseToken, -}; - -enum CmpType { - Eq, - Ne, - Gt, - Ge, - Lt, - Le, -} - -enum CmpCondType { - And, - Or, -} - -trait PrivCmp { - fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool; - - fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool; - - fn cmp_string(&self, v1: &String, v2: &String) -> bool; -} - -trait IntoType { - fn into_type(&self) -> CmpType; -} - -struct CmpEq; - -impl PrivCmp for CmpEq { - fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { - v1 == v2 - } - - fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { - v1 == v2 - } - - fn cmp_string(&self, v1: &String, v2: &String) -> bool { - v1 == v2 - } -} - -impl IntoType for CmpEq { - fn into_type(&self) -> CmpType { - CmpType::Eq - } -} - -struct CmpNe; - -impl PrivCmp for CmpNe { - fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { - v1 != v2 - } - - fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { - v1 != v2 - } - - fn cmp_string(&self, v1: &String, v2: &String) -> bool { - v1 != v2 - } -} - -impl IntoType for CmpNe { - fn into_type(&self) -> CmpType { - CmpType::Ne - } -} - -struct CmpGt; - -impl PrivCmp for CmpGt { - fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { - v1 > v2 - } - - fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { - v1 > v2 - } - - fn cmp_string(&self, v1: &String, v2: &String) -> bool { - v1 > v2 - } -} - -impl IntoType for CmpGt { - fn into_type(&self) -> CmpType { - CmpType::Gt - } -} - -struct CmpGe; - -impl PrivCmp for CmpGe { - fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { - v1 >= v2 - } - - fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { - v1 >= v2 - } - - fn cmp_string(&self, v1: &String, v2: &String) -> bool { - v1 >= v2 - } -} - -impl IntoType for CmpGe { - fn into_type(&self) -> CmpType { - CmpType::Ge - } -} - -struct CmpLt; - -impl PrivCmp for CmpLt { - fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { - v1 < v2 - } - - fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { - v1 < v2 - } - - fn cmp_string(&self, v1: &String, v2: &String) -> bool { - v1 < v2 - } -} - -impl IntoType for CmpLt { - fn into_type(&self) -> CmpType { - CmpType::Lt - } -} - -struct CmpLe; - -impl PrivCmp for CmpLe { - fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { - v1 <= v2 - } - - fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { - v1 <= v2 - } - - fn cmp_string(&self, v1: &String, v2: &String) -> bool { - v1 <= v2 - } -} - -impl IntoType for CmpLe { - fn into_type(&self) -> CmpType { - CmpType::Le - } -} - -struct CmpAnd; - -impl PrivCmp for CmpAnd { - fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { - *v1 && *v2 - } - - fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { - v1 > &0_f64 && v2 > &0_f64 - } - - fn cmp_string(&self, v1: &String, v2: &String) -> bool { - !v1.is_empty() && !v2.is_empty() - } -} - -struct CmpOr; - -impl PrivCmp for CmpOr { - fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { - *v1 || *v2 - } - - fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { - v1 > &0_f64 || v2 > &0_f64 - } - - fn cmp_string(&self, v1: &String, v2: &String) -> bool { - !v1.is_empty() || !v2.is_empty() - } -} - -#[derive(Debug)] -enum ExprTerm { - String(String), - Number(f64), - Bool(bool), -} - -impl ExprTerm { - fn cmp(&self, other: &ExprTerm, cmp_fn: F, default: bool) -> bool { - match self { - ExprTerm::Bool(v1) => match other { - ExprTerm::Bool(v2) => cmp_fn.cmp_bool(v1, v2), - _ => default - } - ExprTerm::Number(v1) => match other { - ExprTerm::Number(v2) => cmp_fn.cmp_f64(v1, v2), - _ => default - } - ExprTerm::String(v1) => match other { - ExprTerm::String(v2) => cmp_fn.cmp_string(v1, v2), - _ => default - } - } - } -} - -#[derive(Debug)] -enum TermContext { - Constants(ExprTerm), - Json(Option, ValueWrapper), -} - -impl TermContext { - fn cmp(&mut self, other: &mut TermContext, cmp_fn: F, default: bool) -> TermContext { - match self { - TermContext::Constants(et) => { - match other { - TermContext::Constants(oet) => { - TermContext::Constants(ExprTerm::Bool(et.cmp(oet, cmp_fn, default))) - } - TermContext::Json(key, v) => { - TermContext::Json(None, v.take_with(key, et, cmp_fn)) - } - } - } - TermContext::Json(key, v) => { - match other { - TermContext::Json(_, ov) => { - v.cmp(ov, cmp_fn.into_type()) - } - TermContext::Constants(et) => { - TermContext::Json(None, v.take_with(key, et, cmp_fn)) - } - } - } - } - } - - fn cmp_cond(&mut self, other: &mut TermContext, cmp_cond_type: CmpCondType) -> TermContext { - match self { - TermContext::Constants(et) => { - match other { - TermContext::Constants(oet) => { - match cmp_cond_type { - CmpCondType::Or => { - TermContext::Constants(ExprTerm::Bool(et.cmp(oet, CmpOr, false))) - } - CmpCondType::And => { - TermContext::Constants(ExprTerm::Bool(et.cmp(oet, CmpAnd, false))) - } - } - } - TermContext::Json(_, v) => { - TermContext::Json(None, ValueWrapper::new(v.clone_val())) - } - } - } - TermContext::Json(_, v) => { - match other { - TermContext::Json(_, ov) => { - match cmp_cond_type { - CmpCondType::Or => TermContext::Json(None, v.union(ov)), - CmpCondType::And => TermContext::Json(None, v.intersect(ov)), - } - } - _ => { - TermContext::Json(None, ValueWrapper::new(v.clone_val())) - } - } - } - } - } - - fn eq(&mut self, other: &mut TermContext) -> TermContext { - self.cmp(other, CmpEq, false) - } - - fn ne(&mut self, other: &mut TermContext) -> TermContext { - self.cmp(other, CmpNe, true) - } - - fn gt(&mut self, other: &mut TermContext) -> TermContext { - self.cmp(other, CmpGt, false) - } - - fn ge(&mut self, other: &mut TermContext) -> TermContext { - self.cmp(other, CmpGe, false) - } - - fn lt(&mut self, other: &mut TermContext) -> TermContext { - self.cmp(other, CmpLt, false) - } - - fn le(&mut self, other: &mut TermContext) -> TermContext { - self.cmp(other, CmpLe, false) - } - - fn and(&mut self, other: &mut TermContext) -> TermContext { - self.cmp_cond(other, CmpCondType::And) - } - - fn or(&mut self, other: &mut TermContext) -> TermContext { - self.cmp_cond(other, CmpCondType::Or) - } -} - -#[derive(Debug)] -enum ValueFilterKey { - Num(usize), - String(String), - All, -} - -#[derive(Debug)] -struct ValueFilter { - vw: ValueWrapper, - last_key: Option, - filter_mode: bool, -} - -impl ValueFilter { - fn new(v: Value, filter_mode: bool) -> Self { - ValueFilter { vw: ValueWrapper::new(v), last_key: None, filter_mode } - } - - fn iter_to_value_vec<'a, I: Iterator>(iter: I) -> Vec { - iter.map(|v| v.take()) - .filter(|v| !v.is_null()) - .collect() - } - - fn step_leaves_all(&mut self) -> &ValueWrapper { - debug!("step_leaves_all"); - - let mut vw = ValueWrapper::new(Value::Null); - loop { - vw.push(self.step_in_all().clone_val()); - if let Value::Null = self.vw.val { - break; - } - } - self.last_key = Some(ValueFilterKey::All); - self.vw = vw; - &self.vw - } - - fn step_leaves_str(&mut self, key: &str) -> &ValueWrapper { - self.step_leaves_string(&key.to_string()) - } - - fn step_leaves_string(&mut self, key: &String) -> &ValueWrapper { - debug!("step_leaves"); - - let mut buf: Vec = Vec::new(); - - fn _fn(key: &String, v: &Value, buf: &mut Vec) { - match v { - Value::Array(vec) => { - for i in vec { - _fn(key, i, buf); - } - } - Value::Object(v) => { - for (k, v) in v.into_iter() { - if key == k { - buf.push(v.clone()); - } - _fn(key, v, buf); - } - } - _ => {} - } - } - _fn(key, &self.vw.val, &mut buf); - self.last_key = Some(ValueFilterKey::String(key.clone())); - self.vw = ValueWrapper::new(Value::Array(buf)); - &self.vw - } - - fn step_in_all(&mut self) -> &ValueWrapper { - debug!("step_in_all"); - - let vec = match &mut self.vw.val { - Value::Object(map) => Self::iter_to_value_vec(map.values_mut()), - Value::Array(list) => Self::iter_to_value_vec(list.iter_mut()), - Value::Null => Vec::new(), - other => vec![other.take()] - }; - - self.last_key = Some(ValueFilterKey::All); - self.vw.replace(Value::Array(vec)); - trace!("step_in_all - {:?}", self.vw.val); - &self.vw - } - - fn step_in_num(&mut self, key: &usize) -> &ValueWrapper { - debug!("step_in_num"); - - trace!("step_in_num - before: {:?}", self.vw.val); - let v = match self.vw.val.get_mut(&key) { - Some(value) => value.take(), - _ => Value::Null - }; - - self.last_key = Some(ValueFilterKey::Num(key.clone())); - self.vw.replace(v); - trace!("step_in_num - after: {:?}", self.vw.val); - &self.vw - } - - fn step_in_str(&mut self, key: &str) -> &ValueWrapper { - self.step_in_string(&key.to_string()) - } - - fn step_in_string(&mut self, key: &String) -> &ValueWrapper { - debug!("step_in_string"); - - trace!("step_in_string - before: {:?}", self.vw.val); - let filter_mode = self.filter_mode; - let v = match &mut self.vw.val { - Value::Array(v) => { - let vec: Vec = v.iter_mut() - .map(|v| { - if v.is_object() && v.as_object().unwrap().contains_key(key) { - if filter_mode { - v.take() - } else { - v.get_mut(key).unwrap().take() - } - } else { - Value::Null - } - }) - .filter(|v| !v.is_null()) - .collect(); - Value::Array(vec) - } - other => match other.get_mut(key) { - Some(v) => v.take(), - _ => Value::Null - } - }; - - self.last_key = Some(ValueFilterKey::String(key.clone())); - self.vw.replace(v); - trace!("step_in_string - after: {:?}", self.vw.val); - &self.vw - } -} - -pub struct JsonValueFilter { - json: Rc>, - filter_stack: Vec, - token_stack: Vec, - term_stack: Vec, -} - -impl JsonValueFilter { - pub fn new(json: &str) -> result::Result { - let json: Value = serde_json::from_str(json) - .map_err(|e| e.description().to_string())?; - Ok(JsonValueFilter { - json: Rc::new(Box::new(json)), - filter_stack: Vec::new(), - token_stack: Vec::new(), - term_stack: Vec::new(), - }) - } - - fn is_peek_token_array(&self) -> bool { - if let Some(ParseToken::Array) = self.token_stack.last() { - true - } else { - false - } - } - - fn push_value_filter(&mut self, from_current: bool) { - if from_current { - self.filter_stack.last() - .map(|vf| ValueFilter::new(vf.vw.clone_val(), from_current)) - .and_then(|vf| Some(self.filter_stack.push(vf))); - } else { - let v: &Value = self.json.as_ref().borrow(); - self.filter_stack.push(ValueFilter::new(v.clone(), !from_current)); - } - } - - fn replace_filter_stack(&mut self, v: Value) { - if self.filter_stack.is_empty() { - self.filter_stack.push(ValueFilter::new(v, false)); - } else { - match self.filter_stack.last_mut() { - Some(vf) => { - if v.is_null() { - vf.vw.replace(v); - } else if vf.vw.is_array() { - vf.vw.replace(v); - } - } - _ => {} - } - } - } - - fn current_value(&self) -> &Value { - match self.filter_stack.last() { - Some(v) => &v.vw.val(), - _ => &Value::Null - } - } -} - -impl NodeVisitor for JsonValueFilter { - fn visit_token(&mut self, token: ParseToken) { - debug!("visit_token: {:?}", token); - - match token { - ParseToken::Absolute - | ParseToken::Relative => { - if self.is_peek_token_array() { - self.token_stack.pop(); - } - self.push_value_filter(ParseToken::Relative == token); - } - ParseToken::In - | ParseToken::Leaves - | ParseToken::Array => { - self.token_stack.push(token); - } - ParseToken::ArrayEof => { - trace!("array_eof - term_stack: {:?}", self.term_stack); - trace!("array_eof - filter_stack: {:?}", self.filter_stack); - - match self.term_stack.pop() { - Some(TermContext::Constants(ExprTerm::Number(v))) => { - let v = v as usize; - match self.filter_stack.last_mut() { - Some(vf) => { - vf.step_in_num(&v); - } - _ => {} - } - } - Some(TermContext::Json(_, mut vw)) => { - self.replace_filter_stack(vw.val.take()) - } - _ => { - match self.filter_stack.pop() { - Some(vf) => { - match vf.vw.val { - Value::Null | Value::Bool(false) => { - self.replace_filter_stack(Value::Null) - } - other => { - self.replace_filter_stack(other) - } - } - } - _ => {} - } - } - } - } - ParseToken::All => { - match self.filter_stack.last_mut() { - Some(vf) => { - match self.token_stack.pop() { - Some(ParseToken::In) => { - vf.step_in_all(); - } - Some(ParseToken::Leaves) => { - vf.step_leaves_all(); - } - _ => {} - } - } - _ => {} - } - } - ParseToken::Key(key) => { - match self.filter_stack.last_mut() { - Some(vf) => { - match self.token_stack.pop() { - Some(ParseToken::In) => { - vf.step_in_string(&key); - } - Some(ParseToken::Leaves) => { - vf.step_leaves_string(&key); - } - _ => {} - } - } - _ => {} - } - } - ParseToken::Filter(ref ft) => { - let right = self.term_stack.pop(); - let left = self.term_stack.pop(); - - trace!("left {:?}", left); - trace!("right {:?}", right); - - if let Some(mut left) = left { - if let Some(mut right) = right { - let tc = match ft { - FilterToken::Equal => left.eq(&mut right), - FilterToken::NotEqual => left.ne(&mut right), - FilterToken::Greater => left.gt(&mut right), - FilterToken::GreaterOrEqual => left.ge(&mut right), - FilterToken::Little => left.lt(&mut right), - FilterToken::LittleOrEqual => left.le(&mut right), - FilterToken::And => left.and(&mut right), - FilterToken::Or => left.or(&mut right), - }; - self.term_stack.push(tc); - } - } - - trace!("filter - {:?}", self.term_stack) - } - - ParseToken::Number(v) => { - self.term_stack.push(TermContext::Constants(ExprTerm::Number(v))) - } - - ParseToken::Range(from, to) => { - self.token_stack.pop(); - match self.filter_stack.last_mut() { - Some(vf) => { - if !vf.vw.is_array() { - return; - } - - let len = if let Some(v) = vf.vw.val.as_array() { v.len() } else { 0 }; - let from = match from { - Some(v) => if v < 0 { 0 } else { v as usize }, - _ => 0 - }; - let to = match to { - Some(v) => if v < 0 { len - v.abs() as usize } else { v as usize } - _ => len - }; - - trace!("range - {}:{}", from, to); - - let v: Vec = (from..to).into_iter() - .map(|i| match vf.vw.val.get_mut(i) { - Some(v) => v.take(), - _ => Value::Null - }) - .filter(|v| !v.is_null()) - .collect(); - - vf.vw.replace(Value::Array(v)); - } - _ => {} - } - } - - ParseToken::Union(v) => { - self.token_stack.pop(); - match self.filter_stack.last_mut() { - Some(vf) => { - if !vf.vw.is_array() { - return; - } - - let v: Vec = v.into_iter() - .map(|i| match vf.vw.val.get_mut(i as usize) { - Some(v) => v.take(), - _ => Value::Null - }) - .filter(|v| !v.is_null()) - .collect(); - - trace!("union - {:?}", v); - - vf.vw.replace(Value::Array(v)); - } - _ => {} - } - } - - ParseToken::Eof => { - debug!("visit_token eof"); - } - } - } - - fn end_term(&mut self) { - debug!("end_term"); - - if let Some(ParseToken::Array) = self.token_stack.last() { - self.token_stack.pop(); - } - - trace!("end_term - term_stack {:?}", self.term_stack); - trace!("end_term - token_stack {:?}", self.token_stack); - trace!("end_term - filter_stack {:?}", self.filter_stack); - - if self.token_stack.is_empty() && self.filter_stack.len() > 1 { - match self.filter_stack.pop() { - Some(vf) => { - self.term_stack.push(TermContext::Json(vf.last_key, vf.vw)); - } - _ => {} - } - } - - if match self.token_stack.last() { - Some(ParseToken::Key(_)) - | Some(ParseToken::Number(_)) => true, - _ => false - } { - match self.token_stack.pop() { - Some(ParseToken::Key(ref v)) if v.eq_ignore_ascii_case("true") => { - self.term_stack.push(TermContext::Constants(ExprTerm::Bool(true))) - } - Some(ParseToken::Key(ref v)) if v.eq_ignore_ascii_case("false") => { - self.term_stack.push(TermContext::Constants(ExprTerm::Bool(false))) - } - Some(ParseToken::Key(v)) => { - self.term_stack.push(TermContext::Constants(ExprTerm::String(v))) - } - Some(ParseToken::Number(v)) => { - self.term_stack.push(TermContext::Constants(ExprTerm::Number(v))) - } - _ => {} - } - } - } -} - -#[derive(Debug)] -struct ValueWrapper { - val: Value, -} - -impl ValueWrapper { - fn new(v: Value) -> Self { - ValueWrapper { val: v } - } - - fn cmp(&mut self, other: &mut ValueWrapper, cmp_type: CmpType) -> TermContext { - match cmp_type { - CmpType::Eq => { - TermContext::Json(None, self.intersect(other)) - } - CmpType::Ne => { - TermContext::Json(None, self.except(other)) - } - CmpType::Gt | CmpType::Ge | CmpType::Lt | CmpType::Le => { - TermContext::Constants(ExprTerm::Bool(false)) - } - } - } - - fn cmp_with_term(val: &Value, et: &ExprTerm, cmp_fn: &F, default: bool) -> bool { - match val { - Value::Bool(ref v1) => { - match et { - ExprTerm::Bool(v2) => cmp_fn.cmp_bool(v1, v2), - _ => default - } - } - Value::Number(ref v1) => match v1.as_f64() { - Some(ref v1) => { - match et { - ExprTerm::Number(v2) => cmp_fn.cmp_f64(v1, v2), - _ => default - } - } - _ => default - }, - Value::String(ref v1) => { - match et { - ExprTerm::String(v2) => cmp_fn.cmp_string(v1, v2), - _ => default - } - } - _ => default - } - } - - fn take_object_in_array(&mut self, key: &String, et: &ExprTerm, cmp: &F) -> Option { - match self.val.take() { - Value::Array(mut vec) => { - let mut ret: Vec = vec.iter_mut() - .filter(|v| { - match &v { - Value::Object(map) => { - match map.get(key) { - Some(vv) => Self::cmp_with_term(vv, et, cmp, false), - _ => false - } - } - _ => false - } - }) - .map(|v| v.take()) - .collect(); - Some(ValueWrapper::new(Value::Array(ret))) - } - _ => None - } - } - - fn take_with_key_type(&mut self, key: &Option, et: &ExprTerm, cmp: &F) -> Option { - match key { - Some(ValueFilterKey::String(key)) => { - self.take_object_in_array(key, et, cmp) - } - _ => None - } - } - - fn take_with(&mut self, key: &Option, et: &ExprTerm, cmp: F) -> Self { - match self.take_with_key_type(key, et, &cmp) { - Some(vw) => vw, - _ => { - match self.val.take() { - Value::Array(mut vec) => { - let mut ret = vec.iter_mut() - .filter(|v| Self::cmp_with_term(&v, et, &cmp, false)) - .map(|v| v.take()) - .collect(); - ValueWrapper::new(Value::Array(ret)) - } - other => { - if Self::cmp_with_term(&other, et, &cmp, false) { - ValueWrapper::new(other) - } else { - ValueWrapper::new(Value::Null) - } - } - } - } - } - } - - fn replace(&mut self, val: Value) { - let is_null = match &val { - Value::Array(v) => if v.is_empty() { true } else { false }, - Value::Object(m) => if m.is_empty() { true } else { false }, - _ => val.is_null() - }; - self.val = if is_null { Value::Null } else { val }; - } - - fn push(&mut self, v: Value) { - if let Value::Array(values) = &mut self.val { - values.push(v); - return; - } - - let data = self.val.take(); - if data.is_null() { - self.val = v; - } else { - let mut values = Vec::new(); - values.push(data); - values.push(v); - self.val = Value::Array(values); - } - } - - fn clone_val(&self) -> Value { - self.val.clone() - } - - fn is_array(&self) -> bool { - self.val.is_array() - } - - fn uuid(v: &Value) -> String { - fn _fn(v: &Value) -> String { - match v { - Value::Null => "null".to_string(), - Value::String(v) => v.to_string(), - Value::Bool(v) => v.to_string(), - Value::Number(v) => v.to_string(), - Value::Array(v) => { - v.iter().enumerate() - .map(|(i, v)| { format!("{}{}", i, _fn(v)) }) - .collect() - } - Value::Object(v) => { - v.into_iter().map(|(k, v)| { format!("{}{}", k, _fn(v)) }).collect() - } - } - } - _fn(v) - } - - fn into_map(&mut self) -> HashMap { - let mut map: HashMap = HashMap::new(); - match &mut self.val { - Value::Array(v1) => { - for v in v1 { - map.insert(Self::uuid(v), v.take()); - } - } - other => { - map.insert(Self::uuid(other), other.take()); - } - } - map - } - - fn except(&mut self, other: &mut Self) -> Self { - let map = self.into_map(); - let mut ret: HashMap = HashMap::new(); - match &mut other.val { - Value::Array(v1) => { - for v in v1 { - let key = Self::uuid(v); - if !map.contains_key(&key) { - ret.insert(key, v.take()); - } - } - } - other => { - let key = Self::uuid(other); - if !map.contains_key(&key) { - ret.insert(key, other.take()); - } - } - } - - let v = ret.values_mut().into_iter().map(|v| v.take()).collect(); - ValueWrapper::new(v) - } - - fn intersect(&mut self, other: &mut Self) -> Self { - let map = self.into_map(); - let mut ret: HashMap = HashMap::new(); - match &mut other.val { - Value::Array(v1) => { - for v in v1 { - let key = Self::uuid(v); - if map.contains_key(&key) { - ret.insert(key, v.take()); - } - } - } - other => { - let key = Self::uuid(other); - if map.contains_key(&key) { - ret.insert(key, other.take()); - } - } - } - - let v = ret.values_mut().into_iter().map(|v| v.take()).collect(); - ValueWrapper::new(v) - } - - fn union(&mut self, other: &mut Self) -> Self { - let mut map = self.into_map(); - match &mut other.val { - Value::Array(v1) => { - for v in v1 { - let key = Self::uuid(v); - if !map.contains_key(&key) { - map.insert(key, v.take()); - } - } - } - other => { - let key = Self::uuid(other); - if !map.contains_key(&key) { - map.insert(key, other.take()); - } - } - } - - let mut vw = ValueWrapper::new(Value::Null); - let list: Vec = map.values_mut().into_iter().map(|val| val.take()).collect(); - vw.replace(Value::Array(list)); - vw - } - - fn val(&self) -> &Value { - &self.val - } -} - -#[cfg(test)] -mod tests { - extern crate env_logger; - - use std::io::Read; - use std::sync::{Once, ONCE_INIT}; - - use jsonpath::parser::Parser; - - use super::*; - - static INIT: Once = ONCE_INIT; - - fn setup() { - INIT.call_once(|| { - env_logger::init(); - }); - } - - fn new_value_filter(file: &str) -> ValueFilter { - let string = read_json(file); - let json: Value = serde_json::from_str(string.as_str()).unwrap(); - ValueFilter::new(json, false) - } - - fn do_filter(path: &str, file: &str) -> JsonValueFilter { - let string = read_json(file); - let mut jf = JsonValueFilter::new(string.as_str()).unwrap(); - let mut parser = Parser::new(path); - parser.parse(&mut jf).unwrap(); - jf - } - - fn read_json(path: &str) -> String { - let mut f = std::fs::File::open(path).unwrap(); - let mut contents = String::new(); - f.read_to_string(&mut contents).unwrap(); - contents - } - - #[test] - fn step_in() { - setup(); - - let mut jf = new_value_filter("./benches/data_obj.json"); - { - let current = jf.step_in_str("friends"); - assert_eq!(current.is_array(), true); - } - - let mut jf = new_value_filter("./benches/data_array.json"); - { - let current = jf.step_in_num(&1); - assert_eq!(current.val.is_object(), true); - } - { - let current = jf.step_in_str("friends"); - assert_eq!(current.is_array(), true); - } - let mut jf = new_value_filter("./benches/data_obj.json"); - { - jf.step_in_str("school"); - jf.step_in_str("friends"); - jf.step_in_all(); - let current = jf.step_in_str("name"); - let friends = json!([ - "Millicent Norman", - "Vincent Cannon", - "Gray Berry" - ]); - assert_eq!(&friends, ¤t.val); - } - let mut jf = new_value_filter("./benches/data_obj.json"); - { - let current = jf.step_leaves_str("name"); - let names = json!([ - "Leonor Herman", - "Millicent Norman", - "Vincent Cannon", - "Gray Berry", - "Vincent Cannon", - "Gray Berry" - ]); - assert_eq!(&names, ¤t.val); - } - } - - #[test] - fn array() { - setup(); - - let friends = json!([ - {"id": 1, "name": "Vincent Cannon" }, - {"id": 2, "name": "Gray Berry"} - ]); - - let jf = do_filter("$.school.friends[1, 2]", "./benches/data_obj.json"); - assert_eq!(&friends, jf.current_value()); - - let jf = do_filter("$.school.friends[1:]", "./benches/data_obj.json"); - assert_eq!(&friends, jf.current_value()); - - let jf = do_filter("$.school.friends[:-2]", "./benches/data_obj.json"); - let friends = json!([ - {"id": 0, "name": "Millicent Norman"} - ]); - assert_eq!(&friends, jf.current_value()); - } - - #[test] - fn return_type() { - setup(); - - let friends = json!({ - "friends": [ - {"id": 0, "name": "Millicent Norman"}, - {"id": 1, "name": "Vincent Cannon" }, - {"id": 2, "name": "Gray Berry"} - ] - }); - - let jf = do_filter("$.school", "./benches/data_obj.json"); - assert_eq!(&friends, jf.current_value()); - - let jf = do_filter("$.school[?(@.friends[0])]", "./benches/data_obj.json"); - assert_eq!(&friends, jf.current_value()); - - let jf = do_filter("$.school[?(@.friends[10])]", "./benches/data_obj.json"); - assert_eq!(&Value::Null, jf.current_value()); - - let jf = do_filter("$.school[?(1==1)]", "./benches/data_obj.json"); - assert_eq!(&friends, jf.current_value()); - - let jf = do_filter("$.school.friends[?(1==1)]", "./benches/data_obj.json"); - let friends = json!([ - {"id": 0, "name": "Millicent Norman"}, - {"id": 1, "name": "Vincent Cannon" }, - {"id": 2, "name": "Gray Berry"} - ]); - assert_eq!(&friends, jf.current_value()); - } - - #[test] - fn op() { - setup(); - - let jf = do_filter("$.school[?(@.friends == @.friends)]", "./benches/data_obj.json"); - let friends = json!({ - "friends": [ - {"id": 0, "name": "Millicent Norman"}, - {"id": 1, "name": "Vincent Cannon" }, - {"id": 2, "name": "Gray Berry"} - ] - }); - assert_eq!(&friends, jf.current_value()); - - let jf = do_filter("$.friends[?(@.name)]", "./benches/data_obj.json"); - let friends = json!([ - { "id" : 1, "name" : "Vincent Cannon" }, - { "id" : 2, "name" : "Gray Berry" } - ]); - assert_eq!(&friends, jf.current_value()); - - let jf = do_filter("$.friends[?(@.id >= 2)]", "./benches/data_obj.json"); - let friends = json!([ - { "id" : 2, "name" : "Gray Berry" } - ]); - assert_eq!(&friends, jf.current_value()); - - // TODO order - let jf = do_filter("$.friends[?(@.id >= 2 || @.id == 1)]", "./benches/data_obj.json"); - let friends = json!([ - { "id" : 2, "name" : "Gray Berry" }, - { "id" : 1, "name" : "Vincent Cannon" } - ]); - assert_eq!(&friends, jf.current_value()); - - let jf = do_filter("$.friends[?( (@.id >= 2 || @.id == 1) && @.id == 0)]", "./benches/data_obj.json"); - assert_eq!(&Value::Null, jf.current_value()); - } - - #[test] - fn example() { - let jf = do_filter("$.store.book[*].author", "./benches/example.json"); - let ret = json!(["Nigel Rees","Evelyn Waugh","Herman Melville","J. R. R. Tolkien"]); - assert_eq!(&ret, jf.current_value()); - - let jf = do_filter("$..author", "./benches/example.json"); - assert_eq!(&ret, jf.current_value()); - - let jf = do_filter("$.store.*", "./benches/example.json"); - let ret = json!([ - [ - {"category" : "reference", "author" : "Nigel Rees","title" : "Sayings of the Century", "price" : 8.95}, - {"category" : "fiction", "author" : "Evelyn Waugh","title" : "Sword of Honour","price" : 12.99}, - {"category" : "fiction", "author" : "Herman Melville","title" : "Moby Dick","isbn" : "0-553-21311-3","price" : 8.99}, - {"category" : "fiction", "author" : "J. R. R. Tolkien","title" : "The Lord of the Rings","isbn" : "0-395-19395-8","price" : 22.99} - ], - {"color" : "red","price" : 19.95}, - ]); - assert_eq!(&ret, jf.current_value()); - } - -} \ No newline at end of file diff --git a/src/jsonpath/json_filter/cmp.rs b/src/jsonpath/json_filter/cmp.rs new file mode 100644 index 0000000..0785f60 --- /dev/null +++ b/src/jsonpath/json_filter/cmp.rs @@ -0,0 +1,189 @@ +pub enum CmpType { + Eq, + Ne, + Gt, + Ge, + Lt, + Le, +} + +pub enum CmpCondType { + And, + Or, +} + +pub trait PrivCmp { + fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool; + + fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool; + + fn cmp_string(&self, v1: &String, v2: &String) -> bool; +} + +pub trait IntoType { + fn into_type(&self) -> CmpType; +} + +pub struct CmpEq; + +impl PrivCmp for CmpEq { + fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { + v1 == v2 + } + + fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { + v1 == v2 + } + + fn cmp_string(&self, v1: &String, v2: &String) -> bool { + v1 == v2 + } +} + +impl IntoType for CmpEq { + fn into_type(&self) -> CmpType { + CmpType::Eq + } +} + +pub struct CmpNe; + +impl PrivCmp for CmpNe { + fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { + v1 != v2 + } + + fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { + v1 != v2 + } + + fn cmp_string(&self, v1: &String, v2: &String) -> bool { + v1 != v2 + } +} + +impl IntoType for CmpNe { + fn into_type(&self) -> CmpType { + CmpType::Ne + } +} + +pub struct CmpGt; + +impl PrivCmp for CmpGt { + fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { + v1 > v2 + } + + fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { + v1 > v2 + } + + fn cmp_string(&self, v1: &String, v2: &String) -> bool { + v1 > v2 + } +} + +impl IntoType for CmpGt { + fn into_type(&self) -> CmpType { + CmpType::Gt + } +} + +pub struct CmpGe; + +impl PrivCmp for CmpGe { + fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { + v1 >= v2 + } + + fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { + v1 >= v2 + } + + fn cmp_string(&self, v1: &String, v2: &String) -> bool { + v1 >= v2 + } +} + +impl IntoType for CmpGe { + fn into_type(&self) -> CmpType { + CmpType::Ge + } +} + +pub struct CmpLt; + +impl PrivCmp for CmpLt { + fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { + v1 < v2 + } + + fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { + v1 < v2 + } + + fn cmp_string(&self, v1: &String, v2: &String) -> bool { + v1 < v2 + } +} + +impl IntoType for CmpLt { + fn into_type(&self) -> CmpType { + CmpType::Lt + } +} + +pub struct CmpLe; + +impl PrivCmp for CmpLe { + fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { + v1 <= v2 + } + + fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { + v1 <= v2 + } + + fn cmp_string(&self, v1: &String, v2: &String) -> bool { + v1 <= v2 + } +} + +impl IntoType for CmpLe { + fn into_type(&self) -> CmpType { + CmpType::Le + } +} + +pub struct CmpAnd; + +impl PrivCmp for CmpAnd { + fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { + *v1 && *v2 + } + + fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { + v1 > &0_f64 && v2 > &0_f64 + } + + fn cmp_string(&self, v1: &String, v2: &String) -> bool { + !v1.is_empty() && !v2.is_empty() + } +} + +pub struct CmpOr; + +impl PrivCmp for CmpOr { + fn cmp_bool(&self, v1: &bool, v2: &bool) -> bool { + *v1 || *v2 + } + + fn cmp_f64(&self, v1: &f64, v2: &f64) -> bool { + v1 > &0_f64 || v2 > &0_f64 + } + + fn cmp_string(&self, v1: &String, v2: &String) -> bool { + !v1.is_empty() || !v2.is_empty() + } +} \ No newline at end of file diff --git a/src/jsonpath/json_filter/mod.rs b/src/jsonpath/json_filter/mod.rs new file mode 100644 index 0000000..a45d964 --- /dev/null +++ b/src/jsonpath/json_filter/mod.rs @@ -0,0 +1,330 @@ +mod cmp; +mod term; +mod value_filter; +mod value_wrapper; + + +#[cfg(test)] +mod tests { + extern crate env_logger; + + use std::io::Read; + use std::sync::{Once, ONCE_INIT}; + + use jsonpath::parser::Parser; + + use serde_json::Value; + + use super::value_filter::*; + + static INIT: Once = ONCE_INIT; + + fn setup() { + INIT.call_once(|| { + env_logger::init(); + }); + } + + fn new_value_filter(file: &str) -> ValueFilter { + let string = read_json(file); + let json: Value = serde_json::from_str(string.as_str()).unwrap(); + ValueFilter::new(json, false, false) + } + + fn do_filter(path: &str, file: &str) -> JsonValueFilter { + let string = read_json(file); + let mut jf = JsonValueFilter::new(string.as_str()).unwrap(); + let mut parser = Parser::new(path); + parser.parse(&mut jf).unwrap(); + jf + } + + fn read_json(path: &str) -> String { + let mut f = std::fs::File::open(path).unwrap(); + let mut contents = String::new(); + f.read_to_string(&mut contents).unwrap(); + contents + } + + #[test] + fn step_in() { + setup(); + + let mut jf = new_value_filter("./benches/data_obj.json"); + { + let current = jf.step_in_str("friends"); + assert_eq!(current.is_array(), true); + } + + let mut jf = new_value_filter("./benches/data_array.json"); + { + let current = jf.step_in_num(&1.0); + assert_eq!(current.get_val().is_object(), true); + } + { + let current = jf.step_in_str("friends"); + assert_eq!(current.is_array(), true); + } + let mut jf = new_value_filter("./benches/data_obj.json"); + { + jf.step_in_str("school"); + jf.step_in_str("friends"); + jf.step_in_all(); + let current = jf.step_in_str("name"); + let friends = json!([ + "Millicent Norman", + "Vincent Cannon", + "Gray Berry" + ]); + assert_eq!(&friends, current.get_val()); + } + let mut jf = new_value_filter("./benches/data_obj.json"); + { + let current = jf.step_leaves_str("name"); + let names = json!([ + "Leonor Herman", + "Millicent Norman", + "Vincent Cannon", + "Gray Berry", + "Vincent Cannon", + "Gray Berry" + ]); + assert_eq!(&names, current.get_val()); + } + } + + #[test] + fn array() { + setup(); + + let friends = json!([ + {"id": 1, "name": "Vincent Cannon" }, + {"id": 2, "name": "Gray Berry"} + ]); + + let jf = do_filter("$.school.friends[1, 2]", "./benches/data_obj.json"); + assert_eq!(&friends, jf.current_value()); + + let jf = do_filter("$.school.friends[1:]", "./benches/data_obj.json"); + assert_eq!(&friends, jf.current_value()); + + let jf = do_filter("$.school.friends[:-2]", "./benches/data_obj.json"); + let friends = json!([ + {"id": 0, "name": "Millicent Norman"} + ]); + assert_eq!(&friends, jf.current_value()); + } + + #[test] + fn return_type() { + setup(); + + let friends = json!({ + "friends": [ + {"id": 0, "name": "Millicent Norman"}, + {"id": 1, "name": "Vincent Cannon" }, + {"id": 2, "name": "Gray Berry"} + ] + }); + + let jf = do_filter("$.school", "./benches/data_obj.json"); + assert_eq!(&friends, jf.current_value()); + + let jf = do_filter("$.school[?(@.friends[0])]", "./benches/data_obj.json"); + assert_eq!(&friends, jf.current_value()); + + let jf = do_filter("$.school[?(@.friends[10])]", "./benches/data_obj.json"); + assert_eq!(&Value::Null, jf.current_value()); + + let jf = do_filter("$.school[?(1==1)]", "./benches/data_obj.json"); + assert_eq!(&friends, jf.current_value()); + + let jf = do_filter("$.school.friends[?(1==1)]", "./benches/data_obj.json"); + let friends = json!([ + {"id": 0, "name": "Millicent Norman"}, + {"id": 1, "name": "Vincent Cannon" }, + {"id": 2, "name": "Gray Berry"} + ]); + assert_eq!(&friends, jf.current_value()); + } + + #[test] + fn op() { + setup(); + + let jf = do_filter("$.school[?(@.friends == @.friends)]", "./benches/data_obj.json"); + let friends = json!({ + "friends": [ + {"id": 0, "name": "Millicent Norman"}, + {"id": 1, "name": "Vincent Cannon" }, + {"id": 2, "name": "Gray Berry"} + ] + }); + assert_eq!(&friends, jf.current_value()); + + let jf = do_filter("$.friends[?(@.name)]", "./benches/data_obj.json"); + let friends = json!([ + { "id" : 1, "name" : "Vincent Cannon" }, + { "id" : 2, "name" : "Gray Berry" } + ]); + assert_eq!(&friends, jf.current_value()); + + let jf = do_filter("$.friends[?(@.id >= 2)]", "./benches/data_obj.json"); + let friends = json!([ + { "id" : 2, "name" : "Gray Berry" } + ]); + assert_eq!(&friends, jf.current_value()); + + let jf = do_filter("$.friends[?(@.id >= 2 || @.id == 1)]", "./benches/data_obj.json"); + let friends = json!([ + { "id" : 2, "name" : "Gray Berry" }, + { "id" : 1, "name" : "Vincent Cannon" } + ]); + assert_eq!(&friends, jf.current_value()); + + let jf = do_filter("$.friends[?( (@.id >= 2 || @.id == 1) && @.id == 0)]", "./benches/data_obj.json"); + assert_eq!(&Value::Null, jf.current_value()); + } + + #[test] + fn example() { + setup(); + + let jf = do_filter("$.store.book[*].author", "./benches/example.json"); + let ret = json!(["Nigel Rees","Evelyn Waugh","Herman Melville","J. R. R. Tolkien"]); + assert_eq!(&ret, jf.current_value()); + + let jf = do_filter("$..author", "./benches/example.json"); + assert_eq!(&ret, jf.current_value()); + + let jf = do_filter("$.store.*", "./benches/example.json"); + let ret = json!([ + [ + {"category" : "reference", "author" : "Nigel Rees","title" : "Sayings of the Century", "price" : 8.95}, + {"category" : "fiction", "author" : "Evelyn Waugh","title" : "Sword of Honour","price" : 12.99}, + {"category" : "fiction", "author" : "Herman Melville","title" : "Moby Dick","isbn" : "0-553-21311-3","price" : 8.99}, + {"category" : "fiction", "author" : "J. R. R. Tolkien","title" : "The Lord of the Rings","isbn" : "0-395-19395-8","price" : 22.99} + ], + {"color" : "red","price" : 19.95}, + ]); + assert_eq!(&ret, jf.current_value()); + + let jf = do_filter("$.store..price", "./benches/example.json"); + let ret = json!([8.95, 12.99, 8.99, 22.99, 19.95]); + assert_eq!(&ret, jf.current_value()); + + let jf = do_filter("$..book[2]", "./benches/example.json"); + let ret = json!([{ + "category" : "fiction", + "author" : "Herman Melville", + "title" : "Moby Dick", + "isbn" : "0-553-21311-3", + "price" : 8.99 + }]); + assert_eq!(&ret, jf.current_value()); + + let jf = do_filter("$..book[-2]", "./benches/example.json"); + let ret = json!([{ + "category" : "fiction", + "author" : "Herman Melville", + "title" : "Moby Dick", + "isbn" : "0-553-21311-3", + "price" : 8.99 + }]); + assert_eq!(&ret, jf.current_value()); + + let jf = do_filter("$..book[0,1]", "./benches/example.json"); + let ret = json!([ + { + "category" : "reference", + "author" : "Nigel Rees", + "title" : "Sayings of the Century", + "price" : 8.95 + }, + { + "category" : "fiction", + "author" : "Evelyn Waugh", + "title" : "Sword of Honour", + "price" : 12.99 + } + ]); + assert_eq!(&ret, jf.current_value()); + + let jf = do_filter("$..book[:2]", "./benches/example.json"); + let ret = json!([ + { + "category" : "reference", + "author" : "Nigel Rees", + "title" : "Sayings of the Century", + "price" : 8.95 + }, + { + "category" : "fiction", + "author" : "Evelyn Waugh", + "title" : "Sword of Honour", + "price" : 12.99 + } + ]); + assert_eq!(&ret, jf.current_value()); + + let jf = do_filter("$..book[2:]", "./benches/example.json"); + let ret = json!([ + { + "category" : "fiction", + "author" : "Herman Melville", + "title" : "Moby Dick", + "isbn" : "0-553-21311-3", + "price" : 8.99 + }, + { + "category" : "fiction", + "author" : "J. R. R. Tolkien", + "title" : "The Lord of the Rings", + "isbn" : "0-395-19395-8", + "price" : 22.99 + } + ]); + assert_eq!(&ret, jf.current_value()); + + let jf = do_filter("$..book[?(@.isbn)]", "./benches/example.json"); + let ret = json!([ + { + "category" : "fiction", + "author" : "Herman Melville", + "title" : "Moby Dick", + "isbn" : "0-553-21311-3", + "price" : 8.99 + }, + { + "category" : "fiction", + "author" : "J. R. R. Tolkien", + "title" : "The Lord of the Rings", + "isbn" : "0-395-19395-8", + "price" : 22.99 + } + ]); + assert_eq!(&ret, jf.current_value()); + + let jf = do_filter("$.store.book[?(@.price < 10)]", "./benches/example.json"); + let ret = json!([ + { + "category" : "reference", + "author" : "Nigel Rees", + "title" : "Sayings of the Century", + "price" : 8.95 + }, + { + "category" : "fiction", + "author" : "Herman Melville", + "title" : "Moby Dick", + "isbn" : "0-553-21311-3", + "price" : 8.99 + } + ]); + assert_eq!(&ret, jf.current_value()); + + let jf = do_filter("$..*", "./benches/example.json"); + let json: Value = serde_json::from_str(read_json("./benches/giveme_every_thing_result.json").as_str()).unwrap(); + assert_eq!(&json, jf.current_value()); + } +} \ No newline at end of file diff --git a/src/jsonpath/json_filter/term.rs b/src/jsonpath/json_filter/term.rs new file mode 100644 index 0000000..b9e4827 --- /dev/null +++ b/src/jsonpath/json_filter/term.rs @@ -0,0 +1,130 @@ +use super::cmp::*; +use super::value_wrapper::*; +use super::value_filter::ValueFilterKey; + +#[derive(Debug)] +pub enum TermContext { + Constants(ExprTerm), + Json(Option, ValueWrapper), +} + +impl TermContext { + fn cmp(&mut self, other: &mut TermContext, cmp_fn: F, default: bool) -> TermContext { + match self { + TermContext::Constants(et) => { + match other { + TermContext::Constants(oet) => { + TermContext::Constants(ExprTerm::Bool(et.cmp(oet, cmp_fn, default))) + } + TermContext::Json(key, v) => { + TermContext::Json(None, v.take_with(key, et, cmp_fn)) + } + } + } + TermContext::Json(key, v) => { + match other { + TermContext::Json(_, ov) => { + v.cmp(ov, cmp_fn.into_type()) + } + TermContext::Constants(et) => { + TermContext::Json(None, v.take_with(key, et, cmp_fn)) + } + } + } + } + } + + fn cmp_cond(&mut self, other: &mut TermContext, cmp_cond_type: CmpCondType) -> TermContext { + match self { + TermContext::Constants(et) => { + match other { + TermContext::Constants(oet) => { + match cmp_cond_type { + CmpCondType::Or => { + TermContext::Constants(ExprTerm::Bool(et.cmp(oet, CmpOr, false))) + } + CmpCondType::And => { + TermContext::Constants(ExprTerm::Bool(et.cmp(oet, CmpAnd, false))) + } + } + } + TermContext::Json(_, v) => { + TermContext::Json(None, ValueWrapper::new(v.clone_val(), false)) + } + } + } + TermContext::Json(_, v) => { + match other { + TermContext::Json(_, ov) => { + match cmp_cond_type { + CmpCondType::Or => TermContext::Json(None, v.union(ov)), + CmpCondType::And => TermContext::Json(None, v.intersect(ov)), + } + } + _ => { + TermContext::Json(None, ValueWrapper::new(v.clone_val(), false)) + } + } + } + } + } + + pub fn eq(&mut self, other: &mut TermContext) -> TermContext { + self.cmp(other, CmpEq, false) + } + + pub fn ne(&mut self, other: &mut TermContext) -> TermContext { + self.cmp(other, CmpNe, true) + } + + pub fn gt(&mut self, other: &mut TermContext) -> TermContext { + self.cmp(other, CmpGt, false) + } + + pub fn ge(&mut self, other: &mut TermContext) -> TermContext { + self.cmp(other, CmpGe, false) + } + + pub fn lt(&mut self, other: &mut TermContext) -> TermContext { + self.cmp(other, CmpLt, false) + } + + pub fn le(&mut self, other: &mut TermContext) -> TermContext { + self.cmp(other, CmpLe, false) + } + + pub fn and(&mut self, other: &mut TermContext) -> TermContext { + self.cmp_cond(other, CmpCondType::And) + } + + pub fn or(&mut self, other: &mut TermContext) -> TermContext { + self.cmp_cond(other, CmpCondType::Or) + } +} + + +#[derive(Debug)] +pub enum ExprTerm { + String(String), + Number(f64), + Bool(bool), +} + +impl ExprTerm { + fn cmp(&self, other: &ExprTerm, cmp_fn: F, default: bool) -> bool { + match self { + ExprTerm::Bool(v1) => match other { + ExprTerm::Bool(v2) => cmp_fn.cmp_bool(v1, v2), + _ => default + } + ExprTerm::Number(v1) => match other { + ExprTerm::Number(v2) => cmp_fn.cmp_f64(v1, v2), + _ => default + } + ExprTerm::String(v1) => match other { + ExprTerm::String(v2) => cmp_fn.cmp_string(v1, v2), + _ => default + } + } + } +} diff --git a/src/jsonpath/json_filter/value_filter.rs b/src/jsonpath/json_filter/value_filter.rs new file mode 100644 index 0000000..fb8b64e --- /dev/null +++ b/src/jsonpath/json_filter/value_filter.rs @@ -0,0 +1,568 @@ +use core::borrow::Borrow; +use std::error::Error; +use std::rc::Rc; +use std::result; + +use serde_json::Value; + +use jsonpath::parser::*; + +use super::term::*; +use super::value_wrapper::*; + +trait ArrayIndex { + fn index(&self, v: &Value) -> usize; + + fn take_value(&self, v: &mut Value) -> Value { + let idx = self.index(v); + match v.get_mut(idx) { + Some(v) => v.take(), + _ => Value::Null + } + } +} + +impl ArrayIndex for f64 { + fn index(&self, v: &Value) -> usize { + if v.is_array() && self < &0_f64 { + (v.as_array().unwrap().len() as f64 + self) as usize + } else { + *self as usize + } + } +} + +impl ArrayIndex for isize { + fn index(&self, v: &Value) -> usize { + if v.is_array() && self < &0_isize { + (v.as_array().unwrap().len() as isize + self) as usize + } else { + *self as usize + } + } +} + +impl ArrayIndex for usize { + fn index(&self, _: &Value) -> usize { + *self as usize + } +} + +#[derive(Debug)] +pub enum ValueFilterKey { + Num(usize), + String(String), + All, +} + +#[derive(Debug)] +pub struct ValueFilter { + vw: ValueWrapper, + last_key: Option, + filter_mode: bool, +} + +impl ValueFilter { + pub fn new(v: Value, is_leaves: bool, filter_mode: bool) -> Self { + ValueFilter { vw: ValueWrapper::new(v, is_leaves), last_key: None, filter_mode } + } + + fn iter_to_value_vec<'a, I: Iterator>(iter: I) -> Vec { + iter.map(|v| v.take()) + .filter(|v| !v.is_null()) + .collect() + } + + fn get_nested_array(v: &mut Value, key: F, filter_mode: bool) -> Value { + if v.is_array() && v.as_array().unwrap().get(key.index(v)).is_some() { + if filter_mode { + v.take() + } else { + let idx = key.index(v); + v.get_mut(idx).unwrap().take() + } + } else { + key.take_value(v) + } + } + + fn get_nested_object(v: &mut Value, key: &String, filter_mode: bool) -> Value { + if v.is_object() && v.as_object().unwrap().contains_key(key) { + if filter_mode { + v.take() + } else { + v.get_mut(key).unwrap().take() + } + } else { + Value::Null + } + } + + fn collect_all(key: Option<&String>, v: &Value, buf: &mut Vec) { + match v { + Value::Array(vec) => { + if key.is_none() { + for v in vec { + buf.push(v.clone()); + } + } + for i in vec { + Self::collect_all(key, &i, buf); + } + } + Value::Object(v) => { + for (k, v) in v.into_iter() { + if match key { + Some(map_key) => map_key == k, + _ => true + } { + buf.push(v.clone()); + } + } + for (_, v) in v.into_iter() { + Self::collect_all(key, &v, buf); + } + } + _ => {} + } + } + + pub fn step_leaves_all(&mut self) -> &ValueWrapper { + debug!("step_leaves_all"); + let mut buf = Vec::new(); + Self::collect_all(None, &self.vw.get_val(), &mut buf); + trace!("step_leaves_all - {:?}", buf); + self.last_key = Some(ValueFilterKey::All); + self.vw = ValueWrapper::new(Value::Array(buf), true); + &self.vw + } + + pub fn step_leaves_str(&mut self, key: &str) -> &ValueWrapper { + self.step_leaves_string(&key.to_string()) + } + + pub fn step_leaves_string(&mut self, key: &String) -> &ValueWrapper { + debug!("step_leaves_string"); + let mut buf: Vec = Vec::new(); + Self::collect_all(Some(key), &self.vw.get_val(), &mut buf); + trace!("step_leaves_string - {:?}", buf); + self.last_key = Some(ValueFilterKey::String(key.clone())); + self.vw = ValueWrapper::new(Value::Array(buf), true); + &self.vw + } + + pub fn step_in_all(&mut self) -> &ValueWrapper { + debug!("step_in_all"); + + let vec = match &mut self.vw.get_val_mut() { + Value::Object(map) => Self::iter_to_value_vec(map.values_mut()), + Value::Array(list) => Self::iter_to_value_vec(list.iter_mut()), + Value::Null => Vec::new(), + other => vec![other.take()] + }; + + self.last_key = Some(ValueFilterKey::All); + self.vw.replace(Value::Array(vec)); + trace!("step_in_all - {:?}", self.vw.get_val()); + &self.vw + } + + pub fn step_in_num(&mut self, key: &f64) -> &ValueWrapper { + debug!("step_in_num"); + trace!("step_in_num - before: {} - {:?}", self.filter_mode, self.vw.get_val()); + + let v = if self.vw.is_leaves() { + let filter_mode = self.filter_mode; + match &mut self.vw.get_val_mut() { + Value::Array(v) => { + let vec: Vec = v.iter_mut() + .map(|v| Self::get_nested_array(v, *key, filter_mode)) + .filter(|v| !v.is_null()) + .collect(); + Value::Array(vec) + } + other => key.take_value(other) + } + } else { + key.take_value(self.vw.get_val_mut()) + }; + + self.last_key = Some(ValueFilterKey::Num(key.index(&v))); + self.vw.replace(v); + trace!("step_in_num - after: {:?}", self.vw.get_val()); + &self.vw + } + + pub fn step_in_str(&mut self, key: &str) -> &ValueWrapper { + self.step_in_string(&key.to_string()) + } + + pub fn step_in_string(&mut self, key: &String) -> &ValueWrapper { + debug!("step_in_string"); + trace!("step_in_string - before: {},{},{:?}", self.vw.is_leaves(), self.filter_mode, self.vw.get_val()); + + let filter_mode = self.filter_mode; + let is_leaves = self.vw.is_leaves(); + let v = match &mut self.vw.get_val_mut() { + Value::Array(ref mut vec) if is_leaves => { + let mut buf = Vec::new(); + for mut item in vec { + if let Value::Array(v) = item { + let mut ret: Vec = v.iter_mut() + .map(|v| Self::get_nested_object(v, key, filter_mode)) + .filter(|v| !v.is_null()) + .collect(); + buf.append(&mut ret); + } + } + + Value::Array(buf) + } + Value::Array(v) if !is_leaves => { + let vec: Vec = v.iter_mut() + .map(|v| Self::get_nested_object(v, key, filter_mode)) + .filter(|v| !v.is_null()) + .collect(); + + Value::Array(vec) + } + other => { + match other.get_mut(key) { + Some(v) => v.take(), + _ => Value::Null + } + } + }; + + self.last_key = Some(ValueFilterKey::String(key.clone())); + self.vw.replace(v); + trace!("step_in_string - after: {:?}", self.vw.get_val()); + &self.vw + } +} + +pub struct JsonValueFilter { + json: Rc>, + filter_stack: Vec, + token_stack: Vec, + term_stack: Vec, +} + +impl JsonValueFilter { + pub fn new(json: &str) -> result::Result { + let json: Value = serde_json::from_str(json) + .map_err(|e| e.description().to_string())?; + Ok(JsonValueFilter { + json: Rc::new(Box::new(json)), + filter_stack: Vec::new(), + token_stack: Vec::new(), + term_stack: Vec::new(), + }) + } + + fn is_peek_token_array(&self) -> bool { + if let Some(ParseToken::Array) = self.token_stack.last() { + true + } else { + false + } + } + + fn push_value_filter(&mut self, from_current: bool) { + if from_current { + self.filter_stack.last() + .map(|vf| { + ValueFilter::new(vf.vw.clone_val(), vf.vw.is_leaves(), from_current) + }) + .and_then(|vf| { + Some(self.filter_stack.push(vf)) + }); + } else { + let v: &Value = self.json.as_ref().borrow(); + self.filter_stack.push({ + ValueFilter::new(v.clone(), false, from_current) + }); + } + } + + fn replace_filter_stack(&mut self, v: Value) { + if self.filter_stack.is_empty() { + self.filter_stack.push(ValueFilter::new(v, false, false)); + } else { + match self.filter_stack.last_mut() { + Some(vf) => { + if v.is_null() { + vf.vw.replace(v); + } else if vf.vw.is_array() { + vf.vw.replace(v); + } + } + _ => {} + } + } + } + + pub fn current_value(&self) -> &Value { + match self.filter_stack.last() { + Some(v) => &v.vw.get_val(), + _ => &Value::Null + } + } + + fn token_union(&mut self, indices: Vec) { + self.token_stack.pop(); + + match self.filter_stack.last_mut() { + Some(ref mut vf) if vf.vw.is_array() && vf.vw.is_leaves() => { + if let Value::Array(mut val) = vf.vw.get_val_mut().take() { + let mut ret = Vec::new(); + for mut v in &mut val { + for i in &indices { + let v = i.take_value(v); + if !v.is_null() { + ret.push(v); + } + } + } + vf.vw.replace(Value::Array(ret)); + } + } + Some(ref mut vf) if vf.vw.is_array() && !vf.vw.is_leaves() => { + let ret = indices.into_iter() + .map(|i| i.take_value(vf.vw.get_val_mut())) + .filter(|v| !v.is_null()) + .collect(); + vf.vw.replace(Value::Array(ret)); + } + _ => {} + } + } + + fn token_range(&mut self, from: Option, to: Option) { + self.token_stack.pop(); + + fn _from_to(from: Option, to: Option, val: &Value) -> (usize, usize) { + let from = match from { + Some(v) => v.index(val), + _ => 0 + }; + let to = match to { + Some(v) => v.index(val), + _ => if let Value::Array(v) = val { v.len() } else { 0 } + }; + (from, to) + } + + fn _range(from: usize, to: usize, v: &mut Value) -> Vec { + trace!("range - {}:{}", from, to); + + (from..to).into_iter() + .map(|i| i.take_value(v)) + .filter(|v| !v.is_null()) + .collect() + } + + match self.filter_stack.last_mut() { + Some(ref mut vf) if vf.vw.is_array() && vf.vw.is_leaves() => { + if let Value::Array(mut vec) = vf.vw.get_val_mut().take() { + let mut buf = Vec::new(); + for mut item in &mut vec { + let (from, to) = _from_to(from, to, item); + let mut v: Vec = _range(from, to, item); + buf.append(&mut v); + } + vf.vw.replace(Value::Array(buf)); + } + } + Some(ref mut vf) if vf.vw.is_array() && !vf.vw.is_leaves() => { + let (from, to) = _from_to(from, to, vf.vw.get_val()); + let v: Vec = _range(from, to, vf.vw.get_val_mut()); + vf.vw.replace(Value::Array(v)); + } + _ => {} + } + } + + fn token_key(&mut self, key: String) { + match self.filter_stack.last_mut() { + Some(vf) => { + match self.token_stack.pop() { + Some(ParseToken::In) => { + vf.step_in_string(&key); + } + Some(ParseToken::Leaves) => { + vf.step_leaves_string(&key); + } + _ => {} + } + } + _ => {} + } + } + + fn token_all(&mut self) { + match self.filter_stack.last_mut() { + Some(vf) => { + match self.token_stack.pop() { + Some(ParseToken::In) => { + vf.step_in_all(); + } + Some(ParseToken::Leaves) => { + vf.step_leaves_all(); + } + _ => {} + } + } + _ => {} + } + } + + fn token_end_array(&mut self) { + trace!("array_eof - term_stack: {:?}", self.term_stack); + trace!("array_eof - filter_stack: {:?}", self.filter_stack); + + match self.term_stack.pop() { + Some(TermContext::Constants(ExprTerm::Number(v))) => { + match self.filter_stack.last_mut() { + Some(vf) => { + vf.step_in_num(&v); + } + _ => {} + } + } + Some(TermContext::Json(_, mut vw)) => { + self.replace_filter_stack(vw.get_val_mut().take()); + } + _ => { + match self.filter_stack.pop() { + Some(mut vf) => { + match vf.vw.get_val_mut() { + Value::Null | Value::Bool(false) => { + self.replace_filter_stack(Value::Null); + } + other => { + self.replace_filter_stack(other.take()); + } + } + } + _ => {} + } + } + } + } + + fn token_op(&mut self, ft: &FilterToken) { + let right = self.term_stack.pop(); + let left = self.term_stack.pop(); + + trace!("left {:?}", left); + trace!("right {:?}", right); + + if left.is_some() && right.is_some() { + let mut left = left.unwrap(); + let mut right = right.unwrap(); + + let tc = match ft { + FilterToken::Equal => left.eq(&mut right), + FilterToken::NotEqual => left.ne(&mut right), + FilterToken::Greater => left.gt(&mut right), + FilterToken::GreaterOrEqual => left.ge(&mut right), + FilterToken::Little => left.lt(&mut right), + FilterToken::LittleOrEqual => left.le(&mut right), + FilterToken::And => left.and(&mut right), + FilterToken::Or => left.or(&mut right), + }; + self.term_stack.push(tc); + } + + trace!("filter - {:?}", self.term_stack) + } +} + +impl NodeVisitor for JsonValueFilter { + fn visit_token(&mut self, token: ParseToken) { + debug!("visit_token: {:?}", token); + + match token { + ParseToken::Absolute + | ParseToken::Relative => { + if self.is_peek_token_array() { + self.token_stack.pop(); + } + self.push_value_filter(ParseToken::Relative == token); + } + ParseToken::In + | ParseToken::Leaves + | ParseToken::Array => { + self.token_stack.push(token); + } + ParseToken::ArrayEof => { + self.token_end_array(); + } + ParseToken::All => { + self.token_all(); + } + ParseToken::Key(key) => { + self.token_key(key); + } + ParseToken::Filter(ref ft) => { + self.token_op(ft); + } + ParseToken::Number(v) => { + self.term_stack.push(TermContext::Constants(ExprTerm::Number(v))) + } + ParseToken::Range(from, to) => { + self.token_range(from, to); + } + ParseToken::Union(v) => { + self.token_union(v); + } + ParseToken::Eof => { + debug!("visit_token eof"); + } + } + } + + fn end_term(&mut self) { + debug!("end_term"); + + if let Some(ParseToken::Array) = self.token_stack.last() { + self.token_stack.pop(); + } + + trace!("end_term - term_stack {:?}", self.term_stack); + trace!("end_term - token_stack {:?}", self.token_stack); + trace!("end_term - filter_stack {:?}", self.filter_stack); + + if self.token_stack.is_empty() && self.filter_stack.len() > 1 { + match self.filter_stack.pop() { + Some(vf) => { + self.term_stack.push(TermContext::Json(vf.last_key, vf.vw)); + } + _ => {} + } + } + + if match self.token_stack.last() { + Some(ParseToken::Key(_)) + | Some(ParseToken::Number(_)) => true, + _ => false + } { + match self.token_stack.pop() { + Some(ParseToken::Key(ref v)) if v.eq_ignore_ascii_case("true") => { + self.term_stack.push(TermContext::Constants(ExprTerm::Bool(true))) + } + Some(ParseToken::Key(ref v)) if v.eq_ignore_ascii_case("false") => { + self.term_stack.push(TermContext::Constants(ExprTerm::Bool(false))) + } + Some(ParseToken::Key(v)) => { + self.term_stack.push(TermContext::Constants(ExprTerm::String(v))) + } + Some(ParseToken::Number(v)) => { + self.term_stack.push(TermContext::Constants(ExprTerm::Number(v))) + } + _ => {} + } + } + } +} diff --git a/src/jsonpath/json_filter/value_wrapper.rs b/src/jsonpath/json_filter/value_wrapper.rs new file mode 100644 index 0000000..8ca8964 --- /dev/null +++ b/src/jsonpath/json_filter/value_wrapper.rs @@ -0,0 +1,256 @@ +use serde_json::Value; +use indexmap::map::IndexMap; + +use super::cmp::*; +use super::term::*; +use super::value_filter::*; + +#[derive(Debug)] +pub struct ValueWrapper { + val: Value, + leaves: bool, +} + +impl ValueWrapper { + pub fn new(val: Value, leaves: bool) -> Self { + ValueWrapper { val, leaves } + } + + pub fn is_leaves(&self) -> bool { + self.leaves + } + + pub fn cmp(&mut self, other: &mut ValueWrapper, cmp_type: CmpType) -> TermContext { + match cmp_type { + CmpType::Eq => { + TermContext::Json(None, self.intersect(other)) + } + CmpType::Ne => { + TermContext::Json(None, self.except(other)) + } + CmpType::Gt | CmpType::Ge | CmpType::Lt | CmpType::Le => { + TermContext::Constants(ExprTerm::Bool(false)) + } + } + } + + fn cmp_with_term(val: &Value, et: &ExprTerm, cmp_fn: &F, default: bool) -> bool { + match val { + Value::Bool(ref v1) => { + match et { + ExprTerm::Bool(v2) => cmp_fn.cmp_bool(v1, v2), + _ => default + } + } + Value::Number(ref v1) => match v1.as_f64() { + Some(ref v1) => { + match et { + ExprTerm::Number(v2) => cmp_fn.cmp_f64(v1, v2), + _ => default + } + } + _ => default + }, + Value::String(ref v1) => { + match et { + ExprTerm::String(v2) => cmp_fn.cmp_string(v1, v2), + _ => default + } + } + _ => default + } + } + + fn take_object_in_array(&mut self, key: &String, et: &ExprTerm, cmp: &F) -> Option { + fn _filter_with_object bool>(v: &&mut Value, key: &String, fun: F) -> bool { + match &v { + Value::Object(map) => { + match map.get(key) { + Some(vv) => fun(vv), + _ => false + } + } + _ => false + } + } + + match self.val.take() { + Value::Array(mut vec) => { + let mut ret: Vec = vec.iter_mut() + .filter(|v| { + _filter_with_object(v, key, |vv| Self::cmp_with_term(vv, et, cmp, false)) + }) + .map(|v| v.take()) + .collect(); + Some(ValueWrapper::new(Value::Array(ret), false)) + } + _ => None + } + } + + fn take_with_key_type(&mut self, key: &Option, et: &ExprTerm, cmp: &F) -> Option { + match key { + Some(ValueFilterKey::String(key)) => { + self.take_object_in_array(key, et, cmp) + } + _ => None + } + } + + pub fn take_with(&mut self, key: &Option, et: &ExprTerm, cmp: F) -> Self { + match self.take_with_key_type(key, et, &cmp) { + Some(vw) => vw, + _ => { + match self.val.take() { + Value::Array(mut vec) => { + let mut ret = vec.iter_mut() + .filter(|v| Self::cmp_with_term(&v, et, &cmp, false)) + .map(|v| v.take()) + .collect(); + ValueWrapper::new(Value::Array(ret), false) + } + other => { + if Self::cmp_with_term(&other, et, &cmp, false) { + ValueWrapper::new(other, false) + } else { + ValueWrapper::new(Value::Null, false) + } + } + } + } + } + } + + pub fn replace(&mut self, val: Value) { + let is_null = match &val { + Value::Array(v) => if v.is_empty() { true } else { false }, + Value::Object(m) => if m.is_empty() { true } else { false }, + _ => val.is_null() + }; + self.val = if is_null { Value::Null } else { val }; + } + + pub fn get_val(&self) -> &Value { + &self.val + } + + pub fn get_val_mut(&mut self) -> &mut Value { + &mut self.val + } + + pub fn clone_val(&self) -> Value { + self.val.clone() + } + + pub fn is_array(&self) -> bool { + self.val.is_array() + } + + fn uuid(v: &Value) -> String { + fn _fn(v: &Value) -> String { + match v { + Value::Null => "null".to_string(), + Value::String(v) => v.to_string(), + Value::Bool(v) => v.to_string(), + Value::Number(v) => v.to_string(), + Value::Array(v) => { + v.iter().enumerate() + .map(|(i, v)| { format!("{}{}", i, _fn(v)) }) + .collect() + } + Value::Object(v) => { + v.into_iter().map(|(k, v)| { format!("{}{}", k, _fn(v)) }).collect() + } + } + } + _fn(v) + } + + fn into_map(&mut self) -> IndexMap { + let mut map = IndexMap::new(); + match &mut self.val { + Value::Array(v1) => { + for v in v1 { + map.insert(Self::uuid(v), v.take()); + } + } + other => { + map.insert(Self::uuid(other), other.take()); + } + } + map + } + + pub fn except(&mut self, other: &mut Self) -> Self { + let map = self.into_map(); + let mut ret: IndexMap = IndexMap::new(); + match &mut other.val { + Value::Array(v1) => { + for v in v1 { + let key = Self::uuid(v); + if !map.contains_key(&key) { + ret.insert(key, v.take()); + } + } + } + other => { + let key = Self::uuid(other); + if !map.contains_key(&key) { + ret.insert(key, other.take()); + } + } + } + + let v = ret.values_mut().into_iter().map(|v| v.take()).collect(); + ValueWrapper::new(v, false) + } + + pub fn intersect(&mut self, other: &mut Self) -> Self { + let map = self.into_map(); + let mut ret: IndexMap = IndexMap::new(); + match &mut other.val { + Value::Array(v1) => { + for v in v1 { + let key = Self::uuid(v); + if map.contains_key(&key) { + ret.insert(key, v.take()); + } + } + } + other => { + let key = Self::uuid(other); + if map.contains_key(&key) { + ret.insert(key, other.take()); + } + } + } + + let v = ret.values_mut().into_iter().map(|v| v.take()).collect(); + ValueWrapper::new(v, false) + } + + pub fn union(&mut self, other: &mut Self) -> Self { + let mut map = self.into_map(); + match &mut other.val { + Value::Array(v1) => { + for v in v1 { + let key = Self::uuid(v); + if !map.contains_key(&key) { + map.insert(key, v.take()); + } + } + } + other => { + let key = Self::uuid(other); + if !map.contains_key(&key) { + map.insert(key, other.take()); + } + } + } + + let mut vw = ValueWrapper::new(Value::Null, false); + let list: Vec = map.values_mut().into_iter().map(|val| val.take()).collect(); + vw.replace(Value::Array(list)); + vw + } +} diff --git a/src/lib.rs b/src/lib.rs index d33b7bc..ff7b8c4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,5 +5,5 @@ extern crate serde; #[macro_use] extern crate serde_json; extern crate core; - +extern crate indexmap; pub mod jsonpath; \ No newline at end of file