Skip to main content

quick_xml/writer/
async_tokio.rs

1use std::future::Future;
2use std::result::Result as StdResult;
3
4use tokio::io::{AsyncWrite, AsyncWriteExt};
5
6use crate::errors::{Error, Result};
7use crate::events::{BytesCData, BytesPI, BytesText, Event};
8use crate::{ElementWriter, Writer};
9
10impl<W: AsyncWrite + Unpin> Writer<W> {
11    /// Writes the given event to the underlying writer. Async version of [`Writer::write_event`].
12    pub async fn write_event_async<'a, E: Into<Event<'a>>>(&mut self, event: E) -> Result<()> {
13        let mut next_should_line_break = true;
14        let result = match event.into() {
15            Event::Start(e) => {
16                let result = self.write_wrapped_async("<", &e, ">").await;
17                if let Some(i) = self.indent.as_mut() {
18                    i.grow();
19                }
20                result
21            }
22            Event::End(e) => {
23                if let Some(i) = self.indent.as_mut() {
24                    i.shrink();
25                }
26                self.write_wrapped_async("</", &e, ">").await
27            }
28            Event::Empty(e) => self.write_wrapped_async("<", &e, "/>").await,
29            Event::Text(e) => {
30                next_should_line_break = false;
31                self.write_async(e.as_bytes()).await
32            }
33            Event::Comment(e) => self.write_wrapped_async("<!--", &e, "-->").await,
34            Event::CData(e) => {
35                next_should_line_break = false;
36                self.write_async(b"<![CDATA[").await?;
37                self.write_async(e.as_bytes()).await?;
38                self.write_async(b"]]>").await
39            }
40            Event::Decl(e) => self.write_wrapped_async("<?", &e, "?>").await,
41            Event::PI(e) => self.write_wrapped_async("<?", &e, "?>").await,
42            Event::DocType(e) => self.write_wrapped_async("<!DOCTYPE ", &e, ">").await,
43            Event::GeneralRef(e) => self.write_wrapped_async("&", &e, ";").await,
44            Event::Eof => Ok(()),
45        };
46        if let Some(i) = self.indent.as_mut() {
47            i.should_line_break = next_should_line_break;
48        }
49        result
50    }
51
52    /// Manually write a newline and indentation at the proper level. Async version of
53    /// [`Writer::write_indent`].
54    ///
55    /// This method will do nothing if `Writer` was not constructed with [`Writer::new_with_indent`].
56    pub async fn write_indent_async(&mut self) -> Result<()> {
57        if let Some(ref i) = self.indent {
58            self.writer.write_all(b"\n").await?;
59            self.writer.write_all(i.current().as_bytes()).await?;
60        }
61        Ok(())
62    }
63
64    #[inline]
65    async fn write_async(&mut self, value: &[u8]) -> Result<()> {
66        self.writer.write_all(value).await.map_err(Into::into)
67    }
68
69    #[inline]
70    async fn write_wrapped_async(&mut self, before: &str, value: &str, after: &str) -> Result<()> {
71        if let Some(ref i) = self.indent {
72            if i.should_line_break {
73                self.writer.write_all(b"\n").await?;
74                self.writer.write_all(i.current().as_bytes()).await?;
75            }
76        }
77        self.write_async(before.as_bytes()).await?;
78        self.write_async(value.as_bytes()).await?;
79        self.write_async(after.as_bytes()).await?;
80        Ok(())
81    }
82}
83
84impl<'a, W: AsyncWrite + Unpin> ElementWriter<'a, W> {
85    /// Write some text inside the current element.
86    ///
87    /// # Example
88    ///
89    /// ```
90    /// # use quick_xml::writer::Writer;
91    /// # use quick_xml::events::BytesText;
92    /// # use tokio::io::AsyncWriteExt;
93    /// # #[tokio::main(flavor = "current_thread")] async fn main() {
94    /// let mut buffer = Vec::new();
95    /// let mut tokio_buffer = tokio::io::BufWriter::new(&mut buffer);
96    /// let mut writer = Writer::new_with_indent(&mut tokio_buffer, b' ', 4);
97    ///
98    /// writer
99    ///     .create_element("paired")
100    ///     .with_attribute(("attr1", "value1"))
101    ///     .with_attribute(("attr2", "value2"))
102    ///     .write_text_content_async(BytesText::new("text"))
103    ///     .await
104    ///     .expect("cannot write content");
105    ///
106    /// tokio_buffer.flush().await.expect("flush failed");
107    ///
108    /// assert_eq!(
109    ///     std::str::from_utf8(&buffer).unwrap(),
110    ///     r#"<paired attr1="value1" attr2="value2">text</paired>"#
111    /// );
112    /// # }
113    pub async fn write_text_content_async(self, text: BytesText<'_>) -> Result<&'a mut Writer<W>> {
114        self.writer
115            .write_event_async(Event::Start(self.start_tag.borrow()))
116            .await?;
117        self.writer.write_event_async(Event::Text(text)).await?;
118        self.writer
119            .write_event_async(Event::End(self.start_tag.to_end()))
120            .await?;
121        Ok(self.writer)
122    }
123
124    /// Write a CData event `<![CDATA[...]]>` inside the current element.
125    ///
126    /// # Example
127    ///
128    /// ```
129    /// # use quick_xml::writer::Writer;
130    /// # use quick_xml::events::BytesCData;
131    /// # use tokio::io::AsyncWriteExt;
132    /// # #[tokio::main(flavor = "current_thread")] async fn main() {
133    /// let mut buffer = Vec::new();
134    /// let mut tokio_buffer = tokio::io::BufWriter::new(&mut buffer);
135    /// let mut writer = Writer::new_with_indent(&mut tokio_buffer, b' ', 4);
136    ///
137    /// writer
138    ///     .create_element("paired")
139    ///     .with_attribute(("attr1", "value1"))
140    ///     .with_attribute(("attr2", "value2"))
141    ///     .write_cdata_content_async(BytesCData::new("text & content"))
142    ///     .await
143    ///     .expect("cannot write content");
144    ///
145    /// tokio_buffer.flush().await.expect("flush failed");
146    ///
147    /// assert_eq!(
148    ///     std::str::from_utf8(&buffer).unwrap(),
149    ///     r#"<paired attr1="value1" attr2="value2"><![CDATA[text & content]]></paired>"#
150    /// );
151    /// # }
152    pub async fn write_cdata_content_async(
153        self,
154        text: BytesCData<'_>,
155    ) -> Result<&'a mut Writer<W>> {
156        self.writer
157            .write_event_async(Event::Start(self.start_tag.borrow()))
158            .await?;
159        self.writer.write_event_async(Event::CData(text)).await?;
160        self.writer
161            .write_event_async(Event::End(self.start_tag.to_end()))
162            .await?;
163        Ok(self.writer)
164    }
165
166    /// Write a processing instruction `<?...?>` inside the current element.
167    ///
168    /// # Example
169    ///
170    /// ```
171    /// # use quick_xml::writer::Writer;
172    /// # use quick_xml::events::BytesPI;
173    /// # use tokio::io::AsyncWriteExt;
174    /// # #[tokio::main(flavor = "current_thread")] async fn main() {
175    /// let mut buffer = Vec::new();
176    /// let mut tokio_buffer = tokio::io::BufWriter::new(&mut buffer);
177    /// let mut writer = Writer::new_with_indent(&mut tokio_buffer, b' ', 4);
178    ///
179    /// writer
180    ///     .create_element("paired")
181    ///     .with_attribute(("attr1", "value1"))
182    ///     .with_attribute(("attr2", "value2"))
183    ///     .write_pi_content_async(BytesPI::new(r#"xml-stylesheet href="style.css""#))
184    ///     .await
185    ///     .expect("cannot write content");
186    ///
187    /// tokio_buffer.flush().await.expect("flush failed");
188    ///
189    /// assert_eq!(
190    ///     std::str::from_utf8(&buffer).unwrap(),
191    ///     r#"<paired attr1="value1" attr2="value2">
192    ///     <?xml-stylesheet href="style.css"?>
193    /// </paired>"#
194    /// );
195    /// # }
196    pub async fn write_pi_content_async(self, text: BytesPI<'_>) -> Result<&'a mut Writer<W>> {
197        self.writer
198            .write_event_async(Event::Start(self.start_tag.borrow()))
199            .await?;
200        self.writer.write_event_async(Event::PI(text)).await?;
201        self.writer
202            .write_event_async(Event::End(self.start_tag.to_end()))
203            .await?;
204        Ok(self.writer)
205    }
206
207    /// Write an empty (self-closing) tag.
208    ///
209    /// # Example
210    ///
211    /// ```
212    /// # use quick_xml::writer::Writer;
213    /// # use quick_xml::events::BytesText;
214    /// # use tokio::io::AsyncWriteExt;
215    /// # #[tokio::main(flavor = "current_thread")] async fn main() {
216    /// let mut buffer = Vec::new();
217    /// let mut tokio_buffer = tokio::io::BufWriter::new(&mut buffer);
218    /// let mut writer = Writer::new_with_indent(&mut tokio_buffer, b' ', 4);
219    ///
220    /// writer
221    ///     .create_element("empty")
222    ///     .with_attribute(("attr1", "value1"))
223    ///     .with_attribute(("attr2", "value2"))
224    ///     .write_empty_async()
225    ///     .await
226    ///     .expect("cannot write content");
227    ///
228    /// tokio_buffer.flush().await.expect("flush failed");
229    ///
230    /// assert_eq!(
231    ///     std::str::from_utf8(&buffer).unwrap(),
232    ///     r#"<empty attr1="value1" attr2="value2"/>"#
233    /// );
234    /// # }
235    pub async fn write_empty_async(self) -> Result<&'a mut Writer<W>> {
236        self.writer
237            .write_event_async(Event::Empty(self.start_tag))
238            .await?;
239        Ok(self.writer)
240    }
241
242    /// Create a new scope for writing XML inside the current element.
243    ///
244    /// # Example
245    ///
246    /// ```
247    /// # use quick_xml::writer::Writer;
248    /// # use quick_xml::events::BytesText;
249    /// # use tokio::io::AsyncWriteExt;
250    /// use quick_xml::Error;
251    ///
252    /// # #[tokio::main(flavor = "current_thread")] async fn main() {
253    /// let mut buffer = Vec::new();
254    /// let mut tokio_buffer = tokio::io::BufWriter::new(&mut buffer);
255    /// let mut writer = Writer::new_with_indent(&mut tokio_buffer, b' ', 4);
256    ///
257    /// writer
258    ///     .create_element("outer")
259    ///     .with_attributes([("attr1", "value1"), ("attr2", "value2")])
260    ///     // We need to provide error type, because it is not named somewhere explicitly
261    ///     .write_inner_content_async::<_, _, Error>(|writer| async move {
262    ///         let fruits = ["apple", "orange", "banana"];
263    ///         for (quant, item) in fruits.iter().enumerate() {
264    ///             writer
265    ///                 .create_element("fruit")
266    ///                 .with_attributes([("quantity", quant.to_string().as_str())])
267    ///                 .write_text_content_async(BytesText::new(item))
268    ///                 .await?;
269    ///         }
270    ///         writer
271    ///             .create_element("inner")
272    ///             .write_inner_content_async(|writer| async move {
273    ///                 writer.create_element("empty").write_empty_async().await
274    ///             })
275    ///             .await?;
276    ///
277    ///         Ok(writer)
278    ///     })
279    ///     .await
280    ///     .expect("cannot write content");
281    ///
282    /// tokio_buffer.flush().await.expect("flush failed");
283    /// assert_eq!(
284    ///     std::str::from_utf8(&buffer).unwrap(),
285    ///     r#"<outer attr1="value1" attr2="value2">
286    ///     <fruit quantity="0">apple</fruit>
287    ///     <fruit quantity="1">orange</fruit>
288    ///     <fruit quantity="2">banana</fruit>
289    ///     <inner>
290    ///         <empty/>
291    ///     </inner>
292    /// </outer>"#
293    /// );
294    /// # }
295    pub async fn write_inner_content_async<F, Fut, E>(
296        mut self,
297        closure: F,
298    ) -> StdResult<&'a mut Writer<W>, E>
299    where
300        F: FnOnce(&'a mut Writer<W>) -> Fut,
301        Fut: Future<Output = StdResult<&'a mut Writer<W>, E>>,
302        E: From<Error>,
303    {
304        self.writer
305            .write_event_async(Event::Start(self.start_tag.borrow()))
306            .await?;
307        self.writer = closure(self.writer).await?;
308        self.writer
309            .write_event_async(Event::End(self.start_tag.to_end()))
310            .await?;
311        Ok(self.writer)
312    }
313}
314
315#[cfg(test)]
316mod tests {
317    use super::*;
318    use crate::events::*;
319    use pretty_assertions::assert_eq;
320
321    macro_rules! test {
322        ($name: ident, $event: expr, $expected: expr) => {
323            #[tokio::test]
324            async fn $name() {
325                let mut buffer = Vec::new();
326                let mut writer = Writer::new(&mut buffer);
327
328                writer
329                    .write_event_async($event)
330                    .await
331                    .expect("write event failed");
332
333                assert_eq!(std::str::from_utf8(&buffer).unwrap(), $expected,);
334            }
335        };
336    }
337
338    test!(
339        xml_header,
340        Event::Decl(BytesDecl::new("1.0", Some("UTF-8"), Some("no"))),
341        r#"<?xml version="1.0" encoding="UTF-8" standalone="no"?>"#
342    );
343
344    test!(empty_tag, Event::Empty(BytesStart::new("tag")), r#"<tag/>"#);
345
346    test!(
347        comment,
348        Event::Comment(BytesText::new("this is a comment")),
349        r#"<!--this is a comment-->"#
350    );
351
352    test!(
353        cdata,
354        Event::CData(BytesCData::new("this is a cdata")),
355        r#"<![CDATA[this is a cdata]]>"#
356    );
357
358    test!(
359        pi,
360        Event::PI(BytesPI::new("this is a processing instruction")),
361        r#"<?this is a processing instruction?>"#
362    );
363
364    test!(
365        doctype,
366        Event::DocType(BytesText::new("this is a doctype")),
367        r#"<!DOCTYPE this is a doctype>"#
368    );
369
370    #[tokio::test]
371    async fn full_tag() {
372        let mut buffer = Vec::new();
373        let mut writer = Writer::new(&mut buffer);
374
375        let start = Event::Start(BytesStart::new("tag"));
376        let text = Event::Text(BytesText::new("inner text"));
377        let end = Event::End(BytesEnd::new("tag"));
378        for i in [start, text, end] {
379            writer.write_event_async(i).await.expect("write tag failed");
380        }
381
382        assert_eq!(
383            std::str::from_utf8(&buffer).unwrap(),
384            r#"<tag>inner text</tag>"#
385        );
386    }
387}
388
389#[cfg(test)]
390mod indentation_async {
391    use super::*;
392    use crate::events::*;
393    use pretty_assertions::assert_eq;
394
395    #[tokio::test]
396    async fn self_closed() {
397        let mut buffer = Vec::new();
398        let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
399
400        let tag = BytesStart::new("self-closed")
401            .with_attributes(vec![("attr1", "value1"), ("attr2", "value2")].into_iter());
402        writer
403            .write_event_async(Event::Empty(tag))
404            .await
405            .expect("write tag failed");
406
407        assert_eq!(
408            std::str::from_utf8(&buffer).unwrap(),
409            r#"<self-closed attr1="value1" attr2="value2"/>"#
410        );
411    }
412
413    #[tokio::test]
414    async fn empty_paired() {
415        let mut buffer = Vec::new();
416        let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
417
418        let start = BytesStart::new("paired")
419            .with_attributes(vec![("attr1", "value1"), ("attr2", "value2")].into_iter());
420        let end = start.to_end();
421        writer
422            .write_event_async(Event::Start(start.clone()))
423            .await
424            .expect("write start tag failed");
425        writer
426            .write_event_async(Event::End(end))
427            .await
428            .expect("write end tag failed");
429
430        assert_eq!(
431            std::str::from_utf8(&buffer).unwrap(),
432            r#"<paired attr1="value1" attr2="value2">
433</paired>"#
434        );
435    }
436
437    #[tokio::test]
438    async fn paired_with_inner() {
439        let mut buffer = Vec::new();
440        let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
441
442        let start = BytesStart::new("paired")
443            .with_attributes(vec![("attr1", "value1"), ("attr2", "value2")].into_iter());
444        let end = start.to_end();
445        let inner = BytesStart::new("inner");
446
447        writer
448            .write_event_async(Event::Start(start.clone()))
449            .await
450            .expect("write start tag failed");
451        writer
452            .write_event_async(Event::Empty(inner))
453            .await
454            .expect("write inner tag failed");
455        writer
456            .write_event_async(Event::End(end))
457            .await
458            .expect("write end tag failed");
459
460        assert_eq!(
461            std::str::from_utf8(&buffer).unwrap(),
462            r#"<paired attr1="value1" attr2="value2">
463    <inner/>
464</paired>"#
465        );
466    }
467
468    #[tokio::test]
469    async fn paired_with_text() {
470        let mut buffer = Vec::new();
471        let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
472
473        let start = BytesStart::new("paired")
474            .with_attributes(vec![("attr1", "value1"), ("attr2", "value2")].into_iter());
475        let end = start.to_end();
476        let text = BytesText::new("text");
477
478        writer
479            .write_event_async(Event::Start(start.clone()))
480            .await
481            .expect("write start tag failed");
482        writer
483            .write_event_async(Event::Text(text))
484            .await
485            .expect("write text failed");
486        writer
487            .write_event_async(Event::End(end))
488            .await
489            .expect("write end tag failed");
490
491        assert_eq!(
492            std::str::from_utf8(&buffer).unwrap(),
493            r#"<paired attr1="value1" attr2="value2">text</paired>"#
494        );
495    }
496
497    #[tokio::test]
498    async fn mixed_content() {
499        let mut buffer = Vec::new();
500        let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
501
502        let start = BytesStart::new("paired")
503            .with_attributes(vec![("attr1", "value1"), ("attr2", "value2")].into_iter());
504        let end = start.to_end();
505        let text = BytesText::new("text");
506        let inner = BytesStart::new("inner");
507
508        writer
509            .write_event_async(Event::Start(start.clone()))
510            .await
511            .expect("write start tag failed");
512        writer
513            .write_event_async(Event::Text(text))
514            .await
515            .expect("write text failed");
516        writer
517            .write_event_async(Event::Empty(inner))
518            .await
519            .expect("write inner tag failed");
520        writer
521            .write_event_async(Event::End(end))
522            .await
523            .expect("write end tag failed");
524
525        assert_eq!(
526            std::str::from_utf8(&buffer).unwrap(),
527            r#"<paired attr1="value1" attr2="value2">text<inner/>
528</paired>"#
529        );
530    }
531
532    #[tokio::test]
533    async fn nested() {
534        let mut buffer = Vec::new();
535        let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
536
537        let start = BytesStart::new("paired")
538            .with_attributes(vec![("attr1", "value1"), ("attr2", "value2")].into_iter());
539        let end = start.to_end();
540        let inner = BytesStart::new("inner");
541
542        writer
543            .write_event_async(Event::Start(start.clone()))
544            .await
545            .expect("write start 1 tag failed");
546        writer
547            .write_event_async(Event::Start(start.clone()))
548            .await
549            .expect("write start 2 tag failed");
550        writer
551            .write_event_async(Event::Empty(inner))
552            .await
553            .expect("write inner tag failed");
554        writer
555            .write_event_async(Event::End(end.clone()))
556            .await
557            .expect("write end tag 2 failed");
558        writer
559            .write_event_async(Event::End(end))
560            .await
561            .expect("write end tag 1 failed");
562
563        assert_eq!(
564            std::str::from_utf8(&buffer).unwrap(),
565            r#"<paired attr1="value1" attr2="value2">
566    <paired attr1="value1" attr2="value2">
567        <inner/>
568    </paired>
569</paired>"#
570        );
571    }
572}