xml_disassembler/builders/
merge_xml_elements.rs1use serde_json::{Map, Value};
4
5use crate::types::XmlElement;
6
7fn is_mergeable_object(value: &Value) -> bool {
8 value.is_object() && !value.is_array()
9}
10
11fn merge_element_content(target: &mut Map<String, Value>, source: &Map<String, Value>) {
12 for (key, value) in source {
13 if value.is_array() {
14 merge_array_value(target, key, value.as_array().unwrap());
15 } else if is_mergeable_object(value) {
16 merge_object_value(target, key, value.as_object().unwrap());
17 } else {
18 merge_primitive_value(target, key, value);
19 }
20 }
21}
22
23fn merge_array_value(target: &mut Map<String, Value>, key: &str, value: &[Value]) {
24 if !target.contains_key(key) {
25 target.insert(key.to_string(), Value::Array(value.to_vec()));
26 } else if let Some(Value::Array(arr)) = target.get_mut(key) {
27 arr.extend(value.iter().cloned());
28 } else {
29 let existing = target.remove(key).unwrap();
30 target.insert(
31 key.to_string(),
32 Value::Array(
33 [vec![existing], value.to_vec()]
34 .into_iter()
35 .flatten()
36 .collect(),
37 ),
38 );
39 }
40}
41
42fn merge_object_value(target: &mut Map<String, Value>, key: &str, value: &Map<String, Value>) {
43 if let Some(Value::Array(arr)) = target.get_mut(key) {
44 arr.push(Value::Object(value.clone()));
45 } else if let Some(existing) = target.get(key) {
46 let existing = existing.clone();
47 target.insert(
48 key.to_string(),
49 Value::Array(vec![existing, Value::Object(value.clone())]),
50 );
51 } else {
52 target.insert(key.to_string(), Value::Object(value.clone()));
53 }
54}
55
56fn merge_primitive_value(target: &mut Map<String, Value>, key: &str, value: &Value) {
57 if !target.contains_key(key) {
58 target.insert(key.to_string(), value.clone());
59 }
60}
61
62fn default_xml_declaration() -> Value {
63 let mut decl = Map::new();
64 decl.insert("@version".to_string(), Value::String("1.0".to_string()));
65 decl.insert("@encoding".to_string(), Value::String("UTF-8".to_string()));
66 Value::Object(decl)
67}
68
69fn build_final_xml_element(
70 declaration: Option<&Value>,
71 root_key: &str,
72 content: Map<String, Value>,
73) -> XmlElement {
74 let mut result = Map::new();
75 let decl = declaration.cloned().unwrap_or_else(default_xml_declaration);
76 result.insert("?xml".to_string(), decl);
77 result.insert(root_key.to_string(), Value::Object(content));
78 Value::Object(result)
79}
80
81pub fn reorder_root_keys(element: &XmlElement, key_order: &[String]) -> Option<XmlElement> {
84 let obj = element.as_object()?;
85 let root_key = obj.keys().find(|k| *k != "?xml")?.clone();
86 let root_content = obj.get(&root_key)?.as_object()?;
87 let mut reordered = Map::new();
88 for key in key_order {
89 if let Some(v) = root_content.get(key) {
90 reordered.insert(key.clone(), v.clone());
91 }
92 }
93 for (key, value) in root_content {
94 if !reordered.contains_key(key) {
95 reordered.insert(key.clone(), value.clone());
96 }
97 }
98 let mut result = Map::new();
99 if let Some(decl) = obj.get("?xml") {
100 result.insert("?xml".to_string(), decl.clone());
101 }
102 result.insert(root_key, Value::Object(reordered));
103 Some(Value::Object(result))
104}
105
106pub fn merge_xml_elements(elements: &[XmlElement]) -> Option<XmlElement> {
108 if elements.is_empty() {
109 log::error!("No elements to merge.");
110 return None;
111 }
112
113 let first = &elements[0];
114 let root_key = first.as_object()?.keys().find(|k| *k != "?xml")?.clone();
115 let mut merged_content = Map::new();
116
117 for element in elements {
118 if let Some(obj) = element.as_object() {
119 if let Some(root_content) = obj.get(&root_key) {
120 if let Some(content_obj) = root_content.as_object() {
121 merge_element_content(&mut merged_content, content_obj);
122 }
123 }
124 }
125 }
126
127 let declaration = first.as_object()?.get("?xml");
128 Some(build_final_xml_element(
129 declaration,
130 &root_key,
131 merged_content,
132 ))
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138 use serde_json::json;
139
140 #[test]
141 fn merge_empty_returns_none() {
142 assert!(merge_xml_elements(&[]).is_none());
143 }
144
145 #[test]
146 fn merge_single_element_preserves_structure() {
147 let el = json!({
148 "?xml": { "@version": "1.0", "@encoding": "UTF-8" },
149 "Root": { "@xmlns": "http://example.com", "child": "a" }
150 });
151 let merged = merge_xml_elements(std::slice::from_ref(&el)).unwrap();
152 assert!(merged.get("?xml").is_some());
153 let root = merged.get("Root").and_then(|v| v.as_object()).unwrap();
154 assert_eq!(root.get("child").and_then(|v| v.as_str()), Some("a"));
155 assert_eq!(
156 root.get("@xmlns").and_then(|v| v.as_str()),
157 Some("http://example.com")
158 );
159 }
160
161 #[test]
162 fn merge_two_elements_combines_nested_objects_into_array() {
163 let a = json!({ "Root": { "section": { "name": "first" } } });
164 let b = json!({ "Root": { "section": { "name": "second" } } });
165 let merged = merge_xml_elements(&[a, b]).unwrap();
166 let root = merged.get("Root").and_then(|v| v.as_object()).unwrap();
167 let sections = root.get("section").and_then(|v| v.as_array()).unwrap();
168 assert_eq!(sections.len(), 2);
169 assert_eq!(
170 sections[0].get("name").and_then(|v| v.as_str()),
171 Some("first")
172 );
173 assert_eq!(
174 sections[1].get("name").and_then(|v| v.as_str()),
175 Some("second")
176 );
177 }
178}