diff --git a/Cargo.toml b/Cargo.toml index 93f89e7..f90ba25 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jsonpath_lib" -version = "0.1.5" +version = "0.1.6" authors = ["Changseok Han "] description = "JsonPath in Rust and Webassembly - Webassembly Demo: https://freestrings.github.io/jsonpath" diff --git a/nodejs/native/Cargo.toml b/nodejs/native/Cargo.toml index 6f9ed3f..0d0ac44 100644 --- a/nodejs/native/Cargo.toml +++ b/nodejs/native/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jsonpath-rs" -version = "0.1.1" +version = "0.1.2" authors = ["Changseok Han "] description = "JsonPath engine for NodeJs with Rust native implementation." keywords = ["library", "jsonpath", "json"] diff --git a/src/filter/term.rs b/src/filter/term.rs index 4828491..8c25979 100644 --- a/src/filter/term.rs +++ b/src/filter/term.rs @@ -14,9 +14,11 @@ impl TermContext { TermContext::Constants(et) => { match other { TermContext::Constants(oet) => { + trace!("const-const"); TermContext::Constants(ExprTerm::Bool(et.cmp(oet, cmp_fn, default))) } TermContext::Json(key, v) => { + trace!("const-json"); TermContext::Json(None, v.take_with(key, et, cmp_fn, true)) } } @@ -24,6 +26,7 @@ impl TermContext { TermContext::Json(key, v) => { match other { TermContext::Json(key_other, ov) => { + trace!("json-json"); fn is_json(t: &TermContext) -> bool { match t { @@ -32,18 +35,16 @@ impl TermContext { } } - let mut v = v.filter(key); - let mut ov = ov.filter(key_other); let mut c = v.into_term(key); let mut oc = ov.into_term(key_other); - if is_json(&c) && is_json(&oc) { - v.cmp(&mut ov, cmp_fn.into_type()) + v.cmp(&ov, cmp_fn.into_type()) } else { c.cmp(&mut oc, cmp_fn, default) } } TermContext::Constants(et) => { + trace!("json-const"); TermContext::Json(None, v.take_with(key, et, cmp_fn, false)) } } @@ -87,26 +88,32 @@ impl TermContext { } pub fn eq(&self, other: &TermContext) -> TermContext { + trace!("eq"); self.cmp(other, CmpEq, false) } pub fn ne(&self, other: &TermContext) -> TermContext { + trace!("ne"); self.cmp(other, CmpNe, true) } pub fn gt(&self, other: &TermContext) -> TermContext { + trace!("gt"); self.cmp(other, CmpGt, false) } pub fn ge(&self, other: &TermContext) -> TermContext { + trace!("ge"); self.cmp(other, CmpGe, false) } pub fn lt(&self, other: &TermContext) -> TermContext { + trace!("lt"); self.cmp(other, CmpLt, false) } pub fn le(&self, other: &TermContext) -> TermContext { + trace!("le"); self.cmp(other, CmpLe, false) } diff --git a/src/filter/value_filter.rs b/src/filter/value_filter.rs index a3feccd..85b7134 100644 --- a/src/filter/value_filter.rs +++ b/src/filter/value_filter.rs @@ -1,6 +1,6 @@ use std::error::Error; -use std::result::Result; use std::ops::Deref; +use std::result::Result; use serde_json::Value; @@ -324,6 +324,8 @@ impl JsonValueFilter { vf.vw.set_leaves(is_leaves); if v.is_null() { vf.vw.replace(v); + } else if v.is_array() && v.as_array().unwrap().is_empty() { + vf.vw.replace(RefValue::Null.into()); } else if vf.vw.is_array() { vf.vw.replace(v); } @@ -480,6 +482,9 @@ impl JsonValueFilter { _ => {} } } + Some(TermContext::Constants(ExprTerm::Bool(false))) => { + self.replace_filter_stack(RefValue::Null.into(), false); + } Some(TermContext::Json(_, vw)) => { self.replace_filter_stack(vw.get_val().clone(), vw.is_leaves()); } diff --git a/src/filter/value_wrapper.rs b/src/filter/value_wrapper.rs index 314c81d..d654806 100644 --- a/src/filter/value_wrapper.rs +++ b/src/filter/value_wrapper.rs @@ -155,6 +155,7 @@ impl ValueWrapper { } fn into_hashset(&self) -> IndexSet { + trace!("into_hashset"); let mut hashset = IndexSet::new(); match self.val.deref() { RefValue::Array(ref v1) => { @@ -170,9 +171,10 @@ impl ValueWrapper { } pub fn except(&self, other: &Self) -> Self { + trace!("except"); let hashset = self.into_hashset(); let mut ret: IndexSet = IndexSet::new(); - match &(*other.val) { + match other.val.deref() { RefValue::Array(ref v1) => { for v in v1 { if !hashset.contains(v) { @@ -192,6 +194,7 @@ impl ValueWrapper { } pub fn intersect(&self, other: &Self) -> Self { + trace!("intersect"); let hashset = self.into_hashset(); let mut ret: IndexSet = IndexSet::new(); match other.val.deref() { @@ -214,6 +217,7 @@ impl ValueWrapper { } pub fn union(&self, other: &Self) -> Self { + trace!("union"); let mut hashset = self.into_hashset(); match other.val.deref() { RefValue::Array(ref v1) => { @@ -249,6 +253,7 @@ impl ValueWrapper { } pub fn filter(&self, key: &Option) -> Self { + trace!("filter"); let v = match self.val.deref() { RefValue::Array(ref vec) => { let mut ret = Vec::new(); diff --git a/src/ref_value/de.rs b/src/ref_value/de.rs index b5f3311..a1552b6 100644 --- a/src/ref_value/de.rs +++ b/src/ref_value/de.rs @@ -14,106 +14,118 @@ impl<'de> Deserialize<'de> for RefValue { where D: Deserializer<'de>, { + + struct RefValueVisitor {} + + impl<'de> Visitor<'de> for RefValueVisitor { + type Value = RefValue; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("any valid JSON value") + } + + #[inline] + fn visit_bool(self, v: bool) -> Result + where + E: Error, { + Ok(RefValue::Bool(v)) + } + + #[inline] + fn visit_i64(self, v: i64) -> Result + where + E: serde::de::Error, { + Ok(RefValue::Number(v.into())) + } + + #[inline] + fn visit_u64(self, v: u64) -> Result + where + E: serde::de::Error, { + Ok(RefValue::Number(v.into())) + } + + #[inline] + fn visit_f64(self, v: f64) -> Result + where + E: serde::de::Error, { + let n: Value = v.into(); + if let Value::Number(n) = n { + Ok(RefValue::Number(n)) + } else { + unreachable!() + } + } + + #[inline] + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, { + self.visit_string(String::from(v)) + } + + #[inline] + fn visit_string(self, v: String) -> Result + where + E: serde::de::Error, { + Ok(RefValue::String(v)) + } + + #[inline] + fn visit_none(self) -> Result + where + E: serde::de::Error, { + Ok(RefValue::Null) + } + + #[inline] + fn visit_some(self, deserializer: D) -> Result where + D: Deserializer<'de>, { + Deserialize::deserialize(deserializer) + } + + #[inline] + fn visit_unit(self) -> Result + where + E: serde::de::Error, { + Ok(RefValue::Null) + } + + #[inline] + fn visit_seq(self, mut visitor: A) -> Result + where + A: SeqAccess<'de>, { + let mut vec = Vec::new(); + + while let Some(elem) = visitor.next_element()? { + let e: RefValue = elem; + let v: RefValueWrapper = e.into(); + vec.push(v); + } + + Ok(RefValue::Array(vec)) + } + + #[inline] + fn visit_map(self, mut visitor: A) -> Result + where + A: MapAccess<'de>, { + let mut values = IndexMap::new(); + match visitor.next_key() { + Ok(Some(first_key)) => { + let next: RefValue = visitor.next_value()?; + values.insert(first_key, next.into()); + while let Some((k, v)) = visitor.next_entry()? { + let value: RefValue = v; + values.insert(k, value.into()); + } + Ok(RefValue::Object(values)) + } + _ => Ok(RefValue::Object(IndexMap::new())), + } + } + } + deserializer.deserialize_any(RefValueVisitor {}) } -} - -struct RefValueVisitor {} - -impl<'de> Visitor<'de> for RefValueVisitor { - type Value = RefValue; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("any valid JSON value") - } - - fn visit_bool(self, v: bool) -> Result - where - E: Error, { - Ok(RefValue::Bool(v)) - } - - fn visit_i64(self, v: i64) -> Result - where - E: serde::de::Error, { - Ok(RefValue::Number(v.into())) - } - - fn visit_u64(self, v: u64) -> Result - where - E: serde::de::Error, { - Ok(RefValue::Number(v.into())) - } - - fn visit_f64(self, v: f64) -> Result - where - E: serde::de::Error, { - let n: Value = v.into(); - if let Value::Number(n) = n { - Ok(RefValue::Number(n)) - } else { - unreachable!() - } - } - - fn visit_str(self, v: &str) -> Result - where - E: serde::de::Error, { - self.visit_string(String::from(v)) - } - - fn visit_string(self, v: String) -> Result - where - E: serde::de::Error, { - Ok(RefValue::String(v)) - } - - fn visit_none(self) -> Result - where - E: serde::de::Error, { - Ok(RefValue::Null) - } - - fn visit_some(self, deserializer: D) -> Result where - D: Deserializer<'de>, { - Deserialize::deserialize(deserializer) - } - - fn visit_unit(self) -> Result - where - E: serde::de::Error, { - Ok(RefValue::Null) - } - - fn visit_seq(self, mut visitor: A) -> Result - where - A: SeqAccess<'de>, { - let mut vec = Vec::new(); - - while let Some(elem) = visitor.next_element()? { - let e: RefValue = elem; - let v: RefValueWrapper = e.into(); - vec.push(v); - } - - Ok(RefValue::Array(vec)) - } - - fn visit_map(self, mut visitor: A) -> Result - where - A: MapAccess<'de>, { - let mut values = IndexMap::new(); - match visitor.next_key() { - Ok(Some(first_key)) => { - let next: RefValue = visitor.next_value()?; - values.insert(first_key, next.into()); - while let Some((k, v)) = visitor.next_entry()? { - let value: RefValue = v; - values.insert(k, value.into()); - } - Ok(RefValue::Object(values)) - } - _ => Ok(RefValue::Object(IndexMap::new())), - } - } } \ No newline at end of file diff --git a/src/ref_value/model.rs b/src/ref_value/model.rs index c977b2a..e063495 100644 --- a/src/ref_value/model.rs +++ b/src/ref_value/model.rs @@ -124,11 +124,17 @@ pub enum RefValue { Object(IndexMap), } +static REF_VALUE_NULL: &'static str = "$jsonpath::ref_value::model::RefValue::Null"; + impl Hash for RefValue { fn hash(&self, state: &mut H) { match self { - RefValue::Null => RefValue::Null.hash(state), - RefValue::Bool(b) => b.hash(state), + RefValue::Null => { + REF_VALUE_NULL.hash(state) + }, + RefValue::Bool(b) => { + b.hash(state) + }, RefValue::Number(n) => { if n.is_f64() { n.as_f64().unwrap().to_string().hash(state) @@ -138,7 +144,9 @@ impl Hash for RefValue { n.as_u64().unwrap().hash(state); } } - RefValue::String(s) => s.hash(state), + RefValue::String(s) => { + s.hash(state) + }, RefValue::Object(map) => { for (_, v) in map { v.hash(state); diff --git a/src/ref_value/utf8.rs b/src/ref_value/utf8.rs deleted file mode 100644 index d798531..0000000 --- a/src/ref_value/utf8.rs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2017 Serde Developers -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -const TAG_CONT: u8 = 0b1000_0000; -const TAG_TWO_B: u8 = 0b1100_0000; -const TAG_THREE_B: u8 = 0b1110_0000; -const TAG_FOUR_B: u8 = 0b1111_0000; -const MAX_ONE_B: u32 = 0x80; -const MAX_TWO_B: u32 = 0x800; -const MAX_THREE_B: u32 = 0x10000; - -#[inline] -pub fn encode(c: char) -> Encode { - let code = c as u32; - let mut buf = [0; 4]; - let pos = if code < MAX_ONE_B { - buf[3] = code as u8; - 3 - } else if code < MAX_TWO_B { - buf[2] = (code >> 6 & 0x1F) as u8 | TAG_TWO_B; - buf[3] = (code & 0x3F) as u8 | TAG_CONT; - 2 - } else if code < MAX_THREE_B { - buf[1] = (code >> 12 & 0x0F) as u8 | TAG_THREE_B; - buf[2] = (code >> 6 & 0x3F) as u8 | TAG_CONT; - buf[3] = (code & 0x3F) as u8 | TAG_CONT; - 1 - } else { - buf[0] = (code >> 18 & 0x07) as u8 | TAG_FOUR_B; - buf[1] = (code >> 12 & 0x3F) as u8 | TAG_CONT; - buf[2] = (code >> 6 & 0x3F) as u8 | TAG_CONT; - buf[3] = (code & 0x3F) as u8 | TAG_CONT; - 0 - }; - Encode { buf: buf, pos: pos } -} - -pub struct Encode { - buf: [u8; 4], - pos: usize, -} - -impl Encode { - pub fn as_str(&self) -> &str { - std::str::from_utf8(&self.buf[self.pos..]).unwrap() - } -} diff --git a/tests/filter.rs b/tests/filter.rs index 0db5c3b..7200693 100644 --- a/tests/filter.rs +++ b/tests/filter.rs @@ -154,17 +154,17 @@ fn return_type() { } #[test] -fn op() { +fn op_default() { 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"} - ] - }); + "friends": [ + {"id": 0, "name": "Millicent Norman"}, + {"id": 1, "name": "Vincent Cannon" }, + {"id": 2, "name": "Gray Berry"} + ] + }); assert_eq!(friends, jf.into_value()); let jf = do_filter("$.friends[?(@.name)]", "./benches/data_obj.json"); @@ -203,6 +203,81 @@ fn op() { assert_eq!(friends, jf.into_value()); } +#[test] +fn op_number() { + setup(); + + let json = json!({ "a": 1 }); + let ret = jsonpath::select(&json, "$.[?(@.a == 1)]").unwrap(); + assert_eq!(json, ret); + let ret = jsonpath::select(&json, "$.[?(@.a != 2)]").unwrap(); + assert_eq!(json, ret); + let ret = jsonpath::select(&json, "$.[?(@.a < 2)]").unwrap(); + assert_eq!(json, ret); + let ret = jsonpath::select(&json, "$.[?(@.a <= 1)]").unwrap(); + assert_eq!(json, ret); + let ret = jsonpath::select(&json, "$.[?(@.a > 0)]").unwrap(); + assert_eq!(json, ret); + let ret = jsonpath::select(&json, "$.[?(@.a >= 0)]").unwrap(); + assert_eq!(json, ret); +} + +#[test] +fn op_string() { + setup(); + + let json = json!({ "a": "b" }); + let ret = jsonpath::select(&json, r#"$.[?(@.a == "b")]"#).unwrap(); + assert_eq!(json!({ "a": "b" }), ret); + let ret = jsonpath::select(&json, r#"$.[?(@.a != "c")]"#).unwrap(); + assert_eq!(json!({ "a": "b" }), ret); + let ret = jsonpath::select(&json, r#"$.[?(@.a < "b")]"#).unwrap(); + assert_eq!(Value::Null, ret); + let ret = jsonpath::select(&json, r#"$.[?(@.a <= "b")]"#).unwrap(); + assert_eq!(json!({ "a": "b" }), ret); + let ret = jsonpath::select(&json, r#"$.[?(@.a > "b")]"#).unwrap(); + assert_eq!(Value::Null, ret); + let ret = jsonpath::select(&json, r#"$.[?(@.a >= "b")]"#).unwrap(); + assert_eq!(json!({ "a": "b" }), ret); +} + +#[test] +fn op_object() { + setup(); + + let json = json!({ + "a": { "1": 1 }, + "b": { "2": 2 }, + "c": { "1": 1 }, + }); + let ret = jsonpath::select(&json, r#"$.[?(@.a == @.c)]"#).unwrap(); + assert_eq!(json, ret); + let ret = jsonpath::select(&json, r#"$.[?(@.a != @.c)]"#).unwrap(); + assert_eq!(Value::Null, ret); + let ret = jsonpath::select(&json, r#"$.[?(@.a < @.c)]"#).unwrap(); + assert_eq!(Value::Null, ret); + let ret = jsonpath::select(&json, r#"$.[?(@.a <= @.c)]"#).unwrap(); + assert_eq!(Value::Null, ret); + let ret = jsonpath::select(&json, r#"$.[?(@.a > @.c)]"#).unwrap(); + assert_eq!(Value::Null, ret); + let ret = jsonpath::select(&json, r#"$.[?(@.a >= @.c)]"#).unwrap(); + assert_eq!(Value::Null, ret); +} + +#[test] +fn op_complex() { + setup(); + + let json = json!({ "a": { "b": 1 } }); + let ret = jsonpath::select(&json, r#"$.[?(1 == @.a)]"#).unwrap(); + assert_eq!(Value::Null, ret); + let ret = jsonpath::select(&json, r#"$.[?("1" != @.a)]"#).unwrap(); + assert_eq!(Value::Null, ret); + let ret = jsonpath::select(&json, r#"$.[?(@.a <= 1)]"#).unwrap(); + assert_eq!(Value::Null, ret); + let ret = jsonpath::select(&json, r#"$.[?(@.a > "1")]"#).unwrap(); + assert_eq!(Value::Null, ret); +} #[test] fn example() { setup(); diff --git a/wasm/Cargo.toml b/wasm/Cargo.toml index ba881df..9b4a7a8 100644 --- a/wasm/Cargo.toml +++ b/wasm/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "jsonpath-wasm" -version = "0.1.5" +version = "0.1.6" authors = ["Changseok Han "] description = "JsonPath Webassembly version compiled by Rust - Demo: https://freestrings.github.io/jsonpath" keywords = ["library", "jsonpath", "json", "webassembly"]