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(&self, w: &mut W) -> Result<()>; fn write_xml_document(&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 { let mut buffer = Vec::::new(); self.write_xml_document(&mut buffer)?; String::from_utf8(buffer).context(error::FromUtf8 {}) } fn write_xml_to_string(&self) -> Result { let mut writer = Writer::new(Vec::::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 + 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 + 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 + 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( &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( &mut self, tag: &str, attrs: IndexMap + 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 XmlWriter for Writer { fn empty_tag_with_attrs( &mut self, tag: &str, attrs: IndexMap + 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 + 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(()) } }