quick_xml/
writer.rs

1//! Contains high-level interface for an events-based XML emitter.
2
3use std::io::Write;
4
5use crate::encoding::UTF8_BOM;
6use crate::errors::Result;
7use crate::events::{attributes::Attribute, BytesCData, BytesStart, BytesText, Event};
8
9#[cfg(feature = "async-tokio")]
10mod async_tokio;
11
12/// XML writer. Writes XML [`Event`]s to a [`std::io::Write`] or [`tokio::io::AsyncWrite`] implementor.
13#[cfg(feature = "serialize")]
14use {crate::de::DeError, serde::Serialize};
15
16/// XML writer. Writes XML [`Event`]s to a [`std::io::Write`] implementor.
17///
18/// # Examples
19///
20/// ```
21/// # use pretty_assertions::assert_eq;
22/// use quick_xml::events::{Event, BytesEnd, BytesStart};
23/// use quick_xml::reader::Reader;
24/// use quick_xml::writer::Writer;
25/// use std::io::Cursor;
26///
27/// let xml = r#"<this_tag k1="v1" k2="v2"><child>text</child></this_tag>"#;
28/// let mut reader = Reader::from_str(xml);
29/// reader.trim_text(true);
30/// let mut writer = Writer::new(Cursor::new(Vec::new()));
31/// loop {
32///     match reader.read_event() {
33///         Ok(Event::Start(e)) if e.name().as_ref() == b"this_tag" => {
34///
35///             // crates a new element ... alternatively we could reuse `e` by calling
36///             // `e.into_owned()`
37///             let mut elem = BytesStart::new("my_elem");
38///
39///             // collect existing attributes
40///             elem.extend_attributes(e.attributes().map(|attr| attr.unwrap()));
41///
42///             // copy existing attributes, adds a new my-key="some value" attribute
43///             elem.push_attribute(("my-key", "some value"));
44///
45///             // writes the event to the writer
46///             assert!(writer.write_event(Event::Start(elem)).is_ok());
47///         },
48///         Ok(Event::End(e)) if e.name().as_ref() == b"this_tag" => {
49///             assert!(writer.write_event(Event::End(BytesEnd::new("my_elem"))).is_ok());
50///         },
51///         Ok(Event::Eof) => break,
52///         // we can either move or borrow the event to write, depending on your use-case
53///         Ok(e) => assert!(writer.write_event(e).is_ok()),
54///         Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e),
55///     }
56/// }
57///
58/// let result = writer.into_inner().into_inner();
59/// let expected = r#"<my_elem k1="v1" k2="v2" my-key="some value"><child>text</child></my_elem>"#;
60/// assert_eq!(result, expected.as_bytes());
61/// ```
62#[derive(Clone)]
63pub struct Writer<W> {
64    /// underlying writer
65    writer: W,
66    indent: Option<Indentation>,
67}
68
69impl<W> Writer<W> {
70    /// Creates a `Writer` from a generic writer.
71    pub fn new(inner: W) -> Writer<W> {
72        Writer {
73            writer: inner,
74            indent: None,
75        }
76    }
77
78    /// Creates a `Writer` with configured indents from a generic writer.
79    pub fn new_with_indent(inner: W, indent_char: u8, indent_size: usize) -> Writer<W> {
80        Writer {
81            writer: inner,
82            indent: Some(Indentation::new(indent_char, indent_size)),
83        }
84    }
85
86    /// Consumes this `Writer`, returning the underlying writer.
87    pub fn into_inner(self) -> W {
88        self.writer
89    }
90
91    /// Get a mutable reference to the underlying writer.
92    pub fn get_mut(&mut self) -> &mut W {
93        &mut self.writer
94    }
95
96    /// Get a reference to the underlying writer.
97    pub fn get_ref(&self) -> &W {
98        &self.writer
99    }
100}
101
102impl<W: Write> Writer<W> {
103    /// Write a [Byte-Order-Mark] character to the document.
104    ///
105    /// # Example
106    ///
107    /// ```rust
108    /// # use quick_xml::Result;
109    /// # fn main() -> Result<()> {
110    /// use quick_xml::events::{BytesStart, BytesText, Event};
111    /// use quick_xml::writer::Writer;
112    /// use quick_xml::Error;
113    /// use std::io::Cursor;
114    ///
115    /// let mut buffer = Vec::new();
116    /// let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
117    ///
118    /// writer.write_bom()?;
119    /// writer
120    ///     .create_element("empty")
121    ///     .with_attribute(("attr1", "value1"))
122    ///     .write_empty()
123    ///     .expect("failure");
124    ///
125    /// assert_eq!(
126    ///     std::str::from_utf8(&buffer).unwrap(),
127    ///     "\u{FEFF}<empty attr1=\"value1\"/>"
128    /// );
129    /// # Ok(())
130    /// # }
131    /// ```
132    /// [Byte-Order-Mark]: https://unicode.org/faq/utf_bom.html#BOM
133    pub fn write_bom(&mut self) -> Result<()> {
134        self.write(UTF8_BOM)
135    }
136
137    /// Writes the given event to the underlying writer.
138    pub fn write_event<'a, E: AsRef<Event<'a>>>(&mut self, event: E) -> Result<()> {
139        let mut next_should_line_break = true;
140        let result = match *event.as_ref() {
141            Event::Start(ref e) => {
142                let result = self.write_wrapped(b"<", e, b">");
143                if let Some(i) = self.indent.as_mut() {
144                    i.grow();
145                }
146                result
147            }
148            Event::End(ref e) => {
149                if let Some(i) = self.indent.as_mut() {
150                    i.shrink();
151                }
152                self.write_wrapped(b"</", e, b">")
153            }
154            Event::Empty(ref e) => self.write_wrapped(b"<", e, b"/>"),
155            Event::Text(ref e) => {
156                next_should_line_break = false;
157                self.write(e)
158            }
159            Event::Comment(ref e) => self.write_wrapped(b"<!--", e, b"-->"),
160            Event::CData(ref e) => {
161                next_should_line_break = false;
162                self.write(b"<![CDATA[")?;
163                self.write(e)?;
164                self.write(b"]]>")
165            }
166            Event::Decl(ref e) => self.write_wrapped(b"<?", e, b"?>"),
167            Event::PI(ref e) => self.write_wrapped(b"<?", e, b"?>"),
168            Event::DocType(ref e) => self.write_wrapped(b"<!DOCTYPE ", e, b">"),
169            Event::Eof => Ok(()),
170        };
171        if let Some(i) = self.indent.as_mut() {
172            i.should_line_break = next_should_line_break;
173        }
174        result
175    }
176
177    /// Writes bytes
178    #[inline]
179    pub(crate) fn write(&mut self, value: &[u8]) -> Result<()> {
180        self.writer.write_all(value).map_err(Into::into)
181    }
182
183    #[inline]
184    fn write_wrapped(&mut self, before: &[u8], value: &[u8], after: &[u8]) -> Result<()> {
185        if let Some(ref i) = self.indent {
186            if i.should_line_break {
187                self.writer.write_all(b"\n")?;
188                self.writer.write_all(i.current())?;
189            }
190        }
191        self.write(before)?;
192        self.write(value)?;
193        self.write(after)?;
194        Ok(())
195    }
196
197    /// Manually write a newline and indentation at the proper level.
198    ///
199    /// This can be used when the heuristic to line break and indent after any
200    /// [`Event`] apart from [`Text`] fails such as when a [`Start`] occurs directly
201    /// after [`Text`].
202    ///
203    /// This method will do nothing if `Writer` was not constructed with [`new_with_indent`].
204    ///
205    /// [`Text`]: Event::Text
206    /// [`Start`]: Event::Start
207    /// [`new_with_indent`]: Self::new_with_indent
208    pub fn write_indent(&mut self) -> Result<()> {
209        if let Some(ref i) = self.indent {
210            self.writer.write_all(b"\n")?;
211            self.writer.write_all(i.current())?;
212        }
213        Ok(())
214    }
215
216    /// Provides a simple, high-level API for writing XML elements.
217    ///
218    /// Returns an [`ElementWriter`] that simplifies setting attributes and writing
219    /// content inside the element.
220    ///
221    /// # Example
222    ///
223    /// ```rust
224    /// # use quick_xml::Result;
225    /// # fn main() -> Result<()> {
226    /// use quick_xml::events::{BytesStart, BytesText, Event};
227    /// use quick_xml::writer::Writer;
228    /// use quick_xml::Error;
229    /// use std::io::Cursor;
230    ///
231    /// let mut writer = Writer::new(Cursor::new(Vec::new()));
232    ///
233    /// // writes <tag attr1="value1"/>
234    /// writer.create_element("tag")
235    ///     .with_attribute(("attr1", "value1"))  // chain `with_attribute()` calls to add many attributes
236    ///     .write_empty()?;
237    ///
238    /// // writes <tag attr1="value1" attr2="value2">with some text inside</tag>
239    /// writer.create_element("tag")
240    ///     .with_attributes(vec![("attr1", "value1"), ("attr2", "value2")].into_iter())  // or add attributes from an iterator
241    ///     .write_text_content(BytesText::new("with some text inside"))?;
242    ///
243    /// // writes <tag><fruit quantity="0">apple</fruit><fruit quantity="1">orange</fruit></tag>
244    /// writer.create_element("tag")
245    ///     .write_inner_content(|writer| {
246    ///         let fruits = ["apple", "orange"];
247    ///         for (quant, item) in fruits.iter().enumerate() {
248    ///             writer
249    ///                 .create_element("fruit")
250    ///                 .with_attribute(("quantity", quant.to_string().as_str()))
251    ///                 .write_text_content(BytesText::new(item))?;
252    ///         }
253    ///         Ok(())
254    ///     })?;
255    /// # Ok(())
256    /// # }
257    /// ```
258    #[must_use]
259    pub fn create_element<'a, N>(&'a mut self, name: &'a N) -> ElementWriter<W>
260    where
261        N: 'a + AsRef<str> + ?Sized,
262    {
263        ElementWriter {
264            writer: self,
265            start_tag: BytesStart::new(name.as_ref()),
266        }
267    }
268
269    /// Write an arbitrary serializable type
270    ///
271    /// Note: If you are attempting to write XML in a non-UTF-8 encoding, this may not
272    /// be safe to use. Rust basic types assume UTF-8 encodings.
273    ///
274    /// ```rust
275    /// # use pretty_assertions::assert_eq;
276    /// # use serde::Serialize;
277    /// # use quick_xml::events::{BytesStart, Event};
278    /// # use quick_xml::writer::Writer;
279    /// # use quick_xml::DeError;
280    /// # fn main() -> Result<(), DeError> {
281    /// #[derive(Debug, PartialEq, Serialize)]
282    /// struct MyData {
283    ///     question: String,
284    ///     answer: u32,
285    /// }
286    ///
287    /// let data = MyData {
288    ///     question: "The Ultimate Question of Life, the Universe, and Everything".into(),
289    ///     answer: 42,
290    /// };
291    ///
292    /// let mut buffer = Vec::new();
293    /// let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
294    ///
295    /// let start = BytesStart::new("root");
296    /// let end = start.to_end();
297    ///
298    /// writer.write_event(Event::Start(start.clone()))?;
299    /// writer.write_serializable("my_data", &data)?;
300    /// writer.write_event(Event::End(end))?;
301    ///
302    /// assert_eq!(
303    ///     std::str::from_utf8(&buffer)?,
304    ///     r#"<root>
305    ///     <my_data>
306    ///         <question>The Ultimate Question of Life, the Universe, and Everything</question>
307    ///         <answer>42</answer>
308    ///     </my_data>
309    /// </root>"#
310    /// );
311    /// # Ok(())
312    /// # }
313    /// ```
314    #[cfg(feature = "serialize")]
315    pub fn write_serializable<T: Serialize>(
316        &mut self,
317        tag_name: &str,
318        content: &T,
319    ) -> std::result::Result<(), DeError> {
320        use crate::se::{Indent, Serializer};
321
322        self.write_indent()?;
323        let mut fmt = ToFmtWrite(&mut self.writer);
324        let mut serializer = Serializer::with_root(&mut fmt, Some(tag_name))?;
325
326        if let Some(indent) = &mut self.indent {
327            serializer.set_indent(Indent::Borrow(indent));
328        }
329
330        content.serialize(serializer)?;
331
332        Ok(())
333    }
334}
335
336/// A struct to write an element. Contains methods to add attributes and inner
337/// elements to the element
338pub struct ElementWriter<'a, W: Write> {
339    writer: &'a mut Writer<W>,
340    start_tag: BytesStart<'a>,
341}
342
343impl<'a, W: Write> ElementWriter<'a, W> {
344    /// Adds an attribute to this element.
345    pub fn with_attribute<'b, I>(mut self, attr: I) -> Self
346    where
347        I: Into<Attribute<'b>>,
348    {
349        self.start_tag.push_attribute(attr);
350        self
351    }
352
353    /// Add additional attributes to this element using an iterator.
354    ///
355    /// The yielded items must be convertible to [`Attribute`] using `Into`.
356    pub fn with_attributes<'b, I>(mut self, attributes: I) -> Self
357    where
358        I: IntoIterator,
359        I::Item: Into<Attribute<'b>>,
360    {
361        self.start_tag.extend_attributes(attributes);
362        self
363    }
364
365    /// Write some text inside the current element.
366    pub fn write_text_content(self, text: BytesText) -> Result<&'a mut Writer<W>> {
367        self.writer
368            .write_event(Event::Start(self.start_tag.borrow()))?;
369        self.writer.write_event(Event::Text(text))?;
370        self.writer
371            .write_event(Event::End(self.start_tag.to_end()))?;
372        Ok(self.writer)
373    }
374
375    /// Write a CData event `<![CDATA[...]]>` inside the current element.
376    pub fn write_cdata_content(self, text: BytesCData) -> Result<&'a mut Writer<W>> {
377        self.writer
378            .write_event(Event::Start(self.start_tag.borrow()))?;
379        self.writer.write_event(Event::CData(text))?;
380        self.writer
381            .write_event(Event::End(self.start_tag.to_end()))?;
382        Ok(self.writer)
383    }
384
385    /// Write a processing instruction `<?...?>` inside the current element.
386    pub fn write_pi_content(self, text: BytesText) -> Result<&'a mut Writer<W>> {
387        self.writer
388            .write_event(Event::Start(self.start_tag.borrow()))?;
389        self.writer.write_event(Event::PI(text))?;
390        self.writer
391            .write_event(Event::End(self.start_tag.to_end()))?;
392        Ok(self.writer)
393    }
394
395    /// Write an empty (self-closing) tag.
396    pub fn write_empty(self) -> Result<&'a mut Writer<W>> {
397        self.writer.write_event(Event::Empty(self.start_tag))?;
398        Ok(self.writer)
399    }
400
401    /// Create a new scope for writing XML inside the current element.
402    pub fn write_inner_content<F>(self, closure: F) -> Result<&'a mut Writer<W>>
403    where
404        F: FnOnce(&mut Writer<W>) -> Result<()>,
405    {
406        self.writer
407            .write_event(Event::Start(self.start_tag.borrow()))?;
408        closure(self.writer)?;
409        self.writer
410            .write_event(Event::End(self.start_tag.to_end()))?;
411        Ok(self.writer)
412    }
413}
414#[cfg(feature = "serialize")]
415struct ToFmtWrite<T>(pub T);
416
417#[cfg(feature = "serialize")]
418impl<T> std::fmt::Write for ToFmtWrite<T>
419where
420    T: std::io::Write,
421{
422    fn write_str(&mut self, s: &str) -> std::fmt::Result {
423        self.0.write_all(s.as_bytes()).map_err(|_| std::fmt::Error)
424    }
425}
426
427#[derive(Clone)]
428pub(crate) struct Indentation {
429    /// todo: this is an awkward fit as it has no impact on indentation logic, but it is
430    /// only applicable when an indentation exists. Potentially refactor later
431    should_line_break: bool,
432    /// The character code to be used for indentations (e.g. ` ` or `\t`)
433    indent_char: u8,
434    /// How many instances of the indent character ought to be used for each level of indentation
435    indent_size: usize,
436    /// Used as a cache for the bytes used for indentation
437    indents: Vec<u8>,
438    /// The current amount of indentation
439    current_indent_len: usize,
440}
441
442impl Indentation {
443    pub fn new(indent_char: u8, indent_size: usize) -> Self {
444        Self {
445            should_line_break: false,
446            indent_char,
447            indent_size,
448            indents: vec![indent_char; 128],
449            current_indent_len: 0, // invariant - needs to remain less than indents.len()
450        }
451    }
452
453    /// Increase indentation by one level
454    pub fn grow(&mut self) {
455        self.current_indent_len += self.indent_size;
456        if self.current_indent_len > self.indents.len() {
457            self.indents
458                .resize(self.current_indent_len, self.indent_char);
459        }
460    }
461
462    /// Decrease indentation by one level. Do nothing, if level already zero
463    pub fn shrink(&mut self) {
464        self.current_indent_len = self.current_indent_len.saturating_sub(self.indent_size);
465    }
466
467    /// Returns indent string for current level
468    pub fn current(&self) -> &[u8] {
469        &self.indents[..self.current_indent_len]
470    }
471}
472
473#[cfg(test)]
474mod indentation {
475    use super::*;
476    use crate::events::*;
477    use pretty_assertions::assert_eq;
478
479    #[test]
480    fn self_closed() {
481        let mut buffer = Vec::new();
482        let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
483
484        let tag = BytesStart::new("self-closed")
485            .with_attributes(vec![("attr1", "value1"), ("attr2", "value2")].into_iter());
486        writer
487            .write_event(Event::Empty(tag))
488            .expect("write tag failed");
489
490        assert_eq!(
491            std::str::from_utf8(&buffer).unwrap(),
492            r#"<self-closed attr1="value1" attr2="value2"/>"#
493        );
494    }
495
496    #[test]
497    fn empty_paired() {
498        let mut buffer = Vec::new();
499        let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
500
501        let start = BytesStart::new("paired")
502            .with_attributes(vec![("attr1", "value1"), ("attr2", "value2")].into_iter());
503        let end = start.to_end();
504        writer
505            .write_event(Event::Start(start.clone()))
506            .expect("write start tag failed");
507        writer
508            .write_event(Event::End(end))
509            .expect("write end tag failed");
510
511        assert_eq!(
512            std::str::from_utf8(&buffer).unwrap(),
513            r#"<paired attr1="value1" attr2="value2">
514</paired>"#
515        );
516    }
517
518    #[test]
519    fn paired_with_inner() {
520        let mut buffer = Vec::new();
521        let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
522
523        let start = BytesStart::new("paired")
524            .with_attributes(vec![("attr1", "value1"), ("attr2", "value2")].into_iter());
525        let end = start.to_end();
526        let inner = BytesStart::new("inner");
527
528        writer
529            .write_event(Event::Start(start.clone()))
530            .expect("write start tag failed");
531        writer
532            .write_event(Event::Empty(inner))
533            .expect("write inner tag failed");
534        writer
535            .write_event(Event::End(end))
536            .expect("write end tag failed");
537
538        assert_eq!(
539            std::str::from_utf8(&buffer).unwrap(),
540            r#"<paired attr1="value1" attr2="value2">
541    <inner/>
542</paired>"#
543        );
544    }
545
546    #[test]
547    fn paired_with_text() {
548        let mut buffer = Vec::new();
549        let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
550
551        let start = BytesStart::new("paired")
552            .with_attributes(vec![("attr1", "value1"), ("attr2", "value2")].into_iter());
553        let end = start.to_end();
554        let text = BytesText::new("text");
555
556        writer
557            .write_event(Event::Start(start.clone()))
558            .expect("write start tag failed");
559        writer
560            .write_event(Event::Text(text))
561            .expect("write text failed");
562        writer
563            .write_event(Event::End(end))
564            .expect("write end tag failed");
565
566        assert_eq!(
567            std::str::from_utf8(&buffer).unwrap(),
568            r#"<paired attr1="value1" attr2="value2">text</paired>"#
569        );
570    }
571
572    #[test]
573    fn mixed_content() {
574        let mut buffer = Vec::new();
575        let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
576
577        let start = BytesStart::new("paired")
578            .with_attributes(vec![("attr1", "value1"), ("attr2", "value2")].into_iter());
579        let end = start.to_end();
580        let text = BytesText::new("text");
581        let inner = BytesStart::new("inner");
582
583        writer
584            .write_event(Event::Start(start.clone()))
585            .expect("write start tag failed");
586        writer
587            .write_event(Event::Text(text))
588            .expect("write text failed");
589        writer
590            .write_event(Event::Empty(inner))
591            .expect("write inner tag failed");
592        writer
593            .write_event(Event::End(end))
594            .expect("write end tag failed");
595
596        assert_eq!(
597            std::str::from_utf8(&buffer).unwrap(),
598            r#"<paired attr1="value1" attr2="value2">text<inner/>
599</paired>"#
600        );
601    }
602
603    #[test]
604    fn nested() {
605        let mut buffer = Vec::new();
606        let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
607
608        let start = BytesStart::new("paired")
609            .with_attributes(vec![("attr1", "value1"), ("attr2", "value2")].into_iter());
610        let end = start.to_end();
611        let inner = BytesStart::new("inner");
612
613        writer
614            .write_event(Event::Start(start.clone()))
615            .expect("write start 1 tag failed");
616        writer
617            .write_event(Event::Start(start.clone()))
618            .expect("write start 2 tag failed");
619        writer
620            .write_event(Event::Empty(inner))
621            .expect("write inner tag failed");
622        writer
623            .write_event(Event::End(end.clone()))
624            .expect("write end tag 2 failed");
625        writer
626            .write_event(Event::End(end))
627            .expect("write end tag 1 failed");
628
629        assert_eq!(
630            std::str::from_utf8(&buffer).unwrap(),
631            r#"<paired attr1="value1" attr2="value2">
632    <paired attr1="value1" attr2="value2">
633        <inner/>
634    </paired>
635</paired>"#
636        );
637    }
638
639    #[cfg(feature = "serialize")]
640    #[test]
641    fn serializable() {
642        #[derive(Serialize)]
643        struct Foo {
644            #[serde(rename = "@attribute")]
645            attribute: &'static str,
646
647            element: Bar,
648            list: Vec<&'static str>,
649
650            #[serde(rename = "$text")]
651            text: &'static str,
652
653            val: String,
654        }
655
656        #[derive(Serialize)]
657        struct Bar {
658            baz: usize,
659            bat: usize,
660        }
661
662        let mut buffer = Vec::new();
663        let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
664
665        let content = Foo {
666            attribute: "attribute",
667            element: Bar { baz: 42, bat: 43 },
668            list: vec!["first element", "second element"],
669            text: "text",
670            val: "foo".to_owned(),
671        };
672
673        let start = BytesStart::new("paired")
674            .with_attributes(vec![("attr1", "value1"), ("attr2", "value2")].into_iter());
675        let end = start.to_end();
676
677        writer
678            .write_event(Event::Start(start.clone()))
679            .expect("write start tag failed");
680        writer
681            .write_serializable("foo_element", &content)
682            .expect("write serializable inner contents failed");
683        writer
684            .write_event(Event::End(end))
685            .expect("write end tag failed");
686
687        assert_eq!(
688            std::str::from_utf8(&buffer).unwrap(),
689            r#"<paired attr1="value1" attr2="value2">
690    <foo_element attribute="attribute">
691        <element>
692            <baz>42</baz>
693            <bat>43</bat>
694        </element>
695        <list>first element</list>
696        <list>second element</list>
697        text
698        <val>foo</val>
699    </foo_element>
700</paired>"#
701        );
702    }
703
704    #[test]
705    fn element_writer_empty() {
706        let mut buffer = Vec::new();
707        let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
708
709        writer
710            .create_element("empty")
711            .with_attribute(("attr1", "value1"))
712            .with_attribute(("attr2", "value2"))
713            .write_empty()
714            .expect("failure");
715
716        assert_eq!(
717            std::str::from_utf8(&buffer).unwrap(),
718            r#"<empty attr1="value1" attr2="value2"/>"#
719        );
720    }
721
722    #[test]
723    fn element_writer_text() {
724        let mut buffer = Vec::new();
725        let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
726
727        writer
728            .create_element("paired")
729            .with_attribute(("attr1", "value1"))
730            .with_attribute(("attr2", "value2"))
731            .write_text_content(BytesText::new("text"))
732            .expect("failure");
733
734        assert_eq!(
735            std::str::from_utf8(&buffer).unwrap(),
736            r#"<paired attr1="value1" attr2="value2">text</paired>"#
737        );
738    }
739
740    #[test]
741    fn element_writer_nested() {
742        let mut buffer = Vec::new();
743        let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
744
745        writer
746            .create_element("outer")
747            .with_attribute(("attr1", "value1"))
748            .with_attribute(("attr2", "value2"))
749            .write_inner_content(|writer| {
750                let fruits = ["apple", "orange", "banana"];
751                for (quant, item) in fruits.iter().enumerate() {
752                    writer
753                        .create_element("fruit")
754                        .with_attribute(("quantity", quant.to_string().as_str()))
755                        .write_text_content(BytesText::new(item))?;
756                }
757                writer
758                    .create_element("inner")
759                    .write_inner_content(|writer| {
760                        writer.create_element("empty").write_empty()?;
761                        Ok(())
762                    })?;
763
764                Ok(())
765            })
766            .expect("failure");
767
768        assert_eq!(
769            std::str::from_utf8(&buffer).unwrap(),
770            r#"<outer attr1="value1" attr2="value2">
771    <fruit quantity="0">apple</fruit>
772    <fruit quantity="1">orange</fruit>
773    <fruit quantity="2">banana</fruit>
774    <inner>
775        <empty/>
776    </inner>
777</outer>"#
778        );
779    }
780}