Skip to main content

linkrs_server/
value.rs

1//! `Value` ↔ `serde_json::Value` codec.
2//!
3//! Single canonical implementation of the wire encoding for graph
4//! [`Value`]s, previously duplicated between the HTTP query handler and the
5//! SSE stream handler. The JSON shape is the one the streaming handler
6//! already emitted (complete variant coverage; `Vertex`/`Edge`/`Path` are
7//! serialized through their own `Serialize` impls).
8
9use linkrs_core::{List, Value};
10
11/// Encode a core [`Value`] into its JSON wire representation.
12pub fn to_json(value: Value) -> serde_json::Value {
13    match value {
14        Value::Empty => serde_json::Value::Null,
15        Value::Null(_) => serde_json::Value::Null,
16        Value::Bool(b) => serde_json::Value::Bool(b),
17        Value::SmallInt(i) => serde_json::Value::Number(i.into()),
18        Value::Int(i) => serde_json::Value::Number(i.into()),
19        Value::BigInt(i) => serde_json::Value::Number(i.into()),
20        Value::Float(f) => serde_json::Value::Number(
21            serde_json::Number::from_f64(f as f64).unwrap_or(serde_json::Number::from(0)),
22        ),
23        Value::Double(f) => serde_json::Value::Number(
24            serde_json::Number::from_f64(f).unwrap_or(serde_json::Number::from(0)),
25        ),
26        Value::Decimal128(d) => serde_json::Value::String(d.to_string()),
27        Value::String(s) => serde_json::Value::String(s.to_string()),
28        Value::FixedString(data) => serde_json::Value::String(data.to_string()),
29        Value::Blob(blob) => serde_json::Value::String(format!("{:?}", blob)),
30        Value::Date(d) => serde_json::Value::String(d.to_string()),
31        Value::Time(t) => serde_json::Value::String(t.to_string()),
32        Value::DateTime(dt) => serde_json::Value::String(dt.to_string()),
33        Value::Vertex(v) => serde_json::json!(v),
34        Value::Edge(e) => serde_json::json!(e),
35        Value::EdgeHeader(h) => serde_json::json!(h),
36        Value::Path(p) => serde_json::json!(p),
37        Value::List(list) => serde_json::Value::Array(list.into_iter().map(to_json).collect()),
38        Value::Map(map) => {
39            let obj: serde_json::Map<String, serde_json::Value> = map
40                .into_iter()
41                .map(|(k, v)| (format!("{}", k), to_json(v)))
42                .collect();
43            serde_json::Value::Object(obj)
44        }
45        Value::Set(set) => serde_json::Value::Array(set.into_iter().map(to_json).collect()),
46        Value::Geography(g) => serde_json::json!(g),
47        Value::Vector(v) => {
48            let arr = v
49                .to_dense()
50                .iter()
51                .map(|&f| {
52                    serde_json::Number::from_f64(f as f64).unwrap_or(serde_json::Number::from(0))
53                })
54                .collect::<Vec<_>>();
55            serde_json::Value::Array(arr.into_iter().map(serde_json::Value::Number).collect())
56        }
57        Value::DataSet(ds) => serde_json::json!(ds),
58        Value::Json(j) => serde_json::from_str(j.as_str()).unwrap_or(serde_json::Value::Null),
59        Value::JsonB(j) => j.as_value().clone(),
60        Value::Uuid(u) => serde_json::Value::String(u.to_hyphenated_string()),
61        Value::Interval(i) => serde_json::Value::String(i.to_postgresql()),
62        Value::VertexId(vid) => serde_json::Value::String(format!("{:?}", vid)),
63        Value::EdgeId(eid) => serde_json::Value::String(format!("{:?}", eid)),
64        Value::Struct(s) => {
65            let obj: serde_json::Map<String, serde_json::Value> =
66                s.fields.into_iter().map(|(k, v)| (k, to_json(v))).collect();
67            serde_json::Value::Object(obj)
68        }
69        Value::Array(a) => serde_json::Value::Array(a.values.into_iter().map(to_json).collect()),
70    }
71}
72
73/// Decode a `serde_json::Value` (query parameter binding) into a core
74/// [`Value`].
75pub fn from_json(value: &serde_json::Value) -> Value {
76    match value {
77        serde_json::Value::Null => Value::Null(Default::default()),
78        serde_json::Value::Bool(b) => Value::Bool(*b),
79        serde_json::Value::Number(n) => {
80            if let Some(i) = n.as_i64() {
81                Value::BigInt(i)
82            } else if let Some(u) = n.as_u64() {
83                Value::BigInt(u as i64)
84            } else {
85                Value::Double(n.as_f64().unwrap_or(0.0))
86            }
87        }
88        serde_json::Value::String(s) => Value::string(s.as_str()),
89        serde_json::Value::Array(items) => {
90            Value::list(List::from_vec(items.iter().map(from_json).collect()))
91        }
92        serde_json::Value::Object(map) => Value::Map(Box::new(
93            map.iter()
94                .map(|(k, v)| (Value::string(k.clone()), from_json(v)))
95                .collect(),
96        )),
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103    use linkrs_core::types::DataType;
104
105    #[test]
106    fn scalars_encode() {
107        assert_eq!(to_json(Value::Bool(true)), serde_json::json!(true));
108        assert_eq!(to_json(Value::Int(7)), serde_json::json!(7));
109        assert_eq!(to_json(Value::BigInt(-9)), serde_json::json!(-9));
110        assert_eq!(to_json(Value::Double(1.5)), serde_json::json!(1.5));
111        assert_eq!(to_json(Value::string("hi")), serde_json::json!("hi"));
112        assert_eq!(
113            to_json(Value::Null(Default::default())),
114            serde_json::Value::Null
115        );
116        assert_eq!(to_json(Value::Empty), serde_json::Value::Null);
117    }
118
119    #[test]
120    fn list_and_map_encode() {
121        let list = Value::list(List::from_vec(vec![Value::Int(1), Value::Int(2)]));
122        assert_eq!(to_json(list), serde_json::json!([1, 2]));
123
124        let map: Value = Value::Map(Box::new(
125            [(Value::string("a"), Value::BigInt(1))]
126                .into_iter()
127                .collect(),
128        ));
129        assert_eq!(to_json(map), serde_json::json!({"a": 1}));
130    }
131
132    #[test]
133    fn scalar_roundtrip() {
134        let cases = vec![
135            serde_json::json!(null),
136            serde_json::json!(true),
137            serde_json::json!(42),
138            serde_json::json!(-7),
139            serde_json::json!(3.25),
140            serde_json::json!("text"),
141        ];
142        for case in cases {
143            let core = from_json(&case);
144            let back = to_json(core);
145            assert_eq!(back, case);
146        }
147    }
148
149    #[test]
150    fn list_roundtrip() {
151        let case = serde_json::json!([1, "two", false, null]);
152        let core = from_json(&case);
153        assert!(matches!(core, Value::List(_)));
154        assert_eq!(to_json(core), case);
155    }
156
157    #[test]
158    fn map_roundtrip() {
159        let case = serde_json::json!({"a": 1, "b": [true]});
160        let core = from_json(&case);
161        assert!(matches!(core, Value::Map(_)));
162        assert_eq!(to_json(core), case);
163    }
164
165    #[test]
166    fn data_type_display_is_wire_format() {
167        assert_eq!(DataType::Bool.to_string(), "BOOL");
168    }
169}