Skip to main content

nodedb_types/
conversion.rs

1//! Conversions between `serde_json::Value` and `nodedb_types::Value`.
2//!
3//! Shared across crates to avoid duplicating JSON-to-Value logic.
4
5use crate::Value;
6
7/// Convert a `serde_json::Value` to a `Value` by consuming ownership.
8///
9/// Nested objects are preserved as `Value::Object`.
10pub fn json_to_value(v: serde_json::Value) -> Value {
11    match v {
12        serde_json::Value::Null => Value::Null,
13        serde_json::Value::Bool(b) => Value::Bool(b),
14        serde_json::Value::Number(n) => {
15            if let Some(i) = n.as_i64() {
16                Value::Integer(i)
17            } else {
18                Value::Float(n.as_f64().unwrap_or(0.0))
19            }
20        }
21        serde_json::Value::String(s) => Value::String(s),
22        serde_json::Value::Array(arr) => Value::Array(arr.into_iter().map(json_to_value).collect()),
23        serde_json::Value::Object(obj) => Value::Object(
24            obj.into_iter()
25                .map(|(k, v)| (k, json_to_value(v)))
26                .collect(),
27        ),
28    }
29}
30
31/// Convert a `&serde_json::Value` to a `Value` by reference (cloning).
32///
33/// Nested objects are serialized to JSON strings for tabular display.
34pub fn json_to_value_display(v: &serde_json::Value) -> Value {
35    match v {
36        serde_json::Value::Null => Value::Null,
37        serde_json::Value::Bool(b) => Value::Bool(*b),
38        serde_json::Value::Number(n) => {
39            if let Some(i) = n.as_i64() {
40                Value::Integer(i)
41            } else {
42                Value::Float(n.as_f64().unwrap_or(0.0))
43            }
44        }
45        serde_json::Value::String(s) => Value::String(s.clone()),
46        serde_json::Value::Array(arr) => {
47            Value::Array(arr.iter().map(json_to_value_display).collect())
48        }
49        serde_json::Value::Object(_) => Value::String(serde_json::to_string(v).unwrap_or_default()),
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56
57    #[test]
58    fn owned_preserves_nested_objects() {
59        let v = serde_json::json!({"a": 1, "b": {"nested": true}});
60        let val = json_to_value(v);
61        match val {
62            Value::Object(map) => {
63                assert_eq!(map.get("a"), Some(&Value::Integer(1)));
64                assert!(matches!(map.get("b"), Some(Value::Object(_))));
65            }
66            _ => panic!("expected Object"),
67        }
68    }
69
70    #[test]
71    fn display_flattens_nested_objects() {
72        let v = serde_json::json!({"nested": true});
73        let val = json_to_value_display(&v);
74        assert!(matches!(val, Value::String(_)));
75    }
76
77    #[test]
78    fn primitives_roundtrip() {
79        assert_eq!(json_to_value(serde_json::Value::Null), Value::Null);
80        assert_eq!(
81            json_to_value(serde_json::Value::Bool(true)),
82            Value::Bool(true)
83        );
84        assert_eq!(json_to_value(serde_json::json!(42)), Value::Integer(42));
85        assert_eq!(
86            json_to_value(serde_json::json!("hello")),
87            Value::String("hello".into())
88        );
89    }
90}