use crate::{JsonObject, Value};
pub type JsonNodePath<'a> = [JsonNodePathSegment<'a>];
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum JsonNodePathSegment<'a> {
Key(&'a str),
Index(u32),
}
impl<'a> From<&'a str> for JsonNodePathSegment<'a> {
fn from(key: &'a str) -> Self {
Self::Key(key)
}
}
impl From<u32> for JsonNodePathSegment<'_> {
fn from(index: u32) -> Self {
Self::Index(index)
}
}
impl std::fmt::Display for JsonNodePathSegment<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
JsonNodePathSegment::Key(key) => write!(f, "Key: {}", key),
JsonNodePathSegment::Index(ind) => write!(f, "Ind: {}", ind),
}
}
}
#[macro_export]
macro_rules! path {
[ $( $segment:expr ),* ] => {
&[ $( $crate::JsonNodePathSegment::from($segment) ),* ]
};
}
pub struct DisplayWrapper<'a, T: ?Sized>(pub &'a T);
impl std::fmt::Display for DisplayWrapper<'_, JsonNodePath<'_>> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "$")?;
for segment in self.0 {
match segment {
JsonNodePathSegment::Key(key) => write!(f, ".{}", key)?,
JsonNodePathSegment::Index(index) => write!(f, "[{}]", index)?,
}
}
Ok(())
}
}
fn index_by_path<'v>(mut value: &'v Value, path: &JsonNodePath) -> Option<&'v Value> {
for segment in path {
match (value, segment) {
(Value::Array(array), JsonNodePathSegment::Index(index)) => {
value = array.get(*index as usize)?;
}
(Value::Object(object), JsonNodePathSegment::Key(key)) => {
value = object.get(*key)?;
}
_ => return None,
}
}
Some(value)
}
fn index_object_by_path<'o>(object: &'o JsonObject, path: &JsonNodePath) -> Option<&'o Value> {
let (head, tail) = path.split_first()?;
match head {
JsonNodePathSegment::Key(key) => index_by_path(object.get(*key)?, tail),
_ => None,
}
}
pub(crate) fn index_mut_object_by_path<'a>(
object: &'a mut JsonObject,
path: &JsonNodePath,
) -> Option<&'a mut Value> {
let (head, tail) = path.split_first()?;
match head {
JsonNodePathSegment::Key(key) => index_mut_by_path(object.get_mut(*key)?, tail),
_ => None,
}
}
fn index_mut_by_path<'a>(mut value: &'a mut Value, path: &JsonNodePath) -> Option<&'a mut Value> {
for segment in path {
match (value, segment) {
(Value::Array(array), JsonNodePathSegment::Index(index)) => {
value = array.get_mut(*index as usize)?;
}
(Value::Object(object), JsonNodePathSegment::Key(key)) => {
value = object.get_mut(*key)?;
}
_ => return None,
}
}
Some(value)
}
pub(crate) fn paths_exist<'a, 'p>(
value: &'a JsonObject,
paths: &[&'p JsonNodePath<'a>],
) -> Result<(), Vec<&'p JsonNodePath<'a>>> {
let mut nonexistent_paths = vec![];
for path in paths {
if !path.is_empty() && index_object_by_path(value, path).is_none() {
nonexistent_paths.push(*path);
}
}
if nonexistent_paths.is_empty() {
Ok(())
} else {
Err(nonexistent_paths)
}
}
impl<'a> From<&serde_json_path::PathElement<'a>> for JsonNodePathSegment<'a> {
fn from(value: &serde_json_path::PathElement<'a>) -> Self {
match *value {
serde_json_path::PathElement::Index(index) => Self::Index(index as u32),
serde_json_path::PathElement::Name(name) => Self::Key(name),
}
}
}