use crate::AsType;
use serde::de::DeserializeOwned;
use serde::Serialize;
use serde_json::{json, Map, Value};
use std::collections::VecDeque;
pub trait JsonValueExt {
fn x_new_object() -> Value;
fn x_contains<T: DeserializeOwned>(&self, name_or_pointer: &str) -> bool;
fn x_get<T: DeserializeOwned>(&self, name_or_pointer: &str) -> Result<T>;
fn x_get_as<'a, T: AsType<'a>>(&'a self, name_or_pointer: &str) -> Result<T>;
fn x_get_str(&self, name_or_pointer: &str) -> Result<&str> {
self.x_get_as(name_or_pointer)
}
fn x_get_i64(&self, name_or_pointer: &str) -> Result<i64> {
self.x_get_as(name_or_pointer)
}
fn x_get_f64(&self, name_or_pointer: &str) -> Result<f64> {
self.x_get_as(name_or_pointer)
}
fn x_get_bool(&self, name_or_pointer: &str) -> Result<bool> {
self.x_get_as(name_or_pointer)
}
fn x_take<T: DeserializeOwned>(&mut self, name_or_pointer: &str) -> Result<T>;
fn x_remove<T: DeserializeOwned>(&mut self, name_or_pointer: &str) -> Result<T>;
fn x_insert<T: Serialize>(&mut self, name_or_pointer: &str, value: T) -> Result<()>;
fn x_merge(&mut self, other: Value) -> Result<()>;
fn x_walk<F>(&mut self, callback: F) -> bool
where
F: FnMut(&mut Map<String, Value>, &str) -> bool;
fn x_pretty(&self) -> Result<String>;
}
impl JsonValueExt for Value {
fn x_new_object() -> Value {
Value::Object(Map::new())
}
fn x_contains<T: DeserializeOwned>(&self, name_or_pointer: &str) -> bool {
if name_or_pointer.starts_with('/') {
self.pointer(name_or_pointer).is_some()
} else {
self.get(name_or_pointer).is_some()
}
}
fn x_get<T: DeserializeOwned>(&self, name_or_pointer: &str) -> Result<T> {
let value = if name_or_pointer.starts_with('/') {
self.pointer(name_or_pointer)
.ok_or_else(|| JsonValueExtError::PropertyNotFound(name_or_pointer.to_string()))?
} else {
self.get(name_or_pointer)
.ok_or_else(|| JsonValueExtError::PropertyNotFound(name_or_pointer.to_string()))?
};
let value: T =
serde_json::from_value(value.clone())
.map_err(JsonValueExtError::from)
.map_err(|err| match err {
JsonValueExtError::ValueNotOfType(not_of_type) => JsonValueExtError::PropertyValueNotOfType {
name: name_or_pointer.to_string(),
not_of_type,
},
other => other,
})?;
Ok(value)
}
fn x_get_as<'a, T: AsType<'a>>(&'a self, name_or_pointer: &str) -> Result<T> {
let value = if name_or_pointer.starts_with('/') {
self.pointer(name_or_pointer)
.ok_or_else(|| JsonValueExtError::PropertyNotFound(name_or_pointer.to_string()))?
} else {
self.get(name_or_pointer)
.ok_or_else(|| JsonValueExtError::PropertyNotFound(name_or_pointer.to_string()))?
};
T::from_value(value).map_err(|err| match err {
JsonValueExtError::ValueNotOfType(not_of_type) => JsonValueExtError::PropertyValueNotOfType {
name: name_or_pointer.to_string(),
not_of_type,
},
other => other,
})
}
fn x_take<T: DeserializeOwned>(&mut self, name_or_pointer: &str) -> Result<T> {
let value = if name_or_pointer.starts_with('/') {
self.pointer_mut(name_or_pointer)
.map(Value::take)
.ok_or_else(|| JsonValueExtError::PropertyNotFound(name_or_pointer.to_string()))?
} else {
self.get_mut(name_or_pointer)
.map(Value::take)
.ok_or_else(|| JsonValueExtError::PropertyNotFound(name_or_pointer.to_string()))?
};
let value: T = serde_json::from_value(value)?;
Ok(value)
}
fn x_remove<T: DeserializeOwned>(&mut self, name_or_pointer: &str) -> Result<T> {
if !name_or_pointer.starts_with('/') {
match self {
Value::Object(map) => {
let removed = map
.remove(name_or_pointer)
.ok_or_else(|| JsonValueExtError::PropertyNotFound(name_or_pointer.to_string()))?;
let value: T = serde_json::from_value(removed)?;
Ok(value)
}
_ => Err(JsonValueExtError::custom("Value is not an Object; cannot x_remove")),
}
} else {
let parts: Vec<&str> = name_or_pointer.split('/').skip(1).collect();
if parts.is_empty() {
return Err(JsonValueExtError::custom("Invalid path"));
}
let mut current = self;
for &part in &parts[..parts.len() - 1] {
match current {
Value::Object(map) => {
current = map
.get_mut(part)
.ok_or_else(|| JsonValueExtError::PropertyNotFound(name_or_pointer.to_string()))?;
}
Value::Array(arr) => {
let index: usize = part
.parse()
.map_err(|_| JsonValueExtError::custom("Invalid array index in pointer"))?;
if index < arr.len() {
current = &mut arr[index];
} else {
return Err(JsonValueExtError::PropertyNotFound(name_or_pointer.to_string()));
}
}
_ => return Err(JsonValueExtError::custom("Path does not point to an Object or Array")),
}
}
let last_part = parts
.last()
.ok_or_else(|| JsonValueExtError::custom("Last element not found"))?;
match current {
Value::Object(map) => {
let removed = map
.remove(*last_part)
.ok_or_else(|| JsonValueExtError::PropertyNotFound(name_or_pointer.to_string()))?;
let value: T = serde_json::from_value(removed)?;
Ok(value)
}
Value::Array(arr) => {
let index: usize = last_part
.parse()
.map_err(|_| JsonValueExtError::custom("Invalid array index in pointer"))?;
if index < arr.len() {
let removed = arr.remove(index);
let value: T = serde_json::from_value(removed)?;
Ok(value)
} else {
Err(JsonValueExtError::PropertyNotFound(name_or_pointer.to_string()))
}
}
_ => Err(JsonValueExtError::custom("Path does not point to an Object or Array")),
}
}
}
fn x_insert<T: Serialize>(&mut self, name_or_pointer: &str, value: T) -> Result<()> {
let new_value = serde_json::to_value(value)?;
if !name_or_pointer.starts_with('/') {
match self {
Value::Object(map) => {
map.insert(name_or_pointer.to_string(), new_value);
Ok(())
}
_ => Err(JsonValueExtError::custom("Value is not an Object; cannot x_insert")),
}
} else {
let parts: Vec<&str> = name_or_pointer.split('/').skip(1).collect();
let mut current = self;
for &part in &parts[..parts.len() - 1] {
match current {
Value::Object(map) => {
current = map.entry(part).or_insert_with(|| json!({}));
}
_ => return Err(JsonValueExtError::custom("Path does not point to an Object")),
}
}
if let Some(&last_part) = parts.last() {
match current {
Value::Object(map) => {
map.insert(last_part.to_string(), new_value);
Ok(())
}
_ => Err(JsonValueExtError::custom("Path does not point to an Object")),
}
} else {
Err(JsonValueExtError::custom("Invalid path"))
}
}
}
fn x_merge(&mut self, other: Value) -> Result<()> {
if other.is_null() {
return Ok(());
}
let other_map = match other {
Value::Object(map) => map,
_ => return Err(JsonValueExtError::custom("Other value is not an Object; cannot x_merge")),
};
match self {
Value::Object(map) => {
map.extend(other_map);
Ok(())
}
_ => Err(JsonValueExtError::custom("Value is not an Object; cannot x_merge")),
}
}
fn x_pretty(&self) -> Result<String> {
let content = serde_json::to_string_pretty(self)?;
Ok(content)
}
fn x_walk<F>(&mut self, mut callback: F) -> bool
where
F: FnMut(&mut Map<String, Value>, &str) -> bool,
{
let mut queue = VecDeque::new();
queue.push_back(self);
while let Some(current) = queue.pop_front() {
if let Value::Object(map) = current {
for key in map.keys().cloned().collect::<Vec<_>>() {
let res = callback(map, &key);
if !res {
return false;
}
}
for value in map.values_mut() {
if value.is_object() || value.is_array() {
queue.push_back(value);
}
}
} else if let Value::Array(arr) = current {
for value in arr.iter_mut() {
if value.is_object() || value.is_array() {
queue.push_back(value);
}
}
}
}
true
}
}
type Result<T> = core::result::Result<T, JsonValueExtError>;
#[derive(Debug, derive_more::From)]
pub enum JsonValueExtError {
Custom(String),
PropertyNotFound(String),
PropertyValueNotOfType {
name: String,
not_of_type: &'static str,
},
ValueNotOfType(&'static str),
#[from]
SerdeJson(serde_json::Error),
}
impl JsonValueExtError {
pub(crate) fn custom(val: impl std::fmt::Display) -> Self {
Self::Custom(val.to_string())
}
}
impl core::fmt::Display for JsonValueExtError {
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::result::Result<(), core::fmt::Error> {
write!(fmt, "{self:?}")
}
}
impl std::error::Error for JsonValueExtError {}