pub mod automaton;
pub mod builder;
pub mod error;
mod json_string;
mod nonnegative_array_index;
mod parser;
pub use json_string::JsonString;
pub use nonnegative_array_index::NonNegativeArrayIndex;
use log::*;
use std::fmt::{self, Display};
#[derive(Debug, PartialEq, Eq)]
pub enum JsonPathQueryNode {
Root(Option<Box<JsonPathQueryNode>>),
Child(JsonString, Option<Box<JsonPathQueryNode>>),
AnyChild(Option<Box<JsonPathQueryNode>>),
Descendant(JsonString, Option<Box<JsonPathQueryNode>>),
AnyDescendant(Option<Box<JsonPathQueryNode>>),
ArrayIndexChild(NonNegativeArrayIndex, Option<Box<JsonPathQueryNode>>),
ArrayIndexDescendant(NonNegativeArrayIndex, Option<Box<JsonPathQueryNode>>),
}
use JsonPathQueryNode::*;
use self::error::ParserError;
impl JsonPathQueryNode {
#[must_use]
#[inline(always)]
pub fn child(&self) -> Option<&Self> {
match self {
Root(node)
| Child(_, node)
| AnyChild(node)
| Descendant(_, node)
| AnyDescendant(node)
| ArrayIndexChild(_, node)
| ArrayIndexDescendant(_, node) => node.as_deref(),
}
}
#[must_use]
#[inline(always)]
pub fn iter(&self) -> JsonPathQueryIterator {
JsonPathQueryIterator { node: Some(self) }
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct JsonPathQuery {
root: Box<JsonPathQueryNode>,
}
pub struct JsonPathQueryIterator<'a> {
node: Option<&'a JsonPathQueryNode>,
}
impl<'a> Iterator for JsonPathQueryIterator<'a> {
type Item = &'a JsonPathQueryNode;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let result = self.node;
if let Some(node) = result {
self.node = node.child()
}
result
}
}
impl JsonPathQuery {
#[must_use]
#[inline(always)]
pub fn root(&self) -> &JsonPathQueryNode {
self.root.as_ref()
}
#[inline(always)]
pub fn parse(query_string: &str) -> Result<Self, ParserError> {
self::parser::parse_json_path_query(query_string)
}
#[inline]
#[must_use]
pub fn new(node: Box<JsonPathQueryNode>) -> Self {
let root = if node.is_root() {
node
} else {
info!("Implicitly using the Root expression (`$`) at the start of the query.");
Box::new(Root(Some(node)))
};
Self { root }
}
}
impl Display for JsonPathQuery {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.root.as_ref())
}
}
impl Display for JsonPathQueryNode {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Root(_) => write!(f, "$"),
Child(key, _) => write!(f, "['{}']", key.display()),
AnyChild(_) => write!(f, "[*]"),
Descendant(key, _) => write!(f, "..['{}']", key.display()),
AnyDescendant(_) => write!(f, "..[*]"),
ArrayIndexChild(i, _) => write!(f, "[{i}]"),
ArrayIndexDescendant(i, _) => write!(f, "..[{i}]"),
}?;
if let Some(child) = self.child() {
write!(f, "{child}")
} else {
Ok(())
}
}
}
pub trait JsonPathQueryNodeType {
fn is_root(&self) -> bool;
fn is_descendant(&self) -> bool;
fn is_any_descendant(&self) -> bool;
fn is_child(&self) -> bool;
fn is_any_child(&self) -> bool;
fn member_name(&self) -> Option<&JsonString>;
fn array_index(&self) -> Option<&NonNegativeArrayIndex>;
}
impl JsonPathQueryNodeType for JsonPathQueryNode {
#[inline(always)]
fn is_root(&self) -> bool {
matches!(self, Root(_))
}
#[inline(always)]
fn is_descendant(&self) -> bool {
matches!(self, Descendant(_, _))
}
#[inline(always)]
fn is_any_descendant(&self) -> bool {
matches!(self, AnyDescendant(_))
}
#[inline(always)]
fn is_child(&self) -> bool {
matches!(self, Child(_, _))
}
#[inline(always)]
fn is_any_child(&self) -> bool {
matches!(self, AnyChild(_))
}
#[inline(always)]
fn member_name(&self) -> Option<&JsonString> {
match self {
Child(name, _) | Descendant(name, _) => Some(name),
Root(_) | AnyChild(_) | AnyDescendant(_) | ArrayIndexChild(_, _) | ArrayIndexDescendant(_, _) => None,
}
}
#[inline(always)]
fn array_index(&self) -> Option<&NonNegativeArrayIndex> {
match self {
ArrayIndexChild(i, _) | ArrayIndexDescendant(i, _) => Some(i),
Child(_, _) | Descendant(_, _) | Root(_) | AnyChild(_) | AnyDescendant(_) => None,
}
}
}
impl<T: std::ops::Deref<Target = JsonPathQueryNode>> JsonPathQueryNodeType for Option<T> {
#[inline(always)]
fn is_root(&self) -> bool {
self.as_ref().map_or(false, |x| x.is_root())
}
#[inline(always)]
fn is_descendant(&self) -> bool {
self.as_ref().map_or(false, |x| x.is_descendant())
}
#[inline(always)]
fn is_any_descendant(&self) -> bool {
self.as_ref().map_or(false, |x| x.is_any_descendant())
}
#[inline(always)]
fn is_child(&self) -> bool {
self.as_ref().map_or(false, |x| x.is_child())
}
#[inline(always)]
fn is_any_child(&self) -> bool {
self.as_ref().map_or(false, |x| x.is_any_child())
}
#[inline(always)]
fn member_name(&self) -> Option<&JsonString> {
self.as_ref().and_then(|x| x.member_name())
}
#[inline(always)]
fn array_index(&self) -> Option<&NonNegativeArrayIndex> {
self.as_ref().and_then(|x| x.array_index())
}
}