xml_disassembler/builders/
build_disassembled_file.rs1use crate::builders::build_xml_string;
4use crate::parsers::parse_unique_id_element;
5use crate::transformers::transform_format;
6use crate::types::BuildDisassembledFileOptions;
7use serde_json::{Map, Value};
8use std::path::Path;
9use tokio::fs;
10use tokio::io::AsyncWriteExt;
11
12pub async fn build_disassembled_file(
13 options: BuildDisassembledFileOptions<'_>,
14) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
15 let BuildDisassembledFileOptions {
16 content,
17 disassembled_path,
18 output_file_name,
19 subdirectory,
20 wrap_key,
21 is_grouped_array,
22 root_element_name,
23 root_attributes,
24 xml_declaration,
25 format,
26 unique_id_elements,
27 } = options;
28
29 let target_directory = if let Some(subdir) = subdirectory {
30 Path::new(disassembled_path).join(subdir)
31 } else {
32 Path::new(disassembled_path).to_path_buf()
33 };
34
35 let file_name = if let Some(name) = output_file_name {
36 name.to_string()
37 } else if let Some(wk) = wrap_key {
38 if !is_grouped_array && content.is_object() {
39 let id = parse_unique_id_element(&content, unique_id_elements);
40 format!("{}.{}-meta.{}", id, wk, format)
41 } else {
42 "output".to_string()
43 }
44 } else {
45 "output".to_string()
46 };
47
48 let output_path = target_directory.join(&file_name);
49
50 fs::create_dir_all(&target_directory).await?;
51
52 let root_attrs_obj = root_attributes.as_object().cloned().unwrap_or_default();
53 let mut inner = root_attrs_obj.clone();
54
55 if let Some(wk) = wrap_key {
56 inner.insert(wk.to_string(), content.clone());
57 } else if let Some(obj) = content.as_object() {
58 for (k, v) in obj {
59 inner.insert(k.clone(), v.clone());
60 }
61 }
62
63 let mut wrapped_xml: Value = Value::Object({
64 let mut m = Map::new();
65 m.insert(root_element_name.to_string(), Value::Object(inner));
66 m
67 });
68
69 if let Some(decl) = xml_declaration {
70 if decl.is_object() {
71 let mut root = Map::new();
72 root.insert("?xml".to_string(), decl.clone());
73 if let Some(obj) = wrapped_xml.as_object() {
74 for (k, v) in obj {
75 root.insert(k.clone(), v.clone());
76 }
77 }
78 wrapped_xml = Value::Object(root);
79 }
80 }
81
82 let output_string = if let Some(s) = transform_format(format, &wrapped_xml).await {
83 s
84 } else {
85 build_xml_string(&wrapped_xml)
86 };
87
88 let mut file = fs::File::create(&output_path).await?;
89 file.write_all(output_string.as_bytes()).await?;
90 log::debug!("Created disassembled file: {}", output_path.display());
91
92 Ok(())
93}
94
95#[cfg(test)]
96mod tests {
97 use super::*;
98 use serde_json::json;
99
100 fn opts_base(disassembled_path: &str) -> BuildDisassembledFileOptions<'_> {
101 BuildDisassembledFileOptions {
102 content: json!({ "a": "b" }),
103 disassembled_path,
104 output_file_name: Some("out.xml"),
105 subdirectory: None,
106 wrap_key: None,
107 is_grouped_array: false,
108 root_element_name: "Root",
109 root_attributes: Value::Object(Map::new()),
110 xml_declaration: None,
111 format: "xml",
112 unique_id_elements: None,
113 }
114 }
115
116 #[tokio::test]
117 async fn build_disassembled_file_file_name_output_when_wrap_key_no_output_name_grouped_array() {
118 let temp = tempfile::tempdir().unwrap();
120 let path = temp.path().to_str().unwrap();
121 let mut opts = opts_base(path);
122 opts.output_file_name = None;
123 opts.wrap_key = Some("wrap");
124 opts.is_grouped_array = true;
125 opts.content = json!([{ "x": "1" }]);
126 build_disassembled_file(opts).await.unwrap();
127 assert!(temp.path().join("output").exists());
128 }
129
130 #[tokio::test]
131 async fn build_disassembled_file_file_name_output_when_wrap_key_content_not_object() {
132 let temp = tempfile::tempdir().unwrap();
134 let path = temp.path().to_str().unwrap();
135 let mut opts = opts_base(path);
136 opts.output_file_name = None;
137 opts.wrap_key = Some("wrap");
138 opts.is_grouped_array = false;
139 opts.content = json!([{ "id": "a" }]);
140 build_disassembled_file(opts).await.unwrap();
141 assert!(temp.path().join("output").exists());
142 }
143
144 #[tokio::test]
145 async fn build_disassembled_file_file_name_output_when_no_wrap_key_no_output_name() {
146 let temp = tempfile::tempdir().unwrap();
148 let path = temp.path().to_str().unwrap();
149 let mut opts = opts_base(path);
150 opts.output_file_name = None;
151 opts.wrap_key = None;
152 build_disassembled_file(opts).await.unwrap();
153 assert!(temp.path().join("output").exists());
154 }
155
156 #[tokio::test]
157 async fn build_disassembled_file_content_not_object_no_spread() {
158 let temp = tempfile::tempdir().unwrap();
160 let path = temp.path().to_str().unwrap();
161 let mut opts = opts_base(path);
162 opts.output_file_name = Some("single.xml");
163 opts.wrap_key = None;
164 opts.content = json!(42);
165 build_disassembled_file(opts).await.unwrap();
166 let out = fs::read_to_string(temp.path().join("single.xml"))
167 .await
168 .unwrap();
169 assert!(out.contains("<Root>"));
170 assert!(out.contains("</Root>"));
172 }
173}