Skip to main content

vertep_runtime/
json.rs

1//! The JSON text encoding of datamodel values in journal entries, snapshots and dead letters.
2//!
3//! Plain JSON for everything JSON can say, with object keys in insertion order; `undefined` and
4//! non-finite numbers, which JSON cannot say, are tagged objects `{"$vt": "undefined"}`,
5//! `{"$vt": "NaN"}`, `{"$vt": "Infinity"}` and `{"$vt": "-Infinity"}`. Decoding is the exact
6//! inverse, so a payload read back from a journal equals the payload that was recorded.
7
8use core::fmt;
9
10use serde::de::{self, MapAccess, SeqAccess, Visitor};
11use serde::ser::{SerializeMap, SerializeSeq};
12use serde::{Deserialize, Deserializer, Serialize, Serializer};
13use vertep_core::datamodel::Value;
14
15const TAG: &str = "$vt";
16
17/// A [`Value`] that (de)serializes as the Vertep JSON encoding.
18#[derive(Clone, Debug, PartialEq)]
19pub struct JsonValue(pub Value);
20
21impl Serialize for JsonValue {
22    fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
23        ser(&self.0, s)
24    }
25}
26
27fn tagged<S: Serializer>(tag: &str, s: S) -> Result<S::Ok, S::Error> {
28    let mut m = s.serialize_map(Some(1))?;
29    m.serialize_entry(TAG, tag)?;
30    m.end()
31}
32
33fn ser<S: Serializer>(v: &Value, s: S) -> Result<S::Ok, S::Error> {
34    match v {
35        Value::Undefined => tagged("undefined", s),
36        Value::Null => s.serialize_unit(),
37        Value::Bool(b) => s.serialize_bool(*b),
38        Value::Number(n) if n.is_nan() => tagged("NaN", s),
39        Value::Number(n) if n.is_infinite() && *n > 0.0 => tagged("Infinity", s),
40        Value::Number(n) if n.is_infinite() => tagged("-Infinity", s),
41        Value::Number(n) => s.serialize_f64(*n),
42        Value::String(t) => s.serialize_str(t),
43        Value::Array(items) => {
44            let mut q = s.serialize_seq(Some(items.len()))?;
45            for i in items {
46                q.serialize_element(&JsonValue(i.clone()))?;
47            }
48            q.end()
49        }
50        Value::Object(entries) => {
51            let mut m = s.serialize_map(Some(entries.len()))?;
52            for (k, v) in entries {
53                m.serialize_entry(k, &JsonValue(v.clone()))?;
54            }
55            m.end()
56        }
57    }
58}
59
60impl<'de> Deserialize<'de> for JsonValue {
61    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
62        d.deserialize_any(ValueVisitor).map(JsonValue)
63    }
64}
65
66struct ValueVisitor;
67
68impl<'de> Visitor<'de> for ValueVisitor {
69    type Value = Value;
70
71    fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72        f.write_str("a JSON value")
73    }
74
75    fn visit_unit<E: de::Error>(self) -> Result<Value, E> {
76        Ok(Value::Null)
77    }
78    fn visit_none<E: de::Error>(self) -> Result<Value, E> {
79        Ok(Value::Null)
80    }
81    fn visit_bool<E: de::Error>(self, b: bool) -> Result<Value, E> {
82        Ok(Value::Bool(b))
83    }
84    fn visit_i64<E: de::Error>(self, n: i64) -> Result<Value, E> {
85        Ok(Value::Number(n as f64))
86    }
87    fn visit_u64<E: de::Error>(self, n: u64) -> Result<Value, E> {
88        Ok(Value::Number(n as f64))
89    }
90    fn visit_f64<E: de::Error>(self, n: f64) -> Result<Value, E> {
91        Ok(Value::Number(n))
92    }
93    fn visit_str<E: de::Error>(self, s: &str) -> Result<Value, E> {
94        Ok(Value::String(s.to_owned()))
95    }
96    fn visit_string<E: de::Error>(self, s: String) -> Result<Value, E> {
97        Ok(Value::String(s))
98    }
99    fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Value, A::Error> {
100        let mut items = Vec::new();
101        while let Some(JsonValue(v)) = seq.next_element()? {
102            items.push(v);
103        }
104        Ok(Value::Array(items))
105    }
106    fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Value, A::Error> {
107        let mut entries: Vec<(String, Value)> = Vec::new();
108        while let Some((k, JsonValue(v))) = map.next_entry::<String, JsonValue>()? {
109            entries.push((k, v));
110        }
111        if let [(k, Value::String(tag))] = entries.as_slice()
112            && k == TAG
113        {
114            return Ok(match tag.as_str() {
115                "undefined" => Value::Undefined,
116                "NaN" => Value::Number(f64::NAN),
117                "Infinity" => Value::Number(f64::INFINITY),
118                "-Infinity" => Value::Number(f64::NEG_INFINITY),
119                _ => Value::Object(entries),
120            });
121        }
122        Ok(Value::Object(entries))
123    }
124}
125
126/// The JSON text of `v`.
127pub fn to_text(v: &Value) -> String {
128    serde_json::to_string(&JsonValue(v.clone())).unwrap_or_else(|_| "null".into())
129}
130
131/// The value encoded by `text`.
132///
133/// # Errors
134///
135/// The JSON parse error.
136pub fn from_text(text: &str) -> Result<Value, serde_json::Error> {
137    serde_json::from_str::<JsonValue>(text).map(|j| j.0)
138}
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143
144    #[test]
145    fn round_trip_keeps_order_and_undefined() {
146        let v = Value::Object(vec![
147            ("z".into(), Value::Number(1.5)),
148            ("a".into(), Value::Undefined),
149            (
150                "list".into(),
151                Value::Array(vec![
152                    Value::Null,
153                    Value::Bool(true),
154                    Value::String("x".into()),
155                ]),
156            ),
157        ]);
158        let t = to_text(&v);
159        assert_eq!(
160            t,
161            r#"{"z":1.5,"a":{"$vt":"undefined"},"list":[null,true,"x"]}"#
162        );
163        assert_eq!(from_text(&t).unwrap(), v);
164        assert_eq!(
165            from_text(&to_text(&Value::Undefined)).unwrap(),
166            Value::Undefined
167        );
168    }
169}