Skip to content

Commit 35be8a4

Browse files
Rémi Labeyriedralley
Rémi Labeyrie
authored andcommitted
Add Writer::write_serializable
Allow serializing individual objects using serde with the raw Writer API closes tafia#610
1 parent 5a536d0 commit 35be8a4

File tree

3 files changed

+158
-12
lines changed

3 files changed

+158
-12
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 `Writer::write_serializable` 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/mod.rs

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -528,8 +528,25 @@ impl<'w, 'r, W: Write> Serializer<'w, 'r, W> {
528528
}
529529

530530
/// Configure indent for a serializer
531-
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));
531+
pub fn indent(
532+
&mut self,
533+
indent_char: char,
534+
indent_size: usize,
535+
) -> &mut Self {
536+
self.ser.indent = Indent::Owned(Indentation::new(
537+
indent_char as u8,
538+
indent_size,
539+
));
540+
self
541+
}
542+
543+
/// Configure the current level of indentation
544+
pub fn set_indent_level(&mut self, level: u8) -> &mut Self {
545+
match &mut self.ser.indent {
546+
Indent::Owned(ref mut i) => i.set_level(level),
547+
Indent::Borrow(i) => i.set_level(level),
548+
_ => (),
549+
};
533550
self
534551
}
535552

src/writer.rs

Lines changed: 131 additions & 7 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
///
@@ -261,6 +265,33 @@ impl<W: Write> Writer<W> {
261265
start_tag: BytesStart::new(name.as_ref()),
262266
}
263267
}
268+
269+
/// Write an arbitrary serializable object
270+
#[cfg(feature = "serialize")]
271+
pub fn write_serializable<T: Serialize>(
272+
&mut self,
273+
tag_name: &str,
274+
content: &T,
275+
) -> std::result::Result<(), DeError> {
276+
use crate::se::Serializer;
277+
278+
self.write_indent()?;
279+
let indent = self.indent.clone();
280+
let mut fmt = ToFmtWrite(self.get_mut());
281+
let mut serializer = Serializer::with_root(&mut fmt, Some(tag_name))?;
282+
283+
if let Some(indent) = indent {
284+
serializer.indent(
285+
indent.indent_char as char,
286+
indent.indent_size,
287+
);
288+
serializer.set_indent_level(1);
289+
}
290+
291+
content.serialize(serializer)?;
292+
293+
Ok(())
294+
}
264295
}
265296

266297
/// A struct to write an element. Contains methods to add attributes and inner
@@ -341,14 +372,31 @@ impl<'a, W: Write> ElementWriter<'a, W> {
341372
Ok(self.writer)
342373
}
343374
}
375+
#[cfg(feature = "serialize")]
376+
struct ToFmtWrite<T>(pub T);
377+
378+
#[cfg(feature = "serialize")]
379+
impl<T> std::fmt::Write for ToFmtWrite<T>
380+
where
381+
T: std::io::Write,
382+
{
383+
fn write_str(&mut self, s: &str) -> std::fmt::Result {
384+
self.0.write_all(s.as_bytes()).map_err(|_| std::fmt::Error)
385+
}
386+
}
344387

345388
#[derive(Clone)]
346389
pub(crate) struct Indentation {
390+
/// todo: does this even belong here? It has no impact on indentation logic.
347391
should_line_break: bool,
392+
/// The character code to be used for indentations (e.g. ` ` or `\t`)
348393
indent_char: u8,
394+
/// How many instances of the indent character ought to be used for each level of indentation
349395
indent_size: usize,
396+
/// Used as a cache for the bytes used for indentation
350397
indents: Vec<u8>,
351-
indents_len: usize,
398+
/// The current amount of indentation
399+
current_indent_len: usize,
352400
}
353401

354402
impl Indentation {
@@ -358,26 +406,37 @@ impl Indentation {
358406
indent_char,
359407
indent_size,
360408
indents: vec![indent_char; 128],
361-
indents_len: 0,
409+
current_indent_len: 0,
410+
}
411+
}
412+
413+
/// Grow or shrink the indentation to the desired level
414+
pub fn set_level(&mut self, level: u8) {
415+
self.current_indent_len = self.indent_size * level as usize;
416+
if self.current_indent_len > self.indents.len() {
417+
self.indents
418+
.resize(self.current_indent_len, self.indent_char);
362419
}
363420
}
364421

365422
/// Increase indentation by one level
366423
pub fn grow(&mut self) {
367-
self.indents_len += self.indent_size;
368-
if self.indents_len > self.indents.len() {
369-
self.indents.resize(self.indents_len, self.indent_char);
424+
self.current_indent_len += self.indent_size;
425+
if self.current_indent_len > self.indents.len() {
426+
self.indents
427+
.resize(self.current_indent_len, self.indent_char);
370428
}
371429
}
372430

373431
/// Decrease indentation by one level. Do nothing, if level already zero
374432
pub fn shrink(&mut self) {
375-
self.indents_len = self.indents_len.saturating_sub(self.indent_size);
433+
self.current_indent_len = self.current_indent_len.saturating_sub(self.indent_size);
376434
}
377435

378436
/// Returns indent string for current level
379437
pub fn current(&self) -> &[u8] {
380-
&self.indents[..self.indents_len]
438+
// todo: might be buggy if initialized > 128 without a call to grow() first
439+
&self.indents[..self.current_indent_len]
381440
}
382441
}
383442

@@ -547,6 +606,71 @@ mod indentation {
547606
);
548607
}
549608

609+
#[cfg(feature = "serialize")]
610+
#[test]
611+
fn serializable() {
612+
#[derive(Serialize)]
613+
struct Foo {
614+
#[serde(rename = "@attribute")]
615+
attribute: &'static str,
616+
617+
element: Bar,
618+
list: Vec<&'static str>,
619+
620+
#[serde(rename = "$text")]
621+
text: &'static str,
622+
623+
val: String,
624+
}
625+
626+
#[derive(Serialize)]
627+
struct Bar {
628+
baz: usize,
629+
bat: usize,
630+
}
631+
632+
let mut buffer = Vec::new();
633+
let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4);
634+
635+
let content = Foo {
636+
attribute: "attribute",
637+
element: Bar { baz: 42, bat: 43 },
638+
list: vec!["first element", "second element"],
639+
text: "text",
640+
val: "foo".to_owned(),
641+
};
642+
643+
let start = BytesStart::new("paired")
644+
.with_attributes(vec![("attr1", "value1"), ("attr2", "value2")].into_iter());
645+
let end = start.to_end();
646+
647+
writer
648+
.write_event(Event::Start(start.clone()))
649+
.expect("write start tag failed");
650+
writer
651+
.write_serializable("foo_element", &content)
652+
.expect("write serializable inner contents failed");
653+
writer
654+
.write_event(Event::End(end))
655+
.expect("write end tag failed");
656+
657+
assert_eq!(
658+
std::str::from_utf8(&buffer).unwrap(),
659+
r#"<paired attr1="value1" attr2="value2">
660+
<foo_element attribute="attribute">
661+
<element>
662+
<baz>42</baz>
663+
<bat>43</bat>
664+
</element>
665+
<list>first element</list>
666+
<list>second element</list>
667+
text
668+
<val>foo</val>
669+
</foo_element>
670+
</paired>"#
671+
);
672+
}
673+
550674
#[test]
551675
fn element_writer_empty() {
552676
let mut buffer = Vec::new();

0 commit comments

Comments
 (0)