use serde_json::Value;
use crate::path::JsonLike;
use super::errors::JsonPathParserError;
use super::parse_json_path;
use std::fmt::{Display, Formatter};
use std::{convert::TryFrom, str::FromStr};
#[derive(Debug, Clone)]
pub enum JsonPath<T = Value> {
Root,
Field(String),
Chain(Vec<JsonPath<T>>),
Descent(String),
DescentW,
Index(JsonPathIndex<T>),
Current(Box<JsonPath<T>>),
Wildcard,
Empty,
Fn(Function),
}
impl<T: Display> Display for JsonPath<T> {
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::<String>(),
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<T> TryFrom<&str> for JsonPath<T>
where
T: JsonLike,
{
type Error = JsonPathParserError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
parse_json_path(value)
}
}
impl<T> FromStr for JsonPath<T>
where
T: JsonLike,
{
type Err = JsonPathParserError;
fn from_str(value: &str) -> Result<Self, Self::Err> {
parse_json_path(value)
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum Function {
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<T> {
Single(T),
UnionIndex(Vec<T>),
UnionKeys(Vec<String>),
Slice(i32, i32, usize),
Filter(FilterExpression<T>),
}
impl<T: Display> Display for JsonPathIndex<T> {
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::<Vec<_>>()
.join(",")
)
}
JsonPathIndex::UnionKeys(elems) => {
format!(
"[{}]",
elems
.iter()
.map(|el| format!("'{}'", el))
.collect::<Vec<_>>()
.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<T> {
Atom(Operand<T>, FilterSign, Operand<T>),
And(Box<FilterExpression<T>>, Box<FilterExpression<T>>),
Or(Box<FilterExpression<T>>, Box<FilterExpression<T>>),
Not(Box<FilterExpression<T>>),
}
impl<T: Display> Display for FilterExpression<T> {
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<T> FilterExpression<T> {
pub fn exists(op: Operand<T>) -> Self {
FilterExpression::Atom(
op,
FilterSign::Exists,
Operand::Dynamic(Box::new(JsonPath::Empty)),
)
}
}
#[derive(Debug, Clone)]
pub enum Operand<T> {
Static(T),
Dynamic(Box<JsonPath<T>>),
}
impl<T: Display> Display for Operand<T> {
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<T> Operand<T> {
pub fn val(v: T) -> Self {
Operand::Static(v)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum FilterSign {
Equal,
Unequal,
Less,
Greater,
LeOrEq,
GrOrEq,
Regex,
In,
Nin,
Size,
NoneOf,
AnyOf,
SubSetOf,
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 {
"==" => FilterSign::Equal,
"!=" => FilterSign::Unequal,
"<" => FilterSign::Less,
">" => FilterSign::Greater,
"<=" => FilterSign::LeOrEq,
">=" => FilterSign::GrOrEq,
"~=" => FilterSign::Regex,
"in" => FilterSign::In,
"nin" => FilterSign::Nin,
"size" => FilterSign::Size,
"noneOf" => FilterSign::NoneOf,
"anyOf" => FilterSign::AnyOf,
"subsetOf" => FilterSign::SubSetOf,
_ => FilterSign::Exists,
}
}
}
impl<T> PartialEq for JsonPath<T>
where
T: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(JsonPath::Root, JsonPath::Root) => true,
(JsonPath::Descent(k1), JsonPath::Descent(k2)) => k1 == k2,
(JsonPath::DescentW, JsonPath::DescentW) => true,
(JsonPath::Field(k1), JsonPath::Field(k2)) => k1 == k2,
(JsonPath::Wildcard, JsonPath::Wildcard) => true,
(JsonPath::Empty, JsonPath::Empty) => true,
(JsonPath::Current(jp1), JsonPath::Current(jp2)) => jp1 == jp2,
(JsonPath::Chain(ch1), JsonPath::Chain(ch2)) => ch1 == ch2,
(JsonPath::Index(idx1), JsonPath::Index(idx2)) => idx1 == idx2,
(JsonPath::Fn(fn1), JsonPath::Fn(fn2)) => fn2 == fn1,
(_, _) => false,
}
}
}
impl<T> PartialEq for JsonPathIndex<T>
where
T: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(JsonPathIndex::Slice(s1, e1, st1), JsonPathIndex::Slice(s2, e2, st2)) => {
s1 == s2 && e1 == e2 && st1 == st2
}
(JsonPathIndex::Single(el1), JsonPathIndex::Single(el2)) => el1 == el2,
(JsonPathIndex::UnionIndex(elems1), JsonPathIndex::UnionIndex(elems2)) => {
elems1 == elems2
}
(JsonPathIndex::UnionKeys(elems1), JsonPathIndex::UnionKeys(elems2)) => {
elems1 == elems2
}
(JsonPathIndex::Filter(left), JsonPathIndex::Filter(right)) => left.eq(right),
(_, _) => false,
}
}
}
impl<T> PartialEq for Operand<T>
where
T: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Operand::Static(v1), Operand::Static(v2)) => v1 == v2,
(Operand::Dynamic(jp1), Operand::Dynamic(jp2)) => jp1 == jp2,
(_, _) => false,
}
}
}