forma_json 0.1.0

JSON serialization and deserialization for forma_core.
Documentation
use crate::error::Error;
use forma_core::de::{Deserialize, DeserializeOwned, Deserializer, MapAccess, SeqAccess, Visitor};
use forma_core::error::DeError;
use forma_core::ser::{self, Serialize, Serializer};
use std::collections::BTreeMap;
use std::fmt;

/// A dynamic JSON value.
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
    Null,
    Bool(bool),
    Number(Number),
    String(String),
    Array(Vec<Value>),
    Object(BTreeMap<String, Value>),
}

/// A JSON number — either integer or floating point.
#[derive(Debug, Clone)]
pub enum Number {
    PosInt(u64),
    NegInt(i64),
    Float(f64),
}

impl PartialEq for Number {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (Number::PosInt(a), Number::PosInt(b)) => a == b,
            (Number::NegInt(a), Number::NegInt(b)) => a == b,
            (Number::Float(a), Number::Float(b)) => a.to_bits() == b.to_bits(),
            _ => false,
        }
    }
}

impl Number {
    pub fn as_f64(&self) -> f64 {
        match *self {
            Number::PosInt(v) => v as f64,
            Number::NegInt(v) => v as f64,
            Number::Float(v) => v,
        }
    }

    pub fn as_i64(&self) -> Option<i64> {
        match *self {
            Number::PosInt(v) => i64::try_from(v).ok(),
            Number::NegInt(v) => Some(v),
            Number::Float(_) => None,
        }
    }

    pub fn as_u64(&self) -> Option<u64> {
        match *self {
            Number::PosInt(v) => Some(v),
            Number::NegInt(_) => None,
            Number::Float(_) => None,
        }
    }

    pub fn is_f64(&self) -> bool {
        matches!(self, Number::Float(_))
    }
}

impl fmt::Display for Number {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Number::PosInt(v) => write!(f, "{v}"),
            Number::NegInt(v) => write!(f, "{v}"),
            Number::Float(v) => write!(f, "{v}"),
        }
    }
}

// ── Value constructors ──────────────────────────────────────────────

impl Value {
    pub fn is_null(&self) -> bool {
        matches!(self, Value::Null)
    }

    pub fn is_bool(&self) -> bool {
        matches!(self, Value::Bool(_))
    }

    pub fn is_number(&self) -> bool {
        matches!(self, Value::Number(_))
    }

    pub fn is_string(&self) -> bool {
        matches!(self, Value::String(_))
    }

    pub fn is_array(&self) -> bool {
        matches!(self, Value::Array(_))
    }

    pub fn is_object(&self) -> bool {
        matches!(self, Value::Object(_))
    }

    pub fn as_bool(&self) -> Option<bool> {
        match self {
            Value::Bool(b) => Some(*b),
            _ => None,
        }
    }

    pub fn as_f64(&self) -> Option<f64> {
        match self {
            Value::Number(n) => Some(n.as_f64()),
            _ => None,
        }
    }

    pub fn as_i64(&self) -> Option<i64> {
        match self {
            Value::Number(n) => n.as_i64(),
            _ => None,
        }
    }

    pub fn as_u64(&self) -> Option<u64> {
        match self {
            Value::Number(n) => n.as_u64(),
            _ => None,
        }
    }

    pub fn as_str(&self) -> Option<&str> {
        match self {
            Value::String(s) => Some(s),
            _ => None,
        }
    }

    pub fn as_array(&self) -> Option<&Vec<Value>> {
        match self {
            Value::Array(a) => Some(a),
            _ => None,
        }
    }

    pub fn as_object(&self) -> Option<&BTreeMap<String, Value>> {
        match self {
            Value::Object(m) => Some(m),
            _ => None,
        }
    }

    pub fn as_array_mut(&mut self) -> Option<&mut Vec<Value>> {
        match self {
            Value::Array(a) => Some(a),
            _ => None,
        }
    }

    pub fn as_object_mut(&mut self) -> Option<&mut BTreeMap<String, Value>> {
        match self {
            Value::Object(m) => Some(m),
            _ => None,
        }
    }

    /// Index into a JSON object by key. Returns `None` if not an object
    /// or key doesn't exist.
    pub fn get(&self, key: &str) -> Option<&Value> {
        self.as_object()?.get(key)
    }

    /// Index into a JSON array by index. Returns `None` if not an array
    /// or index is out of bounds.
    pub fn get_index(&self, index: usize) -> Option<&Value> {
        self.as_array()?.get(index)
    }
}

// ── Index impls ─────────────────────────────────────────────────────

impl std::ops::Index<&str> for Value {
    type Output = Value;
    fn index(&self, key: &str) -> &Value {
        static NULL: Value = Value::Null;
        self.get(key).unwrap_or(&NULL)
    }
}

impl std::ops::Index<usize> for Value {
    type Output = Value;
    fn index(&self, index: usize) -> &Value {
        static NULL: Value = Value::Null;
        self.get_index(index).unwrap_or(&NULL)
    }
}

// ── From impls ──────────────────────────────────────────────────────

impl From<bool> for Value {
    fn from(v: bool) -> Self {
        Value::Bool(v)
    }
}

impl From<i32> for Value {
    fn from(v: i32) -> Self {
        if v >= 0 {
            Value::Number(Number::PosInt(v as u64))
        } else {
            Value::Number(Number::NegInt(v as i64))
        }
    }
}

impl From<i64> for Value {
    fn from(v: i64) -> Self {
        if v >= 0 {
            Value::Number(Number::PosInt(v as u64))
        } else {
            Value::Number(Number::NegInt(v))
        }
    }
}

impl From<u64> for Value {
    fn from(v: u64) -> Self {
        Value::Number(Number::PosInt(v))
    }
}

impl From<f64> for Value {
    fn from(v: f64) -> Self {
        Value::Number(Number::Float(v))
    }
}

impl From<String> for Value {
    fn from(v: String) -> Self {
        Value::String(v)
    }
}

impl From<&str> for Value {
    fn from(v: &str) -> Self {
        Value::String(v.to_string())
    }
}

impl<T: Into<Value>> From<Vec<T>> for Value {
    fn from(v: Vec<T>) -> Self {
        Value::Array(v.into_iter().map(Into::into).collect())
    }
}

impl From<()> for Value {
    fn from(_: ()) -> Self {
        Value::Null
    }
}

impl<T: Into<Value>> From<Option<T>> for Value {
    fn from(v: Option<T>) -> Self {
        match v {
            Some(v) => v.into(),
            None => Value::Null,
        }
    }
}

// ── Display ─────────────────────────────────────────────────────────

impl fmt::Display for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match crate::to_string(self) {
            Ok(s) => f.write_str(&s),
            Err(_) => Err(fmt::Error),
        }
    }
}

// ── Serialize ───────────────────────────────────────────────────────

impl Serialize for Value {
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        match self {
            Value::Null => serializer.serialize_unit(),
            Value::Bool(b) => serializer.serialize_bool(*b),
            Value::Number(Number::PosInt(v)) => serializer.serialize_u64(*v),
            Value::Number(Number::NegInt(v)) => serializer.serialize_i64(*v),
            Value::Number(Number::Float(v)) => serializer.serialize_f64(*v),
            Value::String(s) => serializer.serialize_str(s),
            Value::Array(arr) => {
                let mut seq = serializer.serialize_seq(Some(arr.len()))?;
                for item in arr {
                    ser::SerializeSeq::serialize_element(&mut seq, item)?;
                }
                ser::SerializeSeq::end(seq)
            }
            Value::Object(map) => {
                let mut m = serializer.serialize_map(Some(map.len()))?;
                for (key, value) in map {
                    ser::SerializeMap::serialize_entry(&mut m, key, value)?;
                }
                ser::SerializeMap::end(m)
            }
        }
    }
}

// ── Deserialize ─────────────────────────────────────────────────────

impl<'de> Deserialize<'de> for Value {
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        deserializer.deserialize_any(ValueVisitor)
    }
}

struct ValueVisitor;

impl<'de> Visitor<'de> for ValueVisitor {
    type Value = Value;

    fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("any JSON value")
    }

    fn visit_bool<E: DeError>(self, v: bool) -> Result<Value, E> {
        Ok(Value::Bool(v))
    }

    fn visit_i64<E: DeError>(self, v: i64) -> Result<Value, E> {
        Ok(Value::from(v))
    }

    fn visit_u64<E: DeError>(self, v: u64) -> Result<Value, E> {
        Ok(Value::Number(Number::PosInt(v)))
    }

    fn visit_f64<E: DeError>(self, v: f64) -> Result<Value, E> {
        Ok(Value::Number(Number::Float(v)))
    }

    fn visit_str<E: DeError>(self, v: &str) -> Result<Value, E> {
        Ok(Value::String(v.to_string()))
    }

    fn visit_string<E: DeError>(self, v: String) -> Result<Value, E> {
        Ok(Value::String(v))
    }

    fn visit_none<E: DeError>(self) -> Result<Value, E> {
        Ok(Value::Null)
    }

    fn visit_unit<E: DeError>(self) -> Result<Value, E> {
        Ok(Value::Null)
    }

    fn visit_some<D: Deserializer<'de>>(self, deserializer: D) -> Result<Value, D::Error> {
        Value::deserialize(deserializer)
    }

    fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Value, A::Error> {
        let mut arr = Vec::new();
        while let Some(elem) = seq.next_element::<Value>()? {
            arr.push(elem);
        }
        Ok(Value::Array(arr))
    }

    fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Value, A::Error> {
        let mut obj = BTreeMap::new();
        while let Some((key, value)) = map.next_entry::<String, Value>()? {
            obj.insert(key, value);
        }
        Ok(Value::Object(obj))
    }
}

// ── Convenience: from_value / to_value ──────────────────────────────

/// Deserialize a `T` from a `Value`.
pub fn from_value<T: DeserializeOwned>(value: Value) -> Result<T, Error> {
    let s = crate::to_string(&value)?;
    // from_str requires 'de to match the string's lifetime, but since
    // T: DeserializeOwned (= for<'de> Deserialize<'de>), any lifetime works.
    crate::from_str(&s)
}

/// Serialize a `T` into a `Value`.
pub fn to_value<T: Serialize>(value: &T) -> Result<Value, Error> {
    let s = crate::to_string(value)?;
    crate::from_str(&s)
}