use crate::formatter::{sort_json_value, Formatter};
use crate::types::Changes;
pub struct ChangesFormatter {
pretty: bool,
sort: bool,
}
impl ChangesFormatter {
pub fn new(sort: bool) -> Self {
Self { pretty: true, sort }
}
}
impl Default for ChangesFormatter {
fn default() -> Self {
Self::new(false)
}
}
impl Formatter for ChangesFormatter {
fn format(&self, changes: &Changes) -> Result<String, Box<dyn std::error::Error>> {
let json = serde_json::to_value(changes)?;
if self.sort {
let sorted = sort_json_value(&json);
if self.pretty {
Ok(serde_json::to_string_pretty(&sorted)?)
} else {
Ok(serde_json::to_string(&sorted)?)
}
} else if self.pretty {
Ok(serde_json::to_string_pretty(changes)?)
} else {
Ok(serde_json::to_string(changes)?)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::{Change, Changes};
use serde_json::Value;
#[test]
fn test_format_empty_changes() {
let formatter = ChangesFormatter::new(false);
let changes = Changes::new();
let result = formatter.format(&changes).unwrap();
let parsed: Value = serde_json::from_str(&result).unwrap();
assert!(parsed["added"].is_array());
assert!(parsed["removed"].is_array());
assert!(parsed["modified"].is_array());
assert_eq!(parsed["added"].as_array().unwrap().len(), 0);
assert_eq!(parsed["removed"].as_array().unwrap().len(), 0);
assert_eq!(parsed["modified"].as_array().unwrap().len(), 0);
}
#[test]
fn test_format_with_changes() {
let formatter = ChangesFormatter::new(false);
let mut changes = Changes::new();
changes.push(Change::Added {
path: "users[0].name".parse().unwrap(),
value: Value::String("Alice".to_string()),
});
changes.push(Change::Removed {
path: "users[0].phone".parse().unwrap(),
value: Value::String("555-1234".to_string()),
});
changes.push(Change::Modified {
path: "users[0].age".parse().unwrap(),
old_value: Value::Number(25.into()),
new_value: Value::Number(26.into()),
});
let result = formatter.format(&changes).unwrap();
let parsed: Value = serde_json::from_str(&result).unwrap();
assert_eq!(parsed["added"].as_array().unwrap().len(), 1);
assert_eq!(parsed["removed"].as_array().unwrap().len(), 1);
assert_eq!(parsed["modified"].as_array().unwrap().len(), 1);
assert_eq!(parsed["added"][0]["path"], "users[0].name");
assert_eq!(parsed["added"][0]["value"], "Alice");
assert_eq!(parsed["removed"][0]["path"], "users[0].phone");
assert_eq!(parsed["removed"][0]["value"], "555-1234");
assert_eq!(parsed["modified"][0]["path"], "users[0].age");
assert_eq!(parsed["modified"][0]["oldValue"], 25);
assert_eq!(parsed["modified"][0]["newValue"], 26);
}
#[test]
fn test_format_with_sort() {
let formatter = ChangesFormatter::new(true);
let mut changes = Changes::new();
changes.push(Change::Added {
path: "z".parse().unwrap(),
value: Value::String("last".to_string()),
});
changes.push(Change::Added {
path: "a".parse().unwrap(),
value: Value::String("first".to_string()),
});
let result = formatter.format(&changes).unwrap();
let parsed: Value = serde_json::from_str(&result).unwrap();
let obj = parsed.as_object().unwrap();
let keys: Vec<&str> = obj.keys().map(|s| s.as_str()).collect();
assert_eq!(keys, vec!["added", "modified", "removed"]);
}
#[test]
fn test_format_with_sort_nested() {
let formatter = ChangesFormatter::new(true);
let mut changes = Changes::new();
let mut nested = serde_json::Map::new();
nested.insert("z_key".to_string(), Value::String("z_value".to_string()));
nested.insert("a_key".to_string(), Value::String("a_value".to_string()));
changes.push(Change::Added {
path: "obj".parse().unwrap(),
value: Value::Object(nested),
});
let result = formatter.format(&changes).unwrap();
let parsed: Value = serde_json::from_str(&result).unwrap();
let obj = parsed.as_object().unwrap();
let added = obj["added"].as_array().unwrap();
let nested_obj = added[0]["value"].as_object().unwrap();
let nested_keys: Vec<&str> = nested_obj.keys().map(|s| s.as_str()).collect();
assert_eq!(nested_keys, vec!["a_key", "z_key"]);
}
}