fast_xml/
writer.rs

1//! A module to handle `Writer`
2
3use crate::errors::{Error, Result};
4use crate::events::{attributes::Attribute, BytesCData, BytesStart, BytesText, Event};
5use std::io::Write;
6
7/// XML writer.
8///
9/// Writes XML `Event`s to a `Write` implementor.
10///
11/// # Examples
12///
13/// ```rust
14/// # use pretty_assertions::assert_eq;
15/// use fast_xml::{Reader, Writer};
16/// use fast_xml::events::{Event, BytesEnd, BytesStart};
17/// use std::io::Cursor;
18///
19/// let xml = r#"<this_tag k1="v1" k2="v2"><child>text</child></this_tag>"#;
20/// let mut reader = Reader::from_str(xml);
21/// reader.trim_text(true);
22/// let mut writer = Writer::new(Cursor::new(Vec::new()));
23/// let mut buf = Vec::new();
24/// loop {
25///     match reader.read_event(&mut buf) {
26///         Ok(Event::Start(ref e)) if e.name() == b"this_tag" => {
27///
28///             // crates a new element ... alternatively we could reuse `e` by calling
29///             // `e.into_owned()`
30///             let mut elem = BytesStart::owned(b"my_elem".to_vec(), "my_elem".len());
31///
32///             // collect existing attributes
33///             elem.extend_attributes(e.attributes().map(|attr| attr.unwrap()));
34///
35///             // copy existing attributes, adds a new my-key="some value" attribute
36///             elem.push_attribute(("my-key", "some value"));
37///
38///             // writes the event to the writer
39///             assert!(writer.write_event(Event::Start(elem)).is_ok());
40///         },
41///         Ok(Event::End(ref e)) if e.name() == b"this_tag" => {
42///             assert!(writer.write_event(Event::End(BytesEnd::borrowed(b"my_elem"))).is_ok());
43///         },
44///         Ok(Event::Eof) => break,
45///         // we can either move or borrow the event to write, depending on your use-case
46///         Ok(e) => assert!(writer.write_event(&e).is_ok()),
47///         Err(e) => panic!("{}", e),
48///     }
49///     buf.clear();
50/// }
51///
52/// let result = writer.into_inner().into_inner();
53/// let expected = r#"<my_elem k1="v1" k2="v2" my-key="some value"><child>text</child></my_elem>"#;
54/// assert_eq!(result, expected.as_bytes());
55/// ```
56#[derive(Clone)]
57pub struct Writer<W: Write> {
58    /// underlying writer
59    writer: W,
60    indent: Option<Indentation>,
61}
62
63impl<W: Write> Writer<W> {
64    /// Creates a Writer from a generic Write
65    pub fn new(inner: W) -> Writer<W> {
66        Writer {
67            writer: inner,
68            indent: None,
69        }
70    }
71
72    /// Creates a Writer with configured whitespace indents from a generic Write
73    pub fn new_with_indent(inner: W, indent_char: u8, indent_size: usize) -> Writer<W> {
74        Writer {
75            writer: inner,
76            indent: Some(Indentation::new(indent_char, indent_size)),
77        }
78    }
79
80    /// Consumes this `Writer`, returning the underlying writer.
81    pub fn into_inner(self) -> W {
82        self.writer
83    }
84
85    /// Get inner writer, keeping ownership
86    pub fn inner(&mut self) -> &mut W {
87        &mut self.writer
88    }
89
90    /// Writes the given event to the underlying writer.
91    pub fn write_event<'a, E: AsRef<Event<'a>>>(&mut self, event: E) -> Result<()> {
92        let mut next_should_line_break = true;
93        let result = match *event.as_ref() {
94            Event::Start(ref e) => {
95                let result = self.write_wrapped(b"<", e, b">");
96                if let Some(i) = self.indent.as_mut() {
97                    i.grow();
98                }
99                result
100            }
101            Event::End(ref e) => {
102                if let Some(i) = self.indent.as_mut() {
103                    i.shrink();
104                }
105                self.write_wrapped(b"</", e, b">")
106            }
107            Event::Empty(ref e) => self.write_wrapped(b"<", e, b"/>"),
108            Event::Text(ref e) => {
109                next_should_line_break = false;
110                self.write(&e.escaped())
111            }
112            Event::Comment(ref e) => self.write_wrapped(b"<!--", e, b"-->"),
113            Event::CData(ref e) => {
114                next_should_line_break = false;
115                self.write(b"<![CDATA[")?;
116                self.write(e)?;
117                self.write(b"]]>")
118            }
119            Event::Decl(ref e) => self.write_wrapped(b"<?", e, b"?>"),
120            Event::PI(ref e) => self.write_wrapped(b"<?", e, b"?>"),
121            Event::DocType(ref e) => self.write_wrapped(b"<!DOCTYPE ", e, b">"),
122            Event::Eof => Ok(()),
123        };
124        if let Some(i) = self.indent.as_mut() {
125            i.should_line_break = next_should_line_break;
126        }
127        result
128    }
129
130    /// Writes bytes
131    #[inline]
132    pub fn write(&mut self, value: &[u8]) -> Result<()> {
133        self.writer.write_all(value).map_err(Error::Io)
134    }
135
136    #[inline]
137    fn write_wrapped(&mut self, before: &[u8], value: &[u8], after: &[u8]) -> Result<()> {
138        if let Some(ref i) = self.indent {
139            if i.should_line_break {
140                self.writer.write_all(b"\n").map_err(Error::Io)?;
141                self.writer
142                    .write_all(&i.indents[..i.indents_len])
143                    .map_err(Error::Io)?;
144            }
145        }
146        self.write(before)?;
147        self.write(value)?;
148        self.write(after)?;
149        Ok(())
150    }
151
152    /// Manually write a newline and indentation at the proper level.
153    ///
154    /// This can be used when the heuristic to line break and indent after any [Event] apart
155    /// from [Text] fails such as when a [Start] occurs directly after [Text].
156    /// This method will do nothing if `Writer` was not constructed with `new_with_indent`.
157    ///
158    /// [Event]: events/enum.Event.html
159    /// [Text]: events/enum.Event.html#variant.Text
160    /// [Start]: events/enum.Event.html#variant.Start
161    pub fn write_indent(&mut self) -> Result<()> {
162        if let Some(ref i) = self.indent {
163            self.writer.write_all(b"\n").map_err(Error::Io)?;
164            self.writer
165                .write_all(&i.indents[..i.indents_len])
166                .map_err(Error::Io)?;
167        }
168        Ok(())
169    }
170
171    /// Provides a simple, high-level API for writing XML elements.
172    ///
173    /// Returns an [ElementWriter] that simplifies setting attributes and writing content inside the element.
174    ///
175    /// # Example
176    ///
177    /// ```rust
178    /// # use fast_xml::Result;
179    /// # fn main() -> Result<()> {
180    /// use fast_xml::{Error, Writer};
181    /// use fast_xml::events::{BytesStart, BytesText, Event};
182    /// use std::io::Cursor;
183    ///
184    /// let mut writer = Writer::new(Cursor::new(Vec::new()));
185    ///
186    /// // writes <tag attr1="value1"/>
187    /// writer.create_element("tag")
188    ///     .with_attribute(("attr1", "value1"))  // chain `with_attribute()` calls to add many attributes
189    ///     .write_empty()?;
190    ///
191    /// // writes <tag attr1="value1" attr2="value2">with some text inside</tag>
192    /// writer.create_element("tag")
193    ///     .with_attributes(vec![("attr1", "value1"), ("attr2", "value2")].into_iter())  // or add attributes from an iterator
194    ///     .write_text_content(BytesText::from_plain_str("with some text inside"))?;
195    ///
196    /// // writes <tag><fruit quantity="0">apple</fruit><fruit quantity="1">orange</fruit></tag>
197    /// writer.create_element("tag")
198    ///     .write_inner_content(|writer| {
199    ///         let fruits = ["apple", "orange"];
200    ///         for (quant, item) in fruits.iter().enumerate() {
201    ///             writer
202    ///                 .create_element("fruit")
203    ///                 .with_attribute(("quantity", quant.to_string().as_str()))
204    ///                 .write_text_content(BytesText::from_plain_str(item))?;
205    ///         }
206    ///         Ok(())
207    ///     })?;
208    /// # Ok(())
209    /// # }
210    /// ```
211    #[must_use]
212    pub fn create_element<'a, N>(&'a mut self, name: &'a N) -> ElementWriter<W>
213    where
214        N: 'a + AsRef<[u8]> + ?Sized,
215    {
216        ElementWriter {
217            writer: self,
218            start_tag: BytesStart::borrowed_name(name.as_ref()),
219        }
220    }
221}
222
223/// A struct to write an element. Contains methods to add attributes and inner
224/// elements to the element
225pub struct ElementWriter<'a, W: Write> {
226    writer: &'a mut Writer<W>,
227    start_tag: BytesStart<'a>,
228}
229
230impl<'a, W: Write> ElementWriter<'a, W> {
231    /// Adds an attribute to this element.
232    pub fn with_attribute<'b, I>(mut self, attr: I) -> Self
233    where
234        I: Into<Attribute<'b>>,
235    {
236        self.start_tag.push_attribute(attr);
237        self
238    }
239
240    /// Add additional attributes to this element using an iterator.
241    ///
242    /// The yielded items must be convertible to [`Attribute`] using `Into`.
243    pub fn with_attributes<'b, I>(mut self, attributes: I) -> Self
244    where
245        I: IntoIterator,
246        I::Item: Into<Attribute<'b>>,
247    {
248        self.start_tag.extend_attributes(attributes);
249        self
250    }
251
252    /// Write some text inside the current element.
253    pub fn write_text_content(self, text: BytesText) -> Result<&'a mut Writer<W>> {
254        self.writer
255            .write_event(Event::Start(self.start_tag.to_borrowed()))?;
256        self.writer.write_event(Event::Text(text))?;
257        self.writer
258            .write_event(Event::End(self.start_tag.to_end()))?;
259        Ok(self.writer)
260    }
261
262    /// Write a CData event `<![CDATA[...]]>` inside the current element.
263    pub fn write_cdata_content(self, text: BytesCData) -> Result<&'a mut Writer<W>> {
264        self.writer
265            .write_event(Event::Start(self.start_tag.to_borrowed()))?;
266        self.writer.write_event(Event::CData(text))?;
267        self.writer
268            .write_event(Event::End(self.start_tag.to_end()))?;
269        Ok(self.writer)
270    }
271
272    /// Write a processing instruction `<?...?>` inside the current element.
273    pub fn write_pi_content(self, text: BytesText) -> Result<&'a mut Writer<W>> {
274        self.writer
275            .write_event(Event::Start(self.start_tag.to_borrowed()))?;
276        self.writer.write_event(Event::PI(text))?;
277        self.writer
278            .write_event(Event::End(self.start_tag.to_end()))?;
279        Ok(self.writer)
280    }
281
282    /// Write an empty (self-closing) tag.
283    pub fn write_empty(self) -> Result<&'a mut Writer<W>> {
284        self.writer.write_event(Event::Empty(self.start_tag))?;
285        Ok(self.writer)
286    }
287
288    /// Create a new scope for writing XML inside the current element.
289    pub fn write_inner_content<F>(mut self, closure: F) -> Result<&'a mut Writer<W>>
290    where
291        F: Fn(&mut Writer<W>) -> Result<()>,
292    {
293        self.writer
294            .write_event(Event::Start(self.start_tag.to_borrowed()))?;
295        closure(&mut self.writer)?;
296        self.writer
297            .write_event(Event::End(self.start_tag.to_end()))?;
298        Ok(self.writer)
299    }
300}
301
302#[derive(Clone)]
303struct Indentation {
304    should_line_break: bool,
305    indent_char: u8,
306    indent_size: usize,
307    indents: Vec<u8>,
308    indents_len: usize,
309}
310
311impl Indentation {
312    fn new(indent_char: u8, indent_size: usize) -> Indentation {
313        Indentation {
314            should_line_break: false,
315            indent_char,
316            indent_size,
317            indents: vec![indent_char; 128],
318            indents_len: 0,
319        }
320    }
321
322    fn grow(&mut self) {
323        self.indents_len += self.indent_size;
324        if self.indents_len > self.indents.len() {
325            self.indents.resize(self.indents_len, self.indent_char);
326        }
327    }
328
329    fn shrink(&mut self) {
330        self.indents_len = match self.indents_len.checked_sub(self.indent_size) {
331            Some(result) => result,
332            None => 0,
333        };
334    }
335}
336
337#[cfg(test)]
338mod indentation {
339    use super::*;
340    use crate::events::*;
341    use pretty_assertions::assert_eq;
342
343    #[test]
344    fn self_closed() {
345        let mut buffer = Vec::new();
346        let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
347
348        let tag = BytesStart::borrowed_name(b"self-closed")
349            .with_attributes(vec![("attr1", "value1"), ("attr2", "value2")].into_iter());
350        writer
351            .write_event(Event::Empty(tag))
352            .expect("write tag failed");
353
354        assert_eq!(
355            std::str::from_utf8(&buffer).unwrap(),
356            r#"<self-closed attr1="value1" attr2="value2"/>"#
357        );
358    }
359
360    #[test]
361    fn empty_paired() {
362        let mut buffer = Vec::new();
363        let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
364
365        let name = b"paired";
366        let start = BytesStart::borrowed_name(name)
367            .with_attributes(vec![("attr1", "value1"), ("attr2", "value2")].into_iter());
368        let end = BytesEnd::borrowed(name);
369        writer
370            .write_event(Event::Start(start))
371            .expect("write start tag failed");
372        writer
373            .write_event(Event::End(end))
374            .expect("write end tag failed");
375
376        assert_eq!(
377            std::str::from_utf8(&buffer).unwrap(),
378            r#"<paired attr1="value1" attr2="value2">
379</paired>"#
380        );
381    }
382
383    #[test]
384    fn paired_with_inner() {
385        let mut buffer = Vec::new();
386        let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
387
388        let name = b"paired";
389        let start = BytesStart::borrowed_name(name)
390            .with_attributes(vec![("attr1", "value1"), ("attr2", "value2")].into_iter());
391        let end = BytesEnd::borrowed(name);
392        let inner = BytesStart::borrowed_name(b"inner");
393
394        writer
395            .write_event(Event::Start(start))
396            .expect("write start tag failed");
397        writer
398            .write_event(Event::Empty(inner))
399            .expect("write inner tag failed");
400        writer
401            .write_event(Event::End(end))
402            .expect("write end tag failed");
403
404        assert_eq!(
405            std::str::from_utf8(&buffer).unwrap(),
406            r#"<paired attr1="value1" attr2="value2">
407    <inner/>
408</paired>"#
409        );
410    }
411
412    #[test]
413    fn paired_with_text() {
414        let mut buffer = Vec::new();
415        let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
416
417        let name = b"paired";
418        let start = BytesStart::borrowed_name(name)
419            .with_attributes(vec![("attr1", "value1"), ("attr2", "value2")].into_iter());
420        let end = BytesEnd::borrowed(name);
421        let text = BytesText::from_plain(b"text");
422
423        writer
424            .write_event(Event::Start(start))
425            .expect("write start tag failed");
426        writer
427            .write_event(Event::Text(text))
428            .expect("write text failed");
429        writer
430            .write_event(Event::End(end))
431            .expect("write end tag failed");
432
433        assert_eq!(
434            std::str::from_utf8(&buffer).unwrap(),
435            r#"<paired attr1="value1" attr2="value2">text</paired>"#
436        );
437    }
438
439    #[test]
440    fn mixed_content() {
441        let mut buffer = Vec::new();
442        let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
443
444        let name = b"paired";
445        let start = BytesStart::borrowed_name(name)
446            .with_attributes(vec![("attr1", "value1"), ("attr2", "value2")].into_iter());
447        let end = BytesEnd::borrowed(name);
448        let text = BytesText::from_plain(b"text");
449        let inner = BytesStart::borrowed_name(b"inner");
450
451        writer
452            .write_event(Event::Start(start))
453            .expect("write start tag failed");
454        writer
455            .write_event(Event::Text(text))
456            .expect("write text failed");
457        writer
458            .write_event(Event::Empty(inner))
459            .expect("write inner tag failed");
460        writer
461            .write_event(Event::End(end))
462            .expect("write end tag failed");
463
464        assert_eq!(
465            std::str::from_utf8(&buffer).unwrap(),
466            r#"<paired attr1="value1" attr2="value2">text<inner/>
467</paired>"#
468        );
469    }
470
471    #[test]
472    fn nested() {
473        let mut buffer = Vec::new();
474        let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
475
476        let name = b"paired";
477        let start = BytesStart::borrowed_name(name)
478            .with_attributes(vec![("attr1", "value1"), ("attr2", "value2")].into_iter());
479        let end = BytesEnd::borrowed(name);
480        let inner = BytesStart::borrowed_name(b"inner");
481
482        writer
483            .write_event(Event::Start(start.clone()))
484            .expect("write start 1 tag failed");
485        writer
486            .write_event(Event::Start(start))
487            .expect("write start 2 tag failed");
488        writer
489            .write_event(Event::Empty(inner))
490            .expect("write inner tag failed");
491        writer
492            .write_event(Event::End(end.clone()))
493            .expect("write end tag 2 failed");
494        writer
495            .write_event(Event::End(end))
496            .expect("write end tag 1 failed");
497
498        assert_eq!(
499            std::str::from_utf8(&buffer).unwrap(),
500            r#"<paired attr1="value1" attr2="value2">
501    <paired attr1="value1" attr2="value2">
502        <inner/>
503    </paired>
504</paired>"#
505        );
506    }
507    #[test]
508    fn element_writer_empty() {
509        let mut buffer = Vec::new();
510        let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
511
512        writer
513            .create_element(b"empty")
514            .with_attribute(("attr1", "value1"))
515            .with_attribute(("attr2", "value2"))
516            .write_empty()
517            .expect("failure");
518
519        assert_eq!(
520            std::str::from_utf8(&buffer).unwrap(),
521            r#"<empty attr1="value1" attr2="value2"/>"#
522        );
523    }
524
525    #[test]
526    fn element_writer_text() {
527        let mut buffer = Vec::new();
528        let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
529
530        writer
531            .create_element("paired")
532            .with_attribute(("attr1", "value1"))
533            .with_attribute(("attr2", "value2"))
534            .write_text_content(BytesText::from_plain_str("text"))
535            .expect("failure");
536
537        assert_eq!(
538            std::str::from_utf8(&buffer).unwrap(),
539            r#"<paired attr1="value1" attr2="value2">text</paired>"#
540        );
541    }
542
543    #[test]
544    fn element_writer_nested() {
545        let mut buffer = Vec::new();
546        let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
547
548        writer
549            .create_element("outer")
550            .with_attribute(("attr1", "value1"))
551            .with_attribute(("attr2", "value2"))
552            .write_inner_content(|writer| {
553                let fruits = ["apple", "orange", "banana"];
554                for (quant, item) in fruits.iter().enumerate() {
555                    writer
556                        .create_element("fruit")
557                        .with_attribute(("quantity", quant.to_string().as_str()))
558                        .write_text_content(BytesText::from_plain_str(item))?;
559                }
560                writer
561                    .create_element("inner")
562                    .write_inner_content(|writer| {
563                        writer.create_element("empty").write_empty()?;
564                        Ok(())
565                    })?;
566
567                Ok(())
568            })
569            .expect("failure");
570
571        assert_eq!(
572            std::str::from_utf8(&buffer).unwrap(),
573            r#"<outer attr1="value1" attr2="value2">
574    <fruit quantity="0">apple</fruit>
575    <fruit quantity="1">orange</fruit>
576    <fruit quantity="2">banana</fruit>
577    <inner>
578        <empty/>
579    </inner>
580</outer>"#
581        );
582    }
583}