serde_json_merge 0.0.7

Merge, index, iterate, and sort a serde_json::Value (recursively)
//! Borrowed sortable representations of JSON values.

use crate::index::{IndexRef, Kind as IndexKind, Path as IndexPath};
use crate::iter::{Iter, dfs::Dfs};
use ordered_float::OrderedFloat;
use std::cmp::Ordering;

/// Stores sortable JSON object entries while preserving their keys.
#[derive(Clone, Default, Debug)]
pub struct Map<'a>(indexmap::IndexMap<String, Value<'a>>);

impl<'a> std::ops::Deref for Map<'a> {
    type Target = indexmap::IndexMap<String, Value<'a>>;

    #[inline]
    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl std::ops::DerefMut for Map<'_> {
    #[inline]
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.0
    }
}

impl<'a> FromIterator<(String, Value<'a>)> for Map<'a> {
    #[inline]
    fn from_iter<I>(iter: I) -> Self
    where
        I: IntoIterator<Item = (String, Value<'a>)>,
    {
        Self(indexmap::IndexMap::from_iter(iter))
    }
}

/// Represents a JSON value in a form that can be ordered.
#[derive(Clone, Debug, Ord, PartialOrd, PartialEq, Eq)]
pub enum Value<'a> {
    /// Represents JSON null.
    Null,
    /// Represents a borrowed JSON boolean.
    Bool(&'a bool),
    /// Represents a JSON number.
    Number(OrderedFloat<f64>),
    /// Represents a borrowed JSON string.
    String(&'a str),
    /// Represents a JSON array.
    Array(Vec<Value<'a>>),
    /// Represents a JSON object.
    Object(Map<'a>),
}

impl Value<'_> {
    /// Looks up one indexed child and returns `None` when the kind does not match.
    #[inline]
    pub fn get(&self, index: &IndexRef) -> Option<&Self> {
        match index.kind() {
            IndexKind::ObjectKey(key) => match self {
                Value::Object(map) => map.get(key),
                _ => None,
            },
            IndexKind::ArrayIndex(idx) => match self {
                Value::Array(vec) => vec.get(*idx),
                _ => None,
            },
        }
    }

    /// Gets or inserts a child and returns a mutable reference to it.
    ///
    /// A null value is initialized as an object or array according to the index kind.
    ///
    /// # Panics
    ///
    /// Panics when an object index addresses a non-object value.
    /// Panics when an array index addresses a non-array value or exceeds the array length.
    #[inline]
    #[allow(
        clippy::panic,
        reason = "intentional panics matching IndexMut contract, documented in # Panics"
    )]
    pub fn get_or_insert(&mut self, index: &IndexRef) -> &mut Self {
        match index.kind() {
            IndexKind::ObjectKey(key) => {
                if let Value::Null = self {
                    *self = Value::Object(Map::default());
                }
                match self {
                    Value::Object(map) => map.entry(key.to_string()).or_insert(Value::Null),
                    _ => panic!("cannot access key {key:?} in {self:?}"),
                }
            }
            IndexKind::ArrayIndex(idx) => {
                if let Value::Null = self {
                    *self = Value::Array(Vec::default());
                }
                match self {
                    Value::Array(vec) => {
                        let len = vec.len();
                        vec.get_mut(*idx).unwrap_or_else(|| {
                            panic!("cannot access index {idx} of array of length {len}")
                        })
                    }
                    _ => panic!("cannot access index {idx} of {self:?}"),
                }
            }
        }
    }
}

static NULL: Value<'static> = Value::Null;

impl<'a> std::ops::Index<&IndexPath> for Value<'a> {
    type Output = Value<'a>;

    #[inline]
    fn index(&self, index_path: &IndexPath) -> &Self::Output {
        let mut val: &Value = self;
        for index in index_path {
            match val.get(index) {
                Some(new_val) => val = new_val,
                None => return &NULL,
            }
        }
        val
    }
}

impl std::ops::IndexMut<&IndexPath> for Value<'_> {
    #[inline]
    fn index_mut(&mut self, index_path: &IndexPath) -> &mut Self::Output {
        let mut val: &mut Value = self;
        for index in index_path {
            val = val.get_or_insert(index);
        }
        val
    }
}

impl<'a> From<&'a serde_json::Value> for Value<'a> {
    #[inline]
    fn from(value: &'a serde_json::Value) -> Value<'a> {
        let mut res = Value::Null;
        for (idx, value) in value.iter_recursive::<Dfs>() {
            res[&idx] = match value {
                serde_json::Value::Null => Value::Null,
                serde_json::Value::Bool(b) => Value::Bool(b),
                serde_json::Value::Number(f) => {
                    let f = OrderedFloat(f.as_f64().unwrap_or(f64::NAN));
                    Value::Number(f)
                }
                serde_json::Value::String(s) => Value::String(s.as_str()),
                serde_json::Value::Array(a) => Value::Array(vec![Value::Null; a.len()]),
                serde_json::Value::Object(_) => Value::Object(Map::default()),
            };
        }
        res
    }
}

impl PartialOrd for Map<'_> {
    #[inline]
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(std::cmp::Ord::cmp(&self, &other))
    }
}

impl<'a> std::cmp::Ord for Map<'a> {
    #[inline]
    fn cmp(&self, other: &Self) -> Ordering {
        let self_iter: Vec<(&String, &Value<'a>)> = self.iter().collect();
        let other_iter: Vec<(&String, &Value<'a>)> = other.iter().collect();
        std::cmp::Ord::cmp(&self_iter, &other_iter)
    }
}

impl Eq for Map<'_> {}

impl<'a> PartialEq for Map<'a> {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        let self_iter: Vec<(&String, &Value<'a>)> = self.iter().collect();
        let other_iter: Vec<(&String, &Value<'a>)> = other.iter().collect();
        std::cmp::PartialEq::eq(&self_iter, &other_iter)
    }
}

/// Provides ordering for JSON values and references to JSON values.
pub trait Ord {
    /// Compares two values using the crate's sortable representation.
    fn cmp(&self, other: &Self) -> Ordering;
}

impl self::Ord for serde_json::Value {
    #[inline]
    fn cmp(&self, other: &Self) -> Ordering {
        std::cmp::Ord::cmp(&Value::from(self), &Value::from(other))
    }
}

impl self::Ord for &serde_json::Value {
    #[inline]
    fn cmp(&self, other: &Self) -> Ordering {
        self::Ord::cmp(*self, *other)
    }
}

#[cfg(test)]
mod test {
    use super::{Map as SMap, Value as SV};
    use pretty_assertions::assert_eq;
    use serde_json::json;

    #[test]
    fn object_from_value() {
        let value = json!({
            "a": "a",
            "c": "c",
        });
        let expected = SV::Object(SMap::from_iter([
            (String::from("a"), SV::String("a")),
            (String::from("c"), SV::String("c")),
        ]));
        assert_eq!(&SV::from(&value), &expected);
    }

    #[test]
    fn array_from_value() {
        let value = json!([3, 2, 1]);
        let expected = SV::Array(vec![
            SV::Number(3f64.into()),
            SV::Number(2f64.into()),
            SV::Number(1f64.into()),
        ]);
        assert_eq!(&SV::from(&value), &expected);
    }

    #[test]
    fn null_from_value() {
        assert_eq!(&SV::from(&json!(null)), &SV::Null);
    }

    #[test]
    fn string_from_value() {
        assert_eq!(&SV::from(&json!("test")), &SV::String("test"));
    }

    #[test]
    fn bool_from_value() {
        assert_eq!(&SV::from(&json!(false)), &SV::Bool(&false));
    }

    #[test]
    fn number_from_value() {
        assert_eq!(&SV::from(&json!(12)), &SV::Number(12f64.into()));
        assert_eq!(&SV::from(&json!(-10)), &SV::Number((-10f64).into()));
        assert_eq!(
            &SV::from(&json!(-10.8273)),
            &SV::Number((-10.8273f64).into())
        );
        assert_eq!(&SV::from(&json!(f64::NAN)), &SV::Null);
    }

    #[test]
    fn complex_object_from_value() {
        let value = json!({
            "a": "a",
            "b": true,
            "c": null,
            "d": [3, 2, 1],
            "e": { "1": false, "2": [1, 2] },
        });
        let expected = SV::Object(SMap::from_iter([
            (String::from("a"), SV::String("a")),
            (String::from("b"), SV::Bool(&true)),
            (String::from("c"), SV::Null),
            (
                String::from("d"),
                SV::Array(vec![
                    SV::Number(3f64.into()),
                    SV::Number(2f64.into()),
                    SV::Number(1f64.into()),
                ]),
            ),
            (
                String::from("e"),
                SV::Object(SMap::from_iter([
                    (String::from("1"), SV::Bool(&false)),
                    (
                        String::from("2"),
                        SV::Array(vec![SV::Number(1f64.into()), SV::Number(2f64.into())]),
                    ),
                ])),
            ),
        ]));
        assert_eq!(&SV::from(&value), &expected);
    }

    #[test]
    fn value_ord() {
        use std::cmp::{Ord, Ordering};
        assert_eq!(Ord::cmp(&SV::String("a"), &SV::String("b")), Ordering::Less);
        assert_eq!(
            Ord::cmp(&SV::String("b"), &SV::String("a")),
            Ordering::Greater
        );
        assert_eq!(
            Ord::cmp(&SV::Number(1f64.into()), &SV::Number(2f64.into())),
            Ordering::Less
        );
        assert_eq!(
            Ord::cmp(&SV::Bool(&false), &SV::Bool(&true)),
            Ordering::Less
        );
        assert_eq!(
            Ord::cmp(
                &SV::Array(vec![SV::String("a"), SV::String("b")]),
                &SV::Array(vec![SV::String("a")])
            ),
            Ordering::Greater
        );
        assert_eq!(
            Ord::cmp(
                &SV::Array(vec![SV::Bool(&false), SV::String("a")]),
                &SV::Array(vec![SV::String("a")])
            ),
            Ordering::Less
        );
    }
}