karo 0.1.2

Spreadsheet export
Documentation
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(())
    }
}