Skip to content

Commit 5fc695e

Browse files
authored
Merge pull request #606 from damb/async-indentation
Indentation for `AsyncWrite` trait implementations
2 parents 84b07b4 + 49cd4be commit 5fc695e

File tree

3 files changed

+237
-12
lines changed

3 files changed

+237
-12
lines changed

Changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@
1414

1515
- [#601]: Add `serde_helper` module to the crate root with some useful utility
1616
functions and document using of enum's unit variants as a text content of element.
17+
- [#606]: Implement indentation for `AsyncWrite` trait implementations.
1718

1819
### Bug Fixes
1920

2021
### Misc Changes
2122

2223
[#601]: https://github.com/tafia/quick-xml/pull/601
24+
[#606]: https://github.com/tafia/quick-xml/pull/606
2325

2426

2527
## 0.28.2 -- 2023-04-12

src/writer.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@ impl<W> Writer<W> {
7171
}
7272
}
7373

74+
/// Creates a `Writer` with configured indents from a generic writer.
75+
pub fn new_with_indent(inner: W, indent_char: u8, indent_size: usize) -> Writer<W> {
76+
Writer {
77+
writer: inner,
78+
indent: Some(Indentation::new(indent_char, indent_size)),
79+
}
80+
}
81+
7482
/// Consumes this `Writer`, returning the underlying writer.
7583
pub fn into_inner(self) -> W {
7684
self.writer
@@ -88,14 +96,6 @@ impl<W> Writer<W> {
8896
}
8997

9098
impl<W: Write> Writer<W> {
91-
/// Creates a `Writer` with configured whitespace indents from a generic writer.
92-
pub fn new_with_indent(inner: W, indent_char: u8, indent_size: usize) -> Writer<W> {
93-
Writer {
94-
writer: inner,
95-
indent: Some(Indentation::new(indent_char, indent_size)),
96-
}
97-
}
98-
9999
/// Write a [Byte-Order-Mark] character to the document.
100100
///
101101
/// # Example

src/writer/async_tokio.rs

Lines changed: 227 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,29 @@ use crate::Writer;
77
impl<W: AsyncWrite + Unpin> Writer<W> {
88
/// Writes the given event to the underlying writer. Async version of [`Writer::write_event`].
99
pub async fn write_event_async<'a, E: AsRef<Event<'a>>>(&mut self, event: E) -> Result<()> {
10-
match *event.as_ref() {
11-
Event::Start(ref e) => self.write_wrapped_async(b"<", e, b">").await,
12-
Event::End(ref e) => self.write_wrapped_async(b"</", e, b">").await,
10+
let mut next_should_line_break = true;
11+
let result = match *event.as_ref() {
12+
Event::Start(ref e) => {
13+
let result = self.write_wrapped_async(b"<", e, b">").await;
14+
if let Some(i) = self.indent.as_mut() {
15+
i.grow();
16+
}
17+
result
18+
}
19+
Event::End(ref e) => {
20+
if let Some(i) = self.indent.as_mut() {
21+
i.shrink();
22+
}
23+
self.write_wrapped_async(b"</", e, b">").await
24+
}
1325
Event::Empty(ref e) => self.write_wrapped_async(b"<", e, b"/>").await,
14-
Event::Text(ref e) => self.write_async(e).await,
26+
Event::Text(ref e) => {
27+
next_should_line_break = false;
28+
self.write_async(e).await
29+
}
1530
Event::Comment(ref e) => self.write_wrapped_async(b"<!--", e, b"-->").await,
1631
Event::CData(ref e) => {
32+
next_should_line_break = false;
1733
self.write_async(b"<![CDATA[").await?;
1834
self.write_async(e).await?;
1935
self.write_async(b"]]>").await
@@ -22,7 +38,23 @@ impl<W: AsyncWrite + Unpin> Writer<W> {
2238
Event::PI(ref e) => self.write_wrapped_async(b"<?", e, b"?>").await,
2339
Event::DocType(ref e) => self.write_wrapped_async(b"<!DOCTYPE ", e, b">").await,
2440
Event::Eof => Ok(()),
41+
};
42+
if let Some(i) = self.indent.as_mut() {
43+
i.should_line_break = next_should_line_break;
44+
}
45+
result
46+
}
47+
48+
/// Manually write a newline and indentation at the proper level. Async version of
49+
/// [`Writer::write_indent`].
50+
///
51+
/// This method will do nothing if `Writer` was not constructed with [`Writer::new_with_indent`].
52+
pub async fn write_indent_async(&mut self) -> Result<()> {
53+
if let Some(ref i) = self.indent {
54+
self.writer.write_all(b"\n").await?;
55+
self.writer.write_all(i.current()).await?;
2556
}
57+
Ok(())
2658
}
2759

2860
#[inline]
@@ -37,6 +69,12 @@ impl<W: AsyncWrite + Unpin> Writer<W> {
3769
value: &[u8],
3870
after: &[u8],
3971
) -> Result<()> {
72+
if let Some(ref i) = self.indent {
73+
if i.should_line_break {
74+
self.writer.write_all(b"\n").await?;
75+
self.writer.write_all(i.current()).await?;
76+
}
77+
}
4078
self.write_async(before).await?;
4179
self.write_async(value).await?;
4280
self.write_async(after).await?;
@@ -117,3 +155,188 @@ mod tests {
117155
);
118156
}
119157
}
158+
159+
#[cfg(test)]
160+
mod indentation_async {
161+
use super::*;
162+
use crate::events::*;
163+
use pretty_assertions::assert_eq;
164+
165+
#[tokio::test]
166+
async fn self_closed() {
167+
let mut buffer = Vec::new();
168+
let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
169+
170+
let tag = BytesStart::new("self-closed")
171+
.with_attributes(vec![("attr1", "value1"), ("attr2", "value2")].into_iter());
172+
writer
173+
.write_event_async(Event::Empty(tag))
174+
.await
175+
.expect("write tag failed");
176+
177+
assert_eq!(
178+
std::str::from_utf8(&buffer).unwrap(),
179+
r#"<self-closed attr1="value1" attr2="value2"/>"#
180+
);
181+
}
182+
183+
#[tokio::test]
184+
async fn empty_paired() {
185+
let mut buffer = Vec::new();
186+
let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
187+
188+
let start = BytesStart::new("paired")
189+
.with_attributes(vec![("attr1", "value1"), ("attr2", "value2")].into_iter());
190+
let end = start.to_end();
191+
writer
192+
.write_event_async(Event::Start(start.clone()))
193+
.await
194+
.expect("write start tag failed");
195+
writer
196+
.write_event_async(Event::End(end))
197+
.await
198+
.expect("write end tag failed");
199+
200+
assert_eq!(
201+
std::str::from_utf8(&buffer).unwrap(),
202+
r#"<paired attr1="value1" attr2="value2">
203+
</paired>"#
204+
);
205+
}
206+
207+
#[tokio::test]
208+
async fn paired_with_inner() {
209+
let mut buffer = Vec::new();
210+
let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
211+
212+
let start = BytesStart::new("paired")
213+
.with_attributes(vec![("attr1", "value1"), ("attr2", "value2")].into_iter());
214+
let end = start.to_end();
215+
let inner = BytesStart::new("inner");
216+
217+
writer
218+
.write_event_async(Event::Start(start.clone()))
219+
.await
220+
.expect("write start tag failed");
221+
writer
222+
.write_event_async(Event::Empty(inner))
223+
.await
224+
.expect("write inner tag failed");
225+
writer
226+
.write_event_async(Event::End(end))
227+
.await
228+
.expect("write end tag failed");
229+
230+
assert_eq!(
231+
std::str::from_utf8(&buffer).unwrap(),
232+
r#"<paired attr1="value1" attr2="value2">
233+
<inner/>
234+
</paired>"#
235+
);
236+
}
237+
238+
#[tokio::test]
239+
async fn paired_with_text() {
240+
let mut buffer = Vec::new();
241+
let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
242+
243+
let start = BytesStart::new("paired")
244+
.with_attributes(vec![("attr1", "value1"), ("attr2", "value2")].into_iter());
245+
let end = start.to_end();
246+
let text = BytesText::new("text");
247+
248+
writer
249+
.write_event_async(Event::Start(start.clone()))
250+
.await
251+
.expect("write start tag failed");
252+
writer
253+
.write_event_async(Event::Text(text))
254+
.await
255+
.expect("write text failed");
256+
writer
257+
.write_event_async(Event::End(end))
258+
.await
259+
.expect("write end tag failed");
260+
261+
assert_eq!(
262+
std::str::from_utf8(&buffer).unwrap(),
263+
r#"<paired attr1="value1" attr2="value2">text</paired>"#
264+
);
265+
}
266+
267+
#[tokio::test]
268+
async fn mixed_content() {
269+
let mut buffer = Vec::new();
270+
let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
271+
272+
let start = BytesStart::new("paired")
273+
.with_attributes(vec![("attr1", "value1"), ("attr2", "value2")].into_iter());
274+
let end = start.to_end();
275+
let text = BytesText::new("text");
276+
let inner = BytesStart::new("inner");
277+
278+
writer
279+
.write_event_async(Event::Start(start.clone()))
280+
.await
281+
.expect("write start tag failed");
282+
writer
283+
.write_event_async(Event::Text(text))
284+
.await
285+
.expect("write text failed");
286+
writer
287+
.write_event_async(Event::Empty(inner))
288+
.await
289+
.expect("write inner tag failed");
290+
writer
291+
.write_event_async(Event::End(end))
292+
.await
293+
.expect("write end tag failed");
294+
295+
assert_eq!(
296+
std::str::from_utf8(&buffer).unwrap(),
297+
r#"<paired attr1="value1" attr2="value2">text<inner/>
298+
</paired>"#
299+
);
300+
}
301+
302+
#[tokio::test]
303+
async fn nested() {
304+
let mut buffer = Vec::new();
305+
let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
306+
307+
let start = BytesStart::new("paired")
308+
.with_attributes(vec![("attr1", "value1"), ("attr2", "value2")].into_iter());
309+
let end = start.to_end();
310+
let inner = BytesStart::new("inner");
311+
312+
writer
313+
.write_event_async(Event::Start(start.clone()))
314+
.await
315+
.expect("write start 1 tag failed");
316+
writer
317+
.write_event_async(Event::Start(start.clone()))
318+
.await
319+
.expect("write start 2 tag failed");
320+
writer
321+
.write_event_async(Event::Empty(inner))
322+
.await
323+
.expect("write inner tag failed");
324+
writer
325+
.write_event_async(Event::End(end.clone()))
326+
.await
327+
.expect("write end tag 2 failed");
328+
writer
329+
.write_event_async(Event::End(end))
330+
.await
331+
.expect("write end tag 1 failed");
332+
333+
assert_eq!(
334+
std::str::from_utf8(&buffer).unwrap(),
335+
r#"<paired attr1="value1" attr2="value2">
336+
<paired attr1="value1" attr2="value2">
337+
<inner/>
338+
</paired>
339+
</paired>"#
340+
);
341+
}
342+
}

0 commit comments

Comments
 (0)