diff --git a/.idea/runConfigurations/filter_all.xml b/.idea/runConfigurations/filter_all.xml
new file mode 100644
index 0000000..40f22cf
--- /dev/null
+++ b/.idea/runConfigurations/filter_all.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/filter_filter___trace.xml b/.idea/runConfigurations/filter_filter___trace.xml
new file mode 100644
index 0000000..86b57bc
--- /dev/null
+++ b/.idea/runConfigurations/filter_filter___trace.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/benches/data_array.json b/benches/data_array.json
index e8b4d84..502c1fa 100644
--- a/benches/data_array.json
+++ b/benches/data_array.json
@@ -90,3 +90,4 @@
"favoriteFruit": "banana"
}
]
+
diff --git a/src/jsonpath/json_filter.rs b/src/jsonpath/json_filter.rs
index 9c65fd4..5ed0b54 100644
--- a/src/jsonpath/json_filter.rs
+++ b/src/jsonpath/json_filter.rs
@@ -28,7 +28,9 @@ enum CmpType {
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;
}
@@ -207,128 +209,76 @@ enum ExprTerm {
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(Vec),
+ Json(ValueWrapper),
}
impl TermContext {
- fn cmp_value_term<'a, F: PrivCmp>(et: &'a ExprTerm, cmp_fn: F, default: bool)
- -> impl FnMut(&&'a mut Value) -> bool {
- move |v| match v {
- 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 cmp_values_term(v: &mut Vec, et: &ExprTerm, cmp: F) -> TermContext {
- let ret = v.iter_mut()
- .filter(Self::cmp_value_term(et, cmp, false))
- .map(|v| v.take())
- .collect();
- TermContext::Json(ret)
- }
-
- fn cmp_term_term(v1: &ExprTerm, v2: &ExprTerm, cmp_fn: F, default: bool) -> bool {
- match v1 {
- ExprTerm::Bool(vv1) => match v2 {
- ExprTerm::Bool(vv2) => cmp_fn.cmp_bool(vv1, vv2),
- _ => default
- }
- ExprTerm::Number(vv1) => match v2 {
- ExprTerm::Number(vv2) => cmp_fn.cmp_f64(vv1, vv2),
- _ => default
- }
- ExprTerm::String(vv1) => match v2 {
- ExprTerm::String(vv2) => cmp_fn.cmp_string(vv1, vv2),
- _ => default
- }
- }
- }
-
- fn cmp_value_value(v1: &mut Vec, v2: &mut Vec, cmp_type: CmpType) -> TermContext {
- match cmp_type {
- CmpType::Eq => {
- let mut map: HashMap = HashMap::new();
- for v in v1 {
- map.insert(format!("{:?}", v), v.take());
- }
-
- let mut ret: HashMap = HashMap::new();
- for v in v2 {
- let key = format!("{:?}", v);
- if map.contains_key(&key) {
- ret.insert(key, v.take());
- }
- }
-
- let v = ret.values_mut().into_iter().map(|v| v.take()).collect();
- TermContext::Json(v)
- }
- CmpType::Ne => {
- let mut map: HashMap = HashMap::new();
- for v in v1 {
- map.insert(format!("{:?}", v), v.take());
- }
-
- let mut ret: HashMap = HashMap::new();
- for v in v2 {
- let key = format!("{:?}", v);
- if !map.contains_key(&key) {
- ret.insert(key, v.take());
- }
- }
-
- let v = ret.values_mut().into_iter().map(|v| v.take()).collect();
- TermContext::Json(v)
- }
- CmpType::Gt | CmpType::Ge | CmpType::Lt | CmpType::Le => {
- TermContext::Constants(ExprTerm::Bool(false))
- }
- }
- }
-
fn cmp(&mut self, other: &mut TermContext, cmp_fn: F, default: bool) -> TermContext {
match self {
TermContext::Constants(et) => {
match other {
TermContext::Constants(oet) => {
- let b = Self::cmp_term_term(et, oet, cmp_fn, default);
- TermContext::Constants(ExprTerm::Bool(b))
+ TermContext::Constants(ExprTerm::Bool(et.cmp(oet, cmp_fn, default)))
}
TermContext::Json(v) => {
- Self::cmp_values_term(v, et, cmp_fn)
+ TermContext::Json(v.take_with(et, cmp_fn))
}
}
}
TermContext::Json(v) => {
match other {
TermContext::Json(ov) => {
- Self::cmp_value_value(v, ov, cmp_fn.into_type())
+ v.cmp(ov, cmp_fn.into_type())
}
TermContext::Constants(et) => {
- Self::cmp_values_term(v, et, cmp_fn)
+ TermContext::Json(v.take_with(et, cmp_fn))
+ }
+ }
+ }
+ }
+ }
+
+ fn cmp_cond(&mut self, other: &mut TermContext, cmp_fn: F) -> TermContext {
+ match self {
+ TermContext::Constants(et) => {
+ match other {
+ TermContext::Constants(oet) => {
+ TermContext::Constants(ExprTerm::Bool(et.cmp(oet, cmp_fn, false)))
+ }
+ TermContext::Json(v) => {
+ TermContext::Json(ValueWrapper::new(v.clone_data()))
+ }
+ }
+ }
+ TermContext::Json(v) => {
+ match other {
+ TermContext::Json(ov) => {
+ TermContext::Json(v.union(ov))
+ }
+ _ => {
+ TermContext::Json(ValueWrapper::new(v.clone_data()))
}
}
}
@@ -359,42 +309,6 @@ impl TermContext {
self.cmp(other, CmpLe, false)
}
- fn cmp_cond(&mut self, other: &mut TermContext, cmp_fn: F) -> TermContext {
- match self {
- TermContext::Constants(et) => {
- match other {
- TermContext::Constants(oet) => {
- let b = Self::cmp_term_term(et, oet, cmp_fn, false);
- TermContext::Constants(ExprTerm::Bool(b))
- }
- TermContext::Json(v) => {
- let list = v.iter_mut().map(|v| v.take()).collect();
- TermContext::Json(list)
- }
- }
- }
- TermContext::Json(v) => {
- match other {
- TermContext::Json(ov) => {
- let mut map: HashMap = HashMap::new();
- for val in v {
- map.insert(format!("{:?}", val), val.take());
- }
- for val in ov {
- map.insert(format!("{:?}", val), val.take());
- }
- let list: Vec = map.values_mut().into_iter().map(|val| val.take()).collect();
- TermContext::Json(list)
- }
- TermContext::Constants(et) => {
- let list = v.iter_mut().map(|v| v.take()).collect();
- TermContext::Json(list)
- }
- }
- }
- }
- }
-
fn and(&mut self, other: &mut TermContext) -> TermContext {
self.cmp_cond(other, CmpAnd)
}
@@ -406,7 +320,7 @@ impl TermContext {
pub struct JsonValueFilter {
json: Rc>,
- current: Vec,
+ current: ValueWrapper,
stack: Vec,
filter_stack: Vec,
in_array: bool,
@@ -429,7 +343,7 @@ impl NodeVisitor for JsonValueFilter {
self.in_array = false;
match self.filter_stack.pop() {
Some(TermContext::Constants(_)) => unreachable!(),
- Some(TermContext::Json(v)) => self.current = v,
+ Some(TermContext::Json(v)) => self.current.replace(vec![v.clone_data()]),
_ => {}
}
@@ -521,7 +435,7 @@ impl JsonValueFilter {
let root = json.clone();
Ok(JsonValueFilter {
json: Rc::new(Box::new(json)),
- current: vec![root],
+ current: ValueWrapper::new(root),
stack: Vec::new(),
filter_stack: Vec::new(),
in_array: false,
@@ -532,10 +446,10 @@ impl JsonValueFilter {
JsonValueFilter {
json: self.json.clone(),
current: if from_current {
- self.current.clone()
+ ValueWrapper::new(self.current.clone_data())
} else {
let v: &Value = self.json.as_ref().borrow();
- vec![v.clone()]
+ ValueWrapper::new(v.clone())
},
stack: Vec::new(),
filter_stack: Vec::new(),
@@ -611,35 +525,35 @@ impl JsonValueFilter {
}
}
- fn step_leaves_all(&mut self) -> &Vec {
+ fn step_leaves_all(&mut self) -> &ValueWrapper {
debug!("step_leaves_all");
- let mut buf = Vec::new();
+ let mut vw = ValueWrapper::new(Value::Null);
loop {
- self.step_in_all().iter().map(|v| buf.push(v.clone()));
- if self.current.len() == 0 {
+ vw.push(self.step_in_all().clone_data());
+ if let Value::Null = self.current._val {
break;
}
}
- self.current = buf;
+ self.current = vw;
&self.current
}
- fn step_leaves(&mut self, key: String) -> &Vec {
+ fn step_leaves(&mut self, key: String) -> &ValueWrapper {
debug!("step_leaves");
- let mut buf = Vec::new();
+ let mut vw = ValueWrapper::new(Value::Null);
loop {
- self.step_in(key.clone()).iter().map(|v| buf.push(v.clone()));
- if self.current.len() == 0 {
+ vw.push(self.step_in(key.clone()).clone_data());
+ if let Value::Null = self.current._val {
break;
}
}
- self.current = buf;
+ self.current = vw;
&self.current
}
- fn step_in_all(&mut self) -> &Vec {
+ fn step_in_all(&mut self) -> &ValueWrapper {
debug!("step_in_all");
fn to_vec<'a, I: Iterator- >(iter: I) -> Vec {
@@ -648,40 +562,270 @@ impl JsonValueFilter {
.collect()
}
- self.current = self.current.iter_mut()
- .flat_map(|v| {
- match v {
- Value::Object(map) => to_vec(map.values_mut()),
- Value::Array(list) => to_vec(list.iter_mut()),
- Value::Null => Vec::new(),
- _ => vec![v.take()]
- }
- }).collect();
+ let vec = match &mut self.current._val {
+ Value::Object(map) => to_vec(map.values_mut()),
+ Value::Array(list) => to_vec(list.iter_mut()),
+ Value::Null => Vec::new(),
+ other => vec![other.take()]
+ };
+ self.current.replace(vec);
&self.current
}
- fn step_in(&mut self, key: I) -> &Vec {
+ fn step_in(&mut self, key: I) -> &ValueWrapper {
debug!("step_in");
- self.current = self.current.iter_mut()
- .map(|v| {
- trace!("step_in - map: {:?}", v);
- match v.get_mut(&key) {
- Some(value) => value.take(),
- _ => Value::Null
- }
- })
- .filter(|v| !v.is_null())
- .collect();
+
+ let v = match self.current._val.get_mut(&key) {
+ Some(value) => value.take(),
+ _ => Value::Null
+ };
+
+ trace!("{:?}", v);
+
+ self.current.replace(vec![v]);
&self.current
}
- fn current(&self) -> &Vec {
+ fn current(&self) -> &ValueWrapper {
&self.current
}
}
+#[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(self.intersect(other))
+ }
+ CmpType::Ne => {
+ TermContext::Json(self.except(other))
+ }
+ CmpType::Gt | CmpType::Ge | CmpType::Lt | CmpType::Le => {
+ TermContext::Constants(ExprTerm::Bool(false))
+ }
+ }
+ }
+
+ fn cmp_with_term(&self, et: &ExprTerm, cmp_fn: &F, default: bool) -> bool {
+ match &self._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_with(&mut self, et: &ExprTerm, cmp: F) -> Self {
+ match self._val.take() {
+ Value::Array(vec) => {
+ let mut vw = ValueWrapper::new(Value::Null);
+ for v in vec {
+ if self.cmp_with_term(et, &cmp, false) {
+ vw.push(v);
+ }
+ }
+ vw
+ }
+ other => {
+ if self.cmp_with_term(et, &cmp, false) {
+ ValueWrapper::new(other)
+ } else {
+ ValueWrapper::new(Value::Null)
+ }
+ }
+ }
+ }
+
+ fn replace(&mut self, values: Vec) {
+ self._val.take();
+ for v in values {
+ self.push(v);
+ }
+ }
+
+ 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(v);
+ self._val = Value::Array(values);
+ }
+ }
+
+ fn clone_data(&self) -> Value {
+ self._val.clone()
+ }
+
+ fn is_array(&self) -> bool {
+ self._val.is_array()
+ }
+
+ fn is_object(&self) -> bool {
+ self.data().is_object()
+ }
+
+ fn is_number(&self) -> bool {
+ self.data().is_number()
+ }
+
+ 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 intersect(&mut self, other: &mut Self) -> Self {
+ let map = self.into_map();
+ let mut ret: HashMap = HashMap::new();
+ match &mut other._val {
+ Value::Array(vv2) => {
+ for v in vv2 {
+ 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 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 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(list);
+ vw
+ }
+
+ fn data(&self) -> &Value {
+ match &self._val {
+ Value::Array(v) if v.len() == 1 => {
+ self._val.get(0).unwrap()
+ }
+ other => {
+ other
+ }
+ }
+ }
+}
+
#[cfg(test)]
mod tests {
extern crate env_logger;
@@ -700,6 +844,18 @@ mod tests {
});
}
+ fn new_filter(file: &str) -> JsonValueFilter {
+ let string = read_json(file);
+ JsonValueFilter::new(string.as_str()).unwrap()
+ }
+
+ fn do_filter(path: &str, file: &str) -> JsonValueFilter {
+ let mut jf = new_filter(file);
+ 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();
@@ -711,22 +867,20 @@ mod tests {
fn step_in() {
setup();
- let string = read_json("./benches/data_obj.json");
- let mut jf = JsonValueFilter::new(string.as_str()).unwrap();
+ let mut jf = new_filter("./benches/data_obj.json");
{
let current = jf.step_in("friends");
- assert_eq!(current[0].is_array(), true);
+ assert_eq!(current.is_array(), true);
}
- let string = read_json("./benches/data_array.json");
- let mut jf = JsonValueFilter::new(string.as_str()).unwrap();
+ let mut jf = new_filter("./benches/data_array.json");
{
let current = jf.step_in(1);
- assert_eq!(current[0].is_object(), true);
+ assert_eq!(current.is_object(), true);
}
{
let current = jf.step_in("friends");
- assert_eq!(current[0].is_array(), true);
+ assert_eq!(current.is_array(), true);
}
}
@@ -734,23 +888,22 @@ mod tests {
fn fork() {
setup();
- let string = read_json("./benches/data_obj.json");
- let mut jf = JsonValueFilter::new(string.as_str()).unwrap();
+ let mut jf = new_filter("./benches/data_obj.json");
{
let current = jf.step_in("friends");
- assert_eq!(current[0].is_array(), true);
+ assert_eq!(current.is_array(), true);
}
let jf_from_current = jf.fork(true);
{
let current = jf_from_current.current();
- assert_eq!(current[0].is_array(), true);
+ assert_eq!(current.is_array(), true);
}
let mut jf_from_root = jf_from_current.fork(false);
{
let current = jf_from_root.step_in("age");
- assert_eq!(current[0].is_number(), true);
+ assert_eq!(current.is_number(), true);
}
}
@@ -758,15 +911,14 @@ mod tests {
fn filter() {
setup();
- let string = read_json("./benches/data_obj.json");
- let mut jf = JsonValueFilter::new(string.as_str()).unwrap();
- let mut parser = Parser::new("$.school[?(@.friends==@.friends)]");
- parser.parse(&mut jf).unwrap();
- let v = json!([
- {"id": 0,"name": "Millicent Norman"},
- {"id": 1,"name": "Vincent Cannon" },
- {"id": 2,"name": "Gray Berry"}
- ]);
- assert_eq!(v, jf.current());
+ let jf = do_filter("$.school[?(@.friends)]", "./benches/data_obj.json");
+ let v = json!({
+ "friends" : [
+ {"id": 0,"name": "Millicent Norman"},
+ {"id": 1,"name": "Vincent Cannon" },
+ {"id": 2,"name": "Gray Berry"}
+ ]
+ });
+ assert_eq!(&v, jf.current().data());
}
}
\ No newline at end of file
diff --git a/src/jsonpath/mod.rs b/src/jsonpath/mod.rs
index aca76b7..152d998 100644
--- a/src/jsonpath/mod.rs
+++ b/src/jsonpath/mod.rs
@@ -2,11 +2,4 @@ mod path_reader;
mod tokenizer;
mod parser;
mod json_filter;
-mod utils;
-
-use std::result;
-
-fn read(json: &str, path: &str) -> result::Result<(), String> {
-
- Ok(())
-}
\ No newline at end of file
+mod utils;
\ No newline at end of file