Skip to main content

nodedb_types/
conversion.rs

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