use crate::domain::value_objects::JsonData;
use serde_json::Value as SerdeValue;
use std::collections::HashMap;
pub struct JsonAdapter;
impl JsonAdapter {
pub fn to_serde_value(data: &JsonData) -> SerdeValue {
match data {
JsonData::Null => SerdeValue::Null,
JsonData::Bool(b) => SerdeValue::Bool(*b),
JsonData::Integer(i) => SerdeValue::Number(serde_json::Number::from(*i)),
JsonData::Float(f) => SerdeValue::Number(
serde_json::Number::from_f64(*f).unwrap_or_else(|| serde_json::Number::from(0)),
),
JsonData::String(s) => SerdeValue::String(s.clone()),
JsonData::Array(arr) => {
let values: Vec<SerdeValue> = arr.iter().map(Self::to_serde_value).collect();
SerdeValue::Array(values)
}
JsonData::Object(obj) => {
let mut map = serde_json::Map::new();
for (key, value) in obj {
map.insert(key.clone(), Self::to_serde_value(value));
}
SerdeValue::Object(map)
}
}
}
pub fn from_serde_value(value: &SerdeValue) -> JsonData {
match value {
SerdeValue::Null => JsonData::Null,
SerdeValue::Bool(b) => JsonData::Bool(*b),
SerdeValue::Number(n) => {
if let Some(i) = n.as_i64() {
JsonData::Integer(i)
} else if let Some(f) = n.as_f64() {
JsonData::Float(f)
} else {
JsonData::Integer(0) }
}
SerdeValue::String(s) => JsonData::String(s.clone()),
SerdeValue::Array(arr) => {
let values: Vec<JsonData> = arr.iter().map(Self::from_serde_value).collect();
JsonData::Array(values)
}
SerdeValue::Object(obj) => {
let mut map = HashMap::new();
for (key, value) in obj {
map.insert(key.clone(), Self::from_serde_value(value));
}
JsonData::Object(map)
}
}
}
pub fn parse_json_string(json_str: &str) -> Result<JsonData, serde_json::Error> {
let serde_value: SerdeValue = serde_json::from_str(json_str)?;
Ok(Self::from_serde_value(&serde_value))
}
pub fn to_json_string(data: &JsonData) -> Result<String, serde_json::Error> {
let serde_value = Self::to_serde_value(data);
serde_json::to_string(&serde_value)
}
pub fn to_json_string_pretty(data: &JsonData) -> Result<String, serde_json::Error> {
let serde_value = Self::to_serde_value(data);
serde_json::to_string_pretty(&serde_value)
}
pub fn merge(left: JsonData, right: JsonData) -> JsonData {
match (left, right) {
(JsonData::Object(mut left_obj), JsonData::Object(right_obj)) => {
for (key, value) in right_obj {
if let Some(existing) = left_obj.get(&key).cloned() {
left_obj.insert(key, Self::merge(existing, value));
} else {
left_obj.insert(key, value);
}
}
JsonData::Object(left_obj)
}
(_, right) => right, }
}
pub fn deep_clone(data: &JsonData) -> JsonData {
data.clone() }
pub fn validate_pjs_structure(data: &JsonData) -> Result<(), String> {
match data {
JsonData::Object(_) => Ok(()), JsonData::Array(_) => Ok(()), _ => Err("PJS root data must be object or array".to_string()),
}
}
pub fn extract_leaf_paths(data: &JsonData) -> Vec<String> {
let mut paths = Vec::new();
Self::extract_paths_recursive(data, String::new(), &mut paths);
paths
}
fn extract_paths_recursive(data: &JsonData, current_path: String, paths: &mut Vec<String>) {
match data {
JsonData::Object(obj) => {
if obj.is_empty() {
paths.push(current_path);
} else {
for (key, value) in obj {
let new_path = if current_path.is_empty() {
key.clone()
} else {
format!("{current_path}.{key}")
};
Self::extract_paths_recursive(value, new_path, paths);
}
}
}
JsonData::Array(arr) => {
if arr.is_empty() {
paths.push(current_path);
} else {
for (index, value) in arr.iter().enumerate() {
let new_path = if current_path.is_empty() {
format!("[{index}]")
} else {
format!("{current_path}[{index}]")
};
Self::extract_paths_recursive(value, new_path, paths);
}
}
}
_ => {
paths.push(current_path);
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_serde_conversion_primitives() {
let json_data = JsonData::string("hello");
let serde_value = JsonAdapter::to_serde_value(&json_data);
let converted_back = JsonAdapter::from_serde_value(&serde_value);
assert_eq!(json_data, converted_back);
}
#[test]
fn test_serde_conversion_complex() {
let mut obj = HashMap::new();
obj.insert("name".to_string(), JsonData::string("John"));
obj.insert("age".to_string(), JsonData::integer(30));
obj.insert("active".to_string(), JsonData::bool(true));
let json_data = JsonData::object(obj);
let serde_value = JsonAdapter::to_serde_value(&json_data);
let converted_back = JsonAdapter::from_serde_value(&serde_value);
assert_eq!(json_data, converted_back);
}
#[test]
fn test_json_string_parsing() {
let json_str = r#"{"name":"John","age":30,"active":true}"#;
let parsed = JsonAdapter::parse_json_string(json_str).unwrap();
assert_eq!(parsed.get_path("name").unwrap().as_str(), Some("John"));
assert_eq!(parsed.get_path("age").unwrap().as_f64(), Some(30.0));
assert_eq!(parsed.get_path("active").unwrap().as_bool(), Some(true));
}
#[test]
fn test_json_string_generation() {
let mut obj = HashMap::new();
obj.insert("name".to_string(), JsonData::string("John"));
obj.insert("age".to_string(), JsonData::integer(30));
let json_data = JsonData::object(obj);
let json_str = JsonAdapter::to_json_string(&json_data).unwrap();
assert!(json_str.contains("John"));
assert!(json_str.contains("30"));
}
#[test]
fn test_merge_objects() {
let mut left = HashMap::new();
left.insert("name".to_string(), JsonData::string("John"));
left.insert("age".to_string(), JsonData::integer(25));
let mut right = HashMap::new();
right.insert("age".to_string(), JsonData::integer(30));
right.insert("city".to_string(), JsonData::string("NYC"));
let merged = JsonAdapter::merge(JsonData::object(left), JsonData::object(right));
assert_eq!(merged.get_path("name").unwrap().as_str(), Some("John"));
assert_eq!(merged.get_path("age").unwrap().as_i64(), Some(30)); assert_eq!(merged.get_path("city").unwrap().as_str(), Some("NYC"));
}
#[test]
fn test_extract_leaf_paths() {
let mut user = HashMap::new();
user.insert("name".to_string(), JsonData::string("John"));
user.insert("age".to_string(), JsonData::integer(30));
let mut profile = HashMap::new();
profile.insert("bio".to_string(), JsonData::string("Developer"));
user.insert("profile".to_string(), JsonData::object(profile));
let root = JsonData::object(
[("user".to_string(), JsonData::object(user))]
.into_iter()
.collect(),
);
let paths = JsonAdapter::extract_leaf_paths(&root);
assert!(paths.contains(&"user.name".to_string()));
assert!(paths.contains(&"user.age".to_string()));
assert!(paths.contains(&"user.profile.bio".to_string()));
}
#[test]
fn test_validate_pjs_structure() {
assert!(JsonAdapter::validate_pjs_structure(&JsonData::object(HashMap::new())).is_ok());
assert!(JsonAdapter::validate_pjs_structure(&JsonData::array(vec![])).is_ok());
assert!(JsonAdapter::validate_pjs_structure(&JsonData::string("test")).is_err());
assert!(JsonAdapter::validate_pjs_structure(&JsonData::integer(42)).is_err());
}
}