Skip to content

Commit 80d3e77

Browse files
Rémi Labeyriedralley
Rémi Labeyrie
authored andcommitted
implement write_serializable_content on element writer
closes tafia#610
1 parent 5a536d0 commit 80d3e77

File tree

5 files changed

+120
-11
lines changed

5 files changed

+120
-11
lines changed

Changelog.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,17 @@
1212

1313
### New Features
1414

15+
- [#609]: Added `write_serializable_content` for serializing arbitrary types using serde
16+
when using the `Writer` API.
17+
1518
### Bug Fixes
1619

1720
### Misc Changes
1821

1922

23+
[#609]: https://github.com/tafia/quick-xml/issues/609
24+
25+
2026
## 0.29.0 -- 2023-06-13
2127

2228
### New Features
@@ -45,7 +51,6 @@
4551
[#606]: https://github.com/tafia/quick-xml/pull/606
4652
[#608]: https://github.com/tafia/quick-xml/issues/608
4753

48-
4954
## 0.28.2 -- 2023-04-12
5055

5156
### New Features
@@ -86,8 +91,8 @@
8691
to trim leading and trailing spaces from text events
8792
- [#565]: Allow deserialize special field names `$value` and `$text` into borrowed
8893
fields when use serde deserializer
89-
- [#568]: Rename `Writter::inner` into `Writter::get_mut`
90-
- [#568]: Add method `Writter::get_ref`
94+
- [#568]: Rename `Writer::inner` into `Writer::get_mut`
95+
- [#568]: Add method `Writer::get_ref`
9196
- [#569]: Rewrite the `Reader::read_event_into_async` as an async fn, making the future `Send` if possible.
9297
- [#571]: Borrow element names (`<element>`) when deserialize with serde.
9398
This change allow to deserialize into `HashMap<&str, T>`, for example

src/se/content.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -670,7 +670,7 @@ pub(super) mod tests {
670670
let ser = ContentSerializer {
671671
writer: &mut buffer,
672672
level: QuoteLevel::Full,
673-
indent: Indent::Owned(Indentation::new(b' ', 2)),
673+
indent: Indent::Owned(Indentation::new(b' ', 2, 0)),
674674
write_indent: false,
675675
};
676676

@@ -690,7 +690,7 @@ pub(super) mod tests {
690690
let ser = ContentSerializer {
691691
writer: &mut buffer,
692692
level: QuoteLevel::Full,
693-
indent: Indent::Owned(Indentation::new(b' ', 2)),
693+
indent: Indent::Owned(Indentation::new(b' ', 2, 0)),
694694
write_indent: false,
695695
};
696696

src/se/element.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1531,7 +1531,7 @@ mod tests {
15311531
ser: ContentSerializer {
15321532
writer: &mut buffer,
15331533
level: QuoteLevel::Full,
1534-
indent: Indent::Owned(Indentation::new(b' ', 2)),
1534+
indent: Indent::Owned(Indentation::new(b' ', 2, 0)),
15351535
write_indent: false,
15361536
},
15371537
key: XmlName("root"),
@@ -1554,7 +1554,7 @@ mod tests {
15541554
ser: ContentSerializer {
15551555
writer: &mut buffer,
15561556
level: QuoteLevel::Full,
1557-
indent: Indent::Owned(Indentation::new(b' ', 2)),
1557+
indent: Indent::Owned(Indentation::new(b' ', 2, 0)),
15581558
write_indent: false,
15591559
},
15601560
key: XmlName("root"),

src/se/mod.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,21 @@ impl<'w, 'r, W: Write> Serializer<'w, 'r, W> {
529529

530530
/// Configure indent for a serializer
531531
pub fn indent(&mut self, indent_char: char, indent_size: usize) -> &mut Self {
532-
self.ser.indent = Indent::Owned(Indentation::new(indent_char as u8, indent_size));
532+
self.indent_with_len(indent_char, indent_size, 0)
533+
}
534+
535+
/// Set initial indent level for a serializer
536+
pub fn indent_with_len(
537+
&mut self,
538+
indent_char: char,
539+
indent_size: usize,
540+
indents_len: usize,
541+
) -> &mut Self {
542+
self.ser.indent = Indent::Owned(Indentation::new(
543+
indent_char as u8,
544+
indent_size,
545+
indents_len,
546+
));
533547
self
534548
}
535549

src/writer.rs

Lines changed: 93 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ use crate::events::{attributes::Attribute, BytesCData, BytesStart, BytesText, Ev
1010
mod async_tokio;
1111

1212
/// XML writer. Writes XML [`Event`]s to a [`std::io::Write`] or [`tokio::io::AsyncWrite`] implementor.
13+
#[cfg(feature = "serialize")]
14+
use {crate::de::DeError, serde::Serialize};
15+
16+
/// XML writer. Writes XML [`Event`]s to a [`std::io::Write`] implementor.
1317
///
1418
/// # Examples
1519
///
@@ -75,7 +79,7 @@ impl<W> Writer<W> {
7579
pub fn new_with_indent(inner: W, indent_char: u8, indent_size: usize) -> Writer<W> {
7680
Writer {
7781
writer: inner,
78-
indent: Some(Indentation::new(indent_char, indent_size)),
82+
indent: Some(Indentation::new(indent_char, indent_size, 0)),
7983
}
8084
}
8185

@@ -340,6 +344,49 @@ impl<'a, W: Write> ElementWriter<'a, W> {
340344
.write_event(Event::End(self.start_tag.to_end()))?;
341345
Ok(self.writer)
342346
}
347+
348+
/// Serialize an arbitrary value inside the current element
349+
#[cfg(feature = "serialize")]
350+
pub fn write_serializable_content<T: Serialize>(
351+
self,
352+
content: T,
353+
) -> std::result::Result<&'a mut Writer<W>, DeError> {
354+
use crate::se::Serializer;
355+
356+
self.writer
357+
.write_event(Event::Start(self.start_tag.borrow()))?;
358+
self.writer.write_indent()?;
359+
360+
let indent = self.writer.indent.clone();
361+
let mut serializer = Serializer::new(ToFmtWrite(self.writer.inner()));
362+
363+
if let Some(indent) = indent {
364+
serializer.indent_with_len(
365+
indent.indent_char as char,
366+
indent.indent_size,
367+
indent.indents_len,
368+
);
369+
}
370+
371+
content.serialize(serializer)?;
372+
373+
self.writer
374+
.write_event(Event::End(self.start_tag.to_end()))?;
375+
Ok(self.writer)
376+
}
377+
}
378+
379+
#[cfg(feature = "serialize")]
380+
struct ToFmtWrite<T>(pub T);
381+
382+
#[cfg(feature = "serialize")]
383+
impl<T> std::fmt::Write for ToFmtWrite<T>
384+
where
385+
T: std::io::Write,
386+
{
387+
fn write_str(&mut self, s: &str) -> std::fmt::Result {
388+
self.0.write_all(s.as_bytes()).map_err(|_| std::fmt::Error)
389+
}
343390
}
344391

345392
#[derive(Clone)]
@@ -352,13 +399,13 @@ pub(crate) struct Indentation {
352399
}
353400

354401
impl Indentation {
355-
pub fn new(indent_char: u8, indent_size: usize) -> Self {
402+
pub fn new(indent_char: u8, indent_size: usize, indents_len: usize) -> Self {
356403
Self {
357404
should_line_break: false,
358405
indent_char,
359406
indent_size,
360407
indents: vec![indent_char; 128],
361-
indents_len: 0,
408+
indents_len,
362409
}
363410
}
364411

@@ -623,4 +670,47 @@ mod indentation {
623670
</outer>"#
624671
);
625672
}
673+
674+
#[cfg(feature = "serialize")]
675+
#[test]
676+
fn element_writer_serialize() {
677+
#[derive(Serialize)]
678+
struct Foo {
679+
bar: Bar,
680+
val: String,
681+
}
682+
683+
#[derive(Serialize)]
684+
struct Bar {
685+
baz: usize,
686+
bat: usize,
687+
}
688+
689+
let mut buffer = Vec::new();
690+
let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
691+
let content = Foo {
692+
bar: Bar { baz: 42, bat: 43 },
693+
val: "foo".to_owned(),
694+
};
695+
696+
writer
697+
.create_element("paired")
698+
.with_attribute(("attr1", "value1"))
699+
.with_attribute(("attr2", "value2"))
700+
.write_serializable_content(content)
701+
.expect("failure");
702+
703+
assert_eq!(
704+
std::str::from_utf8(&buffer).unwrap(),
705+
r#"<paired attr1="value1" attr2="value2">
706+
<Foo>
707+
<bar>
708+
<baz>42</baz>
709+
<bat>43</bat>
710+
</bar>
711+
<val>foo</val>
712+
</Foo>
713+
</paired>"#
714+
);
715+
}
626716
}

0 commit comments

Comments
 (0)