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}
781