diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bdb903..a5c2098 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -47,6 +47,10 @@ - **`0.6`** - allow to reuse regex, that improves performance without needing an internal cache -- **`6.1`** +- **`0.6.1`** - Performance improvements - - Change the contract for the struct of errors \ No newline at end of file + - Change the contract for the struct of errors +- **`0.7.0`** + - Bug fixes and api changes +- **`0.7.1`** + - add Display to JsonPath \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index a25bb20..8dd827f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "jsonpath-rust" description = "The library provides the basic functionality to find the set of the data according to the filtering query." -version = "0.6.1" +version = "0.7.1" authors = ["BorisZhguchev "] edition = "2021" license = "MIT" diff --git a/src/lib.rs b/src/lib.rs index 03baf25..c316776 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -378,16 +378,21 @@ impl<'a, Data> JsonPathValue<'a, Data> { #[cfg(test)] mod tests { - // #[test] - // fn no_value_len_field_test() { - // let json: Box = - // Box::new(json!([{"verb": "TEST","a":[1,2,3]},{"verb": "TEST","a":[1,2,3]},{"verb": "TEST"}, {"verb": "RUN"}])); - // let path: Box = Box::from( - // JsonPath::from_str("$.[?(@.verb == 'TEST')].a.length()") - // .expect("the path is correct"), - // ); - // - // let v = json.find_slice(&path); - // assert_eq!(v, vec![NewValue(json!(3))]); - // } + use crate::JsonPath; + use std::str::FromStr; + + #[test] + fn to_string_test() { + let path: Box = Box::from( + JsonPath::from_str( + "$.['a'].a..book[1:3][*][1]['a','b'][?(@)][?(@.verb == 'TEST')].a.length()", + ) + .unwrap(), + ); + + assert_eq!( + path.to_string(), + "$.'a'.'a'..book[1:3:1][*][1]['a','b'][?(@ exists )][?(@.'verb' == \"TEST\")].'a'.length()" + ); + } } diff --git a/src/parser/model.rs b/src/parser/model.rs index 90da1ad..6fe648a 100644 --- a/src/parser/model.rs +++ b/src/parser/model.rs @@ -1,9 +1,9 @@ +use super::errors::JsonPathParserError; use super::parse_json_path; use serde_json::Value; +use std::fmt::{Display, Formatter}; use std::{convert::TryFrom, str::FromStr}; -use super::errors::JsonPathParserError; - /// The basic structures for parsing json paths. /// The common logic of the structures pursues to correspond the internal parsing structure. /// @@ -32,6 +32,26 @@ pub enum JsonPath { Fn(Function), } +impl Display for JsonPath { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let str = match self { + JsonPath::Root => "$".to_string(), + JsonPath::Field(e) => format!(".'{}'", e), + JsonPath::Chain(elems) => elems.iter().map(ToString::to_string).collect::(), + JsonPath::Descent(e) => { + format!("..{}", e) + } + JsonPath::DescentW => "..*".to_string(), + JsonPath::Index(e) => e.to_string(), + JsonPath::Current(e) => format!("@{}", e), + JsonPath::Wildcard => "[*]".to_string(), + JsonPath::Empty => "".to_string(), + JsonPath::Fn(e) => format!(".{}", e), + }; + write!(f, "{}", str) + } +} + impl TryFrom<&str> for JsonPath { type Error = JsonPathParserError; @@ -63,6 +83,16 @@ pub enum Function { /// length() Length, } + +impl Display for Function { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let str = match self { + Function::Length => "length()".to_string(), + }; + write!(f, "{}", str) + } +} + #[derive(Debug, Clone)] pub enum JsonPathIndex { /// A single element in array @@ -77,6 +107,39 @@ pub enum JsonPathIndex { Filter(FilterExpression), } +impl Display for JsonPathIndex { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let str = match self { + JsonPathIndex::Single(e) => format!("[{}]", e), + JsonPathIndex::UnionIndex(elems) => { + format!( + "[{}]", + elems + .iter() + .map(ToString::to_string) + .collect::>() + .join(",") + ) + } + JsonPathIndex::UnionKeys(elems) => { + format!( + "[{}]", + elems + .iter() + .map(|el| format!("'{}'", el)) + .collect::>() + .join(",") + ) + } + JsonPathIndex::Slice(s, e, st) => { + format!("[{}:{}:{}]", s, e, st) + } + JsonPathIndex::Filter(filter) => format!("[?({})]", filter), + }; + write!(f, "{}", str) + } +} + #[derive(Debug, Clone, PartialEq)] pub enum FilterExpression { /// a single expression like a > 2 @@ -89,6 +152,26 @@ pub enum FilterExpression { Not(Box), } +impl Display for FilterExpression { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let str = match self { + FilterExpression::Atom(left, sign, right) => { + format!("{} {} {}", left, sign, right) + } + FilterExpression::And(left, right) => { + format!("{} && {}", left, right) + } + FilterExpression::Or(left, right) => { + format!("{} || {}", left, right) + } + FilterExpression::Not(expr) => { + format!("!{}", expr) + } + }; + write!(f, "{}", str) + } +} + impl FilterExpression { pub fn exists(op: Operand) -> Self { FilterExpression::Atom( @@ -106,6 +189,16 @@ pub enum Operand { Dynamic(Box), } +impl Display for Operand { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let str = match self { + Operand::Static(e) => e.to_string(), + Operand::Dynamic(e) => e.to_string(), + }; + write!(f, "{}", str) + } +} + #[allow(dead_code)] impl Operand { pub fn val(v: Value) -> Self { @@ -132,6 +225,28 @@ pub enum FilterSign { Exists, } +impl Display for FilterSign { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + let str = match self { + FilterSign::Equal => "==", + FilterSign::Unequal => "!=", + FilterSign::Less => "<", + FilterSign::Greater => ">", + FilterSign::LeOrEq => "<=", + FilterSign::GrOrEq => ">=", + FilterSign::Regex => "~=", + FilterSign::In => "in", + FilterSign::Nin => "nin", + FilterSign::Size => "size", + FilterSign::NoneOf => "noneOf", + FilterSign::AnyOf => "anyOf", + FilterSign::SubSetOf => "subsetOf", + FilterSign::Exists => "exists", + }; + write!(f, "{}", str) + } +} + impl FilterSign { pub fn new(key: &str) -> Self { match key {