From 12fee251845812b32a382f91d8d8d4fe59914342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Labeyrie?= Date: Sun, 13 Nov 2022 03:40:02 +0100 Subject: [PATCH 1/4] implement `write_serializable_content` on element writer --- src/errors.rs | 3 ++ src/se/content.rs | 4 +- src/se/element.rs | 4 +- src/se/mod.rs | 12 +++++- src/writer.rs | 96 +++++++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 111 insertions(+), 8 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index 1f60c0b5..978e5b60 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -39,6 +39,8 @@ pub enum Error { EscapeError(EscapeError), /// Specified namespace prefix is unknown, cannot resolve namespace for it UnknownPrefix(Vec), + /// Serialization error + NonSerializable(String), } impl From for Error { @@ -112,6 +114,7 @@ impl fmt::Display for Error { write_byte_string(f, prefix)?; f.write_str("'") } + Error::NonSerializable(e) => write!(f, "error while serializing value: {e}"), } } } diff --git a/src/se/content.rs b/src/se/content.rs index 4a1a97c9..053208b8 100644 --- a/src/se/content.rs +++ b/src/se/content.rs @@ -668,7 +668,7 @@ pub(super) mod tests { let ser = ContentSerializer { writer: String::new(), level: QuoteLevel::Full, - indent: Indent::Owned(Indentation::new(b' ', 2)), + indent: Indent::Owned(Indentation::new(b' ', 2, 0)), write_indent: false, }; @@ -688,7 +688,7 @@ pub(super) mod tests { let ser = ContentSerializer { writer: &mut buffer, level: QuoteLevel::Full, - indent: Indent::Owned(Indentation::new(b' ', 2)), + indent: Indent::Owned(Indentation::new(b' ', 2, 0)), write_indent: false, }; diff --git a/src/se/element.rs b/src/se/element.rs index 57033290..1260c4b8 100644 --- a/src/se/element.rs +++ b/src/se/element.rs @@ -1486,7 +1486,7 @@ mod tests { ser: ContentSerializer { writer: String::new(), level: QuoteLevel::Full, - indent: Indent::Owned(Indentation::new(b' ', 2)), + indent: Indent::Owned(Indentation::new(b' ', 2, 0)), write_indent: false, }, key: XmlName("root"), @@ -1509,7 +1509,7 @@ mod tests { ser: ContentSerializer { writer: &mut buffer, level: QuoteLevel::Full, - indent: Indent::Owned(Indentation::new(b' ', 2)), + indent: Indent::Owned(Indentation::new(b' ', 2, 0)), write_indent: false, }, key: XmlName("root"), diff --git a/src/se/mod.rs b/src/se/mod.rs index 8e559d35..b9fb8619 100644 --- a/src/se/mod.rs +++ b/src/se/mod.rs @@ -366,7 +366,17 @@ impl<'r, W: Write> Serializer<'r, W> { /// Configure indent for a serializer pub fn indent(&mut self, indent_char: char, indent_size: usize) -> &mut Self { - self.ser.indent = Indent::Owned(Indentation::new(indent_char as u8, indent_size)); + self.indent_with_len(indent_char, indent_size, 0) + } + + /// Set initial indent level for a serializer + pub fn indent_with_len( + &mut self, + indent_char: char, + indent_size: usize, + indents_len: usize, + ) -> &mut Self { + self.ser.indent = Indent::Owned(Indentation::new(indent_char as u8, indent_size, indents_len)); self } diff --git a/src/writer.rs b/src/writer.rs index 2c633c74..6120eace 100644 --- a/src/writer.rs +++ b/src/writer.rs @@ -2,6 +2,9 @@ use std::io::Write; +#[cfg(feature = "serialize")] +use serde::Serialize; + use crate::encoding::UTF8_BOM; use crate::errors::{Error, Result}; use crate::events::{attributes::Attribute, BytesCData, BytesStart, BytesText, Event}; @@ -72,7 +75,7 @@ impl Writer { pub fn new_with_indent(inner: W, indent_char: u8, indent_size: usize) -> Writer { Writer { writer: inner, - indent: Some(Indentation::new(indent_char, indent_size)), + indent: Some(Indentation::new(indent_char, indent_size, 0)), } } @@ -330,6 +333,48 @@ impl<'a, W: Write> ElementWriter<'a, W> { .write_event(Event::End(self.start_tag.to_end()))?; Ok(self.writer) } + + /// Serialize an arbitrary value inside the current element + #[cfg(feature = "serialize")] + pub fn write_serializable_content(self, content: T) -> Result<&'a mut Writer> { + use crate::se::Serializer; + + self.writer + .write_event(Event::Start(self.start_tag.borrow()))?; + self.writer.write_indent()?; + + let indent = self.writer.indent.clone(); + let mut serializer = Serializer::new(ToFmtWrite(self.writer.inner())); + + if let Some(indent) = indent { + serializer.indent_with_len( + indent.indent_char as char, + indent.indent_size, + indent.indents_len, + ); + } + + if let Err(error) = content.serialize(serializer) { + return Err(Error::NonSerializable(error.to_string())); + } + + self.writer + .write_event(Event::End(self.start_tag.to_end()))?; + Ok(self.writer) + } +} + +#[cfg(feature = "serialize")] +struct ToFmtWrite(pub T); + +#[cfg(feature = "serialize")] +impl std::fmt::Write for ToFmtWrite +where + T: std::io::Write, +{ + fn write_str(&mut self, s: &str) -> std::fmt::Result { + self.0.write_all(s.as_bytes()).map_err(|_| std::fmt::Error) + } } #[derive(Clone)] @@ -342,13 +387,13 @@ pub(crate) struct Indentation { } impl Indentation { - pub fn new(indent_char: u8, indent_size: usize) -> Self { + pub fn new(indent_char: u8, indent_size: usize, indents_len: usize) -> Self { Self { should_line_break: false, indent_char, indent_size, indents: vec![indent_char; 128], - indents_len: 0, + indents_len: indents_len, } } @@ -613,4 +658,49 @@ mod indentation { "# ); } + + #[cfg(feature = "serialize")] + #[derive(Serialize)] + struct Foo { + bar: Bar, + val: String, + } + + #[cfg(feature = "serialize")] + #[derive(Serialize)] + struct Bar { + baz: usize, + bat: usize, + } + + #[cfg(feature = "serialize")] + #[test] + fn element_writer_serialize() { + let mut buffer = Vec::new(); + let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4); + let content = Foo { + bar: Bar { baz: 42, bat: 43 }, + val: "foo".to_owned(), + }; + + writer + .create_element("paired") + .with_attribute(("attr1", "value1")) + .with_attribute(("attr2", "value2")) + .write_serializable_content(content) + .expect("failure"); + + assert_eq!( + std::str::from_utf8(&buffer).unwrap(), + r#" + + + 42 + 43 + + foo + +"# + ); + } } From 8d833d84ed4f9e705e304a65c06dca3f3e1828fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Labeyrie?= Date: Tue, 15 Nov 2022 05:26:18 +0100 Subject: [PATCH 2/4] change return type of write_serializable_content --- src/errors.rs | 3 --- src/writer.rs | 15 ++++++++------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index 978e5b60..1f60c0b5 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -39,8 +39,6 @@ pub enum Error { EscapeError(EscapeError), /// Specified namespace prefix is unknown, cannot resolve namespace for it UnknownPrefix(Vec), - /// Serialization error - NonSerializable(String), } impl From for Error { @@ -114,7 +112,6 @@ impl fmt::Display for Error { write_byte_string(f, prefix)?; f.write_str("'") } - Error::NonSerializable(e) => write!(f, "error while serializing value: {e}"), } } } diff --git a/src/writer.rs b/src/writer.rs index 6120eace..5a8b5338 100644 --- a/src/writer.rs +++ b/src/writer.rs @@ -2,13 +2,13 @@ use std::io::Write; -#[cfg(feature = "serialize")] -use serde::Serialize; - use crate::encoding::UTF8_BOM; use crate::errors::{Error, Result}; use crate::events::{attributes::Attribute, BytesCData, BytesStart, BytesText, Event}; +#[cfg(feature = "serialize")] +use {crate::de::DeError, serde::Serialize}; + /// XML writer. Writes XML [`Event`]s to a [`std::io::Write`] implementor. /// /// # Examples @@ -336,7 +336,10 @@ impl<'a, W: Write> ElementWriter<'a, W> { /// Serialize an arbitrary value inside the current element #[cfg(feature = "serialize")] - pub fn write_serializable_content(self, content: T) -> Result<&'a mut Writer> { + pub fn write_serializable_content( + self, + content: T, + ) -> std::result::Result<&'a mut Writer, DeError> { use crate::se::Serializer; self.writer @@ -354,9 +357,7 @@ impl<'a, W: Write> ElementWriter<'a, W> { ); } - if let Err(error) = content.serialize(serializer) { - return Err(Error::NonSerializable(error.to_string())); - } + content.serialize(serializer)?; self.writer .write_event(Event::End(self.start_tag.to_end()))?; From 01786238173533ce9b626a28108e668121603ec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Labeyrie?= Date: Tue, 15 Nov 2022 05:27:20 +0100 Subject: [PATCH 3/4] Update src/writer.rs Co-authored-by: Mingun --- src/writer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/writer.rs b/src/writer.rs index 5a8b5338..4018327a 100644 --- a/src/writer.rs +++ b/src/writer.rs @@ -394,7 +394,7 @@ impl Indentation { indent_char, indent_size, indents: vec![indent_char; 128], - indents_len: indents_len, + indents_len, } } From 541aae35131c548f89c09bb648159006be4cc10f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Labeyrie?= Date: Tue, 15 Nov 2022 05:28:21 +0100 Subject: [PATCH 4/4] move struct definitions into test function --- src/writer.rs | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/writer.rs b/src/writer.rs index 4018327a..946572fc 100644 --- a/src/writer.rs +++ b/src/writer.rs @@ -660,23 +660,21 @@ mod indentation { ); } - #[cfg(feature = "serialize")] - #[derive(Serialize)] - struct Foo { - bar: Bar, - val: String, - } - - #[cfg(feature = "serialize")] - #[derive(Serialize)] - struct Bar { - baz: usize, - bat: usize, - } - #[cfg(feature = "serialize")] #[test] fn element_writer_serialize() { + #[derive(Serialize)] + struct Foo { + bar: Bar, + val: String, + } + + #[derive(Serialize)] + struct Bar { + baz: usize, + bat: usize, + } + let mut buffer = Vec::new(); let mut writer = Writer::new_with_indent(&mut buffer, b' ', 4); let content = Foo {