Skip to main content

oxixml_xml/writer/
async_tokio.rs

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