use std::{collections::BTreeSet, fmt, sync::Arc};
use serde::{
Deserialize, Deserializer, Serialize, Serializer,
de::{DeserializeSeed, Error as DeError, MapAccess, SeqAccess, Visitor},
};
use serde_json::{Map as SerdeJsonMap, Value as SerdeJsonValue};
use crate::{
CoreError, CoreResult, DbString, db_string::MAX_DB_STRING_BYTES, json_patch::apply_json_patch,
};
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum JsonPathSelector {
Key(DbString),
Index(i64),
UnsignedIndex(u64),
}
#[derive(Clone, Debug, PartialEq)]
pub struct JsonValue {
value: Arc<SerdeJsonValue>,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct JsonValueRef<'a> {
value: &'a SerdeJsonValue,
}
impl<'a> JsonValueRef<'a> {
#[must_use]
pub fn as_serde(self) -> &'a SerdeJsonValue {
self.value
}
#[must_use]
pub fn to_owned_json_value(self) -> JsonValue {
JsonValue {
value: Arc::new(self.value.clone()),
}
}
}
impl JsonValue {
pub fn new(value: SerdeJsonValue) -> CoreResult<Self> {
validate_json_value(&value)?;
Ok(Self {
value: Arc::new(value),
})
}
pub fn parse_str(text: &str) -> CoreResult<Self> {
let value = parse_json_text(text).map_err(|err| CoreError::JsonParse {
message: err.to_string(),
})?;
Self::new(value)
}
#[must_use]
pub fn as_serde(&self) -> &SerdeJsonValue {
&self.value
}
#[must_use]
pub fn as_arc(&self) -> Arc<SerdeJsonValue> {
Arc::clone(&self.value)
}
#[must_use]
pub fn to_canonical_string(&self) -> String {
let mut output = String::new();
write_json_canonical(self.as_serde(), &mut output);
output
}
#[must_use]
pub fn json_type_name(&self) -> &'static str {
match self.as_serde() {
SerdeJsonValue::Null => "null",
SerdeJsonValue::Bool(_) => "boolean",
SerdeJsonValue::Number(_) => "number",
SerdeJsonValue::String(_) => "string",
SerdeJsonValue::Array(_) => "array",
SerdeJsonValue::Object(_) => "object",
}
}
#[must_use]
pub fn contains(&self, candidate: &Self) -> bool {
json_contains_value(self.as_serde(), candidate.as_serde())
}
#[must_use]
pub fn path_exists(&self, path: &[JsonPathSelector]) -> bool {
select_json_path(self.as_serde(), path).is_some()
}
#[must_use]
pub fn path_value(&self, path: &[JsonPathSelector]) -> Option<Self> {
self.path_value_ref(path)
.map(JsonValueRef::to_owned_json_value)
}
#[must_use]
pub fn path_value_ref(&self, path: &[JsonPathSelector]) -> Option<JsonValueRef<'_>> {
select_json_path(self.as_serde(), path).map(|value| JsonValueRef { value })
}
#[must_use]
pub fn path_contains(&self, path: &[JsonPathSelector], candidate: &Self) -> bool {
select_json_path(self.as_serde(), path)
.is_some_and(|value| json_contains_value(value, candidate.as_serde()))
}
pub fn merge_patch(&self, patch: &Self) -> CoreResult<Self> {
let mut target = self.as_serde().clone();
merge_patch_value(&mut target, patch.as_serde());
Self::new(target)
}
pub fn apply_patch(&self, patch: &Self) -> CoreResult<Self> {
Self::new(apply_json_patch(self.as_serde(), patch.as_serde())?)
}
}
fn select_json_path<'a>(
mut current: &'a SerdeJsonValue,
path: &[JsonPathSelector],
) -> Option<&'a SerdeJsonValue> {
for selector in path {
current = select_json_child(current, selector)?;
}
Some(current)
}
fn select_json_child<'a>(
current: &'a SerdeJsonValue,
selector: &JsonPathSelector,
) -> Option<&'a SerdeJsonValue> {
match (current, selector) {
(SerdeJsonValue::Object(values), JsonPathSelector::Key(key)) => values.get(key.as_str()),
(SerdeJsonValue::Array(values), JsonPathSelector::Index(index)) => {
signed_array_index(*index, values.len()).and_then(|index| values.get(index))
}
(SerdeJsonValue::Array(values), JsonPathSelector::UnsignedIndex(index)) => {
usize::try_from(*index)
.ok()
.and_then(|index| values.get(index))
}
_ => None,
}
}
fn signed_array_index(index: i64, len: usize) -> Option<usize> {
if index >= 0 {
usize::try_from(index).ok().filter(|index| *index < len)
} else {
let offset = usize::try_from(index.unsigned_abs()).ok()?;
(offset <= len).then_some(len - offset)
}
}
impl TryFrom<SerdeJsonValue> for JsonValue {
type Error = CoreError;
fn try_from(value: SerdeJsonValue) -> Result<Self, Self::Error> {
Self::new(value)
}
}
impl From<JsonValue> for SerdeJsonValue {
fn from(value: JsonValue) -> Self {
value.as_serde().clone()
}
}
impl Serialize for JsonValue {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if serializer.is_human_readable() {
self.as_serde().serialize(serializer)
} else {
serializer.serialize_str(&self.to_canonical_string())
}
}
}
impl<'de> Deserialize<'de> for JsonValue {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let value = if deserializer.is_human_readable() {
StrictJsonValueSeed.deserialize(deserializer)?
} else {
let value = String::deserialize(deserializer)?;
parse_json_text(&value).map_err(serde::de::Error::custom)?
};
Self::new(value).map_err(serde::de::Error::custom)
}
}
fn parse_json_text(text: &str) -> Result<SerdeJsonValue, serde_json::Error> {
let mut deserializer = serde_json::Deserializer::from_str(text);
let value = StrictJsonValueSeed.deserialize(&mut deserializer)?;
deserializer.end()?;
Ok(value)
}
struct StrictJsonValueSeed;
impl<'de> DeserializeSeed<'de> for StrictJsonValueSeed {
type Value = SerdeJsonValue;
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_any(StrictJsonValueVisitor)
}
}
struct StrictJsonValueVisitor;
impl<'de> Visitor<'de> for StrictJsonValueVisitor {
type Value = SerdeJsonValue;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a JSON value with unique object keys")
}
fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E> {
Ok(SerdeJsonValue::Bool(value))
}
fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E> {
Ok(SerdeJsonValue::Number(value.into()))
}
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E> {
Ok(SerdeJsonValue::Number(value.into()))
}
fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E>
where
E: DeError,
{
let number = serde_json::Number::from_f64(value)
.ok_or_else(|| E::custom("JSON number is not finite"))?;
Ok(SerdeJsonValue::Number(number))
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> {
Ok(SerdeJsonValue::String(value.to_owned()))
}
fn visit_string<E>(self, value: String) -> Result<Self::Value, E> {
Ok(SerdeJsonValue::String(value))
}
fn visit_none<E>(self) -> Result<Self::Value, E> {
Ok(SerdeJsonValue::Null)
}
fn visit_unit<E>(self) -> Result<Self::Value, E> {
Ok(SerdeJsonValue::Null)
}
fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>,
{
StrictJsonValueSeed.deserialize(deserializer)
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut values = Vec::with_capacity(seq.size_hint().unwrap_or(0));
while let Some(value) = seq.next_element_seed(StrictJsonValueSeed)? {
values.push(value);
}
Ok(SerdeJsonValue::Array(values))
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut seen = BTreeSet::new();
let mut values = SerdeJsonMap::new();
while let Some(key) = map.next_key::<String>()? {
if !seen.insert(key.clone()) {
return Err(A::Error::custom(format!(
"duplicate JSON object key '{key}'"
)));
}
let value = map.next_value_seed(StrictJsonValueSeed)?;
values.insert(key, value);
}
Ok(SerdeJsonValue::Object(values))
}
}
fn validate_json_value(value: &SerdeJsonValue) -> CoreResult<()> {
match value {
SerdeJsonValue::Null | SerdeJsonValue::Bool(_) | SerdeJsonValue::Number(_) => Ok(()),
SerdeJsonValue::String(value) => {
validate_json_string_len(value.len())?;
Ok(())
}
SerdeJsonValue::Array(values) => {
ensure_json_container_len(values.len())?;
for value in values {
validate_json_value(value)?;
}
Ok(())
}
SerdeJsonValue::Object(values) => {
ensure_json_container_len(values.len())?;
for (key, value) in values {
validate_json_string_len(key.len())?;
validate_json_value(value)?;
}
Ok(())
}
}
}
fn validate_json_string_len(len: usize) -> CoreResult<()> {
if len > MAX_DB_STRING_BYTES {
return Err(CoreError::StringTooLong {
got: len,
max: u32::MAX,
});
}
Ok(())
}
fn ensure_json_container_len(len: usize) -> CoreResult<()> {
if len > u32::MAX as usize {
return Err(CoreError::ConstructedValueTooLarge {
got: len,
max: u32::MAX,
});
}
Ok(())
}
fn json_contains_value(target: &SerdeJsonValue, candidate: &SerdeJsonValue) -> bool {
match (target, candidate) {
(SerdeJsonValue::Object(target), SerdeJsonValue::Object(candidate)) => {
candidate.iter().all(|(key, value)| {
target
.get(key)
.is_some_and(|found| json_contains_value(found, value))
})
}
(SerdeJsonValue::Array(target), SerdeJsonValue::Array(candidate)) => candidate
.iter()
.all(|value| target.iter().any(|found| json_contains_value(found, value))),
(SerdeJsonValue::Array(target), candidate) => target
.iter()
.any(|found| json_contains_value(found, candidate)),
_ => target == candidate,
}
}
fn merge_patch_value(target: &mut SerdeJsonValue, patch: &SerdeJsonValue) {
let SerdeJsonValue::Object(patch) = patch else {
*target = patch.clone();
return;
};
if !target.is_object() {
*target = SerdeJsonValue::Object(SerdeJsonMap::new());
}
let target = target
.as_object_mut()
.expect("target was normalized to object");
for (key, value) in patch {
if value.is_null() {
target.remove(key);
} else {
let entry = target.entry(key.clone()).or_insert(SerdeJsonValue::Null);
merge_patch_value(entry, value);
}
}
}
fn write_json_canonical(value: &SerdeJsonValue, output: &mut String) {
match value {
SerdeJsonValue::Null => output.push_str("null"),
SerdeJsonValue::Bool(value) => output.push_str(if *value { "true" } else { "false" }),
SerdeJsonValue::Number(value) => output.push_str(&value.to_string()),
SerdeJsonValue::String(value) => {
output.push_str(&serde_json::to_string(value).expect("JSON string rendering succeeds"));
}
SerdeJsonValue::Array(values) => {
output.push('[');
for (index, value) in values.iter().enumerate() {
if index > 0 {
output.push(',');
}
write_json_canonical(value, output);
}
output.push(']');
}
SerdeJsonValue::Object(values) => {
output.push('{');
let mut entries = values.iter().collect::<Vec<_>>();
entries.sort_unstable_by(|lhs, rhs| lhs.0.cmp(rhs.0));
for (index, (key, value)) in entries.into_iter().enumerate() {
if index > 0 {
output.push(',');
}
output.push_str(&serde_json::to_string(key).expect("JSON key rendering succeeds"));
output.push(':');
write_json_canonical(value, output);
}
output.push('}');
}
}
}
#[cfg(test)]
mod tests;