From cfb122362124d9f6fec2a0f40afa8c1e99d58666 Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Mon, 5 Sep 2022 13:10:27 +0200 Subject: [PATCH] Move user-facing serialization interface out of trait --- instant-xml-macros/src/lib.rs | 5 +++- instant-xml/src/impls.rs | 40 ++++++++++++++++++++++--------- instant-xml/src/lib.rs | 25 ++++++++++++------- instant-xml/src/ser.rs | 4 ++-- instant-xml/tests/entities.rs | 10 ++++---- instant-xml/tests/ser-child-ns.rs | 7 +++--- instant-xml/tests/ser-named.rs | 7 +++--- instant-xml/tests/ser-nested.rs | 7 +++--- instant-xml/tests/ser-unit.rs | 4 ++-- 9 files changed, 67 insertions(+), 42 deletions(-) diff --git a/instant-xml-macros/src/lib.rs b/instant-xml-macros/src/lib.rs index 378f5d5..3fa3c68 100644 --- a/instant-xml-macros/src/lib.rs +++ b/instant-xml-macros/src/lib.rs @@ -49,7 +49,10 @@ pub fn to_xml(input: proc_macro::TokenStream) -> proc_macro::TokenStream { proc_macro::TokenStream::from(quote!( impl #generics ToXml for #ident #generics { - fn serialize(&self, serializer: &mut instant_xml::Serializer) -> Result<(), instant_xml::Error> { + fn serialize( + &self, + serializer: &mut instant_xml::Serializer, + ) -> Result<(), instant_xml::Error> { let _ = serializer.consume_field_context(); let mut field_context = instant_xml::ser::FieldContext { name: #root_name, diff --git a/instant-xml/src/impls.rs b/instant-xml/src/impls.rs index 10416a9..c81c386 100644 --- a/instant-xml/src/impls.rs +++ b/instant-xml/src/impls.rs @@ -54,10 +54,10 @@ impl<'a, T> ToXml for DisplayToXml<'a, T> where T: fmt::Display, { - fn serialize(&self, serializer: &mut Serializer) -> Result<(), Error> - where - W: fmt::Write, - { + fn serialize( + &self, + serializer: &mut Serializer, + ) -> Result<(), Error> { let field_context = match serializer.consume_field_context() { Some(field_context) => field_context, None => return Err(Error::UnexpectedValue), @@ -80,7 +80,7 @@ where macro_rules! to_xml_for_number { ($typ:ty) => { impl ToXml for $typ { - fn serialize( + fn serialize( &self, serializer: &mut Serializer, ) -> Result<(), Error> { @@ -321,7 +321,10 @@ to_xml_for_number!(f32); to_xml_for_number!(f64); impl ToXml for bool { - fn serialize(&self, serializer: &mut Serializer) -> Result<(), Error> { + fn serialize( + &self, + serializer: &mut Serializer, + ) -> Result<(), Error> { let value = match self { true => "true", false => "false", @@ -332,32 +335,47 @@ impl ToXml for bool { } impl ToXml for String { - fn serialize(&self, serializer: &mut Serializer) -> Result<(), Error> { + fn serialize( + &self, + serializer: &mut Serializer, + ) -> Result<(), Error> { DisplayToXml(&escape(self)?).serialize(serializer) } } impl ToXml for char { - fn serialize(&self, serializer: &mut Serializer) -> Result<(), Error> { + fn serialize( + &self, + serializer: &mut Serializer, + ) -> Result<(), Error> { let mut tmp = [0u8; 4]; DisplayToXml(&escape(&*self.encode_utf8(&mut tmp))?).serialize(serializer) } } impl ToXml for &str { - fn serialize(&self, serializer: &mut Serializer) -> Result<(), Error> { + fn serialize( + &self, + serializer: &mut Serializer, + ) -> Result<(), Error> { DisplayToXml(&escape(self)?).serialize(serializer) } } impl ToXml for Cow<'_, str> { - fn serialize(&self, serializer: &mut Serializer) -> Result<(), Error> { + fn serialize( + &self, + serializer: &mut Serializer, + ) -> Result<(), Error> { DisplayToXml(&escape(self)?).serialize(serializer) } } impl ToXml for Option { - fn serialize(&self, serializer: &mut Serializer) -> Result<(), Error> { + fn serialize( + &self, + serializer: &mut Serializer, + ) -> Result<(), Error> { match self { Some(v) => v.serialize(serializer), None => Ok(()), diff --git a/instant-xml/src/lib.rs b/instant-xml/src/lib.rs index a1ade13..5a8cc66 100644 --- a/instant-xml/src/lib.rs +++ b/instant-xml/src/lib.rs @@ -13,14 +13,10 @@ pub mod ser; pub use ser::Serializer; pub trait ToXml { - fn to_xml(&self) -> Result { - let mut output = String::new(); - let mut serializer = Serializer::new(&mut output); - self.serialize(&mut serializer)?; - Ok(output) - } - - fn serialize(&self, serializer: &mut Serializer) -> Result<(), Error>; + fn serialize( + &self, + serializer: &mut Serializer, + ) -> Result<(), Error>; } pub enum FieldAttribute<'xml> { @@ -45,6 +41,19 @@ pub fn from_str<'xml, T: FromXml<'xml>>(input: &'xml str) -> Result { T::deserialize(&mut Deserializer::new(input)) } +pub fn to_string(value: &(impl ToXml + ?Sized)) -> Result { + let mut output = String::new(); + value.serialize(&mut Serializer::new(&mut output))?; + Ok(output) +} + +pub fn to_writer( + value: &(impl ToXml + ?Sized), + output: &mut (impl fmt::Write + ?Sized), +) -> Result<(), Error> { + value.serialize(&mut Serializer::new(output)) +} + pub enum Kind { Scalar, Element(Id<'static>), diff --git a/instant-xml/src/ser.rs b/instant-xml/src/ser.rs index eb3ce61..be8fd6a 100644 --- a/instant-xml/src/ser.rs +++ b/instant-xml/src/ser.rs @@ -3,7 +3,7 @@ use std::fmt::{self, Write}; use super::{Error, FieldAttribute}; -pub struct Serializer<'xml, W: fmt::Write> { +pub struct Serializer<'xml, W: fmt::Write + ?Sized> { // For parent namespaces the key is the namespace and the value is the prefix. We are adding to map // only if the namespaces do not exist, if it does exist then we are using an already defined parent prefix. #[doc(hidden)] @@ -17,7 +17,7 @@ pub struct Serializer<'xml, W: fmt::Write> { next_field_context: Option>, } -impl<'xml, W: fmt::Write> Serializer<'xml, W> { +impl<'xml, W: fmt::Write + ?Sized> Serializer<'xml, W> { pub fn new(output: &'xml mut W) -> Self { Self { parent_namespaces: HashMap::new(), diff --git a/instant-xml/tests/entities.rs b/instant-xml/tests/entities.rs index c8e5a58..eff0dde 100644 --- a/instant-xml/tests/entities.rs +++ b/instant-xml/tests/entities.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use similar_asserts::assert_eq; -use instant_xml::{from_str, Error, FromXml, ToXml}; +use instant_xml::{from_str, to_string, Error, FromXml, ToXml}; #[derive(Debug, PartialEq, Eq, FromXml, ToXml)] #[xml(ns("URI"))] @@ -57,13 +57,11 @@ fn escape_back() { #[test] fn special_entities() { assert_eq!( - StructSpecialEntities{ + to_string(&StructSpecialEntities{ string: "&\"<>\'aa".to_string(), str: "&\"<>\'bb", cow: Cow::from("&\"<>\'cc"), - } - .to_xml() - .unwrap(), - "&"<>'aa&"<>'bb&"<>'cc" + }).unwrap(), + "&"<>'aa&"<>'bb&"<>'cc", ); } diff --git a/instant-xml/tests/ser-child-ns.rs b/instant-xml/tests/ser-child-ns.rs index 4de3fb2..7948d19 100644 --- a/instant-xml/tests/ser-child-ns.rs +++ b/instant-xml/tests/ser-child-ns.rs @@ -1,6 +1,6 @@ use similar_asserts::assert_eq; -use instant_xml::ToXml; +use instant_xml::{to_string, ToXml}; #[derive(Debug, Eq, PartialEq, ToXml)] #[xml(ns(dar = "BAZ", internal = "INTERNAL"))] @@ -33,7 +33,7 @@ struct Nested { #[test] fn struct_child_namespaces() { assert_eq!( - StructChildNamespaces { + to_string(&StructChildNamespaces { different_child_namespace: NestedDifferentNamespace { flag_parent_prefix: true, flag_internal_prefix: false, @@ -42,8 +42,7 @@ fn struct_child_namespaces() { flag_parent_prefix: true, flag_internal_prefix: false, }, - } - .to_xml() + }) .unwrap(), "truefalsetruefalse" ); diff --git a/instant-xml/tests/ser-named.rs b/instant-xml/tests/ser-named.rs index d556c79..f8083de 100644 --- a/instant-xml/tests/ser-named.rs +++ b/instant-xml/tests/ser-named.rs @@ -1,6 +1,6 @@ use similar_asserts::assert_eq; -use instant_xml::ToXml; +use instant_xml::{to_string, ToXml}; #[derive(Debug, Eq, PartialEq, ToXml)] #[xml(ns(bar = "BAZ", foo = "BAR"))] @@ -20,12 +20,11 @@ struct StructWithNamedFields { #[test] fn struct_with_named_fields() { assert_eq!( - StructWithNamedFields { + to_string(&StructWithNamedFields { flag: true, string: "test".to_string(), number: 1, - } - .to_xml() + }) .unwrap(), "truetest1" ); diff --git a/instant-xml/tests/ser-nested.rs b/instant-xml/tests/ser-nested.rs index dc18b0a..415d84b 100644 --- a/instant-xml/tests/ser-nested.rs +++ b/instant-xml/tests/ser-nested.rs @@ -1,6 +1,6 @@ use similar_asserts::assert_eq; -use instant_xml::ToXml; +use instant_xml::{to_string, ToXml}; #[derive(Debug, Eq, PartialEq, ToXml)] #[xml(ns("URI", dar = "BAZ", internal = "INTERNAL"))] @@ -37,7 +37,7 @@ struct StructWithCustomField { #[test] fn struct_with_custom_field() { assert_eq!( - StructWithCustomField { + to_string(&StructWithCustomField { int_attribute: 42, flag_direct_namespace_same_the_same_as_prefix: true, flag_prefix: false, @@ -46,8 +46,7 @@ fn struct_with_custom_field() { flag_parent_prefix: true, flag_internal_prefix: false, }, - } - .to_xml() + }) .unwrap(), "truefalsetruetruefalse" ); diff --git a/instant-xml/tests/ser-unit.rs b/instant-xml/tests/ser-unit.rs index 793937a..15c35b1 100644 --- a/instant-xml/tests/ser-unit.rs +++ b/instant-xml/tests/ser-unit.rs @@ -1,12 +1,12 @@ use similar_asserts::assert_eq; -use instant_xml::ToXml; +use instant_xml::{to_string, ToXml}; #[derive(Debug, Eq, PartialEq, ToXml)] struct Unit; #[test] fn unit() { - assert_eq!(Unit.to_xml().unwrap(), ""); + assert_eq!(to_string(&Unit).unwrap(), ""); //assert_eq!(Unit::from_xml("").unwrap(), Unit); }