xml-core 1.0.0

Low-level generic XML reading/writing, shared building block for all OOXML formats in the toolkit.
Documentation
//! Low-level XML writing.

use std::io::Write;

use quick_xml::events::Event;

use crate::error::Result;

/// Writes a sequence of generic XML events to an underlying [`std::io::Write`].
///
/// This is a thin wrapper around [`quick_xml::Writer`], scoped to
/// `xml-core`'s own error type so that callers never need to depend on
/// `quick_xml` directly.
pub struct Writer<W: Write> {
    inner: quick_xml::Writer<W>,
}

impl<W: Write> Writer<W> {
    /// Creates a new writer around the given output.
    pub fn new(inner: W) -> Self {
        Self {
            inner: quick_xml::Writer::new(inner),
        }
    }

    /// Writes a single XML event to the underlying output.
    pub fn write_event<'a, E: Into<Event<'a>>>(&mut self, event: E) -> Result<()> {
        self.inner.write_event(event).map_err(Into::into)
    }

    /// Consumes this writer, returning the underlying output.
    pub fn into_inner(self) -> W {
        self.inner.into_inner()
    }

    /// Writes a pre-formed, already-valid fragment of UTF-8 XML directly to
    /// the underlying output, bypassing the event model entirely.
    ///
    /// For embedding fixed, non-configurable XML content that has no need
    /// to be rebuilt one event at a time — e.g. a large, entirely static
    /// boilerplate payload a higher-level crate always writes verbatim. The
    /// caller is responsible for the fragment being well-formed and safe to
    /// splice in at its call site (no XML declaration, no dangling open
    /// tags, valid as a sequence of complete sibling elements).
    pub fn write_raw(&mut self, xml: &str) -> Result<()> {
        self.inner.get_mut().write_all(xml.as_bytes())?;
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use quick_xml::events::{BytesEnd, BytesStart, BytesText};

    use super::*;

    #[test]
    fn writes_a_simple_element_with_text() {
        let mut writer = Writer::new(Vec::new());

        writer
            .write_event(Event::Start(BytesStart::new("a")))
            .unwrap();
        writer
            .write_event(Event::Text(BytesText::new("hello")))
            .unwrap();
        writer.write_event(Event::End(BytesEnd::new("a"))).unwrap();

        let output = writer.into_inner();
        assert_eq!(output, b"<a>hello</a>");
    }

    #[test]
    fn writes_an_element_with_an_attribute() {
        let mut writer = Writer::new(Vec::new());

        let mut start = BytesStart::new("a");
        start.push_attribute(("k", "v"));
        writer.write_event(Event::Empty(start)).unwrap();

        let output = writer.into_inner();
        assert_eq!(output, br#"<a k="v"/>"#);
    }

    #[test]
    fn write_raw_splices_a_pre_formed_fragment_in_between_events() {
        let mut writer = Writer::new(Vec::new());

        writer
            .write_event(Event::Start(BytesStart::new("a")))
            .unwrap();
        writer.write_raw("<b/><c>text</c>").unwrap();
        writer.write_event(Event::End(BytesEnd::new("a"))).unwrap();

        let output = writer.into_inner();
        assert_eq!(output, b"<a><b/><c>text</c></a>");
    }
}