pjson_rs/infrastructure/adapters/
json_adapter.rs

1//! JSON Data Adapter
2//!
3//! Provides conversion between domain JsonData and infrastructure serde_json::Value
4//! to maintain Clean Architecture boundaries.
5
6use crate::domain::value_objects::JsonData;
7use serde_json::Value as SerdeValue;
8use std::collections::HashMap;
9
10/// Adapter for converting between domain JsonData and infrastructure JSON
11pub struct JsonAdapter;
12
13impl JsonAdapter {
14    /// Convert domain JsonData to serde_json::Value for infrastructure layer
15    pub fn to_serde_value(data: &JsonData) -> SerdeValue {
16        match data {
17            JsonData::Null => SerdeValue::Null,
18            JsonData::Bool(b) => SerdeValue::Bool(*b),
19            JsonData::Integer(i) => SerdeValue::Number(serde_json::Number::from(*i)),
20            JsonData::Float(f) => SerdeValue::Number(
21                serde_json::Number::from_f64(*f).unwrap_or_else(|| serde_json::Number::from(0)),
22            ),
23            JsonData::String(s) => SerdeValue::String(s.clone()),
24            JsonData::Array(arr) => {
25                let values: Vec<SerdeValue> = arr.iter().map(Self::to_serde_value).collect();
26                SerdeValue::Array(values)
27            }
28            JsonData::Object(obj) => {
29                let mut map = serde_json::Map::new();
30                for (key, value) in obj {
31                    map.insert(key.clone(), Self::to_serde_value(value));
32                }
33                SerdeValue::Object(map)
34            }
35        }
36    }
37
38    /// Convert serde_json::Value to domain JsonData
39    pub fn from_serde_value(value: &SerdeValue) -> JsonData {
40        match value {
41            SerdeValue::Null => JsonData::Null,
42            SerdeValue::Bool(b) => JsonData::Bool(*b),
43            SerdeValue::Number(n) => {
44                if let Some(i) = n.as_i64() {
45                    JsonData::Integer(i)
46                } else if let Some(f) = n.as_f64() {
47                    JsonData::Float(f)
48                } else {
49                    JsonData::Integer(0) // Fallback for edge cases
50                }
51            }
52            SerdeValue::String(s) => JsonData::String(s.clone()),
53            SerdeValue::Array(arr) => {
54                let values: Vec<JsonData> = arr.iter().map(Self::from_serde_value).collect();
55                JsonData::Array(values)
56            }
57            SerdeValue::Object(obj) => {
58                let mut map = HashMap::new();
59                for (key, value) in obj {
60                    map.insert(key.clone(), Self::from_serde_value(value));
61                }
62                JsonData::Object(map)
63            }
64        }
65    }
66
67    /// Convert JSON string to domain JsonData
68    pub fn parse_json_string(json_str: &str) -> Result<JsonData, serde_json::Error> {
69        let serde_value: SerdeValue = serde_json::from_str(json_str)?;
70        Ok(Self::from_serde_value(&serde_value))
71    }
72
73    /// Convert domain JsonData to JSON string
74    pub fn to_json_string(data: &JsonData) -> Result<String, serde_json::Error> {
75        let serde_value = Self::to_serde_value(data);
76        serde_json::to_string(&serde_value)
77    }
78
79    /// Convert domain JsonData to pretty JSON string
80    pub fn to_json_string_pretty(data: &JsonData) -> Result<String, serde_json::Error> {
81        let serde_value = Self::to_serde_value(data);
82        serde_json::to_string_pretty(&serde_value)
83    }
84
85    /// Merge two JsonData objects (right takes precedence)
86    pub fn merge(left: JsonData, right: JsonData) -> JsonData {
87        match (left, right) {
88            (JsonData::Object(mut left_obj), JsonData::Object(right_obj)) => {
89                for (key, value) in right_obj {
90                    if let Some(existing) = left_obj.get(&key).cloned() {
91                        left_obj.insert(key, Self::merge(existing, value));
92                    } else {
93                        left_obj.insert(key, value);
94                    }
95                }
96                JsonData::Object(left_obj)
97            }
98            (_, right) => right, // Right takes precedence for non-objects
99        }
100    }
101
102    /// Deep clone JsonData
103    pub fn deep_clone(data: &JsonData) -> JsonData {
104        data.clone() // JsonData implements Clone
105    }
106
107    /// Validate that JsonData structure is valid for PJS protocol
108    pub fn validate_pjs_structure(data: &JsonData) -> Result<(), String> {
109        match data {
110            JsonData::Object(_) => Ok(()), // Objects are valid root types
111            JsonData::Array(_) => Ok(()),  // Arrays are valid root types
112            _ => Err("PJS root data must be object or array".to_string()),
113        }
114    }
115
116    /// Extract all leaf paths from JsonData for priority analysis
117    pub fn extract_leaf_paths(data: &JsonData) -> Vec<String> {
118        let mut paths = Vec::new();
119        Self::extract_paths_recursive(data, String::new(), &mut paths);
120        paths
121    }
122
123    fn extract_paths_recursive(data: &JsonData, current_path: String, paths: &mut Vec<String>) {
124        match data {
125            JsonData::Object(obj) => {
126                if obj.is_empty() {
127                    paths.push(current_path);
128                } else {
129                    for (key, value) in obj {
130                        let new_path = if current_path.is_empty() {
131                            key.clone()
132                        } else {
133                            format!("{current_path}.{key}")
134                        };
135                        Self::extract_paths_recursive(value, new_path, paths);
136                    }
137                }
138            }
139            JsonData::Array(arr) => {
140                if arr.is_empty() {
141                    paths.push(current_path);
142                } else {
143                    for (index, value) in arr.iter().enumerate() {
144                        let new_path = if current_path.is_empty() {
145                            format!("[{index}]")
146                        } else {
147                            format!("{current_path}[{index}]")
148                        };
149                        Self::extract_paths_recursive(value, new_path, paths);
150                    }
151                }
152            }
153            _ => {
154                // Leaf node
155                paths.push(current_path);
156            }
157        }
158    }
159}
160
161#[cfg(test)]
162mod tests {
163    use super::*;
164
165    #[test]
166    fn test_serde_conversion_primitives() {
167        let json_data = JsonData::string("hello");
168        let serde_value = JsonAdapter::to_serde_value(&json_data);
169        let converted_back = JsonAdapter::from_serde_value(&serde_value);
170
171        assert_eq!(json_data, converted_back);
172    }
173
174    #[test]
175    fn test_serde_conversion_complex() {
176        let mut obj = HashMap::new();
177        obj.insert("name".to_string(), JsonData::string("John"));
178        obj.insert("age".to_string(), JsonData::integer(30));
179        obj.insert("active".to_string(), JsonData::bool(true));
180
181        let json_data = JsonData::object(obj);
182        let serde_value = JsonAdapter::to_serde_value(&json_data);
183        let converted_back = JsonAdapter::from_serde_value(&serde_value);
184
185        assert_eq!(json_data, converted_back);
186    }
187
188    #[test]
189    fn test_json_string_parsing() {
190        let json_str = r#"{"name":"John","age":30,"active":true}"#;
191        let parsed = JsonAdapter::parse_json_string(json_str).unwrap();
192
193        assert_eq!(parsed.get_path("name").unwrap().as_str(), Some("John"));
194        assert_eq!(parsed.get_path("age").unwrap().as_f64(), Some(30.0));
195        assert_eq!(parsed.get_path("active").unwrap().as_bool(), Some(true));
196    }
197
198    #[test]
199    fn test_json_string_generation() {
200        let mut obj = HashMap::new();
201        obj.insert("name".to_string(), JsonData::string("John"));
202        obj.insert("age".to_string(), JsonData::integer(30));
203
204        let json_data = JsonData::object(obj);
205        let json_str = JsonAdapter::to_json_string(&json_data).unwrap();
206
207        assert!(json_str.contains("John"));
208        assert!(json_str.contains("30"));
209    }
210
211    #[test]
212    fn test_merge_objects() {
213        let mut left = HashMap::new();
214        left.insert("name".to_string(), JsonData::string("John"));
215        left.insert("age".to_string(), JsonData::integer(25));
216
217        let mut right = HashMap::new();
218        right.insert("age".to_string(), JsonData::integer(30));
219        right.insert("city".to_string(), JsonData::string("NYC"));
220
221        let merged = JsonAdapter::merge(JsonData::object(left), JsonData::object(right));
222
223        assert_eq!(merged.get_path("name").unwrap().as_str(), Some("John"));
224        assert_eq!(merged.get_path("age").unwrap().as_i64(), Some(30)); // Right wins
225        assert_eq!(merged.get_path("city").unwrap().as_str(), Some("NYC"));
226    }
227
228    #[test]
229    fn test_extract_leaf_paths() {
230        let mut user = HashMap::new();
231        user.insert("name".to_string(), JsonData::string("John"));
232        user.insert("age".to_string(), JsonData::integer(30));
233
234        let mut profile = HashMap::new();
235        profile.insert("bio".to_string(), JsonData::string("Developer"));
236        user.insert("profile".to_string(), JsonData::object(profile));
237
238        let root = JsonData::object(
239            [("user".to_string(), JsonData::object(user))]
240                .into_iter()
241                .collect(),
242        );
243
244        let paths = JsonAdapter::extract_leaf_paths(&root);
245
246        assert!(paths.contains(&"user.name".to_string()));
247        assert!(paths.contains(&"user.age".to_string()));
248        assert!(paths.contains(&"user.profile.bio".to_string()));
249    }
250
251    #[test]
252    fn test_validate_pjs_structure() {
253        // Valid structures
254        assert!(JsonAdapter::validate_pjs_structure(&JsonData::object(HashMap::new())).is_ok());
255        assert!(JsonAdapter::validate_pjs_structure(&JsonData::array(vec![])).is_ok());
256
257        // Invalid structures
258        assert!(JsonAdapter::validate_pjs_structure(&JsonData::string("test")).is_err());
259        assert!(JsonAdapter::validate_pjs_structure(&JsonData::integer(42)).is_err());
260    }
261}