Skip to main content

docx_rs/xml/
writer.rs

1use std::borrow::Cow;
2use std::fmt;
3use std::io::Write;
4use std::marker::PhantomData;
5
6use quick_xml::events::{BytesDecl, BytesText, Event};
7use quick_xml::Writer as QuickWriter;
8
9use super::common::XmlVersion;
10
11pub type Result<T> = std::result::Result<T, Error>;
12
13#[derive(Debug)]
14pub enum Error {
15    Io(std::io::Error),
16    Xml(quick_xml::Error),
17    UnbalancedEndTag,
18}
19
20impl fmt::Display for Error {
21    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22        match self {
23            Error::Io(err) => write!(f, "io error: {}", err),
24            Error::Xml(err) => write!(f, "xml error: {}", err),
25            Error::UnbalancedEndTag => write!(f, "attempted to close more elements than opened"),
26        }
27    }
28}
29
30impl std::error::Error for Error {
31    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
32        match self {
33            Error::Io(err) => Some(err),
34            Error::Xml(err) => Some(err),
35            Error::UnbalancedEndTag => None,
36        }
37    }
38}
39
40impl From<std::io::Error> for Error {
41    fn from(value: std::io::Error) -> Self {
42        Error::Io(value)
43    }
44}
45
46impl From<quick_xml::Error> for Error {
47    fn from(value: quick_xml::Error) -> Self {
48        Error::Xml(value)
49    }
50}
51
52#[derive(Clone, Debug)]
53pub struct EmitterConfig {
54    pub write_document_declaration: bool,
55    pub perform_escaping: bool,
56    pub perform_indent: bool,
57    pub line_separator: Cow<'static, str>,
58}
59
60impl Default for EmitterConfig {
61    fn default() -> Self {
62        Self {
63            write_document_declaration: true,
64            perform_escaping: true,
65            perform_indent: false,
66            line_separator: Cow::Borrowed("\n"),
67        }
68    }
69}
70
71impl EmitterConfig {
72    pub fn create_writer<W: Write>(&self, writer: W) -> EventWriter<W> {
73        EventWriter {
74            writer: QuickWriter::new(writer),
75            perform_escaping: self.perform_escaping,
76            element_stack: Vec::new(),
77        }
78    }
79}
80
81#[derive(Debug)]
82struct ElementState {
83    name: String,
84    attributes: Vec<Attribute>,
85    pending: bool,
86}
87
88pub struct EventWriter<W: Write> {
89    writer: QuickWriter<W>,
90    perform_escaping: bool,
91    element_stack: Vec<ElementState>,
92}
93
94impl<W: Write> EventWriter<W> {
95    pub fn write<'a, E>(&mut self, event: E) -> Result<()>
96    where
97        E: Into<XmlEvent<'a>>,
98    {
99        match event.into() {
100            XmlEvent::StartDocument {
101                version,
102                encoding,
103                standalone,
104            } => {
105                let standalone_text = standalone.map(|flag| if flag { "yes" } else { "no" });
106                let decl = BytesDecl::new(version.as_str(), encoding, standalone_text);
107                self.writer.write_event(Event::Decl(decl))?;
108            }
109            XmlEvent::StartElement(element) => {
110                self.flush_pending()?;
111                let StartElement {
112                    name, attributes, ..
113                } = element;
114                self.element_stack.push(ElementState {
115                    name,
116                    attributes,
117                    pending: true,
118                });
119            }
120            XmlEvent::EndElement => {
121                let state = self.element_stack.pop().ok_or(Error::UnbalancedEndTag)?;
122                if state.pending {
123                    self.write_empty(state)?;
124                } else {
125                    self.write_closing(&state.name)?;
126                }
127            }
128            XmlEvent::Characters(text) => {
129                self.flush_pending()?;
130                let text_event = if self.perform_escaping {
131                    BytesText::new(text.as_ref())
132                } else {
133                    BytesText::from_escaped(text.as_ref())
134                };
135                self.writer.write_event(Event::Text(text_event))?;
136            }
137        }
138        Ok(())
139    }
140
141    pub fn into_inner(self) -> Result<W> {
142        Ok(self.writer.into_inner())
143    }
144
145    pub fn inner_mut(&mut self) -> Result<&mut W> {
146        Ok(self.writer.get_mut())
147    }
148
149    fn flush_pending(&mut self) -> Result<()> {
150        if let Some(state) = self.element_stack.last_mut() {
151            if state.pending {
152                state.pending = false;
153                let writer = self.writer.get_mut();
154                writer.write_all(b"<").map_err(Error::from)?;
155                writer
156                    .write_all(state.name.as_bytes())
157                    .map_err(Error::from)?;
158                for attr in &state.attributes {
159                    writer.write_all(b" ").map_err(Error::from)?;
160                    writer
161                        .write_all(attr.name.as_bytes())
162                        .map_err(Error::from)?;
163                    writer.write_all(b"=\"").map_err(Error::from)?;
164                    writer
165                        .write_all(attr.value.as_bytes())
166                        .map_err(Error::from)?;
167                    writer.write_all(b"\"").map_err(Error::from)?;
168                }
169                writer.write_all(b">").map_err(Error::from)?;
170            }
171        }
172        Ok(())
173    }
174
175    fn write_empty(&mut self, state: ElementState) -> Result<()> {
176        self.write_tag(state.name.as_str(), &state.attributes, b" />")
177    }
178
179    fn write_closing(&mut self, name: &str) -> Result<()> {
180        let writer = self.writer.get_mut();
181        writer.write_all(b"</").map_err(Error::from)?;
182        writer.write_all(name.as_bytes()).map_err(Error::from)?;
183        writer.write_all(b">").map_err(Error::from)
184    }
185
186    fn write_tag(&mut self, name: &str, attributes: &[Attribute], suffix: &[u8]) -> Result<()> {
187        let writer = self.writer.get_mut();
188        writer.write_all(b"<").map_err(Error::from)?;
189        writer.write_all(name.as_bytes()).map_err(Error::from)?;
190        for attr in attributes {
191            writer.write_all(b" ").map_err(Error::from)?;
192            writer
193                .write_all(attr.name.as_bytes())
194                .map_err(Error::from)?;
195            writer.write_all(b"=\"").map_err(Error::from)?;
196            writer
197                .write_all(attr.value.as_bytes())
198                .map_err(Error::from)?;
199            writer.write_all(b"\"").map_err(Error::from)?;
200        }
201        writer.write_all(suffix).map_err(Error::from)
202    }
203}
204
205#[derive(Clone, Debug)]
206pub struct StartElement<'a> {
207    name: String,
208    attributes: Vec<Attribute>,
209    _marker: PhantomData<&'a ()>,
210}
211
212#[derive(Clone, Debug)]
213struct Attribute {
214    name: String,
215    value: String,
216}
217
218#[derive(Clone, Debug)]
219pub enum XmlEvent<'a> {
220    StartDocument {
221        version: XmlVersion,
222        encoding: Option<&'a str>,
223        standalone: Option<bool>,
224    },
225    StartElement(StartElement<'a>),
226    EndElement,
227    Characters(Cow<'a, str>),
228}
229
230impl<'a> XmlEvent<'a> {
231    pub fn start_element(name: &'a str) -> StartElement<'a> {
232        StartElement {
233            name: name.to_string(),
234            attributes: Vec::new(),
235            _marker: std::marker::PhantomData,
236        }
237    }
238
239    pub fn end_element() -> XmlEvent<'static> {
240        XmlEvent::EndElement
241    }
242}
243
244impl<'a> From<StartElement<'a>> for XmlEvent<'a> {
245    fn from(value: StartElement<'a>) -> Self {
246        XmlEvent::StartElement(value)
247    }
248}
249
250impl<'a> From<&'a str> for XmlEvent<'a> {
251    fn from(value: &'a str) -> Self {
252        XmlEvent::Characters(Cow::Borrowed(value))
253    }
254}
255
256impl From<String> for XmlEvent<'static> {
257    fn from(value: String) -> Self {
258        XmlEvent::Characters(Cow::Owned(value))
259    }
260}
261
262impl<'a> StartElement<'a> {
263    pub fn attr(mut self, name: &str, value: &str) -> Self {
264        self.attributes.push(Attribute {
265            name: name.to_string(),
266            value: value.to_string(),
267        });
268        self
269    }
270}