Skip to main content

xml/writer/
emitter.rs

1use std::error::Error;
2use std::io::prelude::*;
3use std::{fmt, io, result};
4
5use crate::attribute::Attribute;
6use crate::common::XmlVersion;
7use crate::escape::{AttributeEscapes, Escaped, PcDataEscapes};
8use crate::name::{Name, OwnedName};
9use crate::namespace::{NamespaceStack, NS_EMPTY_URI, NS_NO_PREFIX, NS_XMLNS_PREFIX, NS_XML_PREFIX};
10
11use crate::writer::config::EmitterConfig;
12
13/// An error which may be returned by `XmlWriter` when writing XML events.
14#[derive(Debug)]
15#[non_exhaustive]
16pub enum EmitterError {
17    /// An I/O error occured in the underlying `Write` instance.
18    Io(io::Error),
19
20    /// Document declaration has already been written to the output stream.
21    DocumentStartAlreadyEmitted,
22
23    /// The name of the last opening element is not available.
24    LastElementNameNotAvailable,
25
26    /// The name of the last opening element is not equal to the name of the provided
27    /// closing element.
28    EndElementNameIsNotEqualToLastStartElementName,
29
30    /// End element name is not specified when it is needed, for example, when automatic
31    /// closing is not enabled in configuration.
32    EndElementNameIsNotSpecified,
33}
34
35impl Clone for EmitterError {
36    #[cold]
37    fn clone(&self) -> Self {
38        match self {
39            Self::Io(io_error) => Self::Io(io::Error::new(io_error.kind(), io_error.to_string())),
40            Self::DocumentStartAlreadyEmitted => Self::DocumentStartAlreadyEmitted,
41            Self::LastElementNameNotAvailable => Self::LastElementNameNotAvailable,
42            Self::EndElementNameIsNotEqualToLastStartElementName => Self::EndElementNameIsNotEqualToLastStartElementName,
43            Self::EndElementNameIsNotSpecified => Self::EndElementNameIsNotSpecified,
44        }
45    }
46}
47
48impl From<io::Error> for EmitterError {
49    #[cold]
50    fn from(err: io::Error) -> Self {
51        Self::Io(err)
52    }
53}
54
55impl fmt::Display for EmitterError {
56    #[cold]
57    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58        f.write_str("emitter error: ")?;
59        match self {
60            Self::Io(e) => write!(f, "I/O error: {e}"),
61            Self::DocumentStartAlreadyEmitted => f.write_str("document start event has already been emitted"),
62            Self::LastElementNameNotAvailable => f.write_str("last element name is not available"),
63            Self::EndElementNameIsNotEqualToLastStartElementName => f.write_str("end element name is not equal to last start element name"),
64            Self::EndElementNameIsNotSpecified => f.write_str("end element name is not specified and can't be inferred"),
65        }
66    }
67}
68
69impl Error for EmitterError {
70    fn source(&self) -> Option<&(dyn Error + 'static)> {
71        match self {
72            Self::Io(e) => e.source(),
73            _ => None,
74        }
75    }
76}
77
78/// A result type yielded by `XmlWriter`.
79pub type Result<T, E = EmitterError> = result::Result<T, E>;
80
81// TODO: split into a low-level fast writer without any checks and formatting logic and a
82// high-level indenting validating writer
83pub struct Emitter {
84    config: EmitterConfig,
85
86    nst: NamespaceStack,
87
88    indent_level: usize,
89    indent_stack: Vec<IndentFlags>,
90
91    element_names: Vec<OwnedName>,
92
93    start_document_emitted: bool,
94    just_wrote_start_element: bool,
95}
96
97impl Emitter {
98    pub fn new(config: EmitterConfig) -> Self {
99        let mut indent_stack = Vec::with_capacity(16);
100        indent_stack.push(IndentFlags::WroteNothing);
101
102        Self {
103            config,
104
105            nst: NamespaceStack::empty(),
106
107            indent_level: 0,
108            indent_stack,
109
110            element_names: Vec::new(),
111
112            start_document_emitted: false,
113            just_wrote_start_element: false,
114        }
115    }
116}
117
118#[derive(Copy, Clone, Eq, PartialEq, Debug)]
119enum IndentFlags {
120    WroteNothing,
121    WroteMarkup,
122    WroteText,
123}
124
125impl Emitter {
126    /// Returns the current state of namespaces.
127    #[inline]
128    pub fn namespace_stack_mut(&mut self) -> &mut NamespaceStack {
129        &mut self.nst
130    }
131
132    #[inline]
133    fn wrote_text(&self) -> bool {
134        self.indent_stack.last().is_some_and(|&e| e == IndentFlags::WroteText)
135    }
136
137    #[inline]
138    fn wrote_markup(&self) -> bool {
139        self.indent_stack.last().is_some_and(|&e| e == IndentFlags::WroteMarkup)
140    }
141
142    #[inline]
143    fn set_wrote_text(&mut self) {
144        if let Some(e) = self.indent_stack.last_mut() {
145            *e = IndentFlags::WroteText;
146        }
147    }
148
149    #[inline]
150    fn set_wrote_markup(&mut self) {
151        if let Some(e) = self.indent_stack.last_mut() {
152            *e = IndentFlags::WroteMarkup;
153        }
154    }
155
156    fn write_newline<W: Write>(&self, target: &mut W, level: usize) -> Result<()> {
157        target.write_all(self.config.line_separator.as_bytes())?;
158        for _ in 0..level {
159            target.write_all(self.config.indent_string.as_bytes())?;
160        }
161        Ok(())
162    }
163
164    fn before_markup<W: Write>(&mut self, target: &mut W) -> Result<()> {
165        if self.config.perform_indent && !self.wrote_text() &&
166           (self.indent_level > 0 || self.wrote_markup()) {
167            let indent_level = self.indent_level;
168            self.write_newline(target, indent_level)?;
169            if self.indent_level > 0 && !self.config.indent_string.is_empty() {
170                self.after_markup();
171            }
172        }
173        Ok(())
174    }
175
176    fn after_markup(&mut self) {
177        self.set_wrote_markup();
178    }
179
180    fn before_start_element<W: Write>(&mut self, target: &mut W) -> Result<()> {
181        self.before_markup(target)?;
182        self.indent_stack.push(IndentFlags::WroteNothing);
183        Ok(())
184    }
185
186    fn after_start_element(&mut self) {
187        self.after_markup();
188        self.indent_level += 1;
189    }
190
191    fn before_end_element<W: Write>(&self, target: &mut W) -> Result<()> {
192        if self.config.perform_indent && self.indent_level > 0 && self.wrote_markup() &&
193           !self.wrote_text() {
194            let indent_level = self.indent_level;
195            self.write_newline(target, indent_level - 1)
196        } else {
197            Ok(())
198        }
199    }
200
201    fn after_end_element(&mut self) {
202        if self.indent_level > 0 {
203            self.indent_level -= 1;
204            self.indent_stack.pop();
205        }
206        self.set_wrote_markup();
207    }
208
209    fn after_text(&mut self) {
210        self.set_wrote_text();
211    }
212
213    pub fn emit_start_document<W: Write>(&mut self, target: &mut W,
214                                         version: XmlVersion,
215                                         encoding: &str,
216                                         standalone: Option<bool>) -> Result<()> {
217        if self.start_document_emitted {
218            return Err(EmitterError::DocumentStartAlreadyEmitted);
219        }
220        self.start_document_emitted = true;
221
222        self.before_markup(target)?;
223        let result = {
224            let mut write = move || {
225                write!(target, "<?xml version=\"{version}\" encoding=\"{encoding}\"")?;
226
227                if let Some(standalone) = standalone {
228                    write!(target, " standalone=\"{}\"", if standalone { "yes" } else { "no" })?;
229                }
230
231                write!(target, "?>")?;
232
233                Ok(())
234            };
235            write()
236        };
237        self.after_markup();
238
239        result
240    }
241
242    fn check_document_started<W: Write>(&mut self, target: &mut W) -> Result<()> {
243        if !self.start_document_emitted && self.config.write_document_declaration {
244            self.emit_start_document(target, XmlVersion::Version10, "UTF-8", None)
245        } else {
246            Ok(())
247        }
248    }
249
250    fn fix_non_empty_element<W: Write>(&mut self, target: &mut W) -> Result<()> {
251        if self.config.normalize_empty_elements && self.just_wrote_start_element {
252            self.just_wrote_start_element = false;
253            target.write_all(b">").map_err(From::from)
254        } else {
255            Ok(())
256        }
257    }
258
259    pub fn emit_processing_instruction<W: Write>(&mut self,
260                                                 target: &mut W,
261                                                 name: &str,
262                                                 data: Option<&str>) -> Result<()> {
263        self.check_document_started(target)?;
264        self.fix_non_empty_element(target)?;
265
266        self.before_markup(target)?;
267
268        let result = {
269            let mut write = move || {
270                write!(target, "<?{name}")?;
271
272                if let Some(data) = data {
273                    write!(target, " {data}")?;
274                }
275
276                write!(target, "?>")?;
277
278                Ok(())
279            };
280            write()
281        };
282
283        self.after_markup();
284
285        result
286    }
287
288    #[track_caller]
289    fn emit_start_element_initial<W>(&mut self, target: &mut W,
290                                     name: Name<'_>,
291                                     attributes: &[Attribute<'_>]) -> Result<()>
292        where W: Write
293    {
294        self.check_document_started(target)?;
295        self.fix_non_empty_element(target)?;
296        self.before_start_element(target)?;
297        write!(target, "<{}", name.repr_display())?;
298        self.emit_current_namespace_attributes(target)?;
299        self.emit_attributes(target, attributes)?;
300        self.after_start_element();
301        Ok(())
302    }
303
304    #[track_caller]
305    pub fn emit_start_element<W>(&mut self, target: &mut W,
306                                 name: Name<'_>,
307                                 attributes: &[Attribute<'_>]) -> Result<()>
308        where W: Write
309    {
310        if self.config.keep_element_names_stack {
311            self.element_names.push(name.to_owned());
312        }
313
314        self.emit_start_element_initial(target, name, attributes)?;
315        self.just_wrote_start_element = true;
316
317        if !self.config.normalize_empty_elements {
318            write!(target, ">")?;
319        }
320
321        Ok(())
322    }
323
324    #[track_caller]
325    pub fn emit_current_namespace_attributes<W>(&self, target: &mut W) -> Result<()>
326        where W: Write
327    {
328        for (prefix, uri) in self.nst.peek() {
329            match prefix {
330                // internal namespaces are not emitted
331                NS_XMLNS_PREFIX | NS_XML_PREFIX => Ok(()),
332                //// there is already a namespace binding with this prefix in scope
333                //prefix if self.nst.get(prefix) == Some(uri) => Ok(()),
334                // emit xmlns only if it is overridden
335                NS_NO_PREFIX => if uri == NS_EMPTY_URI {
336                    Ok(())
337                } else {
338                    write!(target, " xmlns=\"{uri}\"")
339                },
340                // everything else
341                prefix => write!(target, " xmlns:{prefix}=\"{uri}\""),
342            }?;
343        }
344        Ok(())
345    }
346
347    pub fn emit_attributes<W: Write>(&self, target: &mut W, attributes: &[Attribute<'_>]) -> Result<()> {
348        for attr in attributes {
349            write!(target, " {}=\"", attr.name.repr_display())?;
350            if self.config.perform_escaping {
351                write!(target, "{}", Escaped::<AttributeEscapes>::new(attr.value))?;
352            } else {
353                write!(target, "{}", attr.value)?;
354            }
355            write!(target, "\"")?;
356        }
357        Ok(())
358    }
359
360    pub fn emit_end_element<W: Write>(&mut self, target: &mut W, name: Option<Name<'_>>) -> Result<()> {
361        let owned_name = if self.config.keep_element_names_stack {
362            Some(self.element_names.pop().ok_or(EmitterError::LastElementNameNotAvailable)?)
363        } else {
364            None
365        };
366
367        // Check that last started element name equals to the provided name, if there are both
368        if let Some(ref last_name) = owned_name {
369            if let Some(ref name) = name {
370                if last_name.borrow() != *name {
371                    return Err(EmitterError::EndElementNameIsNotEqualToLastStartElementName);
372                }
373            }
374        }
375
376        if let Some(name) = owned_name.as_ref().map(|n| n.borrow()).or(name) {
377            if self.config.normalize_empty_elements && self.just_wrote_start_element {
378                self.just_wrote_start_element = false;
379                let termination = if self.config.pad_self_closing { " />" } else { "/>" };
380                let result = target.write_all(termination.as_bytes()).map_err(From::from);
381                self.after_end_element();
382                result
383            } else {
384                self.just_wrote_start_element = false;
385
386                self.before_end_element(target)?;
387                let result = write!(target, "</{}>", name.repr_display()).map_err(From::from);
388                self.after_end_element();
389
390                result
391            }
392        } else {
393            Err(EmitterError::EndElementNameIsNotSpecified)
394        }
395    }
396
397    pub fn emit_cdata<W: Write>(&mut self, target: &mut W, content: &str) -> Result<()> {
398        self.fix_non_empty_element(target)?;
399        if self.config.cdata_to_characters {
400            self.emit_characters(target, content)
401        } else {
402            target.write_all(b"<![CDATA[")?;
403
404            for chunk in content.split_inclusive("]]>") {
405                let chunk_safe = chunk.strip_suffix("]]>");
406                let emit_escaped = chunk_safe.is_some();
407
408                target.write_all(chunk_safe.unwrap_or(chunk).as_bytes())?;
409                if emit_escaped {
410                    target.write_all(b"]]]]><![CDATA[>")?;
411                }
412            }
413
414            target.write_all(b"]]>")?;
415            self.after_text();
416
417            Ok(())
418        }
419    }
420
421    pub fn emit_characters<W: Write>(&mut self, target: &mut W, content: &str) -> Result<()> {
422        self.check_document_started(target)?;
423        self.fix_non_empty_element(target)?;
424
425        if self.config.perform_escaping {
426            write!(target, "{}", Escaped::<PcDataEscapes>::new(content))?;
427        } else {
428            target.write_all(content.as_bytes())?;
429        }
430
431        self.after_text();
432        Ok(())
433    }
434
435    pub fn emit_raw_characters<W: Write>(&mut self, target: &mut W, content: &str) -> Result<()> {
436        self.check_document_started(target)?;
437        self.fix_non_empty_element(target)?;
438
439        target.write_all(content.as_bytes())?;
440
441        self.after_text();
442        Ok(())
443    }
444
445    pub fn emit_comment<W: Write>(&mut self, target: &mut W, content: &str) -> Result<()> {
446        self.fix_non_empty_element(target)?;
447
448        // TODO: add escaping dashes at the end of the comment
449
450        let autopad_comments = self.config.autopad_comments;
451        let write = move |target: &mut W| -> Result<()> {
452            target.write_all(b"<!--")?;
453
454            if autopad_comments && !content.starts_with(char::is_whitespace) {
455                target.write_all(b" ")?;
456            }
457
458            for chunk in content.split_inclusive("--") {
459                let chunk_safe = chunk.strip_suffix("--");
460                let emit_escaped = chunk_safe.is_some();
461
462                target.write_all(chunk_safe.unwrap_or(chunk).as_bytes())?;
463                if emit_escaped {
464                    target.write_all(b"- ")?;
465                }
466            }
467
468            if autopad_comments && !content.ends_with(char::is_whitespace) {
469                target.write_all(b" ")?;
470            }
471
472            target.write_all(b"-->")?;
473
474            Ok(())
475        };
476
477        self.before_markup(target)?;
478        let result = write(target);
479        self.after_markup();
480
481        result
482    }
483}