use crate::{error, Result};
use core::hash::Hash;
use indexmap::IndexMap;
use quick_xml::events::{
BytesDecl, BytesEnd, BytesStart, BytesText, Event,
};
use quick_xml::Writer;
use snafu::ResultExt;
use std::io::Write;
pub(crate) trait XmlWritable {
fn write_xml<W: XmlWriter>(&self, w: &mut W) -> Result<()>;
fn write_xml_document<W: Write>(&self, w: W) -> Result<()> {
let mut writer = Writer::new(w);
writer
.write_event(Event::Decl(BytesDecl::new(
b"1.0",
Some(b"UTF-8"),
Some(b"yes"),
)))
.context(error::XmlWrite)?;
writer.write("\n".as_bytes()).context(error::XmlWrite)?;
self.write_xml(&mut writer)
}
fn write_xml_document_to_string(&self) -> Result<String> {
let mut buffer = Vec::<u8>::new();
self.write_xml_document(&mut buffer)?;
String::from_utf8(buffer).context(error::FromUtf8 {})
}
fn write_xml_to_string(&self) -> Result<String> {
let mut writer = Writer::new(Vec::<u8>::new());
self.write_xml(&mut writer)?;
String::from_utf8(writer.into_inner()).context(error::FromUtf8 {})
}
}
pub(crate) trait XmlWriter: Sized {
fn empty_tag(&mut self, tag: &str) -> Result<()> {
let attrs: IndexMap<&str, &str> = IndexMap::new();
self.empty_tag_with_attrs(tag, attrs)
}
fn empty_tag_with_attrs(
&mut self,
tag: &str,
attrs: IndexMap<impl AsRef<[u8]> + Hash + Eq, impl AsRef<[u8]>>,
) -> Result<()>;
fn start_tag(&mut self, tag: &str) -> Result<()> {
let attrs: IndexMap<&str, &str> = IndexMap::new();
self.start_tag_with_attrs(tag, attrs)
}
fn start_tag_with_attrs(
&mut self,
tag: &str,
attrs: IndexMap<impl AsRef<[u8]> + Hash + Eq, impl AsRef<[u8]>>,
) -> Result<()>;
fn end_tag(&mut self, tag: &str) -> Result<()>;
fn text(&mut self, text: &str) -> Result<()>;
fn tag_with_text(&mut self, tag: &str, text: &str) -> Result<()> {
let attrs: IndexMap<&str, &str> = IndexMap::new();
self.tag_with_attrs_and_text(tag, attrs, text)
}
fn tag_with_attrs_and_text(
&mut self,
tag: &str,
attrs: IndexMap<impl AsRef<[u8]> + Hash + Eq, impl AsRef<[u8]>>,
text: &str,
) -> Result<()> {
self.start_tag_with_attrs(tag, attrs)?;
self.text(text)?;
self.end_tag(tag)?;
Ok(())
}
fn tag_with_xml_writable<W: XmlWritable>(
&mut self,
tag: &str,
writable: &W,
) -> Result<()> {
let attrs: IndexMap<&str, &str> = IndexMap::new();
self.tag_with_attrs_and_xml_writable(tag, attrs, writable)
}
fn tag_with_attrs_and_xml_writable<W: XmlWritable>(
&mut self,
tag: &str,
attrs: IndexMap<impl AsRef<[u8]> + Hash + Eq, impl AsRef<[u8]>>,
writable: &W,
) -> Result<()> {
self.start_tag_with_attrs(tag, attrs)?;
writable.write_xml(self)?;
self.end_tag(tag)?;
Ok(())
}
}
impl<W: Write> XmlWriter for Writer<W> {
fn empty_tag_with_attrs(
&mut self,
tag: &str,
attrs: IndexMap<impl AsRef<[u8]> + Hash + Eq, impl AsRef<[u8]>>,
) -> Result<()> {
let mut e = BytesStart::borrowed(tag.as_bytes(), tag.len());
for (ref k, ref v) in attrs.iter() {
e.push_attribute((k.as_ref(), v.as_ref()));
}
self.write_event(Event::Empty(e)).context(error::XmlWrite)?;
Ok(())
}
fn start_tag_with_attrs(
&mut self,
tag: &str,
attrs: IndexMap<impl AsRef<[u8]> + Hash + Eq, impl AsRef<[u8]>>,
) -> Result<()> {
let mut e = BytesStart::borrowed(tag.as_bytes(), tag.len());
for (ref k, ref v) in attrs.iter() {
e.push_attribute((k.as_ref(), v.as_ref()));
}
self.write_event(Event::Start(e)).context(error::XmlWrite)?;
Ok(())
}
fn end_tag(&mut self, tag: &str) -> Result<()> {
self.write_event(Event::End(BytesEnd::borrowed(tag.as_ref())))
.context(error::XmlWrite)?;
Ok(())
}
fn text(&mut self, text: &str) -> Result<()> {
let text = BytesText::from_escaped_str(text);
self.write_event(Event::Text(text))
.context(error::XmlWrite)?;
Ok(())
}
}