Skip to main content

xml_core/
writer.rs

1//! Low-level XML writing.
2
3use std::io::Write;
4
5use quick_xml::events::Event;
6
7use crate::error::Result;
8
9/// Writes a sequence of generic XML events to an underlying [`std::io::Write`].
10///
11/// This is a thin wrapper around [`quick_xml::Writer`], scoped to
12/// `xml-core`'s own error type so that callers never need to depend on
13/// `quick_xml` directly.
14pub struct Writer<W: Write> {
15    inner: quick_xml::Writer<W>,
16}
17
18impl<W: Write> Writer<W> {
19    /// Creates a new writer around the given output.
20    pub fn new(inner: W) -> Self {
21        Self {
22            inner: quick_xml::Writer::new(inner),
23        }
24    }
25
26    /// Writes a single XML event to the underlying output.
27    pub fn write_event<'a, E: Into<Event<'a>>>(&mut self, event: E) -> Result<()> {
28        self.inner.write_event(event).map_err(Into::into)
29    }
30
31    /// Consumes this writer, returning the underlying output.
32    pub fn into_inner(self) -> W {
33        self.inner.into_inner()
34    }
35
36    /// Writes a pre-formed, already-valid fragment of UTF-8 XML directly to
37    /// the underlying output, bypassing the event model entirely.
38    ///
39    /// For embedding fixed, non-configurable XML content that has no need
40    /// to be rebuilt one event at a time — e.g. a large, entirely static
41    /// boilerplate payload a higher-level crate always writes verbatim. The
42    /// caller is responsible for the fragment being well-formed and safe to
43    /// splice in at its call site (no XML declaration, no dangling open
44    /// tags, valid as a sequence of complete sibling elements).
45    pub fn write_raw(&mut self, xml: &str) -> Result<()> {
46        self.inner.get_mut().write_all(xml.as_bytes())?;
47        Ok(())
48    }
49}
50
51#[cfg(test)]
52mod tests {
53    use quick_xml::events::{BytesEnd, BytesStart, BytesText};
54
55    use super::*;
56
57    #[test]
58    fn writes_a_simple_element_with_text() {
59        let mut writer = Writer::new(Vec::new());
60
61        writer
62            .write_event(Event::Start(BytesStart::new("a")))
63            .unwrap();
64        writer
65            .write_event(Event::Text(BytesText::new("hello")))
66            .unwrap();
67        writer.write_event(Event::End(BytesEnd::new("a"))).unwrap();
68
69        let output = writer.into_inner();
70        assert_eq!(output, b"<a>hello</a>");
71    }
72
73    #[test]
74    fn writes_an_element_with_an_attribute() {
75        let mut writer = Writer::new(Vec::new());
76
77        let mut start = BytesStart::new("a");
78        start.push_attribute(("k", "v"));
79        writer.write_event(Event::Empty(start)).unwrap();
80
81        let output = writer.into_inner();
82        assert_eq!(output, br#"<a k="v"/>"#);
83    }
84
85    #[test]
86    fn write_raw_splices_a_pre_formed_fragment_in_between_events() {
87        let mut writer = Writer::new(Vec::new());
88
89        writer
90            .write_event(Event::Start(BytesStart::new("a")))
91            .unwrap();
92        writer.write_raw("<b/><c>text</c>").unwrap();
93        writer.write_event(Event::End(BytesEnd::new("a"))).unwrap();
94
95        let output = writer.into_inner();
96        assert_eq!(output, b"<a><b/><c>text</c></a>");
97    }
98}