use super::document::{LsxDocument, LsxNode};
use crate::error::Result;
use quick_xml::Writer;
use quick_xml::events::{BytesDecl, BytesEnd, BytesStart, Event};
use std::fs;
use std::path::Path;
pub fn write_lsx<P: AsRef<Path>>(doc: &LsxDocument, path: P) -> Result<()> {
let xml = serialize_lsx(doc)?;
fs::write(path, xml)?;
Ok(())
}
pub fn serialize_lsx(doc: &LsxDocument) -> Result<String> {
let mut output = Vec::new();
output.extend_from_slice(&[0xEF, 0xBB, 0xBF]);
let mut writer = Writer::new_with_indent(&mut output, b'\t', 1);
writer.write_event(Event::Decl(BytesDecl::new("1.0", Some("utf-8"), None)))?;
writer.write_event(Event::Start(BytesStart::new("save")))?;
let mut version = BytesStart::new("version");
version.push_attribute(("major", doc.major.to_string().as_str()));
version.push_attribute(("minor", doc.minor.to_string().as_str()));
version.push_attribute(("revision", doc.revision.to_string().as_str()));
version.push_attribute(("build", doc.build.to_string().as_str()));
if doc.major >= 4 {
version.push_attribute(("lslib_meta", "v1,bswap_guids"));
} else {
version.push_attribute(("lslib_meta", "v1"));
}
writer.write_event(Event::Empty(version))?;
for region in &doc.regions {
let mut region_tag = BytesStart::new("region");
region_tag.push_attribute(("id", region.id.as_str()));
writer.write_event(Event::Start(region_tag.borrow()))?;
for node in ®ion.nodes {
write_node(&mut writer, node)?;
}
writer.write_event(Event::End(BytesEnd::new("region")))?;
}
writer.write_event(Event::End(BytesEnd::new("save")))?;
let xml = String::from_utf8(output)?;
let xml = xml.replace('\n', "\r\n");
let xml = xml.replace("/>", " />");
Ok(xml)
}
fn write_node<W: std::io::Write>(writer: &mut Writer<W>, node: &LsxNode) -> Result<()> {
let has_attributes = !node.attributes.is_empty();
let has_children = !node.children.is_empty();
let mut node_start = BytesStart::new("node");
node_start.push_attribute(("id", node.id.as_str()));
if let Some(ref key) = node.key {
node_start.push_attribute(("key", key.as_str()));
}
if !has_attributes && !has_children {
writer.write_event(Event::Empty(node_start))?;
return Ok(());
}
writer.write_event(Event::Start(node_start.borrow()))?;
if has_attributes {
for attr in &node.attributes {
let mut attr_tag = BytesStart::new("attribute");
attr_tag.push_attribute(("id", attr.id.as_str()));
attr_tag.push_attribute(("type", attr.type_name.as_str()));
if let Some(ref handle) = attr.handle {
attr_tag.push_attribute(("handle", handle.as_str()));
if !attr.value.is_empty() {
attr_tag.push_attribute(("value", attr.value.as_str()));
}
if let Some(version) = attr.version {
attr_tag.push_attribute(("version", version.to_string().as_str()));
}
} else {
attr_tag.push_attribute(("value", attr.value.as_str()));
}
writer.write_event(Event::Empty(attr_tag))?;
}
}
if has_children {
writer.write_event(Event::Start(BytesStart::new("children")))?;
for child in &node.children {
write_node(writer, child)?;
}
writer.write_event(Event::End(BytesEnd::new("children")))?;
}
writer.write_event(Event::End(BytesEnd::new("node")))?;
Ok(())
}