use crate::Error;
use minidom::{Element, Node};
use quick_xml::{
events::{BytesDecl, BytesEnd, BytesStart, BytesText, Event},
Writer,
};
use std::io::Write;
const XML_VERSION: &str = "1.0";
const ENCODING: &str = "UTF-8";
pub struct ElementWriter<W>
where
W: Write,
{
writer: Writer<W>,
}
impl<W> ElementWriter<W>
where
W: Write,
{
pub fn plain(write: W) -> Self {
ElementWriter {
writer: Writer::new(write),
}
}
pub fn pretty(write: W) -> Self {
ElementWriter {
writer: Writer::new_with_indent(write, 9, 1),
}
}
pub fn into_inner(self) -> W {
self.writer.into_inner()
}
pub fn write(&mut self, element: &Element) -> Result<(), Error> {
let decl_bytes = BytesDecl::new(XML_VERSION.as_bytes(), Some(ENCODING.as_bytes()), None);
self.writer.write_event(Event::Decl(decl_bytes))?;
self.write_element(element)
}
}
impl<W> ElementWriter<W>
where
W: Write,
{
fn write_element(&mut self, element: &Element) -> Result<(), Error> {
let name = if let Some(prefix) = element.prefix() {
format!("{}:{}", prefix, element.name())
} else {
element.name().to_string()
};
let mut start_bytes = BytesStart::borrowed(name.as_bytes(), name.len());
start_bytes.extend_attributes(element.attrs());
self.writer.write_event(Event::Start(start_bytes))?;
for node in element.nodes() {
match node {
Node::Element(e) => {
self.write_element(e)?;
}
Node::Text(t) => {
let text_bytes = BytesText::from_plain_str(t.as_str());
self.writer.write_event(Event::Text(text_bytes))?;
}
Node::Comment(_) => (),
}
}
let end_bytes = BytesEnd::borrowed(name.as_bytes());
self.writer.write_event(Event::End(end_bytes))?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
use std::io::Cursor;
fn tag() -> Element {
let subtag = Element::builder("ns:subtag")
.attr("id", "my_subtag")
.append(Node::Text(String::from("Some text")))
.build();
Element::builder("tag")
.attr("id", "my_tag")
.append(subtag)
.build()
}
#[test]
fn write_xml() {
let tag = tag();
let write = Cursor::new(Vec::new());
let mut element_writer = ElementWriter::plain(write);
element_writer.write(&tag).unwrap();
let expected = r#"<?xml version="1.0" encoding="UTF-8"?><tag id="my_tag"><ns:subtag id="my_subtag">Some text</ns:subtag></tag>"#;
assert_eq!(
expected,
String::from_utf8(element_writer.into_inner().into_inner()).unwrap()
);
}
#[test]
fn write_xml_with_indent() {
let tag = tag();
let write = Cursor::new(Vec::new());
let mut element_writer = ElementWriter::pretty(write);
element_writer.write(&tag).unwrap();
let expected = r#"<?xml version="1.0" encoding="UTF-8"?>
<tag id="my_tag">
<ns:subtag id="my_subtag">Some text</ns:subtag>
</tag>"#;
assert_eq!(
expected,
String::from_utf8(element_writer.into_inner().into_inner()).unwrap()
);
}
}