From 7a187ff7a3a827bf00c1561e4f5744250e53676f Mon Sep 17 00:00:00 2001 From: paomian Date: Wed, 9 Oct 2024 22:00:51 +0800 Subject: [PATCH 1/6] chore: expose jsonlike trait --- src/jsonpath.rs | 18 +-- src/lib.rs | 8 +- src/parser/mod.rs | 1 + src/parser/model.rs | 67 +++++----- src/parser/parser.rs | 104 +++++++++++----- src/path/index.rs | 183 ++++++++++++++++----------- src/path/json.rs | 19 +-- src/path/mod.rs | 192 +++++++++++++++++++++++++---- src/path/top.rs | 287 ++++++++++++++++++++++--------------------- 9 files changed, 568 insertions(+), 311 deletions(-) diff --git a/src/jsonpath.rs b/src/jsonpath.rs index 94daa6d..8aeaa83 100644 --- a/src/jsonpath.rs +++ b/src/jsonpath.rs @@ -1,11 +1,16 @@ use crate::path::json_path_instance; +use crate::path::JsonLike; use crate::JsonPath; use crate::JsonPathValue; use crate::JsonPathValue::NoValue; use crate::JsonPtr; use crate::Value; +use std::fmt::Debug; -impl JsonPath { +impl JsonPath +where + T: JsonLike + Default + Debug + Clone, +{ /// finds a slice of data in the set json. /// The result is a vector of references to the incoming structure. /// @@ -29,12 +34,11 @@ impl JsonPath { /// vec![JsonPathValue::Slice(&expected_value, expected_path)] /// ); /// ``` - pub fn find_slice<'a>(&'a self, json: &'a Value) -> Vec> { + pub fn find_slice<'a>(&'a self, json: &'a T) -> Vec> { use crate::path::Path; let instance = json_path_instance(self, json); let res = instance.find(JsonPathValue::from_root(json)); - let has_v: Vec> = - res.into_iter().filter(|v| v.has_value()).collect(); + let has_v: Vec> = res.into_iter().filter(|v| v.has_value()).collect(); if has_v.is_empty() { vec![NoValue] @@ -45,7 +49,7 @@ impl JsonPath { /// like [`Self::find_slice`] but returns a vector of [`JsonPtr`], which has no [`JsonPathValue::NoValue`]. /// if there is no match, it will return an empty vector - pub fn find_slice_ptr<'a>(&'a self, json: &'a Value) -> Vec> { + pub fn find_slice_ptr<'a>(&'a self, json: &'a T) -> Vec> { use crate::path::Path; json_path_instance(self, json) .find(JsonPathValue::from_root(json)) @@ -76,7 +80,7 @@ impl JsonPath { /// /// assert_eq!(cloned_data, Value::Array(vec![json!({"active":1})])); /// ``` - pub fn find(&self, json: &Value) -> Value { + pub fn find(&self, json: &T) -> Value { let slice = self.find_slice(json); if !slice.is_empty() { if JsonPathValue::only_no_value(&slice) { @@ -114,7 +118,7 @@ impl JsonPath { /// let expected_path = "$.['first'].['second'][0]".to_string(); /// assert_eq!(slice_of_data, Value::Array(vec![Value::String(expected_path)])); /// ``` - pub fn find_as_path(&self, json: &Value) -> Value { + pub fn find_as_path(&self, json: &T) -> T { Value::Array( self.find_slice(json) .into_iter() diff --git a/src/lib.rs b/src/lib.rs index c316776..9404f92 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -309,17 +309,17 @@ impl<'a, Data: Clone + Debug + Default> JsonPathValue<'a, Data> { } impl<'a, Data> JsonPathValue<'a, Data> { - fn only_no_value(input: &[JsonPathValue<'a, Data>]) -> bool { + pub fn only_no_value(input: &[JsonPathValue<'a, Data>]) -> bool { !input.is_empty() && input.iter().filter(|v| v.has_value()).count() == 0 } - fn map_vec(data: Vec<(&'a Data, JsPathStr)>) -> Vec> { + pub fn map_vec(data: Vec<(&'a Data, JsPathStr)>) -> Vec> { data.into_iter() .map(|(data, pref)| Slice(data, pref)) .collect() } - fn map_slice(self, mapper: F) -> Vec> + pub fn map_slice(self, mapper: F) -> Vec> where F: FnOnce(&'a Data, JsPathStr) -> Vec<(&'a Data, JsPathStr)>, { @@ -334,7 +334,7 @@ impl<'a, Data> JsonPathValue<'a, Data> { } } - fn flat_map_slice(self, mapper: F) -> Vec> + pub fn flat_map_slice(self, mapper: F) -> Vec> where F: FnOnce(&'a Data, JsPathStr) -> Vec>, { diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 65cacde..36db3bd 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -9,4 +9,5 @@ pub(crate) mod parser; pub use errors::JsonPathParserError; pub use model::JsonPath; +pub use model::JsonPathIndex; pub use parser::{parse_json_path, Rule}; diff --git a/src/parser/model.rs b/src/parser/model.rs index 6fe648a..e09e22f 100644 --- a/src/parser/model.rs +++ b/src/parser/model.rs @@ -9,21 +9,21 @@ use std::{convert::TryFrom, str::FromStr}; /// /// usually it's created by using [`FromStr`] or [`TryFrom<&str>`] #[derive(Debug, Clone)] -pub enum JsonPath { +pub enum JsonPath { /// The $ operator Root, /// Field represents key Field(String), /// The whole chain of the path. - Chain(Vec), + Chain(Vec>), /// The .. operator Descent(String), /// The ..* operator DescentW, /// The indexes for array - Index(JsonPathIndex), + Index(JsonPathIndex), /// The @ operator - Current(Box), + Current(Box>), /// The * operator Wildcard, /// The item uses to define the unresolved state @@ -32,7 +32,7 @@ pub enum JsonPath { Fn(Function), } -impl Display for JsonPath { +impl Display for JsonPath { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let str = match self { JsonPath::Root => "$".to_string(), @@ -52,7 +52,7 @@ impl Display for JsonPath { } } -impl TryFrom<&str> for JsonPath { +impl TryFrom<&str> for JsonPath { type Error = JsonPathParserError; /// Parses a string into a [JsonPath]. @@ -65,7 +65,7 @@ impl TryFrom<&str> for JsonPath { } } -impl FromStr for JsonPath { +impl FromStr for JsonPath { type Err = JsonPathParserError; /// Parses a string into a [JsonPath]. @@ -94,20 +94,20 @@ impl Display for Function { } #[derive(Debug, Clone)] -pub enum JsonPathIndex { +pub enum JsonPathIndex { /// A single element in array - Single(Value), + Single(T), /// Union represents a several indexes - UnionIndex(Vec), + UnionIndex(Vec), /// Union represents a several keys UnionKeys(Vec), /// DEfault slice where the items are start/end/step respectively Slice(i32, i32, usize), /// Filter ?() - Filter(FilterExpression), + Filter(FilterExpression), } -impl Display for JsonPathIndex { +impl Display for JsonPathIndex { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let str = match self { JsonPathIndex::Single(e) => format!("[{}]", e), @@ -141,18 +141,18 @@ impl Display for JsonPathIndex { } #[derive(Debug, Clone, PartialEq)] -pub enum FilterExpression { +pub enum FilterExpression { /// a single expression like a > 2 - Atom(Operand, FilterSign, Operand), + Atom(Operand, FilterSign, Operand), /// and with && - And(Box, Box), + And(Box>, Box>), /// or with || - Or(Box, Box), + Or(Box>, Box>), /// not with ! - Not(Box), + Not(Box>), } -impl Display for FilterExpression { +impl Display for FilterExpression { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let str = match self { FilterExpression::Atom(left, sign, right) => { @@ -172,8 +172,8 @@ impl Display for FilterExpression { } } -impl FilterExpression { - pub fn exists(op: Operand) -> Self { +impl FilterExpression { + pub fn exists(op: Operand) -> Self { FilterExpression::Atom( op, FilterSign::Exists, @@ -184,12 +184,12 @@ impl FilterExpression { /// Operand for filtering expressions #[derive(Debug, Clone)] -pub enum Operand { - Static(Value), - Dynamic(Box), +pub enum Operand { + Static(T), + Dynamic(Box>), } -impl Display for Operand { +impl Display for Operand { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let str = match self { Operand::Static(e) => e.to_string(), @@ -200,8 +200,8 @@ impl Display for Operand { } #[allow(dead_code)] -impl Operand { - pub fn val(v: Value) -> Self { +impl Operand { + pub fn val(v: T) -> Self { Operand::Static(v) } } @@ -268,7 +268,10 @@ impl FilterSign { } } -impl PartialEq for JsonPath { +impl PartialEq for JsonPath +where + T: PartialEq, +{ fn eq(&self, other: &Self) -> bool { match (self, other) { (JsonPath::Root, JsonPath::Root) => true, @@ -286,7 +289,10 @@ impl PartialEq for JsonPath { } } -impl PartialEq for JsonPathIndex { +impl PartialEq for JsonPathIndex +where + T: PartialEq, +{ fn eq(&self, other: &Self) -> bool { match (self, other) { (JsonPathIndex::Slice(s1, e1, st1), JsonPathIndex::Slice(s2, e2, st2)) => { @@ -305,7 +311,10 @@ impl PartialEq for JsonPathIndex { } } -impl PartialEq for Operand { +impl PartialEq for Operand +where + T: PartialEq, +{ fn eq(&self, other: &Self) -> bool { match (self, other) { (Operand::Static(v1), Operand::Static(v2)) => v1 == v2, diff --git a/src/parser/parser.rs b/src/parser/parser.rs index d99f8f9..81663f2 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -7,7 +7,6 @@ use crate::parser::model::{ }; use pest::iterators::{Pair, Pairs}; use pest::Parser; -use serde_json::Value; #[derive(Parser)] #[grammar = "parser/grammar/json_path.pest"] @@ -18,7 +17,10 @@ struct JsonPathParser; /// # Errors /// /// Returns a variant of [JsonPathParserError] if the parsing operation failed. -pub fn parse_json_path(jp_str: &str) -> Result { +pub fn parse_json_path<'a, T>(jp_str: &'a str) -> Result, JsonPathParserError> +where + T: From<&'a str> + From> + From + From + From + 'static, +{ JsonPathParser::parse(Rule::path, jp_str) .map_err(Box::new)? .next() @@ -31,7 +33,10 @@ pub fn parse_json_path(jp_str: &str) -> Result { /// # Errors /// /// Returns a variant of [JsonPathParserError] if the parsing operation failed -fn parse_internal(rule: Pair) -> Result { +fn parse_internal<'a, T>(rule: Pair<'a, Rule>) -> Result, JsonPathParserError> +where + T: From<&'a str> + From> + From + From + From + 'static, +{ match rule.as_rule() { Rule::path => rule .into_inner() @@ -75,7 +80,7 @@ fn parse_key(rule: Pair) -> Result, JsonPathParserError> { parsed_key } -fn parse_slice(pairs: Pairs) -> Result { +fn parse_slice(pairs: Pairs) -> Result, JsonPathParserError> { let mut start = 0; let mut end = 0; let mut step = 1; @@ -90,7 +95,7 @@ fn parse_slice(pairs: Pairs) -> Result Ok(JsonPathIndex::Slice(start, end, step)) } -fn parse_unit_keys(pairs: Pairs) -> Result { +fn parse_unit_keys(pairs: Pairs) -> Result, JsonPathParserError> { let mut keys = vec![]; for pair in pairs { @@ -99,26 +104,35 @@ fn parse_unit_keys(pairs: Pairs) -> Result Result { +fn number_to_value(number: &str) -> Result +where + T: From + From, +{ match number .parse::() .ok() - .map(Value::from) - .or_else(|| number.parse::().ok().map(Value::from)) + .map(T::from) + .or_else(|| number.parse::().ok().map(T::from)) { Some(value) => Ok(value), None => Err(JsonPathParserError::InvalidNumber(number.to_string())), } } -fn bool_to_value(boolean: &str) -> Value { +fn bool_to_value(boolean: &str) -> T +where + T: From, +{ boolean .parse::() - .map(Value::from) + .map(T::from) .expect("unreachable: according to .pest this is either `true` or `false`") } -fn parse_unit_indexes(pairs: Pairs) -> Result { +fn parse_unit_indexes(pairs: Pairs) -> Result, JsonPathParserError> +where + T: From + From, +{ let mut keys = vec![]; for pair in pairs { @@ -127,20 +141,21 @@ fn parse_unit_indexes(pairs: Pairs) -> Result) -> Result { - let parsed_chain = match parse_internal(rule)? { +fn parse_chain_in_operand<'a, T>(rule: Pair<'a, Rule>) -> Result, JsonPathParserError> +where + T: From<&'a str> + From> + From + From + From + 'static, +{ + let parsed_chain = match parse_internal::(rule)? { JsonPath::Chain(elems) => { if elems.len() == 1 { match elems.first() { Some(JsonPath::Index(JsonPathIndex::UnionKeys(keys))) => { - Operand::val(Value::from(keys.clone())) + Operand::val(T::from(keys.clone())) } Some(JsonPath::Index(JsonPathIndex::UnionIndex(keys))) => { - Operand::val(Value::from(keys.clone())) - } - Some(JsonPath::Field(f)) => { - Operand::val(Value::Array(vec![Value::from(f.clone())])) + Operand::val(T::from(keys.clone())) } + Some(JsonPath::Field(f)) => Operand::val(T::from(vec![f.to_string()])), _ => Operand::Dynamic(Box::new(JsonPath::Chain(elems))), } } else { @@ -152,12 +167,18 @@ fn parse_chain_in_operand(rule: Pair) -> Result) -> Result { +fn parse_filter_index<'a, T>(pair: Pair<'a, Rule>) -> Result, JsonPathParserError> +where + T: From<&'a str> + From> + From + From + From + 'static, +{ Ok(JsonPathIndex::Filter(parse_logic_or(pair.into_inner())?)) } -fn parse_logic_or(pairs: Pairs) -> Result { - let mut expr: Option = None; +fn parse_logic_or<'a, T>(pairs: Pairs<'a, Rule>) -> Result, JsonPathParserError> +where + T: From<&'a str> + From> + From + From + From + 'static, +{ + let mut expr: Option> = None; // only possible for the loop not to produce any value (except Errors) if pairs.len() == 0 { return Err(JsonPathParserError::UnexpectedNoneLogicError( @@ -175,8 +196,13 @@ fn parse_logic_or(pairs: Pairs) -> Result) -> Result { - let mut expr: Option = None; +fn parse_logic_and<'a, T>( + pairs: Pairs<'a, Rule>, +) -> Result, JsonPathParserError> +where + T: From<&'a str> + From> + From + From + From + 'static, +{ + let mut expr: Option> = None; // only possible for the loop not to produce any value (except Errors) if pairs.len() == 0 { return Err(JsonPathParserError::UnexpectedNoneLogicError( @@ -194,7 +220,12 @@ fn parse_logic_and(pairs: Pairs) -> Result) -> Result { +fn parse_logic_not<'a, T>( + mut pairs: Pairs<'a, Rule>, +) -> Result, JsonPathParserError> +where + T: From<&'a str> + From> + From + From + From + 'static, +{ if let Some(rule) = pairs.peek().map(|x| x.as_rule()) { match rule { Rule::not => { @@ -213,17 +244,22 @@ fn parse_logic_not(mut pairs: Pairs) -> Result) -> Result { +fn parse_logic_atom<'a, T>( + mut pairs: Pairs<'a, Rule>, +) -> Result, JsonPathParserError> +where + T: From<&'a str> + From> + From + From + From + 'static, +{ if let Some(rule) = pairs.peek().map(|x| x.as_rule()) { match rule { Rule::logic_or => parse_logic_or(pairs.next().expect("unreachable in arithmetic: should have a value as pairs.peek() was Some(_)").into_inner()), Rule::atom => { - let left: Operand = parse_atom(pairs.next().unwrap())?; + let left: Operand = parse_atom(pairs.next().unwrap())?; if pairs.peek().is_none() { Ok(FilterExpression::exists(left)) } else { let sign: FilterSign = FilterSign::new(pairs.next().expect("unreachable in arithmetic: should have a value as pairs.peek() was Some(_)").as_str()); - let right: Operand = + let right: Operand = parse_atom(pairs.next().expect("unreachable in arithemetic: should have a right side operand"))?; Ok(FilterExpression::Atom(left, sign, right)) } @@ -238,19 +274,25 @@ fn parse_logic_atom(mut pairs: Pairs) -> Result) -> Result { +fn parse_atom<'a, T>(rule: Pair<'a, Rule>) -> Result, JsonPathParserError> +where + T: From<&'a str> + From> + From + From + From + 'static, +{ let atom = down(rule.clone())?; let parsed_atom = match atom.as_rule() { Rule::number => Operand::Static(number_to_value(rule.as_str())?), - Rule::string_qt => Operand::Static(Value::from(down(atom)?.as_str())), + Rule::string_qt => Operand::Static(T::from(down(atom)?.as_str())), Rule::chain => parse_chain_in_operand(down(rule)?)?, Rule::boolean => Operand::Static(bool_to_value(rule.as_str())), - _ => Operand::Static(Value::Null), + _ => Operand::Static(T::from("")), }; Ok(parsed_atom) } -fn parse_index(rule: Pair) -> Result { +fn parse_index<'a, T>(rule: Pair<'a, Rule>) -> Result, JsonPathParserError> +where + T: From<&'a str> + From> + From + From + From + 'static, +{ let next = down(rule)?; let parsed_index = match next.as_rule() { Rule::unsigned => JsonPathIndex::Single(number_to_value(next.as_str())?), diff --git a/src/path/index.rs b/src/path/index.rs index cbb00ea..8f76872 100644 --- a/src/path/index.rs +++ b/src/path/index.rs @@ -1,28 +1,32 @@ +use std::fmt::Debug; + use crate::jsp_idx; use crate::parser::model::{FilterExpression, FilterSign, JsonPath}; use crate::path::json::*; use crate::path::top::ObjectField; use crate::path::{json_path_instance, process_operand, JsonPathValue, Path, PathInstance}; use crate::JsonPathValue::{NoValue, Slice}; -use serde_json::value::Value::Array; + use serde_json::Value; -use super::TopPaths; +use super::{JsonLike, TopPaths}; /// process the slice like [start:end:step] #[derive(Debug)] -pub(crate) struct ArraySlice { +pub struct ArraySlice { start_index: i32, end_index: i32, step: usize, + _t: std::marker::PhantomData, } -impl ArraySlice { - pub(crate) fn new(start_index: i32, end_index: i32, step: usize) -> ArraySlice { +impl ArraySlice { + pub(crate) fn new(start_index: i32, end_index: i32, step: usize) -> Self { ArraySlice { start_index, end_index, step, + _t: std::marker::PhantomData, } } @@ -54,9 +58,9 @@ impl ArraySlice { } } - fn process<'a, T>(&self, elements: &'a [T]) -> Vec<(&'a T, usize)> { + fn process<'a, F>(&self, elements: &'a [F]) -> Vec<(&'a F, usize)> { let len = elements.len() as i32; - let mut filtered_elems: Vec<(&'a T, usize)> = vec![]; + let mut filtered_elems: Vec<(&'a F, usize)> = vec![]; match (self.start(len), self.end(len)) { (Some(start_idx), Some(end_idx)) => { let end_idx = if end_idx == 0 { @@ -76,8 +80,11 @@ impl ArraySlice { } } -impl<'a> Path<'a> for ArraySlice { - type Data = Value; +impl<'a, T> Path<'a> for ArraySlice +where + T: JsonLike + Default + Clone + Debug, +{ + type Data = T; fn find(&self, input: JsonPathValue<'a, Self::Data>) -> Vec> { input.flat_map_slice(|data, pref| { @@ -97,18 +104,25 @@ impl<'a> Path<'a> for ArraySlice { } /// process the simple index like [index] -pub(crate) struct ArrayIndex { +pub struct ArrayIndex { index: usize, + _t: std::marker::PhantomData, } -impl ArrayIndex { +impl ArrayIndex { pub(crate) fn new(index: usize) -> Self { - ArrayIndex { index } + ArrayIndex { + index, + _t: std::marker::PhantomData, + } } } -impl<'a> Path<'a> for ArrayIndex { - type Data = Value; +impl<'a, T> Path<'a> for ArrayIndex +where + T: JsonLike + Default + Clone + Debug, +{ + type Data = T; fn find(&self, input: JsonPathValue<'a, Self::Data>) -> Vec> { input.flat_map_slice(|data, pref| { @@ -121,27 +135,40 @@ impl<'a> Path<'a> for ArrayIndex { } /// process @ element -pub(crate) struct Current<'a> { - tail: Option>, +pub struct Current<'a, T> { + tail: Option>, + _t: std::marker::PhantomData, } -impl<'a> Current<'a> { - pub(crate) fn from(jp: &'a JsonPath, root: &'a Value) -> Self { +impl<'a, T> Current<'a, T> +where + T: JsonLike + Default + Clone + Debug, +{ + pub(crate) fn from(jp: &'a JsonPath, root: &'a T) -> Self { match jp { JsonPath::Empty => Current::none(), tail => Current::new(Box::new(json_path_instance(tail, root))), } } - pub(crate) fn new(tail: PathInstance<'a>) -> Self { - Current { tail: Some(tail) } + pub(crate) fn new(tail: PathInstance<'a, T>) -> Self { + Current { + tail: Some(tail), + _t: std::marker::PhantomData, + } } pub(crate) fn none() -> Self { - Current { tail: None } + Current { + tail: None, + _t: std::marker::PhantomData, + } } } -impl<'a> Path<'a> for Current<'a> { - type Data = Value; +impl<'a, T> Path<'a> for Current<'a, T> +where + T: JsonLike + Default + Clone + Debug, +{ + type Data = T; fn find(&self, input: JsonPathValue<'a, Self::Data>) -> Vec> { self.tail @@ -152,13 +179,16 @@ impl<'a> Path<'a> for Current<'a> { } /// the list of indexes like [1,2,3] -pub(crate) struct UnionIndex<'a> { - indexes: Vec>, +pub struct UnionIndex<'a, T> { + indexes: Vec>, } -impl<'a> UnionIndex<'a> { - pub fn from_indexes(elems: &'a [Value]) -> Self { - let mut indexes: Vec> = vec![]; +impl<'a, T> UnionIndex<'a, T> +where + T: JsonLike + Default + Clone + Debug, +{ + pub fn from_indexes(elems: &'a [T]) -> Self { + let mut indexes: Vec> = vec![]; for idx in elems.iter() { indexes.push(TopPaths::ArrayIndex(ArrayIndex::new( @@ -169,7 +199,7 @@ impl<'a> UnionIndex<'a> { UnionIndex::new(indexes) } pub fn from_keys(elems: &'a [String]) -> Self { - let mut indexes: Vec> = vec![]; + let mut indexes: Vec> = vec![]; for key in elems.iter() { indexes.push(TopPaths::ObjectField(ObjectField::new(key))) @@ -178,13 +208,16 @@ impl<'a> UnionIndex<'a> { UnionIndex::new(indexes) } - pub fn new(indexes: Vec>) -> Self { + pub fn new(indexes: Vec>) -> Self { UnionIndex { indexes } } } -impl<'a> Path<'a> for UnionIndex<'a> { - type Data = Value; +impl<'a, T> Path<'a> for UnionIndex<'a, T> +where + T: JsonLike + Default + Clone + Debug, +{ + type Data = T; fn find(&self, input: JsonPathValue<'a, Self::Data>) -> Vec> { self.indexes @@ -195,27 +228,30 @@ impl<'a> Path<'a> for UnionIndex<'a> { } /// process filter element like [?(op sign op)] -pub enum FilterPath<'a> { +pub enum FilterPath<'a, T> { Filter { - left: PathInstance<'a>, - right: PathInstance<'a>, + left: PathInstance<'a, T>, + right: PathInstance<'a, T>, op: &'a FilterSign, }, Or { - left: PathInstance<'a>, - right: PathInstance<'a>, + left: PathInstance<'a, T>, + right: PathInstance<'a, T>, }, And { - left: PathInstance<'a>, - right: PathInstance<'a>, + left: PathInstance<'a, T>, + right: PathInstance<'a, T>, }, Not { - exp: PathInstance<'a>, + exp: PathInstance<'a, T>, }, } -impl<'a> FilterPath<'a> { - pub(crate) fn new(expr: &'a FilterExpression, root: &'a Value) -> Self { +impl<'a, T> FilterPath<'a, T> +where + T: JsonLike + Default + Clone + Debug, +{ + pub(crate) fn new(expr: &'a FilterExpression, root: &'a T) -> Self { match expr { FilterExpression::Atom(left, op, right) => FilterPath::Filter { left: process_operand(left, root), @@ -238,16 +274,16 @@ impl<'a> FilterPath<'a> { fn compound( one: &'a FilterSign, two: &'a FilterSign, - left: Vec>, - right: Vec>, + left: Vec>, + right: Vec>, ) -> bool { FilterPath::process_atom(one, left.clone(), right.clone()) || FilterPath::process_atom(two, left, right) } fn process_atom( op: &'a FilterSign, - left: Vec>, - right: Vec>, + left: Vec>, + right: Vec>, ) -> bool { match op { FilterSign::Equal => eq( @@ -295,7 +331,7 @@ impl<'a> FilterPath<'a> { } } - fn process(&self, curr_el: &'a Value) -> bool { + fn process(&self, curr_el: &'a T) -> bool { let pref = String::new(); match self { FilterPath::Filter { left, right, op } => FilterPath::process_atom( @@ -324,32 +360,33 @@ impl<'a> FilterPath<'a> { } } -impl<'a> Path<'a> for FilterPath<'a> { - type Data = Value; +impl<'a, T> Path<'a> for FilterPath<'a, T> { + type Data = T; fn find(&self, input: JsonPathValue<'a, Self::Data>) -> Vec> { - input.flat_map_slice(|data, pref| { - let mut res = vec![]; - match data { - Array(elems) => { - for (i, el) in elems.iter().enumerate() { - if self.process(el) { - res.push(Slice(el, jsp_idx(&pref, i))) - } - } - } - el => { - if self.process(el) { - res.push(Slice(el, pref)) - } - } - } - if res.is_empty() { - vec![NoValue] - } else { - res - } - }) + todo!() + // input.flat_map_slice(|data, pref| { + // let mut res = vec![]; + // match data { + // Array(elems) => { + // for (i, el) in elems.iter().enumerate() { + // if self.process(el) { + // res.push(Slice(el, jsp_idx(&pref, i))) + // } + // } + // } + // el => { + // if self.process(el) { + // res.push(Slice(el, pref)) + // } + // } + // } + // if res.is_empty() { + // vec![NoValue] + // } else { + // res + // } + // }) } } @@ -364,13 +401,13 @@ mod tests { use crate::JsonPathValue::NoValue; use crate::path; - use serde_json::json; + use serde_json::{json, Value}; #[test] fn array_slice_end_start_test() { let array = [0, 1, 2, 3, 4, 5]; let len = array.len() as i32; - let mut slice = ArraySlice::new(0, 0, 0); + let mut slice: ArraySlice = ArraySlice::new(0, 0, 0); assert_eq!(slice.start(len).unwrap(), 0); slice.start_index = 1; diff --git a/src/path/json.rs b/src/path/json.rs index 83d9a7e..a3c8206 100644 --- a/src/path/json.rs +++ b/src/path/json.rs @@ -1,10 +1,15 @@ use regex::Regex; use serde_json::Value; +use super::JsonLike; + /// compare sizes of json elements /// The method expects to get a number on the right side and array or string or object on the left /// where the number of characters, elements or fields will be compared respectively. -pub fn size(left: Vec<&Value>, right: Vec<&Value>) -> bool { +pub fn size(left: Vec<&T>, right: Vec<&T>) -> bool +where + T: JsonLike, +{ if let Some(Value::Number(n)) = right.first() { if let Some(sz) = n.as_f64() { for el in left.iter() { @@ -23,7 +28,7 @@ pub fn size(left: Vec<&Value>, right: Vec<&Value>) -> bool { /// ensure the array on the left side is a subset of the array on the right side. //todo change the naive impl to sets -pub fn sub_set_of(left: Vec<&Value>, right: Vec<&Value>) -> bool { +pub fn sub_set_of(left: Vec<&T>, right: Vec<&T>) -> bool { if left.is_empty() { return true; } @@ -57,7 +62,7 @@ pub fn sub_set_of(left: Vec<&Value>, right: Vec<&Value>) -> bool { /// ensure at least one element in the array on the left side belongs to the array on the right side. //todo change the naive impl to sets -pub fn any_of(left: Vec<&Value>, right: Vec<&Value>) -> bool { +pub fn any_of(left: Vec<&T>, right: Vec<&T>) -> bool { if left.is_empty() { return true; } @@ -93,7 +98,7 @@ pub fn any_of(left: Vec<&Value>, right: Vec<&Value>) -> bool { } /// ensure that the element on the left sides mathes the regex on the right side -pub fn regex(left: Vec<&Value>, right: Vec<&Value>) -> bool { +pub fn regex(left: Vec<&T>, right: Vec<&T>) -> bool { if left.is_empty() || right.is_empty() { return false; } @@ -116,7 +121,7 @@ pub fn regex(left: Vec<&Value>, right: Vec<&Value>) -> bool { } /// ensure that the element on the left side belongs to the array on the right side. -pub fn inside(left: Vec<&Value>, right: Vec<&Value>) -> bool { +pub fn inside(left: Vec<&T>, right: Vec<&T>) -> bool { if left.is_empty() { return false; } @@ -145,7 +150,7 @@ pub fn inside(left: Vec<&Value>, right: Vec<&Value>) -> bool { } /// ensure the number on the left side is less the number on the right side -pub fn less(left: Vec<&Value>, right: Vec<&Value>) -> bool { +pub fn less(left: Vec<&T>, right: Vec<&T>) -> bool { if left.len() == 1 && right.len() == 1 { match (left.first(), right.first()) { (Some(Value::Number(l)), Some(Value::Number(r))) => l @@ -160,7 +165,7 @@ pub fn less(left: Vec<&Value>, right: Vec<&Value>) -> bool { } /// compare elements -pub fn eq(left: Vec<&Value>, right: Vec<&Value>) -> bool { +pub fn eq(left: Vec<&T>, right: Vec<&T>) -> bool { if left.len() != right.len() { false } else { diff --git a/src/path/mod.rs b/src/path/mod.rs index 1309865..4b026dc 100644 --- a/src/path/mod.rs +++ b/src/path/mod.rs @@ -1,8 +1,10 @@ -use crate::JsonPathValue; -use serde_json::Value; +use std::fmt::Debug; + +use crate::{jsp_idx, jsp_obj, JsonPathValue}; +use serde_json::{json, Value}; use crate::parser::model::{Function, JsonPath, JsonPathIndex, Operand}; -use crate::path::index::{ArrayIndex, ArraySlice, Current, FilterPath, UnionIndex}; +pub use crate::path::index::{ArrayIndex, ArraySlice, Current, FilterPath, UnionIndex}; use crate::path::top::*; /// The module is in charge of processing [[JsonPathIndex]] elements @@ -12,6 +14,140 @@ mod json; /// The module is responsible for processing of the [[JsonPath]] elements mod top; +pub trait JsonLike { + type Data; + fn get(&self, key: &str) -> Option<&Self::Data>; + fn itre(&self, pref: String) -> Vec>; + fn array_len(&self) -> JsonPathValue<'static, Self::Data>; + fn init_with_usize(cnt: usize) -> Self::Data; + fn deep_flatten(&self, pref: String) -> Vec<(&Self::Data, String)>; + fn deep_path_by_key<'a>( + &'a self, + key: ObjectField<'a, Self::Data>, + pref: String, + ) -> Vec<(&'a Self::Data, String)>; + fn as_u64(&self) -> Option; + fn as_array(&self) -> Option<&Vec>; + fn size(left: Vec<&Self::Data>, right: Vec<&Self::Data>) -> bool; +} + +impl JsonLike for Value { + type Data = Value; + + fn get(&self, key: &str) -> Option<&Self::Data> { + self.get(key) + } + + fn itre(&self, pref: String) -> Vec> { + let res = match self { + Value::Array(elems) => { + let mut res = vec![]; + for (idx, el) in elems.iter().enumerate() { + res.push(JsonPathValue::Slice(el, jsp_idx(&pref, idx))); + } + res + } + Value::Object(elems) => { + let mut res = vec![]; + for (key, el) in elems.into_iter() { + res.push(JsonPathValue::Slice(el, jsp_obj(&pref, key))); + } + res + } + _ => vec![], + }; + if res.is_empty() { + vec![JsonPathValue::NoValue] + } else { + res + } + } + fn array_len(&self) -> JsonPathValue<'static, Self::Data> { + match self { + Value::Array(elems) => JsonPathValue::NewValue(json!(elems.len())), + _ => JsonPathValue::NoValue, + } + } + + fn init_with_usize(cnt: usize) -> Self::Data { + json!(cnt) + } + fn deep_flatten(&self, pref: String) -> Vec<(&Self::Data, String)> { + let mut acc = vec![]; + match self { + Value::Object(elems) => { + for (f, v) in elems.into_iter() { + let pref = jsp_obj(&pref, f); + acc.push((v, pref.clone())); + acc.append(&mut v.deep_flatten(pref)); + } + } + Value::Array(elems) => { + for (i, v) in elems.iter().enumerate() { + let pref = jsp_idx(&pref, i); + acc.push((v, pref.clone())); + acc.append(&mut v.deep_flatten(pref)); + } + } + _ => (), + } + acc + } + fn deep_path_by_key<'a>( + &'a self, + key: ObjectField<'a, Self::Data>, + pref: String, + ) -> Vec<(&'a Self::Data, String)> { + let mut result: Vec<(&'a Value, String)> = + JsonPathValue::vec_as_pair(key.find(JsonPathValue::new_slice(self, pref.clone()))); + match self { + Value::Object(elems) => { + let mut next_levels: Vec<(&'a Value, String)> = elems + .into_iter() + .flat_map(|(k, v)| v.deep_path_by_key(key.clone(), jsp_obj(&pref, k))) + .collect(); + result.append(&mut next_levels); + result + } + Value::Array(elems) => { + let mut next_levels: Vec<(&'a Value, String)> = elems + .iter() + .enumerate() + .flat_map(|(i, v)| v.deep_path_by_key(key.clone(), jsp_idx(&pref, i))) + .collect(); + result.append(&mut next_levels); + result + } + _ => result, + } + } + + fn as_u64(&self) -> Option { + self.as_u64() + } + + fn as_array(&self) -> Option<&Vec> { + self.as_array() + } + + fn size(left: Vec<&Self::Data>, right: Vec<&Self::Data>) -> bool { + if let Some(Value::Number(n)) = right.first() { + if let Some(sz) = n.as_f64() { + for el in left.iter() { + match el { + Value::String(v) if v.len() == sz as usize => true, + Value::Array(elems) if elems.len() == sz as usize => true, + Value::Object(fields) if fields.len() == sz as usize => true, + _ => return false, + }; + } + return true; + } + } + false + } +} + /// The trait defining the behaviour of processing every separated element. /// type Data usually stands for json [[Value]] /// The trait also requires to have a root json to process. @@ -37,24 +173,27 @@ pub trait Path<'a> { } /// all known Paths, mostly to avoid a dynamic Box and vtable for internal function -pub(crate) enum TopPaths<'a> { - RootPointer(RootPointer<'a, Value>), - ObjectField(ObjectField<'a>), - Chain(Chain<'a>), - Wildcard(Wildcard), - DescentObject(DescentObject<'a>), - DescentWildcard(DescentWildcard), - Current(Current<'a>), +pub enum TopPaths<'a, T> { + RootPointer(RootPointer<'a, T>), + ObjectField(ObjectField<'a, T>), + Chain(Chain<'a, T>), + Wildcard(Wildcard), + DescentObject(DescentObject<'a, T>), + DescentWildcard(DescentWildcard), + Current(Current<'a, T>), ArrayIndex(ArrayIndex), ArraySlice(ArraySlice), - UnionIndex(UnionIndex<'a>), - FilterPath(FilterPath<'a>), + UnionIndex(UnionIndex<'a, T>), + FilterPath(FilterPath<'a, T>), IdentityPath(IdentityPath), - FnPath(FnPath), + FnPath(FnPath), } -impl<'a> Path<'a> for TopPaths<'a> { - type Data = Value; +impl<'a, T> Path<'a> for TopPaths<'a, T> +where + T: JsonLike + Default + Clone + Debug, +{ + type Data = T; fn find(&self, input: JsonPathValue<'a, Self::Data>) -> Vec> { match self { @@ -116,17 +255,23 @@ impl<'a> Path<'a> for TopPaths<'a> { } /// The basic type for instances. -pub(crate) type PathInstance<'a> = Box + 'a>; +pub(crate) type PathInstance<'a, T> = Box + 'a>; /// The major method to process the top part of json part -pub(crate) fn json_path_instance<'a>(json_path: &'a JsonPath, root: &'a Value) -> TopPaths<'a> { +pub(crate) fn json_path_instance<'a, T: JsonLike>( + json_path: &'a JsonPath, + root: &'a T, +) -> TopPaths<'a, T> +where + T: JsonLike + Default + Clone + Debug, +{ match json_path { JsonPath::Root => TopPaths::RootPointer(RootPointer::new(root)), JsonPath::Field(key) => TopPaths::ObjectField(ObjectField::new(key)), JsonPath::Chain(chain) => TopPaths::Chain(Chain::from(chain, root)), - JsonPath::Wildcard => TopPaths::Wildcard(Wildcard {}), + JsonPath::Wildcard => TopPaths::Wildcard(Wildcard::new()), JsonPath::Descent(key) => TopPaths::DescentObject(DescentObject::new(key)), - JsonPath::DescentW => TopPaths::DescentWildcard(DescentWildcard), + JsonPath::DescentW => TopPaths::DescentWildcard(DescentWildcard::new()), JsonPath::Current(value) => TopPaths::Current(Current::from(value, root)), JsonPath::Index(JsonPathIndex::Single(index)) => { TopPaths::ArrayIndex(ArrayIndex::new(index.as_u64().unwrap() as usize)) @@ -144,12 +289,15 @@ pub(crate) fn json_path_instance<'a>(json_path: &'a JsonPath, root: &'a Value) - TopPaths::FilterPath(FilterPath::new(fe, root)) } JsonPath::Empty => TopPaths::IdentityPath(IdentityPath {}), - JsonPath::Fn(Function::Length) => TopPaths::FnPath(FnPath::Size), + JsonPath::Fn(Function::Length) => TopPaths::FnPath(FnPath::new_size()), } } /// The method processes the operand inside the filter expressions -fn process_operand<'a>(op: &'a Operand, root: &'a Value) -> PathInstance<'a> { +fn process_operand<'a, T>(op: &'a Operand, root: &'a T) -> PathInstance<'a, T> +where + T: JsonLike + Default + Clone + Debug, +{ Box::new(match op { Operand::Static(v) => json_path_instance(&JsonPath::Root, v), Operand::Dynamic(jp) => json_path_instance(jp, root), diff --git a/src/path/top.rs b/src/path/top.rs index 770cd1f..7265d78 100644 --- a/src/path/top.rs +++ b/src/path/top.rs @@ -1,3 +1,5 @@ +use std::fmt::Debug; + use crate::parser::model::*; use crate::path::{json_path_instance, JsonPathValue, Path}; use crate::JsonPathValue::{NewValue, NoValue, Slice}; @@ -5,45 +7,34 @@ use crate::{jsp_idx, jsp_obj, JsPathStr}; use serde_json::value::Value::{Array, Object}; use serde_json::{json, Value}; -use super::TopPaths; +use super::{JsonLike, TopPaths}; /// to process the element [*] -pub(crate) struct Wildcard {} +pub struct Wildcard { + _t: std::marker::PhantomData, +} -impl<'a> Path<'a> for Wildcard { - type Data = Value; +impl Wildcard { + pub fn new() -> Self { + Self { + _t: std::marker::PhantomData, + } + } +} - fn find(&self, data: JsonPathValue<'a, Self::Data>) -> Vec> { - data.flat_map_slice(|data, pref| { - let res = match data { - Array(elems) => { - let mut res = vec![]; - for (idx, el) in elems.iter().enumerate() { - res.push(Slice(el, jsp_idx(&pref, idx))); - } +impl<'a, T> Path<'a> for Wildcard +where + T: JsonLike, +{ + type Data = T; - res - } - Object(elems) => { - let mut res = vec![]; - for (key, el) in elems.into_iter() { - res.push(Slice(el, jsp_obj(&pref, key))); - } - res - } - _ => vec![], - }; - if res.is_empty() { - vec![NoValue] - } else { - res - } - }) + fn find(&self, data: JsonPathValue<'a, Self::Data>) -> Vec> { + data.flat_map_slice(|data, pref| data.itre(pref)) } } /// empty path. Returns incoming data. -pub(crate) struct IdentityPath {} +pub struct IdentityPath {} impl<'a> Path<'a> for IdentityPath { type Data = Value; @@ -64,7 +55,7 @@ impl<'a> Path<'a> for EmptyPath { } /// process $ element -pub(crate) struct RootPointer<'a, T> { +pub struct RootPointer<'a, T> { root: &'a T, } @@ -74,8 +65,11 @@ impl<'a, T> RootPointer<'a, T> { } } -impl<'a> Path<'a> for RootPointer<'a, Value> { - type Data = Value; +impl<'a, T> Path<'a> for RootPointer<'a, T> +where + T: JsonLike + Clone + core::fmt::Debug + Default + 'a, +{ + type Data = T; fn find(&self, _data: JsonPathValue<'a, Self::Data>) -> Vec> { vec![JsonPathValue::from_root(self.root)] @@ -83,24 +77,62 @@ impl<'a> Path<'a> for RootPointer<'a, Value> { } /// process object fields like ['key'] or .key -pub(crate) struct ObjectField<'a> { +pub struct ObjectField<'a, T> { key: &'a str, + _t: std::marker::PhantomData, } -impl<'a> ObjectField<'a> { - pub(crate) fn new(key: &'a str) -> ObjectField<'a> { - ObjectField { key } +impl<'a, T> ObjectField<'a, T> { + pub(crate) fn new(key: &'a str) -> ObjectField<'a, T> { + ObjectField { + key, + _t: std::marker::PhantomData, + } } } -impl<'a> Clone for ObjectField<'a> { +impl<'a, T> Clone for ObjectField<'a, T> { fn clone(&self) -> Self { ObjectField::new(self.key) } } -impl<'a> Path<'a> for FnPath { - type Data = Value; +impl<'a, T> Path<'a> for ObjectField<'a, T> +where + T: JsonLike + Clone + Default + Debug, +{ + type Data = T; + + fn find(&self, data: JsonPathValue<'a, Self::Data>) -> Vec> { + let take_field = |v: &'a T| v.get(self.key); + + let res = match data { + Slice(js, p) => take_field(js) + .map(|v| JsonPathValue::new_slice(v, jsp_obj(&p, self.key))) + .unwrap_or_else(|| NoValue), + _ => NoValue, + }; + vec![res] + } +} + +pub enum FnPath { + Size { _t: std::marker::PhantomData }, +} + +impl FnPath { + pub fn new_size() -> Self { + FnPath::Size { + _t: std::marker::PhantomData, + } + } +} + +impl<'a, T> Path<'a> for FnPath +where + T: JsonLike + 'static, +{ + type Data = T; fn flat_find( &self, @@ -112,17 +144,14 @@ impl<'a> Path<'a> for FnPath { return vec![NoValue]; } let res = if is_search_length { - NewValue(json!(input.iter().filter(|v| v.has_value()).count())) + NewValue(T::init_with_usize( + input.iter().filter(|v| v.has_value()).count(), + )) } else { - let take_len = |v: &Value| match v { - Array(elems) => NewValue(json!(elems.len())), - _ => NoValue, - }; - match input.first() { Some(v) => match v { - NewValue(d) => take_len(d), - Slice(s, _) => take_len(s), + NewValue(d) => d.array_len(), + Slice(s, _) => s.array_len(), NoValue => NoValue, }, None => NoValue, @@ -136,103 +165,75 @@ impl<'a> Path<'a> for FnPath { } } -pub(crate) enum FnPath { - Size, +/// the top method of the processing ..* +pub struct DescentWildcard { + _t: std::marker::PhantomData, } -impl<'a> Path<'a> for ObjectField<'a> { - type Data = Value; - - fn find(&self, data: JsonPathValue<'a, Self::Data>) -> Vec> { - let take_field = |v: &'a Value| match v { - Object(fields) => fields.get(self.key), - _ => None, - }; - - let res = match data { - Slice(js, p) => take_field(js) - .map(|v| JsonPathValue::new_slice(v, jsp_obj(&p, self.key))) - .unwrap_or_else(|| NoValue), - _ => NoValue, - }; - vec![res] +impl DescentWildcard { + pub fn new() -> Self { + DescentWildcard { + _t: std::marker::PhantomData, + } } } -/// the top method of the processing ..* -pub(crate) struct DescentWildcard; -impl<'a> Path<'a> for DescentWildcard { - type Data = Value; +impl<'a, T> Path<'a> for DescentWildcard +where + T: JsonLike, +{ + type Data = T; fn find(&self, data: JsonPathValue<'a, Self::Data>) -> Vec> { - data.map_slice(deep_flatten) - } -} - -// todo rewrite to tail rec -fn deep_flatten(data: &Value, pref: JsPathStr) -> Vec<(&Value, JsPathStr)> { - let mut acc = vec![]; - match data { - Object(elems) => { - for (f, v) in elems.into_iter() { - let pref = jsp_obj(&pref, f); - acc.push((v, pref.clone())); - acc.append(&mut deep_flatten(v, pref)); - } - } - Array(elems) => { - for (i, v) in elems.iter().enumerate() { - let pref = jsp_idx(&pref, i); - acc.push((v, pref.clone())); - acc.append(&mut deep_flatten(v, pref)); - } - } - _ => (), + data.map_slice(|data, pref| data.deep_flatten(pref)) } - acc } -// todo rewrite to tail rec -fn deep_path_by_key<'a>( - data: &'a Value, - key: ObjectField<'a>, - pref: JsPathStr, -) -> Vec<(&'a Value, JsPathStr)> { - let mut result: Vec<(&'a Value, JsPathStr)> = - JsonPathValue::vec_as_pair(key.find(JsonPathValue::new_slice(data, pref.clone()))); - match data { - Object(elems) => { - let mut next_levels: Vec<(&'a Value, JsPathStr)> = elems - .into_iter() - .flat_map(|(k, v)| deep_path_by_key(v, key.clone(), jsp_obj(&pref, k))) - .collect(); - result.append(&mut next_levels); - result - } - Array(elems) => { - let mut next_levels: Vec<(&'a Value, JsPathStr)> = elems - .iter() - .enumerate() - .flat_map(|(i, v)| deep_path_by_key(v, key.clone(), jsp_idx(&pref, i))) - .collect(); - result.append(&mut next_levels); - result - } - _ => result, - } -} +// // todo rewrite to tail rec +// fn deep_path_by_key<'a>( +// data: &'a Value, +// key: ObjectField<'a>, +// pref: JsPathStr, +// ) -> Vec<(&'a Value, JsPathStr)> { +// let mut result: Vec<(&'a Value, JsPathStr)> = +// JsonPathValue::vec_as_pair(key.find(JsonPathValue::new_slice(data, pref.clone()))); +// match data { +// Object(elems) => { +// let mut next_levels: Vec<(&'a Value, JsPathStr)> = elems +// .into_iter() +// .flat_map(|(k, v)| deep_path_by_key(v, key.clone(), jsp_obj(&pref, k))) +// .collect(); +// result.append(&mut next_levels); +// result +// } +// Array(elems) => { +// let mut next_levels: Vec<(&'a Value, JsPathStr)> = elems +// .iter() +// .enumerate() +// .flat_map(|(i, v)| deep_path_by_key(v, key.clone(), jsp_idx(&pref, i))) +// .collect(); +// result.append(&mut next_levels); +// result +// } +// _ => result, +// } +// } /// processes decent object like .. -pub(crate) struct DescentObject<'a> { +pub struct DescentObject<'a, T> { key: &'a str, + _t: std::marker::PhantomData, } -impl<'a> Path<'a> for DescentObject<'a> { - type Data = Value; +impl<'a, T> Path<'a> for DescentObject<'a, T> +where + T: JsonLike, +{ + type Data = T; fn find(&self, data: JsonPathValue<'a, Self::Data>) -> Vec> { data.flat_map_slice(|data, pref| { - let res_col = deep_path_by_key(data, ObjectField::new(self.key), pref.clone()); + let res_col = data.deep_path_by_key(ObjectField::new(self.key), pref.clone()); if res_col.is_empty() { vec![NoValue] } else { @@ -242,26 +243,32 @@ impl<'a> Path<'a> for DescentObject<'a> { } } -impl<'a> DescentObject<'a> { +impl<'a, T> DescentObject<'a, T> { pub fn new(key: &'a str) -> Self { - DescentObject { key } + DescentObject { + key, + _t: std::marker::PhantomData, + } } } /// the top method of the processing representing the chain of other operators -pub(crate) struct Chain<'a> { - chain: Vec>, +pub struct Chain<'a, T> { + chain: Vec>, is_search_length: bool, } -impl<'a> Chain<'a> { - pub fn new(chain: Vec>, is_search_length: bool) -> Self { +impl<'a, T> Chain<'a, T> +where + T: JsonLike + Default + Clone + Debug, +{ + pub fn new(chain: Vec>, is_search_length: bool) -> Self { Chain { chain, is_search_length, } } - pub fn from(chain: &'a [JsonPath], root: &'a Value) -> Self { + pub fn from(chain: &'a [JsonPath], root: &'a T) -> Self { let chain_len = chain.len(); let is_search_length = if chain_len > 2 { let mut res = false; @@ -307,8 +314,11 @@ impl<'a> Chain<'a> { } } -impl<'a> Path<'a> for Chain<'a> { - type Data = Value; +impl<'a, T> Path<'a> for Chain<'a, T> +where + T: JsonLike + Default + Clone + Debug, +{ + type Data = T; fn find(&self, data: JsonPathValue<'a, Self::Data>) -> Vec> { let mut res = vec![data]; @@ -330,7 +340,7 @@ mod tests { use crate::parser::macros::{chain, idx}; use crate::parser::model::{JsonPath, JsonPathIndex}; use crate::path; - use crate::path::top::{deep_flatten, json_path_instance, Function, ObjectField, RootPointer}; + use crate::path::top::{json_path_instance, Function, ObjectField, RootPointer}; use crate::path::{JsonPathValue, Path}; use crate::JsonPathValue::NoValue; use serde_json::json; @@ -459,8 +469,9 @@ mod tests { } #[test] fn deep_path_test() { + use crate::path::JsonLike; let value = json!([1]); - let r = deep_flatten(&value, "".to_string()); + let r = value.deep_flatten("".to_string()); assert_eq!(r, vec![(&json!(1), "[0]".to_string())]) } From cfce8d4cb0a2f6b46698acff4c0b9a1923e9e7f7 Mon Sep 17 00:00:00 2001 From: paomian Date: Thu, 10 Oct 2024 12:53:12 +0800 Subject: [PATCH 2/6] chore: add JsonLike trait --- benches/equal.rs | 4 +- benches/regex.rs | 6 +- src/jsonpath.rs | 76 +++--- src/lib.rs | 4 +- src/parser/model.rs | 13 +- src/parser/parser.rs | 122 ++++----- src/path/index.rs | 101 +++++--- src/path/json.rs | 600 +++++++++++++++++++++---------------------- src/path/mod.rs | 230 ++++++++++++++--- src/path/top.rs | 72 ++---- 10 files changed, 704 insertions(+), 524 deletions(-) diff --git a/benches/equal.rs b/benches/equal.rs index a549ca6..9cecfc3 100644 --- a/benches/equal.rs +++ b/benches/equal.rs @@ -1,11 +1,11 @@ use criterion::{criterion_group, criterion_main, Criterion}; use jsonpath_rust::{JsonPath, JsonPathQuery}; -use serde_json::json; +use serde_json::{json, Value}; use std::str::FromStr; struct SearchData { json: serde_json::Value, - path: JsonPath, + path: JsonPath, } const PATH: &str = "$.[?(@.author == 'abcd(Rees)')]"; diff --git a/benches/regex.rs b/benches/regex.rs index 0ee1ef9..b8ad522 100644 --- a/benches/regex.rs +++ b/benches/regex.rs @@ -1,11 +1,11 @@ use criterion::{criterion_group, criterion_main, Criterion}; use jsonpath_rust::{JsonPath, JsonPathQuery}; -use serde_json::json; +use serde_json::{json, Value}; use std::str::FromStr; struct SearchData { json: serde_json::Value, - path: JsonPath, + path: JsonPath, } const PATH: &str = "$.[?(@.author ~= '.*(?i)d\\(Rees\\)')]"; @@ -23,7 +23,7 @@ fn regex_perf_test_without_reuse() { } fn json_path_compiling() { - let _v = JsonPath::from_str(PATH).unwrap(); + let _v = JsonPath::::from_str(PATH).unwrap(); } pub fn criterion_benchmark(c: &mut Criterion) { diff --git a/src/jsonpath.rs b/src/jsonpath.rs index 8aeaa83..18ba64f 100644 --- a/src/jsonpath.rs +++ b/src/jsonpath.rs @@ -4,12 +4,10 @@ use crate::JsonPath; use crate::JsonPathValue; use crate::JsonPathValue::NoValue; use crate::JsonPtr; -use crate::Value; -use std::fmt::Debug; impl JsonPath where - T: JsonLike + Default + Debug + Clone, + T: JsonLike, { /// finds a slice of data in the set json. /// The result is a vector of references to the incoming structure. @@ -80,13 +78,13 @@ where /// /// assert_eq!(cloned_data, Value::Array(vec![json!({"active":1})])); /// ``` - pub fn find(&self, json: &T) -> Value { + pub fn find(&self, json: &T) -> T { let slice = self.find_slice(json); if !slice.is_empty() { if JsonPathValue::only_no_value(&slice) { - Value::Null + T::null() } else { - Value::Array( + T::array( slice .into_iter() .filter(|v| v.has_value()) @@ -95,7 +93,7 @@ where ) } } else { - Value::Array(vec![]) + T::array(vec![]) } } @@ -119,7 +117,7 @@ where /// assert_eq!(slice_of_data, Value::Array(vec![Value::String(expected_path)])); /// ``` pub fn find_as_path(&self, json: &T) -> T { - Value::Array( + T::array( self.find_slice(json) .into_iter() .flat_map(|v| v.to_path()) @@ -558,7 +556,7 @@ mod tests { #[test] fn find_slice_test() { let json: Box = serde_json::from_str(template_json()).expect("to get json"); - let path: Box = Box::from( + let path: Box> = Box::from( JsonPath::try_from("$..book[?(@.author size 10)].title").expect("the path is correct"), ); let v = path.find_slice(&json); @@ -569,7 +567,7 @@ mod tests { #[test] fn find_in_array_test() { let json: Box = Box::new(json!([{"verb": "TEST"}, {"verb": "RUN"}])); - let path: Box = + let path: Box> = Box::from(JsonPath::try_from("$.[?(@.verb == 'TEST')]").expect("the path is correct")); let v = path.find_slice(&json); let js = json!({"verb":"TEST"}); @@ -580,7 +578,7 @@ mod tests { fn length_test() { let json: Box = Box::new(json!([{"verb": "TEST"},{"verb": "TEST"}, {"verb": "RUN"}])); - let path: Box = Box::from( + let path: Box> = Box::from( JsonPath::try_from("$.[?(@.verb == 'TEST')].length()").expect("the path is correct"), ); let v = path.find(&json); @@ -589,14 +587,14 @@ mod tests { let json: Box = Box::new(json!([{"verb": "TEST"},{"verb": "TEST"}, {"verb": "RUN"}])); - let path: Box = + let path: Box> = Box::from(JsonPath::try_from("$.length()").expect("the path is correct")); assert_eq!(path.find(&json), json!([3])); // length of search following the wildcard returns correct result let json: Box = Box::new(json!([{"verb": "TEST"},{"verb": "TEST","x":3}, {"verb": "RUN"}])); - let path: Box = Box::from( + let path: Box> = Box::from( JsonPath::try_from("$.[?(@.verb == 'TEST')].[*].length()") .expect("the path is correct"), ); @@ -604,33 +602,33 @@ mod tests { // length of object returns 0 let json: Box = Box::new(json!({"verb": "TEST"})); - let path: Box = + let path: Box> = Box::from(JsonPath::try_from("$.length()").expect("the path is correct")); assert_eq!(path.find(&json), Value::Null); // length of integer returns null let json: Box = Box::new(json!(1)); - let path: Box = + let path: Box> = Box::from(JsonPath::try_from("$.length()").expect("the path is correct")); assert_eq!(path.find(&json), Value::Null); // length of array returns correct result let json: Box = Box::new(json!([[1], [2], [3]])); - let path: Box = + let path: Box> = Box::from(JsonPath::try_from("$.length()").expect("the path is correct")); assert_eq!(path.find(&json), json!([3])); // path does not exist returns length null let json: Box = Box::new(json!([{"verb": "TEST"},{"verb": "TEST"}, {"verb": "RUN"}])); - let path: Box = + let path: Box> = Box::from(JsonPath::try_from("$.not.exist.length()").expect("the path is correct")); assert_eq!(path.find(&json), Value::Null); // seraching one value returns correct length let json: Box = Box::new(json!([{"verb": "TEST"},{"verb": "TEST"}, {"verb": "RUN"}])); - let path: Box = Box::from( + let path: Box> = Box::from( JsonPath::try_from("$.[?(@.verb == 'RUN')].length()").expect("the path is correct"), ); @@ -641,7 +639,7 @@ mod tests { // searching correct path following unexisting key returns length 0 let json: Box = Box::new(json!([{"verb": "TEST"},{"verb": "TEST"}, {"verb": "RUN"}])); - let path: Box = Box::from( + let path: Box> = Box::from( JsonPath::try_from("$.[?(@.verb == 'RUN')].key123.length()") .expect("the path is correct"), ); @@ -653,7 +651,7 @@ mod tests { // fetching first object returns length null let json: Box = Box::new(json!([{"verb": "TEST"},{"verb": "TEST"}, {"verb": "RUN"}])); - let path: Box = + let path: Box> = Box::from(JsonPath::try_from("$.[0].length()").expect("the path is correct")); let v = path.find(&json); @@ -662,7 +660,7 @@ mod tests { // length on fetching the index after search gives length of the object (array) let json: Box = Box::new(json!([{"prop": [["a", "b", "c"], "d"]}])); - let path: Box = Box::from( + let path: Box> = Box::from( JsonPath::try_from("$.[?(@.prop)].prop.[0].length()").expect("the path is correct"), ); @@ -672,7 +670,7 @@ mod tests { // length on fetching the index after search gives length of the object (string) let json: Box = Box::new(json!([{"prop": [["a", "b", "c"], "d"]}])); - let path: Box = Box::from( + let path: Box> = Box::from( JsonPath::try_from("$.[?(@.prop)].prop.[1].length()").expect("the path is correct"), ); @@ -687,7 +685,7 @@ mod tests { "field":"field", })); - let path: Box = + let path: Box> = Box::from(JsonPath::try_from("$.field[1]").expect("the path is correct")); let v = path.find_slice(&json); assert_eq!(v, vec![NoValue]); @@ -696,7 +694,7 @@ mod tests { "field":[0], })); - let path: Box = + let path: Box> = Box::from(JsonPath::try_from("$.field[1]").expect("the path is correct")); let v = path.find_slice(&json); assert_eq!(v, vec![NoValue]); @@ -708,7 +706,7 @@ mod tests { "field":"field", })); - let path: Box = + let path: Box> = Box::from(JsonPath::try_from("$.field[?(@ == 0)]").expect("the path is correct")); let v = path.find_slice(&json); assert_eq!(v, vec![NoValue]); @@ -720,7 +718,7 @@ mod tests { "field":[{"f":1},{"f":0}], })); - let path: Box = + let path: Box> = Box::from(JsonPath::try_from("$.field[?(@.f_ == 0)]").expect("the path is correct")); let v = path.find_slice(&json); assert_eq!(v, vec![NoValue]); @@ -732,7 +730,7 @@ mod tests { "field":[{"f":1},{"f":{"f_":1}}], })); - let path: Box = + let path: Box> = Box::from(JsonPath::try_from("$..f_").expect("the path is correct")); let v = path.find_slice(&json); assert_eq!( @@ -747,12 +745,12 @@ mod tests { "field":{"field":[1]}, })); - let path: Box = + let path: Box> = Box::from(JsonPath::try_from("$.field_.field").expect("the path is correct")); let v = path.find_slice(&json); assert_eq!(v, vec![NoValue]); - let path: Box = Box::from( + let path: Box> = Box::from( JsonPath::try_from("$.field_.field[?(@ == 1)]").expect("the path is correct"), ); let v = path.find_slice(&json); @@ -764,7 +762,7 @@ mod tests { // searching unexisting value returns length 0 let json: Box = Box::new(json!([{"verb": "TEST"},{"verb": "TEST"}, {"verb": "RUN"}])); - let path: Box = Box::from( + let path: Box> = Box::from( JsonPath::try_from("$.[?(@.verb == \"RUN1\")]").expect("the path is correct"), ); let v = path.find(&json); @@ -778,7 +776,7 @@ mod tests { "field":{"field":1}, })); - let path: Box = + let path: Box> = Box::from(JsonPath::try_from("$.field.field.length()").expect("the path is correct")); let v = path.find_slice(&json); assert_eq!(v, vec![NoValue]); @@ -786,7 +784,7 @@ mod tests { let json: Box = Box::new(json!({ "field":[{"a":1},{"a":1}], })); - let path: Box = Box::from( + let path: Box> = Box::from( JsonPath::try_from("$.field[?(@.a == 0)].f.length()").expect("the path is correct"), ); let v = path.find_slice(&json); @@ -817,14 +815,14 @@ mod tests { fn logical_exp_test() { let json: Box = Box::new(json!({"first":{"second":[{"active":1},{"passive":1}]}})); - let path: Box = Box::from( + let path: Box> = Box::from( JsonPath::try_from("$.first[?(@.does_not_exist && @.does_not_exist >= 1.0)]") .expect("the path is correct"), ); let v = path.find_slice(&json); assert_eq!(v, vec![NoValue]); - let path: Box = Box::from( + let path: Box> = Box::from( JsonPath::try_from("$.first[?(@.does_not_exist >= 1.0)]").expect("the path is correct"), ); let v = path.find_slice(&json); @@ -837,7 +835,7 @@ mod tests { "author":"abcd(Rees)", })); - let path: Box = Box::from( + let path: Box> = Box::from( JsonPath::try_from("$.[?(@.author ~= '(?i)d\\(Rees\\)')]") .expect("the path is correct"), ); @@ -850,7 +848,7 @@ mod tests { #[test] fn logical_not_exp_test() { let json: Box = Box::new(json!({"first":{"second":{"active":1}}})); - let path: Box = Box::from( + let path: Box> = Box::from( JsonPath::try_from("$.first[?(!@.does_not_exist >= 1.0)]") .expect("the path is correct"), ); @@ -863,7 +861,7 @@ mod tests { )] ); - let path: Box = Box::from( + let path: Box> = Box::from( JsonPath::try_from("$.first[?(!(@.does_not_exist >= 1.0))]") .expect("the path is correct"), ); @@ -876,7 +874,7 @@ mod tests { )] ); - let path: Box = Box::from( + let path: Box> = Box::from( JsonPath::try_from("$.first[?(!(@.second.active == 1) || @.second.active == 1)]") .expect("the path is correct"), ); @@ -889,7 +887,7 @@ mod tests { )] ); - let path: Box = Box::from( + let path: Box> = Box::from( JsonPath::try_from("$.first[?(!@.second.active == 1 && !@.second.active == 1 || !@.second.active == 2)]") .expect("the path is correct"), ); diff --git a/src/lib.rs b/src/lib.rs index 9404f92..1d11ede 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -378,12 +378,14 @@ impl<'a, Data> JsonPathValue<'a, Data> { #[cfg(test)] mod tests { + use serde_json::Value; + use crate::JsonPath; use std::str::FromStr; #[test] fn to_string_test() { - let path: Box = Box::from( + let path: Box> = Box::from( JsonPath::from_str( "$.['a'].a..book[1:3][*][1]['a','b'][?(@)][?(@.verb == 'TEST')].a.length()", ) diff --git a/src/parser/model.rs b/src/parser/model.rs index e09e22f..d983bdc 100644 --- a/src/parser/model.rs +++ b/src/parser/model.rs @@ -1,6 +1,7 @@ +use crate::path::JsonLike; + use super::errors::JsonPathParserError; use super::parse_json_path; -use serde_json::Value; use std::fmt::{Display, Formatter}; use std::{convert::TryFrom, str::FromStr}; @@ -52,7 +53,10 @@ impl Display for JsonPath { } } -impl TryFrom<&str> for JsonPath { +impl TryFrom<&str> for JsonPath +where + T: JsonLike, +{ type Error = JsonPathParserError; /// Parses a string into a [JsonPath]. @@ -65,7 +69,10 @@ impl TryFrom<&str> for JsonPath { } } -impl FromStr for JsonPath { +impl FromStr for JsonPath +where + T: JsonLike, +{ type Err = JsonPathParserError; /// Parses a string into a [JsonPath]. diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 81663f2..5a6da73 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -5,6 +5,7 @@ use crate::parser::model::FilterExpression::{And, Not, Or}; use crate::parser::model::{ FilterExpression, FilterSign, Function, JsonPath, JsonPathIndex, Operand, }; +use crate::path::JsonLike; use pest::iterators::{Pair, Pairs}; use pest::Parser; @@ -19,7 +20,7 @@ struct JsonPathParser; /// Returns a variant of [JsonPathParserError] if the parsing operation failed. pub fn parse_json_path<'a, T>(jp_str: &'a str) -> Result, JsonPathParserError> where - T: From<&'a str> + From> + From + From + From + 'static, + T: JsonLike, { JsonPathParser::parse(Rule::path, jp_str) .map_err(Box::new)? @@ -35,7 +36,7 @@ where /// Returns a variant of [JsonPathParserError] if the parsing operation failed fn parse_internal<'a, T>(rule: Pair<'a, Rule>) -> Result, JsonPathParserError> where - T: From<&'a str> + From> + From + From + From + 'static, + T: JsonLike, { match rule.as_rule() { Rule::path => rule @@ -143,7 +144,7 @@ where fn parse_chain_in_operand<'a, T>(rule: Pair<'a, Rule>) -> Result, JsonPathParserError> where - T: From<&'a str> + From> + From + From + From + 'static, + T: JsonLike, { let parsed_chain = match parse_internal::(rule)? { JsonPath::Chain(elems) => { @@ -169,14 +170,14 @@ where fn parse_filter_index<'a, T>(pair: Pair<'a, Rule>) -> Result, JsonPathParserError> where - T: From<&'a str> + From> + From + From + From + 'static, + T: JsonLike, { Ok(JsonPathIndex::Filter(parse_logic_or(pair.into_inner())?)) } fn parse_logic_or<'a, T>(pairs: Pairs<'a, Rule>) -> Result, JsonPathParserError> where - T: From<&'a str> + From> + From + From + From + 'static, + T: JsonLike, { let mut expr: Option> = None; // only possible for the loop not to produce any value (except Errors) @@ -200,7 +201,7 @@ fn parse_logic_and<'a, T>( pairs: Pairs<'a, Rule>, ) -> Result, JsonPathParserError> where - T: From<&'a str> + From> + From + From + From + 'static, + T: JsonLike, { let mut expr: Option> = None; // only possible for the loop not to produce any value (except Errors) @@ -224,7 +225,7 @@ fn parse_logic_not<'a, T>( mut pairs: Pairs<'a, Rule>, ) -> Result, JsonPathParserError> where - T: From<&'a str> + From> + From + From + From + 'static, + T: JsonLike, { if let Some(rule) = pairs.peek().map(|x| x.as_rule()) { match rule { @@ -248,7 +249,7 @@ fn parse_logic_atom<'a, T>( mut pairs: Pairs<'a, Rule>, ) -> Result, JsonPathParserError> where - T: From<&'a str> + From> + From + From + From + 'static, + T: JsonLike, { if let Some(rule) = pairs.peek().map(|x| x.as_rule()) { match rule { @@ -276,7 +277,7 @@ where fn parse_atom<'a, T>(rule: Pair<'a, Rule>) -> Result, JsonPathParserError> where - T: From<&'a str> + From> + From + From + From + 'static, + T: JsonLike, { let atom = down(rule.clone())?; let parsed_atom = match atom.as_rule() { @@ -284,14 +285,14 @@ where Rule::string_qt => Operand::Static(T::from(down(atom)?.as_str())), Rule::chain => parse_chain_in_operand(down(rule)?)?, Rule::boolean => Operand::Static(bool_to_value(rule.as_str())), - _ => Operand::Static(T::from("")), + _ => Operand::Static(T::null()), }; Ok(parsed_atom) } fn parse_index<'a, T>(rule: Pair<'a, Rule>) -> Result, JsonPathParserError> where - T: From<&'a str> + From> + From + From + From + 'static, + T: JsonLike, { let next = down(rule)?; let parsed_index = match next.as_rule() { @@ -318,18 +319,21 @@ mod tests { use super::*; use crate::parser::macros::{chain, filter, idx, op}; use crate::path; - use serde_json::json; + use serde_json::{json, Value}; use std::panic; fn test_failed(input: &str) { - match parse_json_path(input) { + match parse_json_path::(input) { Ok(elem) => panic!("should be false but got {:?}", elem), Err(e) => println!("{}", e), } } - fn test(input: &str, expected: Vec) { - match parse_json_path(input) { + fn test(input: &str, expected: Vec>) + where + T: JsonLike, + { + match parse_json_path::(input) { Ok(JsonPath::Chain(elems)) => assert_eq!(elems, expected), Ok(e) => panic!("unexpected value {:?}", e), Err(e) => { @@ -340,7 +344,7 @@ mod tests { #[test] fn path_test() { - test("$.k.['k']['k']..k..['k'].*.[*][*][1][1,2]['k','k'][:][10:][:10][10:10:10][?(@)][?(@.abc >= 10)]", + test::("$.k.['k']['k']..k..['k'].*.[*][*][1][1,2]['k','k'][:][10:][:10][10:10:10][?(@)][?(@.abc >= 10)]", vec![ path!($), path!("k"), @@ -361,7 +365,7 @@ mod tests { path!(idx!(?filter!(op!(chain!(path!(@path!()))), "exists", op!(path!())))), path!(idx!(?filter!(op!(chain!(path!(@,path!("abc")))), ">=", op!(10)))), ]); - test( + test::( "$..*[?(@.isbn)].title", vec![ // Root, DescentW, Index(Filter(Atom(Dynamic(Chain([Current(Chain([Field("isbn")]))])), Exists, Dynamic(Empty)))), Field("title") @@ -375,18 +379,18 @@ mod tests { #[test] fn descent_test() { - test("..abc", vec![path!(.."abc")]); - test("..['abc']", vec![path!(.."abc")]); + test::("..abc", vec![path!(.."abc")]); + test::("..['abc']", vec![path!(.."abc")]); test_failed("...['abc']"); test_failed("...abc"); } #[test] fn field_test() { - test(".abc", vec![path!("abc")]); - test(".['abc']", vec![path!("abc")]); - test("['abc']", vec![path!("abc")]); - test(".['abc\\\"abc']", vec![path!("abc\\\"abc")]); + test::(".abc", vec![path!("abc")]); + test::(".['abc']", vec![path!("abc")]); + test::("['abc']", vec![path!("abc")]); + test::(".['abc\\\"abc']", vec![path!("abc\\\"abc")]); test_failed(".abc()abc"); test_failed("..[abc]"); test_failed(".'abc'"); @@ -394,46 +398,46 @@ mod tests { #[test] fn wildcard_test() { - test(".*", vec![path!(*)]); - test(".[*]", vec![path!(*)]); - test(".abc.*", vec![path!("abc"), path!(*)]); - test(".abc.[*]", vec![path!("abc"), path!(*)]); - test(".abc[*]", vec![path!("abc"), path!(*)]); - test("..*", vec![path!(..*)]); + test::(".*", vec![path!(*)]); + test::(".[*]", vec![path!(*)]); + test::(".abc.*", vec![path!("abc"), path!(*)]); + test::(".abc.[*]", vec![path!("abc"), path!(*)]); + test::(".abc[*]", vec![path!("abc"), path!(*)]); + test::("..*", vec![path!(..*)]); test_failed("abc*"); } #[test] fn index_single_test() { - test("[1]", vec![path!(idx!(1))]); + test::("[1]", vec![path!(idx!(1))]); test_failed("[-1]"); test_failed("[1a]"); } #[test] fn index_slice_test() { - test("[1:1000:10]", vec![path!(idx!([1; 1000; 10]))]); - test("[:1000:10]", vec![path!(idx!([0; 1000; 10]))]); - test("[:1000]", vec![path!(idx!([;1000;]))]); - test("[:]", vec![path!(idx!([;;]))]); - test("[::10]", vec![path!(idx!([;;10]))]); + test::("[1:1000:10]", vec![path!(idx!([1; 1000; 10]))]); + test::("[:1000:10]", vec![path!(idx!([0; 1000; 10]))]); + test::("[:1000]", vec![path!(idx!([;1000;]))]); + test::("[:]", vec![path!(idx!([;;]))]); + test::("[::10]", vec![path!(idx!([;;10]))]); test_failed("[::-1]"); test_failed("[:::0]"); } #[test] fn index_union_test() { - test("[1,2,3]", vec![path!(idx!(idx 1,2,3))]); - test("['abc','bcd']", vec![path!(idx!("abc", "bcd"))]); + test::("[1,2,3]", vec![path!(idx!(idx 1,2,3))]); + test::("['abc','bcd']", vec![path!(idx!("abc", "bcd"))]); test_failed("[]"); - test("[-1,-2]", vec![path!(idx!(idx - 1, -2))]); + test::("[-1,-2]", vec![path!(idx!(idx - 1, -2))]); test_failed("[abc,bcd]"); - test("[\"abc\",\"bcd\"]", vec![path!(idx!("abc", "bcd"))]); + test::("[\"abc\",\"bcd\"]", vec![path!(idx!("abc", "bcd"))]); } #[test] fn array_start_test() { - test( + test::( "$.[?(@.verb== \"TEST\")]", vec![ path!($), @@ -444,7 +448,7 @@ mod tests { #[test] fn logical_filter_test() { - test( + test::( "$.[?(@.verb == 'T' || @.size > 0 && @.size < 10)]", vec![ path!($), @@ -460,7 +464,7 @@ mod tests { ))), ], ); - test( + test::( "$.[?((@.verb == 'T' || @.size > 0) && @.size < 10)]", vec![ path!($), @@ -476,7 +480,7 @@ mod tests { ))), ], ); - test( + test::( "$.[?(@.verb == 'T' || @.size > 0 && @.size < 10 && @.elem == 0)]", vec![ path!($), @@ -500,33 +504,33 @@ mod tests { #[test] fn index_filter_test() { - test( + test::( "[?('abc' == 'abc')]", vec![path!(idx!(?filter!(op!("abc"),"==",op!("abc") )))], ); - test( + test::( "[?('abc' == 1)]", vec![path!(idx!(?filter!( op!("abc"),"==",op!(1))))], ); - test( + test::( "[?('abc' == true)]", vec![path!(idx!(?filter!( op!("abc"),"==",op!(true))))], ); - test( + test::( "[?('abc' == null)]", vec![path!( idx!(?filter!( op!("abc"),"==",Operand::Static(Value::Null))) )], ); - test( + test::( "[?(@.abc in ['abc','bcd'])]", vec![path!( idx!(?filter!(op!(chain!(path!(@,path!("abc")))),"in",Operand::val(json!(["abc","bcd"])))) )], ); - test( + test::( "[?(@.abc.[*] in ['abc','bcd'])]", vec![path!(idx!(?filter!( op!(chain!(path!(@,path!("abc"), path!(*)))), @@ -534,7 +538,7 @@ mod tests { op!(s json!(["abc","bcd"])) )))], ); - test( + test::( "[?(@.[*]..next in ['abc','bcd'])]", vec![path!(idx!(?filter!( op!(chain!(path!(@,path!(*), path!(.."next")))), @@ -543,7 +547,7 @@ mod tests { )))], ); - test( + test::( "[?(@[1] in ['abc','bcd'])]", vec![path!(idx!(?filter!( op!(chain!(path!(@,path!(idx!(1))))), @@ -551,19 +555,19 @@ mod tests { op!(s json!(["abc","bcd"])) )))], ); - test( + test::( "[?(@ == 'abc')]", vec![path!(idx!(?filter!( op!(chain!(path!(@path!()))),"==",op!("abc") )))], ); - test( + test::( "[?(@ subsetOf ['abc'])]", vec![path!(idx!(?filter!( op!(chain!(path!(@path!()))),"subsetOf",op!(s json!(["abc"])) )))], ); - test( + test::( "[?(@[1] subsetOf ['abc','abc'])]", vec![path!(idx!(?filter!( op!(chain!(path!(@,path!(idx!(1))))), @@ -571,7 +575,7 @@ mod tests { op!(s json!(["abc","abc"])) )))], ); - test( + test::( "[?(@ subsetOf [1,2,3])]", vec![path!(idx!(?filter!( op!(chain!(path!(@path!()))),"subsetOf",op!(s json!([1,2,3])) @@ -585,12 +589,12 @@ mod tests { #[test] fn fn_size_test() { - test( + test::( "$.k.length()", vec![path!($), path!("k"), JsonPath::Fn(Function::Length)], ); - test( + test::( "$.k.length.field", vec![path!($), path!("k"), path!("length"), path!("field")], ) @@ -598,7 +602,7 @@ mod tests { #[test] fn parser_error_test_invalid_rule() { - let result = parse_json_path("notapath"); + let result = parse_json_path::("notapath"); assert!(result.is_err()); assert!(result @@ -610,7 +614,7 @@ mod tests { #[test] fn parser_error_test_empty_rule() { - let result = parse_json_path(""); + let result = parse_json_path::(""); assert!(result.is_err()); assert!(result diff --git a/src/path/index.rs b/src/path/index.rs index 8f76872..0104748 100644 --- a/src/path/index.rs +++ b/src/path/index.rs @@ -2,13 +2,11 @@ use std::fmt::Debug; use crate::jsp_idx; use crate::parser::model::{FilterExpression, FilterSign, JsonPath}; -use crate::path::json::*; + use crate::path::top::ObjectField; use crate::path::{json_path_instance, process_operand, JsonPathValue, Path, PathInstance}; use crate::JsonPathValue::{NoValue, Slice}; -use serde_json::Value; - use super::{JsonLike, TopPaths}; /// process the slice like [start:end:step] @@ -82,7 +80,7 @@ impl ArraySlice { impl<'a, T> Path<'a> for ArraySlice where - T: JsonLike + Default + Clone + Debug, + T: JsonLike + Default + Clone + Debug, { type Data = T; @@ -120,7 +118,7 @@ impl ArrayIndex { impl<'a, T> Path<'a> for ArrayIndex where - T: JsonLike + Default + Clone + Debug, + T: JsonLike + Default + Clone + Debug, { type Data = T; @@ -142,7 +140,7 @@ pub struct Current<'a, T> { impl<'a, T> Current<'a, T> where - T: JsonLike + Default + Clone + Debug, + T: JsonLike + Default + Clone + Debug, { pub(crate) fn from(jp: &'a JsonPath, root: &'a T) -> Self { match jp { @@ -166,7 +164,7 @@ where impl<'a, T> Path<'a> for Current<'a, T> where - T: JsonLike + Default + Clone + Debug, + T: JsonLike + Default + Clone + Debug, { type Data = T; @@ -185,7 +183,7 @@ pub struct UnionIndex<'a, T> { impl<'a, T> UnionIndex<'a, T> where - T: JsonLike + Default + Clone + Debug, + T: JsonLike + Default + Clone + Debug, { pub fn from_indexes(elems: &'a [T]) -> Self { let mut indexes: Vec> = vec![]; @@ -215,7 +213,7 @@ where impl<'a, T> Path<'a> for UnionIndex<'a, T> where - T: JsonLike + Default + Clone + Debug, + T: JsonLike + Default + Clone + Debug, { type Data = T; @@ -249,7 +247,7 @@ pub enum FilterPath<'a, T> { impl<'a, T> FilterPath<'a, T> where - T: JsonLike + Default + Clone + Debug, + T: JsonLike, { pub(crate) fn new(expr: &'a FilterExpression, root: &'a T) -> Self { match expr { @@ -286,45 +284,49 @@ where right: Vec>, ) -> bool { match op { - FilterSign::Equal => eq( + FilterSign::Equal => ::eq( JsonPathValue::vec_as_data(left), JsonPathValue::vec_as_data(right), ), + // eq( + // JsonPathValue::vec_as_data(left), + // JsonPathValue::vec_as_data(right), + // ), FilterSign::Unequal => !FilterPath::process_atom(&FilterSign::Equal, left, right), - FilterSign::Less => less( + FilterSign::Less => ::less( JsonPathValue::vec_as_data(left), JsonPathValue::vec_as_data(right), ), FilterSign::LeOrEq => { FilterPath::compound(&FilterSign::Less, &FilterSign::Equal, left, right) } - FilterSign::Greater => less( + FilterSign::Greater => ::less( JsonPathValue::vec_as_data(right), JsonPathValue::vec_as_data(left), ), FilterSign::GrOrEq => { FilterPath::compound(&FilterSign::Greater, &FilterSign::Equal, left, right) } - FilterSign::Regex => regex( + FilterSign::Regex => ::regex( JsonPathValue::vec_as_data(left), JsonPathValue::vec_as_data(right), ), - FilterSign::In => inside( + FilterSign::In => ::inside( JsonPathValue::vec_as_data(left), JsonPathValue::vec_as_data(right), ), FilterSign::Nin => !FilterPath::process_atom(&FilterSign::In, left, right), FilterSign::NoneOf => !FilterPath::process_atom(&FilterSign::AnyOf, left, right), - FilterSign::AnyOf => any_of( + FilterSign::AnyOf => ::any_of( JsonPathValue::vec_as_data(left), JsonPathValue::vec_as_data(right), ), - FilterSign::SubSetOf => sub_set_of( + FilterSign::SubSetOf => ::sub_set_of( JsonPathValue::vec_as_data(left), JsonPathValue::vec_as_data(right), ), FilterSign::Exists => !JsonPathValue::vec_as_data(left).is_empty(), - FilterSign::Size => size( + FilterSign::Size => ::size( JsonPathValue::vec_as_data(left), JsonPathValue::vec_as_data(right), ), @@ -360,33 +362,48 @@ where } } -impl<'a, T> Path<'a> for FilterPath<'a, T> { +impl<'a, T> Path<'a> for FilterPath<'a, T> +where + T: JsonLike, +{ type Data = T; fn find(&self, input: JsonPathValue<'a, Self::Data>) -> Vec> { - todo!() - // input.flat_map_slice(|data, pref| { - // let mut res = vec![]; - // match data { - // Array(elems) => { - // for (i, el) in elems.iter().enumerate() { - // if self.process(el) { - // res.push(Slice(el, jsp_idx(&pref, i))) - // } - // } - // } - // el => { - // if self.process(el) { - // res.push(Slice(el, pref)) - // } - // } - // } - // if res.is_empty() { - // vec![NoValue] - // } else { - // res - // } - // }) + input.flat_map_slice(|data, pref| { + let mut res = vec![]; + if data.is_array() { + let elems = data.as_array().unwrap(); + for (i, el) in elems.iter().enumerate() { + if self.process(el) { + res.push(Slice(el, jsp_idx(&pref, i))) + } + } + } else { + if self.process(data) { + res.push(Slice(data, pref)) + } + } + + // match data { + // Array(elems) => { + // for (i, el) in elems.iter().enumerate() { + // if self.process(el) { + // res.push(Slice(el, jsp_idx(&pref, i))) + // } + // } + // } + // el => { + // if self.process(el) { + // res.push(Slice(el, pref)) + // } + // } + // } + if res.is_empty() { + vec![NoValue] + } else { + res + } + }) } } diff --git a/src/path/json.rs b/src/path/json.rs index a3c8206..d96e2fb 100644 --- a/src/path/json.rs +++ b/src/path/json.rs @@ -1,300 +1,300 @@ -use regex::Regex; -use serde_json::Value; - -use super::JsonLike; - -/// compare sizes of json elements -/// The method expects to get a number on the right side and array or string or object on the left -/// where the number of characters, elements or fields will be compared respectively. -pub fn size(left: Vec<&T>, right: Vec<&T>) -> bool -where - T: JsonLike, -{ - if let Some(Value::Number(n)) = right.first() { - if let Some(sz) = n.as_f64() { - for el in left.iter() { - match el { - Value::String(v) if v.len() == sz as usize => true, - Value::Array(elems) if elems.len() == sz as usize => true, - Value::Object(fields) if fields.len() == sz as usize => true, - _ => return false, - }; - } - return true; - } - } - false -} - -/// ensure the array on the left side is a subset of the array on the right side. -//todo change the naive impl to sets -pub fn sub_set_of(left: Vec<&T>, right: Vec<&T>) -> bool { - if left.is_empty() { - return true; - } - if right.is_empty() { - return false; - } - - if let Some(elems) = left.first().and_then(|e| e.as_array()) { - if let Some(Value::Array(right_elems)) = right.first() { - if right_elems.is_empty() { - return false; - } - - for el in elems { - let mut res = false; - - for r in right_elems.iter() { - if el.eq(r) { - res = true - } - } - if !res { - return false; - } - } - return true; - } - } - false -} - -/// ensure at least one element in the array on the left side belongs to the array on the right side. -//todo change the naive impl to sets -pub fn any_of(left: Vec<&T>, right: Vec<&T>) -> bool { - if left.is_empty() { - return true; - } - if right.is_empty() { - return false; - } - - if let Some(Value::Array(elems)) = right.first() { - if elems.is_empty() { - return false; - } - - for el in left.iter() { - if let Some(left_elems) = el.as_array() { - for l in left_elems.iter() { - for r in elems.iter() { - if l.eq(r) { - return true; - } - } - } - } else { - for r in elems.iter() { - if el.eq(&r) { - return true; - } - } - } - } - } - - false -} - -/// ensure that the element on the left sides mathes the regex on the right side -pub fn regex(left: Vec<&T>, right: Vec<&T>) -> bool { - if left.is_empty() || right.is_empty() { - return false; - } - - match right.first() { - Some(Value::String(str)) => { - if let Ok(regex) = Regex::new(str) { - for el in left.iter() { - if let Some(v) = el.as_str() { - if regex.is_match(v) { - return true; - } - } - } - } - false - } - _ => false, - } -} - -/// ensure that the element on the left side belongs to the array on the right side. -pub fn inside(left: Vec<&T>, right: Vec<&T>) -> bool { - if left.is_empty() { - return false; - } - - match right.first() { - Some(Value::Array(elems)) => { - for el in left.iter() { - if elems.contains(el) { - return true; - } - } - false - } - Some(Value::Object(elems)) => { - for el in left.iter() { - for r in elems.values() { - if el.eq(&r) { - return true; - } - } - } - false - } - _ => false, - } -} - -/// ensure the number on the left side is less the number on the right side -pub fn less(left: Vec<&T>, right: Vec<&T>) -> bool { - if left.len() == 1 && right.len() == 1 { - match (left.first(), right.first()) { - (Some(Value::Number(l)), Some(Value::Number(r))) => l - .as_f64() - .and_then(|v1| r.as_f64().map(|v2| v1 < v2)) - .unwrap_or(false), - _ => false, - } - } else { - false - } -} - -/// compare elements -pub fn eq(left: Vec<&T>, right: Vec<&T>) -> bool { - if left.len() != right.len() { - false - } else { - left.iter().zip(right).map(|(a, b)| a.eq(&b)).all(|a| a) - } -} - -#[cfg(test)] -mod tests { - use crate::path::json::{any_of, eq, less, regex, size, sub_set_of}; - use serde_json::{json, Value}; - - #[test] - fn value_eq_test() { - let left = json!({"value":42}); - let right = json!({"value":42}); - let right_uneq = json!([42]); - - assert!(&left.eq(&right)); - assert!(!&left.eq(&right_uneq)); - } - - #[test] - fn vec_value_test() { - let left = json!({"value":42}); - let left1 = json!(42); - let left2 = json!([1, 2, 3]); - let left3 = json!({"value2":[42],"value":[42]}); - - let right = json!({"value":42}); - let right1 = json!(42); - let right2 = json!([1, 2, 3]); - let right3 = json!({"value":[42],"value2":[42]}); - - assert!(eq(vec![&left], vec![&right])); - - assert!(!eq(vec![], vec![&right])); - assert!(!eq(vec![&right], vec![])); - - assert!(eq( - vec![&left, &left1, &left2, &left3], - vec![&right, &right1, &right2, &right3] - )); - - assert!(!eq( - vec![&left1, &left, &left2, &left3], - vec![&right, &right1, &right2, &right3] - )); - } - - #[test] - fn less_value_test() { - let left = json!(10); - let right = json!(11); - - assert!(less(vec![&left], vec![&right])); - assert!(!less(vec![&right], vec![&left])); - - let left = json!(-10); - let right = json!(-11); - - assert!(!less(vec![&left], vec![&right])); - assert!(less(vec![&right], vec![&left])); - - let left = json!(-10.0); - let right = json!(-11.0); - - assert!(!less(vec![&left], vec![&right])); - assert!(less(vec![&right], vec![&left])); - - assert!(!less(vec![], vec![&right])); - assert!(!less(vec![&right, &right], vec![&left])); - } - - #[test] - fn regex_test() { - let right = json!("[a-zA-Z]+[0-9]#[0-9]+"); - let left1 = json!("a11#"); - let left2 = json!("a1#1"); - let left3 = json!("a#11"); - let left4 = json!("#a11"); - - assert!(regex(vec![&left1, &left2, &left3, &left4], vec![&right])); - assert!(!regex(vec![&left1, &left3, &left4], vec![&right])) - } - - #[test] - fn any_of_test() { - let right = json!([1, 2, 3, 4, 5, 6]); - let left = json!([1, 100, 101]); - assert!(any_of(vec![&left], vec![&right])); - - let left = json!([11, 100, 101]); - assert!(!any_of(vec![&left], vec![&right])); - - let left1 = json!(1); - let left2 = json!(11); - assert!(any_of(vec![&left1, &left2], vec![&right])); - } - - #[test] - fn sub_set_of_test() { - let left1 = json!(1); - let left2 = json!(2); - let left3 = json!(3); - let left40 = json!(40); - let right = json!([1, 2, 3, 4, 5, 6]); - assert!(sub_set_of( - vec![&Value::Array(vec![ - left1.clone(), - left2.clone(), - left3.clone() - ])], - vec![&right] - )); - assert!(!sub_set_of( - vec![&Value::Array(vec![left1, left2, left3, left40])], - vec![&right] - )); - } - - #[test] - fn size_test() { - let left1 = json!("abc"); - let left2 = json!([1, 2, 3]); - let left3 = json!([1, 2, 3, 4]); - let right = json!(3); - assert!(size(vec![&left1], vec![&right])); - assert!(size(vec![&left2], vec![&right])); - assert!(!size(vec![&left3], vec![&right])); - } -} +// use regex::Regex; +// use serde_json::Value; + +// use super::JsonLike; + +// /// compare sizes of json elements +// /// The method expects to get a number on the right side and array or string or object on the left +// /// where the number of characters, elements or fields will be compared respectively. +// pub fn size(left: Vec<&T>, right: Vec<&T>) -> bool +// where +// T: JsonLike, +// { +// if let Some(Value::Number(n)) = right.first() { +// if let Some(sz) = n.as_f64() { +// for el in left.iter() { +// match el { +// Value::String(v) if v.len() == sz as usize => true, +// Value::Array(elems) if elems.len() == sz as usize => true, +// Value::Object(fields) if fields.len() == sz as usize => true, +// _ => return false, +// }; +// } +// return true; +// } +// } +// false +// } + +// /// ensure the array on the left side is a subset of the array on the right side. +// //todo change the naive impl to sets +// pub fn sub_set_of(left: Vec<&T>, right: Vec<&T>) -> bool { +// if left.is_empty() { +// return true; +// } +// if right.is_empty() { +// return false; +// } + +// if let Some(elems) = left.first().and_then(|e| e.as_array()) { +// if let Some(Value::Array(right_elems)) = right.first() { +// if right_elems.is_empty() { +// return false; +// } + +// for el in elems { +// let mut res = false; + +// for r in right_elems.iter() { +// if el.eq(r) { +// res = true +// } +// } +// if !res { +// return false; +// } +// } +// return true; +// } +// } +// false +// } + +// /// ensure at least one element in the array on the left side belongs to the array on the right side. +// //todo change the naive impl to sets +// pub fn any_of(left: Vec<&T>, right: Vec<&T>) -> bool { +// if left.is_empty() { +// return true; +// } +// if right.is_empty() { +// return false; +// } + +// if let Some(Value::Array(elems)) = right.first() { +// if elems.is_empty() { +// return false; +// } + +// for el in left.iter() { +// if let Some(left_elems) = el.as_array() { +// for l in left_elems.iter() { +// for r in elems.iter() { +// if l.eq(r) { +// return true; +// } +// } +// } +// } else { +// for r in elems.iter() { +// if el.eq(&r) { +// return true; +// } +// } +// } +// } +// } + +// false +// } + +// /// ensure that the element on the left sides mathes the regex on the right side +// pub fn regex(left: Vec<&T>, right: Vec<&T>) -> bool { +// if left.is_empty() || right.is_empty() { +// return false; +// } + +// match right.first() { +// Some(Value::String(str)) => { +// if let Ok(regex) = Regex::new(str) { +// for el in left.iter() { +// if let Some(v) = el.as_str() { +// if regex.is_match(v) { +// return true; +// } +// } +// } +// } +// false +// } +// _ => false, +// } +// } + +// /// ensure that the element on the left side belongs to the array on the right side. +// pub fn inside(left: Vec<&T>, right: Vec<&T>) -> bool { +// if left.is_empty() { +// return false; +// } + +// match right.first() { +// Some(Value::Array(elems)) => { +// for el in left.iter() { +// if elems.contains(el) { +// return true; +// } +// } +// false +// } +// Some(Value::Object(elems)) => { +// for el in left.iter() { +// for r in elems.values() { +// if el.eq(&r) { +// return true; +// } +// } +// } +// false +// } +// _ => false, +// } +// } + +// /// ensure the number on the left side is less the number on the right side +// pub fn less(left: Vec<&T>, right: Vec<&T>) -> bool { +// if left.len() == 1 && right.len() == 1 { +// match (left.first(), right.first()) { +// (Some(Value::Number(l)), Some(Value::Number(r))) => l +// .as_f64() +// .and_then(|v1| r.as_f64().map(|v2| v1 < v2)) +// .unwrap_or(false), +// _ => false, +// } +// } else { +// false +// } +// } + +// /// compare elements +// pub fn eq(left: Vec<&T>, right: Vec<&T>) -> bool { +// if left.len() != right.len() { +// false +// } else { +// left.iter().zip(right).map(|(a, b)| a.eq(&b)).all(|a| a) +// } +// } + +// #[cfg(test)] +// mod tests { +// use crate::path::json::{any_of, eq, less, regex, size, sub_set_of}; +// use serde_json::{json, Value}; + +// #[test] +// fn value_eq_test() { +// let left = json!({"value":42}); +// let right = json!({"value":42}); +// let right_uneq = json!([42]); + +// assert!(&left.eq(&right)); +// assert!(!&left.eq(&right_uneq)); +// } + +// #[test] +// fn vec_value_test() { +// let left = json!({"value":42}); +// let left1 = json!(42); +// let left2 = json!([1, 2, 3]); +// let left3 = json!({"value2":[42],"value":[42]}); + +// let right = json!({"value":42}); +// let right1 = json!(42); +// let right2 = json!([1, 2, 3]); +// let right3 = json!({"value":[42],"value2":[42]}); + +// assert!(eq(vec![&left], vec![&right])); + +// assert!(!eq(vec![], vec![&right])); +// assert!(!eq(vec![&right], vec![])); + +// assert!(eq( +// vec![&left, &left1, &left2, &left3], +// vec![&right, &right1, &right2, &right3] +// )); + +// assert!(!eq( +// vec![&left1, &left, &left2, &left3], +// vec![&right, &right1, &right2, &right3] +// )); +// } + +// #[test] +// fn less_value_test() { +// let left = json!(10); +// let right = json!(11); + +// assert!(less(vec![&left], vec![&right])); +// assert!(!less(vec![&right], vec![&left])); + +// let left = json!(-10); +// let right = json!(-11); + +// assert!(!less(vec![&left], vec![&right])); +// assert!(less(vec![&right], vec![&left])); + +// let left = json!(-10.0); +// let right = json!(-11.0); + +// assert!(!less(vec![&left], vec![&right])); +// assert!(less(vec![&right], vec![&left])); + +// assert!(!less(vec![], vec![&right])); +// assert!(!less(vec![&right, &right], vec![&left])); +// } + +// #[test] +// fn regex_test() { +// let right = json!("[a-zA-Z]+[0-9]#[0-9]+"); +// let left1 = json!("a11#"); +// let left2 = json!("a1#1"); +// let left3 = json!("a#11"); +// let left4 = json!("#a11"); + +// assert!(regex(vec![&left1, &left2, &left3, &left4], vec![&right])); +// assert!(!regex(vec![&left1, &left3, &left4], vec![&right])) +// } + +// #[test] +// fn any_of_test() { +// let right = json!([1, 2, 3, 4, 5, 6]); +// let left = json!([1, 100, 101]); +// assert!(any_of(vec![&left], vec![&right])); + +// let left = json!([11, 100, 101]); +// assert!(!any_of(vec![&left], vec![&right])); + +// let left1 = json!(1); +// let left2 = json!(11); +// assert!(any_of(vec![&left1, &left2], vec![&right])); +// } + +// #[test] +// fn sub_set_of_test() { +// let left1 = json!(1); +// let left2 = json!(2); +// let left3 = json!(3); +// let left40 = json!(40); +// let right = json!([1, 2, 3, 4, 5, 6]); +// assert!(sub_set_of( +// vec![&Value::Array(vec![ +// left1.clone(), +// left2.clone(), +// left3.clone() +// ])], +// vec![&right] +// )); +// assert!(!sub_set_of( +// vec![&Value::Array(vec![left1, left2, left3, left40])], +// vec![&right] +// )); +// } + +// #[test] +// fn size_test() { +// let left1 = json!("abc"); +// let left2 = json!([1, 2, 3]); +// let left3 = json!([1, 2, 3, 4]); +// let right = json!(3); +// assert!(size(vec![&left1], vec![&right])); +// assert!(size(vec![&left2], vec![&right])); +// assert!(!size(vec![&left3], vec![&right])); +// } +// } diff --git a/src/path/mod.rs b/src/path/mod.rs index 4b026dc..a36f9a1 100644 --- a/src/path/mod.rs +++ b/src/path/mod.rs @@ -1,6 +1,7 @@ use std::fmt::Debug; use crate::{jsp_idx, jsp_obj, JsonPathValue}; +use regex::Regex; use serde_json::{json, Value}; use crate::parser::model::{Function, JsonPath, JsonPathIndex, Operand}; @@ -14,31 +15,61 @@ mod json; /// The module is responsible for processing of the [[JsonPath]] elements mod top; -pub trait JsonLike { - type Data; - fn get(&self, key: &str) -> Option<&Self::Data>; - fn itre(&self, pref: String) -> Vec>; - fn array_len(&self) -> JsonPathValue<'static, Self::Data>; - fn init_with_usize(cnt: usize) -> Self::Data; - fn deep_flatten(&self, pref: String) -> Vec<(&Self::Data, String)>; +pub trait JsonLike: + Default + + Clone + + Debug + + for<'a> From<&'a str> + + From> + + From + + From + + From + + From> + + From + + PartialEq + + 'static +{ + fn get(&self, key: &str) -> Option<&Self>; + fn itre(&self, pref: String) -> Vec>; + fn array_len(&self) -> JsonPathValue<'static, Self>; + fn init_with_usize(cnt: usize) -> Self; + fn deep_flatten(&self, pref: String) -> Vec<(&Self, String)>; fn deep_path_by_key<'a>( &'a self, - key: ObjectField<'a, Self::Data>, + key: ObjectField<'a, Self>, pref: String, - ) -> Vec<(&'a Self::Data, String)>; + ) -> Vec<(&'a Self, String)>; fn as_u64(&self) -> Option; - fn as_array(&self) -> Option<&Vec>; - fn size(left: Vec<&Self::Data>, right: Vec<&Self::Data>) -> bool; + fn is_array(&self) -> bool; + fn as_array(&self) -> Option<&Vec>; + fn size(left: Vec<&Self>, right: Vec<&Self>) -> bool; + fn sub_set_of(left: Vec<&Self>, right: Vec<&Self>) -> bool; + fn any_of(left: Vec<&Self>, right: Vec<&Self>) -> bool; + fn regex(left: Vec<&Self>, right: Vec<&Self>) -> bool; + fn inside(left: Vec<&Self>, right: Vec<&Self>) -> bool; + fn less(left: Vec<&Self>, right: Vec<&Self>) -> bool; + fn eq(left: Vec<&Self>, right: Vec<&Self>) -> bool; + fn null() -> Self; + fn array(data: Vec) -> Self; } impl JsonLike for Value { - type Data = Value; + fn is_array(&self) -> bool { + self.is_array() + } + fn array(data: Vec) -> Self { + Value::Array(data) + } - fn get(&self, key: &str) -> Option<&Self::Data> { + fn null() -> Self { + Value::Null + } + + fn get(&self, key: &str) -> Option<&Self> { self.get(key) } - fn itre(&self, pref: String) -> Vec> { + fn itre(&self, pref: String) -> Vec> { let res = match self { Value::Array(elems) => { let mut res = vec![]; @@ -62,17 +93,17 @@ impl JsonLike for Value { res } } - fn array_len(&self) -> JsonPathValue<'static, Self::Data> { + fn array_len(&self) -> JsonPathValue<'static, Self> { match self { Value::Array(elems) => JsonPathValue::NewValue(json!(elems.len())), _ => JsonPathValue::NoValue, } } - fn init_with_usize(cnt: usize) -> Self::Data { + fn init_with_usize(cnt: usize) -> Self { json!(cnt) } - fn deep_flatten(&self, pref: String) -> Vec<(&Self::Data, String)> { + fn deep_flatten(&self, pref: String) -> Vec<(&Self, String)> { let mut acc = vec![]; match self { Value::Object(elems) => { @@ -95,9 +126,9 @@ impl JsonLike for Value { } fn deep_path_by_key<'a>( &'a self, - key: ObjectField<'a, Self::Data>, + key: ObjectField<'a, Self>, pref: String, - ) -> Vec<(&'a Self::Data, String)> { + ) -> Vec<(&'a Self, String)> { let mut result: Vec<(&'a Value, String)> = JsonPathValue::vec_as_pair(key.find(JsonPathValue::new_slice(self, pref.clone()))); match self { @@ -126,11 +157,11 @@ impl JsonLike for Value { self.as_u64() } - fn as_array(&self) -> Option<&Vec> { + fn as_array(&self) -> Option<&Vec> { self.as_array() } - fn size(left: Vec<&Self::Data>, right: Vec<&Self::Data>) -> bool { + fn size(left: Vec<&Self>, right: Vec<&Self>) -> bool { if let Some(Value::Number(n)) = right.first() { if let Some(sz) = n.as_f64() { for el in left.iter() { @@ -146,6 +177,147 @@ impl JsonLike for Value { } false } + + fn sub_set_of(left: Vec<&Self>, right: Vec<&Self>) -> bool { + if left.is_empty() { + return true; + } + if right.is_empty() { + return false; + } + + if let Some(elems) = left.first().and_then(|e| e.as_array()) { + if let Some(Value::Array(right_elems)) = right.first() { + if right_elems.is_empty() { + return false; + } + + for el in elems { + let mut res = false; + + for r in right_elems.iter() { + if el.eq(r) { + res = true + } + } + if !res { + return false; + } + } + return true; + } + } + false + } + + fn any_of(left: Vec<&Self>, right: Vec<&Self>) -> bool { + if left.is_empty() { + return true; + } + if right.is_empty() { + return false; + } + + if let Some(Value::Array(elems)) = right.first() { + if elems.is_empty() { + return false; + } + + for el in left.iter() { + if let Some(left_elems) = el.as_array() { + for l in left_elems.iter() { + for r in elems.iter() { + if l.eq(r) { + return true; + } + } + } + } else { + for r in elems.iter() { + if el.eq(&r) { + return true; + } + } + } + } + } + + false + } + + fn regex(left: Vec<&Self>, right: Vec<&Self>) -> bool { + if left.is_empty() || right.is_empty() { + return false; + } + + match right.first() { + Some(Value::String(str)) => { + if let Ok(regex) = Regex::new(str) { + for el in left.iter() { + if let Some(v) = el.as_str() { + if regex.is_match(v) { + return true; + } + } + } + } + false + } + _ => false, + } + } + + fn inside(left: Vec<&Self>, right: Vec<&Self>) -> bool { + if left.is_empty() { + return false; + } + + match right.first() { + Some(Value::Array(elems)) => { + for el in left.iter() { + if elems.contains(el) { + return true; + } + } + false + } + Some(Value::Object(elems)) => { + for el in left.iter() { + for r in elems.values() { + if el.eq(&r) { + return true; + } + } + } + false + } + _ => false, + } + } + + /// ensure the number on the left side is less the number on the right side + fn less(left: Vec<&Self>, right: Vec<&Self>) -> bool { + if left.len() == 1 && right.len() == 1 { + match (left.first(), right.first()) { + (Some(Value::Number(l)), Some(Value::Number(r))) => l + .as_f64() + .and_then(|v1| r.as_f64().map(|v2| v1 < v2)) + .unwrap_or(false), + _ => false, + } + } else { + false + } + } + + /// compare elements + fn eq(left: Vec<&Self>, right: Vec<&Self>) -> bool { + if left.len() != right.len() { + false + } else { + left.iter().zip(right).map(|(a, b)| a.eq(&b)).all(|a| a) + } + } } /// The trait defining the behaviour of processing every separated element. @@ -181,17 +353,17 @@ pub enum TopPaths<'a, T> { DescentObject(DescentObject<'a, T>), DescentWildcard(DescentWildcard), Current(Current<'a, T>), - ArrayIndex(ArrayIndex), - ArraySlice(ArraySlice), + ArrayIndex(ArrayIndex), + ArraySlice(ArraySlice), UnionIndex(UnionIndex<'a, T>), FilterPath(FilterPath<'a, T>), - IdentityPath(IdentityPath), + IdentityPath(IdentityPath), FnPath(FnPath), } impl<'a, T> Path<'a> for TopPaths<'a, T> where - T: JsonLike + Default + Clone + Debug, + T: JsonLike + Default + Clone + Debug, { type Data = T; @@ -258,12 +430,12 @@ where pub(crate) type PathInstance<'a, T> = Box + 'a>; /// The major method to process the top part of json part -pub(crate) fn json_path_instance<'a, T: JsonLike>( +pub(crate) fn json_path_instance<'a, T: JsonLike>( json_path: &'a JsonPath, root: &'a T, ) -> TopPaths<'a, T> where - T: JsonLike + Default + Clone + Debug, + T: JsonLike + Default + Clone + Debug, { match json_path { JsonPath::Root => TopPaths::RootPointer(RootPointer::new(root)), @@ -288,7 +460,7 @@ where JsonPath::Index(JsonPathIndex::Filter(fe)) => { TopPaths::FilterPath(FilterPath::new(fe, root)) } - JsonPath::Empty => TopPaths::IdentityPath(IdentityPath {}), + JsonPath::Empty => TopPaths::IdentityPath(IdentityPath::new()), JsonPath::Fn(Function::Length) => TopPaths::FnPath(FnPath::new_size()), } } @@ -296,7 +468,7 @@ where /// The method processes the operand inside the filter expressions fn process_operand<'a, T>(op: &'a Operand, root: &'a T) -> PathInstance<'a, T> where - T: JsonLike + Default + Clone + Debug, + T: JsonLike + Default + Clone + Debug, { Box::new(match op { Operand::Static(v) => json_path_instance(&JsonPath::Root, v), diff --git a/src/path/top.rs b/src/path/top.rs index 7265d78..44aacf6 100644 --- a/src/path/top.rs +++ b/src/path/top.rs @@ -1,11 +1,9 @@ use std::fmt::Debug; +use crate::jsp_obj; use crate::parser::model::*; use crate::path::{json_path_instance, JsonPathValue, Path}; use crate::JsonPathValue::{NewValue, NoValue, Slice}; -use crate::{jsp_idx, jsp_obj, JsPathStr}; -use serde_json::value::Value::{Array, Object}; -use serde_json::{json, Value}; use super::{JsonLike, TopPaths}; @@ -24,7 +22,7 @@ impl Wildcard { impl<'a, T> Path<'a> for Wildcard where - T: JsonLike, + T: JsonLike, { type Data = T; @@ -34,20 +32,32 @@ where } /// empty path. Returns incoming data. -pub struct IdentityPath {} +pub struct IdentityPath { + _t: std::marker::PhantomData, +} + +impl IdentityPath { + pub fn new() -> Self { + Self { + _t: std::marker::PhantomData, + } + } +} -impl<'a> Path<'a> for IdentityPath { - type Data = Value; +impl<'a, T> Path<'a> for IdentityPath { + type Data = T; fn find(&self, data: JsonPathValue<'a, Self::Data>) -> Vec> { vec![data] } } -pub(crate) struct EmptyPath {} +pub(crate) struct EmptyPath { + _t: std::marker::PhantomData, +} -impl<'a> Path<'a> for EmptyPath { - type Data = Value; +impl<'a, T> Path<'a> for EmptyPath { + type Data = T; fn find(&self, _data: JsonPathValue<'a, Self::Data>) -> Vec> { vec![] @@ -99,7 +109,7 @@ impl<'a, T> Clone for ObjectField<'a, T> { impl<'a, T> Path<'a> for ObjectField<'a, T> where - T: JsonLike + Clone + Default + Debug, + T: JsonLike + Clone + Default + Debug, { type Data = T; @@ -130,7 +140,7 @@ impl FnPath { impl<'a, T> Path<'a> for FnPath where - T: JsonLike + 'static, + T: JsonLike + 'static, { type Data = T; @@ -180,7 +190,7 @@ impl DescentWildcard { impl<'a, T> Path<'a> for DescentWildcard where - T: JsonLike, + T: JsonLike, { type Data = T; @@ -189,36 +199,6 @@ where } } -// // todo rewrite to tail rec -// fn deep_path_by_key<'a>( -// data: &'a Value, -// key: ObjectField<'a>, -// pref: JsPathStr, -// ) -> Vec<(&'a Value, JsPathStr)> { -// let mut result: Vec<(&'a Value, JsPathStr)> = -// JsonPathValue::vec_as_pair(key.find(JsonPathValue::new_slice(data, pref.clone()))); -// match data { -// Object(elems) => { -// let mut next_levels: Vec<(&'a Value, JsPathStr)> = elems -// .into_iter() -// .flat_map(|(k, v)| deep_path_by_key(v, key.clone(), jsp_obj(&pref, k))) -// .collect(); -// result.append(&mut next_levels); -// result -// } -// Array(elems) => { -// let mut next_levels: Vec<(&'a Value, JsPathStr)> = elems -// .iter() -// .enumerate() -// .flat_map(|(i, v)| deep_path_by_key(v, key.clone(), jsp_idx(&pref, i))) -// .collect(); -// result.append(&mut next_levels); -// result -// } -// _ => result, -// } -// } - /// processes decent object like .. pub struct DescentObject<'a, T> { key: &'a str, @@ -227,7 +207,7 @@ pub struct DescentObject<'a, T> { impl<'a, T> Path<'a> for DescentObject<'a, T> where - T: JsonLike, + T: JsonLike, { type Data = T; @@ -260,7 +240,7 @@ pub struct Chain<'a, T> { impl<'a, T> Chain<'a, T> where - T: JsonLike + Default + Clone + Debug, + T: JsonLike + Default + Clone + Debug, { pub fn new(chain: Vec>, is_search_length: bool) -> Self { Chain { @@ -316,7 +296,7 @@ where impl<'a, T> Path<'a> for Chain<'a, T> where - T: JsonLike + Default + Clone + Debug, + T: JsonLike + Default + Clone + Debug, { type Data = T; From 971e41ae3f169c2cc387deecf8d536f2701ad94d Mon Sep 17 00:00:00 2001 From: paomian Date: Sat, 12 Oct 2024 16:56:05 +0800 Subject: [PATCH 3/6] chore: make clippy happy --- src/parser/parser.rs | 26 ++++++++++++-------------- src/path/index.rs | 6 ++---- src/path/mod.rs | 7 ++----- src/path/top.rs | 22 ++++++++++++++++++++-- 4 files changed, 36 insertions(+), 25 deletions(-) diff --git a/src/parser/parser.rs b/src/parser/parser.rs index 5a6da73..355f619 100644 --- a/src/parser/parser.rs +++ b/src/parser/parser.rs @@ -18,7 +18,7 @@ struct JsonPathParser; /// # Errors /// /// Returns a variant of [JsonPathParserError] if the parsing operation failed. -pub fn parse_json_path<'a, T>(jp_str: &'a str) -> Result, JsonPathParserError> +pub fn parse_json_path(jp_str: &str) -> Result, JsonPathParserError> where T: JsonLike, { @@ -34,7 +34,7 @@ where /// # Errors /// /// Returns a variant of [JsonPathParserError] if the parsing operation failed -fn parse_internal<'a, T>(rule: Pair<'a, Rule>) -> Result, JsonPathParserError> +fn parse_internal(rule: Pair<'_, Rule>) -> Result, JsonPathParserError> where T: JsonLike, { @@ -142,7 +142,7 @@ where Ok(JsonPathIndex::UnionIndex(keys)) } -fn parse_chain_in_operand<'a, T>(rule: Pair<'a, Rule>) -> Result, JsonPathParserError> +fn parse_chain_in_operand(rule: Pair<'_, Rule>) -> Result, JsonPathParserError> where T: JsonLike, { @@ -168,14 +168,14 @@ where Ok(parsed_chain) } -fn parse_filter_index<'a, T>(pair: Pair<'a, Rule>) -> Result, JsonPathParserError> +fn parse_filter_index(pair: Pair<'_, Rule>) -> Result, JsonPathParserError> where T: JsonLike, { Ok(JsonPathIndex::Filter(parse_logic_or(pair.into_inner())?)) } -fn parse_logic_or<'a, T>(pairs: Pairs<'a, Rule>) -> Result, JsonPathParserError> +fn parse_logic_or(pairs: Pairs<'_, Rule>) -> Result, JsonPathParserError> where T: JsonLike, { @@ -197,9 +197,7 @@ where Ok(expr.expect("unreachable: above len() == 0 check should have catched this")) } -fn parse_logic_and<'a, T>( - pairs: Pairs<'a, Rule>, -) -> Result, JsonPathParserError> +fn parse_logic_and(pairs: Pairs<'_, Rule>) -> Result, JsonPathParserError> where T: JsonLike, { @@ -221,8 +219,8 @@ where Ok(expr.expect("unreachable: above len() == 0 check should have catched this")) } -fn parse_logic_not<'a, T>( - mut pairs: Pairs<'a, Rule>, +fn parse_logic_not( + mut pairs: Pairs<'_, Rule>, ) -> Result, JsonPathParserError> where T: JsonLike, @@ -245,8 +243,8 @@ where } } -fn parse_logic_atom<'a, T>( - mut pairs: Pairs<'a, Rule>, +fn parse_logic_atom( + mut pairs: Pairs<'_, Rule>, ) -> Result, JsonPathParserError> where T: JsonLike, @@ -275,7 +273,7 @@ where } } -fn parse_atom<'a, T>(rule: Pair<'a, Rule>) -> Result, JsonPathParserError> +fn parse_atom(rule: Pair<'_, Rule>) -> Result, JsonPathParserError> where T: JsonLike, { @@ -290,7 +288,7 @@ where Ok(parsed_atom) } -fn parse_index<'a, T>(rule: Pair<'a, Rule>) -> Result, JsonPathParserError> +fn parse_index(rule: Pair<'_, Rule>) -> Result, JsonPathParserError> where T: JsonLike, { diff --git a/src/path/index.rs b/src/path/index.rs index 0104748..6a84422 100644 --- a/src/path/index.rs +++ b/src/path/index.rs @@ -378,10 +378,8 @@ where res.push(Slice(el, jsp_idx(&pref, i))) } } - } else { - if self.process(data) { - res.push(Slice(data, pref)) - } + } else if self.process(data) { + res.push(Slice(data, pref)) } // match data { diff --git a/src/path/mod.rs b/src/path/mod.rs index a36f9a1..0cdd247 100644 --- a/src/path/mod.rs +++ b/src/path/mod.rs @@ -430,12 +430,9 @@ where pub(crate) type PathInstance<'a, T> = Box + 'a>; /// The major method to process the top part of json part -pub(crate) fn json_path_instance<'a, T: JsonLike>( - json_path: &'a JsonPath, - root: &'a T, -) -> TopPaths<'a, T> +pub(crate) fn json_path_instance<'a, T>(json_path: &'a JsonPath, root: &'a T) -> TopPaths<'a, T> where - T: JsonLike + Default + Clone + Debug, + T: JsonLike, { match json_path { JsonPath::Root => TopPaths::RootPointer(RootPointer::new(root)), diff --git a/src/path/top.rs b/src/path/top.rs index 44aacf6..9c8f2e3 100644 --- a/src/path/top.rs +++ b/src/path/top.rs @@ -20,6 +20,12 @@ impl Wildcard { } } +impl Default for Wildcard { + fn default() -> Self { + Self::new() + } +} + impl<'a, T> Path<'a> for Wildcard where T: JsonLike, @@ -36,6 +42,12 @@ pub struct IdentityPath { _t: std::marker::PhantomData, } +impl Default for IdentityPath { + fn default() -> Self { + Self::new() + } +} + impl IdentityPath { pub fn new() -> Self { Self { @@ -77,7 +89,7 @@ impl<'a, T> RootPointer<'a, T> { impl<'a, T> Path<'a> for RootPointer<'a, T> where - T: JsonLike + Clone + core::fmt::Debug + Default + 'a, + T: JsonLike, { type Data = T; @@ -109,7 +121,7 @@ impl<'a, T> Clone for ObjectField<'a, T> { impl<'a, T> Path<'a> for ObjectField<'a, T> where - T: JsonLike + Clone + Default + Debug, + T: JsonLike, { type Data = T; @@ -180,6 +192,12 @@ pub struct DescentWildcard { _t: std::marker::PhantomData, } +impl Default for DescentWildcard { + fn default() -> Self { + Self::new() + } +} + impl DescentWildcard { pub fn new() -> Self { DescentWildcard { From b0c6d0819ac9836dc38f7932b966515ac5c8791e Mon Sep 17 00:00:00 2001 From: paomian Date: Tue, 15 Oct 2024 14:43:28 +0800 Subject: [PATCH 4/6] chore: fix by pr comment --- benches/equal.rs | 4 +- benches/regex.rs | 2 +- src/parser/model.rs | 4 +- src/path/index.rs | 16 ++--- src/path/json.rs | 124 -------------------------------- src/path/mod.rs | 170 +++++++++++++++++++++++++++++++++++++++++++- src/path/top.rs | 6 +- 7 files changed, 182 insertions(+), 144 deletions(-) diff --git a/benches/equal.rs b/benches/equal.rs index 9cecfc3..a549ca6 100644 --- a/benches/equal.rs +++ b/benches/equal.rs @@ -1,11 +1,11 @@ use criterion::{criterion_group, criterion_main, Criterion}; use jsonpath_rust::{JsonPath, JsonPathQuery}; -use serde_json::{json, Value}; +use serde_json::json; use std::str::FromStr; struct SearchData { json: serde_json::Value, - path: JsonPath, + path: JsonPath, } const PATH: &str = "$.[?(@.author == 'abcd(Rees)')]"; diff --git a/benches/regex.rs b/benches/regex.rs index b8ad522..72f9090 100644 --- a/benches/regex.rs +++ b/benches/regex.rs @@ -5,7 +5,7 @@ use std::str::FromStr; struct SearchData { json: serde_json::Value, - path: JsonPath, + path: JsonPath, } const PATH: &str = "$.[?(@.author ~= '.*(?i)d\\(Rees\\)')]"; diff --git a/src/parser/model.rs b/src/parser/model.rs index d983bdc..2f40e94 100644 --- a/src/parser/model.rs +++ b/src/parser/model.rs @@ -1,3 +1,5 @@ +use serde_json::Value; + use crate::path::JsonLike; use super::errors::JsonPathParserError; @@ -10,7 +12,7 @@ use std::{convert::TryFrom, str::FromStr}; /// /// usually it's created by using [`FromStr`] or [`TryFrom<&str>`] #[derive(Debug, Clone)] -pub enum JsonPath { +pub enum JsonPath { /// The $ operator Root, /// Field represents key diff --git a/src/path/index.rs b/src/path/index.rs index 6a84422..bb33ce8 100644 --- a/src/path/index.rs +++ b/src/path/index.rs @@ -80,7 +80,7 @@ impl ArraySlice { impl<'a, T> Path<'a> for ArraySlice where - T: JsonLike + Default + Clone + Debug, + T: JsonLike, { type Data = T; @@ -118,7 +118,7 @@ impl ArrayIndex { impl<'a, T> Path<'a> for ArrayIndex where - T: JsonLike + Default + Clone + Debug, + T: JsonLike, { type Data = T; @@ -140,7 +140,7 @@ pub struct Current<'a, T> { impl<'a, T> Current<'a, T> where - T: JsonLike + Default + Clone + Debug, + T: JsonLike, { pub(crate) fn from(jp: &'a JsonPath, root: &'a T) -> Self { match jp { @@ -164,7 +164,7 @@ where impl<'a, T> Path<'a> for Current<'a, T> where - T: JsonLike + Default + Clone + Debug, + T: JsonLike, { type Data = T; @@ -183,7 +183,7 @@ pub struct UnionIndex<'a, T> { impl<'a, T> UnionIndex<'a, T> where - T: JsonLike + Default + Clone + Debug, + T: JsonLike, { pub fn from_indexes(elems: &'a [T]) -> Self { let mut indexes: Vec> = vec![]; @@ -213,7 +213,7 @@ where impl<'a, T> Path<'a> for UnionIndex<'a, T> where - T: JsonLike + Default + Clone + Debug, + T: JsonLike, { type Data = T; @@ -288,10 +288,6 @@ where JsonPathValue::vec_as_data(left), JsonPathValue::vec_as_data(right), ), - // eq( - // JsonPathValue::vec_as_data(left), - // JsonPathValue::vec_as_data(right), - // ), FilterSign::Unequal => !FilterPath::process_atom(&FilterSign::Equal, left, right), FilterSign::Less => ::less( JsonPathValue::vec_as_data(left), diff --git a/src/path/json.rs b/src/path/json.rs index d96e2fb..ee4fca2 100644 --- a/src/path/json.rs +++ b/src/path/json.rs @@ -173,128 +173,4 @@ // } // } -// #[cfg(test)] -// mod tests { -// use crate::path::json::{any_of, eq, less, regex, size, sub_set_of}; -// use serde_json::{json, Value}; -// #[test] -// fn value_eq_test() { -// let left = json!({"value":42}); -// let right = json!({"value":42}); -// let right_uneq = json!([42]); - -// assert!(&left.eq(&right)); -// assert!(!&left.eq(&right_uneq)); -// } - -// #[test] -// fn vec_value_test() { -// let left = json!({"value":42}); -// let left1 = json!(42); -// let left2 = json!([1, 2, 3]); -// let left3 = json!({"value2":[42],"value":[42]}); - -// let right = json!({"value":42}); -// let right1 = json!(42); -// let right2 = json!([1, 2, 3]); -// let right3 = json!({"value":[42],"value2":[42]}); - -// assert!(eq(vec![&left], vec![&right])); - -// assert!(!eq(vec![], vec![&right])); -// assert!(!eq(vec![&right], vec![])); - -// assert!(eq( -// vec![&left, &left1, &left2, &left3], -// vec![&right, &right1, &right2, &right3] -// )); - -// assert!(!eq( -// vec![&left1, &left, &left2, &left3], -// vec![&right, &right1, &right2, &right3] -// )); -// } - -// #[test] -// fn less_value_test() { -// let left = json!(10); -// let right = json!(11); - -// assert!(less(vec![&left], vec![&right])); -// assert!(!less(vec![&right], vec![&left])); - -// let left = json!(-10); -// let right = json!(-11); - -// assert!(!less(vec![&left], vec![&right])); -// assert!(less(vec![&right], vec![&left])); - -// let left = json!(-10.0); -// let right = json!(-11.0); - -// assert!(!less(vec![&left], vec![&right])); -// assert!(less(vec![&right], vec![&left])); - -// assert!(!less(vec![], vec![&right])); -// assert!(!less(vec![&right, &right], vec![&left])); -// } - -// #[test] -// fn regex_test() { -// let right = json!("[a-zA-Z]+[0-9]#[0-9]+"); -// let left1 = json!("a11#"); -// let left2 = json!("a1#1"); -// let left3 = json!("a#11"); -// let left4 = json!("#a11"); - -// assert!(regex(vec![&left1, &left2, &left3, &left4], vec![&right])); -// assert!(!regex(vec![&left1, &left3, &left4], vec![&right])) -// } - -// #[test] -// fn any_of_test() { -// let right = json!([1, 2, 3, 4, 5, 6]); -// let left = json!([1, 100, 101]); -// assert!(any_of(vec![&left], vec![&right])); - -// let left = json!([11, 100, 101]); -// assert!(!any_of(vec![&left], vec![&right])); - -// let left1 = json!(1); -// let left2 = json!(11); -// assert!(any_of(vec![&left1, &left2], vec![&right])); -// } - -// #[test] -// fn sub_set_of_test() { -// let left1 = json!(1); -// let left2 = json!(2); -// let left3 = json!(3); -// let left40 = json!(40); -// let right = json!([1, 2, 3, 4, 5, 6]); -// assert!(sub_set_of( -// vec![&Value::Array(vec![ -// left1.clone(), -// left2.clone(), -// left3.clone() -// ])], -// vec![&right] -// )); -// assert!(!sub_set_of( -// vec![&Value::Array(vec![left1, left2, left3, left40])], -// vec![&right] -// )); -// } - -// #[test] -// fn size_test() { -// let left1 = json!("abc"); -// let left2 = json!([1, 2, 3]); -// let left3 = json!([1, 2, 3, 4]); -// let right = json!(3); -// assert!(size(vec![&left1], vec![&right])); -// assert!(size(vec![&left2], vec![&right])); -// assert!(!size(vec![&left3], vec![&right])); -// } -// } diff --git a/src/path/mod.rs b/src/path/mod.rs index 0cdd247..523d708 100644 --- a/src/path/mod.rs +++ b/src/path/mod.rs @@ -29,27 +29,62 @@ pub trait JsonLike: + PartialEq + 'static { + /// Retrieves a reference to the value associated with the given key. fn get(&self, key: &str) -> Option<&Self>; + + /// Iterates over the elements with a given prefix and returns a vector of `JsonPathValue`. fn itre(&self, pref: String) -> Vec>; + + /// Returns the length of the array as a `JsonPathValue`. fn array_len(&self) -> JsonPathValue<'static, Self>; + + /// Initializes an instance with a specific size. fn init_with_usize(cnt: usize) -> Self; + + /// Flattens nested structures and returns a vector of tuples containing references to the elements and their paths. fn deep_flatten(&self, pref: String) -> Vec<(&Self, String)>; + + /// Performs a deep search by key and returns a vector of tuples containing references to the elements and their paths. fn deep_path_by_key<'a>( &'a self, key: ObjectField<'a, Self>, pref: String, ) -> Vec<(&'a Self, String)>; + + /// Converts the element to an `Option`. fn as_u64(&self) -> Option; + + /// Checks if the element is an array. fn is_array(&self) -> bool; + + /// Converts the element to an `Option<&Vec>`. fn as_array(&self) -> Option<&Vec>; + + /// Compares the size of two vectors of references to elements. fn size(left: Vec<&Self>, right: Vec<&Self>) -> bool; + + /// Checks if the left vector is a subset of the right vector. fn sub_set_of(left: Vec<&Self>, right: Vec<&Self>) -> bool; + + /// Checks if any element in the left vector is present in the right vector. fn any_of(left: Vec<&Self>, right: Vec<&Self>) -> bool; + + /// Checks if the elements in the left vector match the regex pattern in the right vector. fn regex(left: Vec<&Self>, right: Vec<&Self>) -> bool; + + /// Checks if any element in the left vector is inside the right vector. fn inside(left: Vec<&Self>, right: Vec<&Self>) -> bool; + + /// Ensures the number on the left side is less than the number on the right side. fn less(left: Vec<&Self>, right: Vec<&Self>) -> bool; + + /// Compares elements for equality. fn eq(left: Vec<&Self>, right: Vec<&Self>) -> bool; + + /// Returns a null value. fn null() -> Self; + + /// Creates an array from a vector of elements. fn array(data: Vec) -> Self; } @@ -363,7 +398,7 @@ pub enum TopPaths<'a, T> { impl<'a, T> Path<'a> for TopPaths<'a, T> where - T: JsonLike + Default + Clone + Debug, + T: JsonLike, { type Data = T; @@ -465,10 +500,141 @@ where /// The method processes the operand inside the filter expressions fn process_operand<'a, T>(op: &'a Operand, root: &'a T) -> PathInstance<'a, T> where - T: JsonLike + Default + Clone + Debug, + T: JsonLike, { Box::new(match op { Operand::Static(v) => json_path_instance(&JsonPath::Root, v), Operand::Dynamic(jp) => json_path_instance(jp, root), }) } + +#[cfg(test)] +mod tests { + + use crate::path::JsonLike; + use serde_json::{json, Value}; + + #[test] + fn value_eq_test() { + let left = json!({"value":42}); + let right = json!({"value":42}); + let right_uneq = json!([42]); + assert!(&left.eq(&right)); + assert!(!&left.eq(&right_uneq)); + } + + #[test] + fn vec_value_test() { + let left = json!({"value":42}); + let left1 = json!(42); + let left2 = json!([1, 2, 3]); + let left3 = json!({"value2":[42],"value":[42]}); + + let right = json!({"value":42}); + let right1 = json!(42); + let right2 = json!([1, 2, 3]); + let right3 = json!({"value":[42],"value2":[42]}); + + assert!(JsonLike::eq(vec![&left], vec![&right])); + + assert!(!JsonLike::eq(vec![], vec![&right])); + assert!(!JsonLike::eq(vec![&right], vec![])); + + assert!(JsonLike::eq( + vec![&left, &left1, &left2, &left3], + vec![&right, &right1, &right2, &right3] + )); + + assert!(!JsonLike::eq( + vec![&left1, &left, &left2, &left3], + vec![&right, &right1, &right2, &right3] + )); + } + + #[test] + fn less_value_test() { + let left = json!(10); + let right = json!(11); + + assert!(JsonLike::less(vec![&left], vec![&right])); + assert!(!JsonLike::less(vec![&right], vec![&left])); + + let left = json!(-10); + let right = json!(-11); + + assert!(!JsonLike::less(vec![&left], vec![&right])); + assert!(JsonLike::less(vec![&right], vec![&left])); + + let left = json!(-10.0); + let right = json!(-11.0); + + assert!(!JsonLike::less(vec![&left], vec![&right])); + assert!(JsonLike::less(vec![&right], vec![&left])); + + assert!(!JsonLike::less(vec![], vec![&right])); + assert!(!JsonLike::less(vec![&right, &right], vec![&left])); + } + + #[test] + fn regex_test() { + let right = json!("[a-zA-Z]+[0-9]#[0-9]+"); + let left1 = json!("a11#"); + let left2 = json!("a1#1"); + let left3 = json!("a#11"); + let left4 = json!("#a11"); + + assert!(JsonLike::regex( + vec![&left1, &left2, &left3, &left4], + vec![&right] + )); + assert!(!JsonLike::regex(vec![&left1, &left3, &left4], vec![&right])) + } + + #[test] + fn any_of_test() { + let right = json!([1, 2, 3, 4, 5, 6]); + let left = json!([1, 100, 101]); + assert!(JsonLike::any_of(vec![&left], vec![&right])); + + let left = json!([11, 100, 101]); + assert!(!JsonLike::any_of(vec![&left], vec![&right])); + + let left1 = json!(1); + let left2 = json!(11); + assert!(JsonLike::any_of(vec![&left1, &left2], vec![&right])); + } + + #[test] + fn sub_set_of_test() { + let left1 = json!(1); + let left2 = json!(2); + let left3 = json!(3); + let left40 = json!(40); + let right = json!([1, 2, 3, 4, 5, 6]); + assert!(JsonLike::sub_set_of( + vec![&Value::Array(vec![ + left1.clone(), + left2.clone(), + left3.clone() + ])], + vec![&right] + )); + assert!(!JsonLike::sub_set_of( + vec![&Value::Array(vec![left1, left2, left3, left40])], + vec![&right] + )); + } + + #[test] + fn size_test() { + let left1 = json!("abc"); + let left2 = json!([1, 2, 3]); + let left3 = json!([1, 2, 3, 4]); + let right = json!(3); + let right1 = json!(4); + assert!(JsonLike::size(vec![&left1], vec![&right])); + assert!(JsonLike::size(vec![&left2], vec![&right])); + assert!(!JsonLike::size(vec![&left3], vec![&right])); + assert!(JsonLike::size(vec![&left3], vec![&right1])); + } +} diff --git a/src/path/top.rs b/src/path/top.rs index 9c8f2e3..220fa79 100644 --- a/src/path/top.rs +++ b/src/path/top.rs @@ -1,5 +1,3 @@ -use std::fmt::Debug; - use crate::jsp_obj; use crate::parser::model::*; use crate::path::{json_path_instance, JsonPathValue, Path}; @@ -258,7 +256,7 @@ pub struct Chain<'a, T> { impl<'a, T> Chain<'a, T> where - T: JsonLike + Default + Clone + Debug, + T: JsonLike, { pub fn new(chain: Vec>, is_search_length: bool) -> Self { Chain { @@ -314,7 +312,7 @@ where impl<'a, T> Path<'a> for Chain<'a, T> where - T: JsonLike + Default + Clone + Debug, + T: JsonLike, { type Data = T; From c50ec5ae6fc0f30b723efd836fc4b784736ba42b Mon Sep 17 00:00:00 2001 From: paomian Date: Tue, 15 Oct 2024 14:45:00 +0800 Subject: [PATCH 5/6] chore: remove path json --- src/path/json.rs | 176 ----------------------------------------------- src/path/mod.rs | 2 - 2 files changed, 178 deletions(-) delete mode 100644 src/path/json.rs diff --git a/src/path/json.rs b/src/path/json.rs deleted file mode 100644 index ee4fca2..0000000 --- a/src/path/json.rs +++ /dev/null @@ -1,176 +0,0 @@ -// use regex::Regex; -// use serde_json::Value; - -// use super::JsonLike; - -// /// compare sizes of json elements -// /// The method expects to get a number on the right side and array or string or object on the left -// /// where the number of characters, elements or fields will be compared respectively. -// pub fn size(left: Vec<&T>, right: Vec<&T>) -> bool -// where -// T: JsonLike, -// { -// if let Some(Value::Number(n)) = right.first() { -// if let Some(sz) = n.as_f64() { -// for el in left.iter() { -// match el { -// Value::String(v) if v.len() == sz as usize => true, -// Value::Array(elems) if elems.len() == sz as usize => true, -// Value::Object(fields) if fields.len() == sz as usize => true, -// _ => return false, -// }; -// } -// return true; -// } -// } -// false -// } - -// /// ensure the array on the left side is a subset of the array on the right side. -// //todo change the naive impl to sets -// pub fn sub_set_of(left: Vec<&T>, right: Vec<&T>) -> bool { -// if left.is_empty() { -// return true; -// } -// if right.is_empty() { -// return false; -// } - -// if let Some(elems) = left.first().and_then(|e| e.as_array()) { -// if let Some(Value::Array(right_elems)) = right.first() { -// if right_elems.is_empty() { -// return false; -// } - -// for el in elems { -// let mut res = false; - -// for r in right_elems.iter() { -// if el.eq(r) { -// res = true -// } -// } -// if !res { -// return false; -// } -// } -// return true; -// } -// } -// false -// } - -// /// ensure at least one element in the array on the left side belongs to the array on the right side. -// //todo change the naive impl to sets -// pub fn any_of(left: Vec<&T>, right: Vec<&T>) -> bool { -// if left.is_empty() { -// return true; -// } -// if right.is_empty() { -// return false; -// } - -// if let Some(Value::Array(elems)) = right.first() { -// if elems.is_empty() { -// return false; -// } - -// for el in left.iter() { -// if let Some(left_elems) = el.as_array() { -// for l in left_elems.iter() { -// for r in elems.iter() { -// if l.eq(r) { -// return true; -// } -// } -// } -// } else { -// for r in elems.iter() { -// if el.eq(&r) { -// return true; -// } -// } -// } -// } -// } - -// false -// } - -// /// ensure that the element on the left sides mathes the regex on the right side -// pub fn regex(left: Vec<&T>, right: Vec<&T>) -> bool { -// if left.is_empty() || right.is_empty() { -// return false; -// } - -// match right.first() { -// Some(Value::String(str)) => { -// if let Ok(regex) = Regex::new(str) { -// for el in left.iter() { -// if let Some(v) = el.as_str() { -// if regex.is_match(v) { -// return true; -// } -// } -// } -// } -// false -// } -// _ => false, -// } -// } - -// /// ensure that the element on the left side belongs to the array on the right side. -// pub fn inside(left: Vec<&T>, right: Vec<&T>) -> bool { -// if left.is_empty() { -// return false; -// } - -// match right.first() { -// Some(Value::Array(elems)) => { -// for el in left.iter() { -// if elems.contains(el) { -// return true; -// } -// } -// false -// } -// Some(Value::Object(elems)) => { -// for el in left.iter() { -// for r in elems.values() { -// if el.eq(&r) { -// return true; -// } -// } -// } -// false -// } -// _ => false, -// } -// } - -// /// ensure the number on the left side is less the number on the right side -// pub fn less(left: Vec<&T>, right: Vec<&T>) -> bool { -// if left.len() == 1 && right.len() == 1 { -// match (left.first(), right.first()) { -// (Some(Value::Number(l)), Some(Value::Number(r))) => l -// .as_f64() -// .and_then(|v1| r.as_f64().map(|v2| v1 < v2)) -// .unwrap_or(false), -// _ => false, -// } -// } else { -// false -// } -// } - -// /// compare elements -// pub fn eq(left: Vec<&T>, right: Vec<&T>) -> bool { -// if left.len() != right.len() { -// false -// } else { -// left.iter().zip(right).map(|(a, b)| a.eq(&b)).all(|a| a) -// } -// } - - diff --git a/src/path/mod.rs b/src/path/mod.rs index 523d708..96130c4 100644 --- a/src/path/mod.rs +++ b/src/path/mod.rs @@ -10,8 +10,6 @@ use crate::path::top::*; /// The module is in charge of processing [[JsonPathIndex]] elements mod index; -/// The module is a helper module providing the set of helping funcitons to process a json elements -mod json; /// The module is responsible for processing of the [[JsonPath]] elements mod top; From 3f0431e1b9ffe6e9f34b0ec4b3634bcbc742ae23 Mon Sep 17 00:00:00 2001 From: paomian Date: Tue, 15 Oct 2024 14:46:26 +0800 Subject: [PATCH 6/6] chore :add doc for JsonLike trait --- src/path/mod.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/path/mod.rs b/src/path/mod.rs index 96130c4..f4322f0 100644 --- a/src/path/mod.rs +++ b/src/path/mod.rs @@ -13,6 +13,15 @@ mod index; /// The module is responsible for processing of the [[JsonPath]] elements mod top; +/// The `JsonLike` trait defines a set of methods and associated types for working with JSON-like data structures. +/// +/// It provides a common interface for accessing and manipulating JSON data, +/// allowing for operations such as +/// - retrieving values by key, +/// - iterating over elements +/// - performing various comparisons and transformations. +/// +/// The trait is implemented for the `serde_json::Value` type already pub trait JsonLike: Default + Clone