From c15a0a985c1e2be7be45fe7e30d08df4be8754b8 Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Fri, 25 Nov 2022 21:04:09 -0800 Subject: [PATCH] Enable support for scalar Vec elements --- instant-xml-macros/src/ser.rs | 33 ++++++++++------- instant-xml/src/impls.rs | 70 ++++++++++++++++++++++++++--------- instant-xml/src/lib.rs | 10 ++--- instant-xml/src/ser.rs | 2 +- instant-xml/tests/vec.rs | 12 ++++-- 5 files changed, 87 insertions(+), 40 deletions(-) diff --git a/instant-xml-macros/src/ser.rs b/instant-xml-macros/src/ser.rs index 7c76578..858ef6e 100644 --- a/instant-xml-macros/src/ser.rs +++ b/instant-xml-macros/src/ser.rs @@ -49,9 +49,24 @@ fn serialize_scalar_enum( impl #impl_generics ToXml for #ident #ty_generics #where_clause { fn serialize( &self, + field: Option<::instant_xml::Id<'_>>, serializer: &mut instant_xml::Serializer, ) -> Result<(), instant_xml::Error> { - serializer.write_str(match self { #variants }) + let prefix = match field { + Some(id) => { + let prefix = serializer.write_start(id.name, id.ns, true)?; + serializer.end_start()?; + Some((prefix, id.name)) + } + None => None, + }; + + serializer.write_str(match self { #variants })?; + if let Some((prefix, name)) = prefix { + serializer.write_close(prefix, name)?; + } + + Ok(()) } const KIND: ::instant_xml::Kind<'static> = ::instant_xml::Kind::Scalar; @@ -95,7 +110,7 @@ fn serialize_wrapped_enum( } let v_ident = &variant.ident; - variants.extend(quote!(#ident::#v_ident(inner) => inner.serialize(serializer)?,)); + variants.extend(quote!(#ident::#v_ident(inner) => inner.serialize(None, serializer)?,)); } let default_namespace = meta.default_namespace(); @@ -124,6 +139,7 @@ fn serialize_wrapped_enum( impl #impl_generics ToXml for #ident #ty_generics #where_clause { fn serialize( &self, + field: Option<::instant_xml::Id<'_>>, serializer: &mut instant_xml::Serializer, ) -> Result<(), instant_xml::Error> { // Start tag @@ -204,6 +220,7 @@ fn serialize_struct( impl #impl_generics ToXml for #ident #ty_generics #where_clause { fn serialize( &self, + field: Option<::instant_xml::Id<'_>>, serializer: &mut instant_xml::Serializer, ) -> Result<(), instant_xml::Error> { // Start tag @@ -318,17 +335,7 @@ fn process_named_field( let mut no_lifetime_type = field.ty.clone(); discard_lifetimes(&mut no_lifetime_type); body.extend(quote!( - match <#no_lifetime_type as ToXml>::KIND { - ::instant_xml::Kind::Element(_) | ::instant_xml::Kind::Vec(_) => { - self.#field_name.serialize(serializer)?; - } - ::instant_xml::Kind::Scalar => { - let prefix = serializer.write_start(#tag, #ns, true)?; - serializer.end_start()?; - self.#field_name.serialize(serializer)?; - serializer.write_close(prefix, #tag)?; - } - } + self.#field_name.serialize(Some(::instant_xml::Id { ns: #ns, name: #tag }), serializer)?; )); Ok(()) diff --git a/instant-xml/src/impls.rs b/instant-xml/src/impls.rs index ec4db89..720de62 100644 --- a/instant-xml/src/impls.rs +++ b/instant-xml/src/impls.rs @@ -5,7 +5,7 @@ use std::str::FromStr; #[cfg(feature = "chrono")] use chrono::{DateTime, Utc}; -use crate::{Deserializer, Error, FromXml, Kind, Serializer, ToXml}; +use crate::{Deserializer, Error, FromXml, Id, Kind, Serializer, ToXml}; // Deserializer struct FromXmlStr(T); @@ -64,9 +64,24 @@ where { fn serialize( &self, + field: Option>, serializer: &mut Serializer, ) -> Result<(), Error> { - serializer.write_str(self.0) + let prefix = match field { + Some(id) => { + let prefix = serializer.write_start(id.name, id.ns, true)?; + serializer.end_start()?; + Some((prefix, id.name)) + } + None => None, + }; + + serializer.write_str(self.0)?; + if let Some((prefix, name)) = prefix { + serializer.write_close(prefix, name)?; + } + + Ok(()) } const KIND: Kind<'static> = Kind::Scalar; @@ -77,9 +92,10 @@ macro_rules! to_xml_for_number { impl ToXml for $typ { fn serialize( &self, + field: Option>, serializer: &mut Serializer, ) -> Result<(), Error> { - DisplayToXml(self).serialize(serializer) + DisplayToXml(self).serialize(field, serializer) } const KIND: Kind<'static> = DisplayToXml::::KIND; @@ -257,6 +273,7 @@ to_xml_for_number!(f64); impl ToXml for bool { fn serialize( &self, + field: Option>, serializer: &mut Serializer, ) -> Result<(), Error> { let value = match self { @@ -264,7 +281,7 @@ impl ToXml for bool { false => "false", }; - DisplayToXml(&value).serialize(serializer) + DisplayToXml(&value).serialize(field, serializer) } const KIND: Kind<'static> = DisplayToXml::::KIND; @@ -273,9 +290,10 @@ impl ToXml for bool { impl ToXml for String { fn serialize( &self, + field: Option>, serializer: &mut Serializer, ) -> Result<(), Error> { - DisplayToXml(&encode(self)?).serialize(serializer) + DisplayToXml(&encode(self)?).serialize(field, serializer) } const KIND: Kind<'static> = DisplayToXml::::KIND; @@ -284,10 +302,11 @@ impl ToXml for String { impl ToXml for char { fn serialize( &self, + field: Option>, serializer: &mut Serializer, ) -> Result<(), Error> { let mut tmp = [0u8; 4]; - DisplayToXml(&encode(&*self.encode_utf8(&mut tmp))?).serialize(serializer) + DisplayToXml(&encode(&*self.encode_utf8(&mut tmp))?).serialize(field, serializer) } const KIND: Kind<'static> = DisplayToXml::::KIND; @@ -296,9 +315,10 @@ impl ToXml for char { impl ToXml for &str { fn serialize( &self, + field: Option>, serializer: &mut Serializer, ) -> Result<(), Error> { - DisplayToXml(&encode(self)?).serialize(serializer) + DisplayToXml(&encode(self)?).serialize(field, serializer) } const KIND: Kind<'static> = DisplayToXml::::KIND; @@ -307,9 +327,10 @@ impl ToXml for &str { impl ToXml for Cow<'_, str> { fn serialize( &self, + field: Option>, serializer: &mut Serializer, ) -> Result<(), Error> { - DisplayToXml(&encode(self)?).serialize(serializer) + DisplayToXml(&encode(self)?).serialize(field, serializer) } const KIND: Kind<'static> = DisplayToXml::::KIND; @@ -318,10 +339,11 @@ impl ToXml for Cow<'_, str> { impl ToXml for Option { fn serialize( &self, + field: Option>, serializer: &mut Serializer, ) -> Result<(), Error> { match self { - Some(v) => v.serialize(serializer), + Some(v) => v.serialize(field, serializer), None => Ok(()), } } @@ -429,36 +451,48 @@ impl<'xml, T: FromXml<'xml>> FromXml<'xml> for Vec { Ok(Vec::new()) } - const KIND: Kind<'static> = Kind::Vec(T::KIND.element()); + const KIND: Kind<'static> = T::KIND; + const WRAPPED: bool = true; } impl ToXml for Vec { fn serialize( &self, + field: Option>, serializer: &mut Serializer, ) -> Result<(), Error> { - match T::KIND { - Kind::Element(_) => {} - _ => return Err(Error::UnexpectedState("only elements allowed in `Vec`")), - } - for i in self { - i.serialize(serializer)?; + i.serialize(field, serializer)?; } Ok(()) } - const KIND: Kind<'static> = Kind::Vec(T::KIND.element()); + const KIND: Kind<'static> = T::KIND; } #[cfg(feature = "chrono")] impl ToXml for DateTime { fn serialize( &self, + field: Option>, serializer: &mut Serializer, ) -> Result<(), Error> { - serializer.write_str(&self.to_rfc3339()) + let prefix = match field { + Some(id) => { + let prefix = serializer.write_start(id.name, id.ns, true)?; + serializer.end_start()?; + Some((prefix, id.name)) + } + None => None, + }; + + serializer.write_str(&self.to_rfc3339())?; + if let Some((prefix, name)) = prefix { + serializer.write_close(prefix, name)?; + } + + Ok(()) } const KIND: Kind<'static> = Kind::Scalar; diff --git a/instant-xml/src/lib.rs b/instant-xml/src/lib.rs index 3fb33f2..01c6b23 100644 --- a/instant-xml/src/lib.rs +++ b/instant-xml/src/lib.rs @@ -16,6 +16,7 @@ pub use ser::Serializer; pub trait ToXml { fn serialize( &self, + field: Option>, serializer: &mut Serializer, ) -> Result<(), Error>; @@ -25,9 +26,10 @@ pub trait ToXml { impl<'a, T: ToXml + ?Sized> ToXml for &'a T { fn serialize( &self, + field: Option>, serializer: &mut Serializer, ) -> Result<(), Error> { - (*self).serialize(serializer) + (*self).serialize(field, serializer) } const KIND: Kind<'static> = T::KIND; @@ -46,6 +48,7 @@ pub trait FromXml<'xml>: Sized { } const KIND: Kind<'static>; + const WRAPPED: bool = false; } pub fn from_str<'xml, T: FromXml<'xml>>(input: &'xml str) -> Result { @@ -53,7 +56,6 @@ pub fn from_str<'xml, T: FromXml<'xml>>(input: &'xml str) -> Result { let id = context.element_id(&root)?; let expected = match T::KIND { Kind::Scalar => return Err(Error::UnexpectedState("found scalar as root")), - Kind::Vec(_) => return Err(Error::UnexpectedState("found list as root")), Kind::Element(expected) => expected, }; @@ -79,7 +81,7 @@ pub fn to_writer( value: &(impl ToXml + ?Sized), output: &mut (impl fmt::Write + ?Sized), ) -> Result<(), Error> { - value.serialize(&mut Serializer::new(output)) + value.serialize(None, &mut Serializer::new(output)) } pub trait FromXmlOwned: for<'xml> FromXml<'xml> {} @@ -124,7 +126,6 @@ pub enum Error { pub enum Kind<'a> { Scalar, Element(Id<'a>), - Vec(Id<'a>), } impl<'a> Kind<'a> { @@ -140,7 +141,6 @@ impl<'a> Kind<'a> { match self { Kind::Scalar => id == field, Kind::Element(name) => id == *name, - Kind::Vec(inner) => id == *inner, } } } diff --git a/instant-xml/src/ser.rs b/instant-xml/src/ser.rs index aafecd4..1172b53 100644 --- a/instant-xml/src/ser.rs +++ b/instant-xml/src/ser.rs @@ -85,7 +85,7 @@ impl<'xml, W: fmt::Write + ?Sized> Serializer<'xml, W> { } self.state = State::Scalar; - value.serialize(self)?; + value.serialize(None, self)?; self.state = State::Attribute; self.output.write_char('"')?; Ok(()) diff --git a/instant-xml/tests/vec.rs b/instant-xml/tests/vec.rs index ea9ca3e..41d77a6 100644 --- a/instant-xml/tests/vec.rs +++ b/instant-xml/tests/vec.rs @@ -8,26 +8,32 @@ struct Foo { #[derive(Debug, Eq, FromXml, PartialEq, ToXml)] struct Bar { foo: Vec, + baz: Vec, } #[test] fn vec() { - let val = Bar { foo: vec![] }; + let val = Bar { + foo: vec![], + baz: vec![], + }; let xml = ""; assert_eq!(xml, to_string(&val).unwrap()); assert_eq!(val, from_str(xml).unwrap()); let val = Bar { foo: vec![Foo { bar: 42 }], + baz: vec!["hello".to_owned()], }; - let xml = "42"; + let xml = "42hello"; assert_eq!(xml, to_string(&val).unwrap()); assert_eq!(val, from_str(xml).unwrap()); let val = Bar { foo: vec![Foo { bar: 42 }, Foo { bar: 73 }], + baz: vec!["hello".to_owned(), "world".to_owned()], }; - let xml = "4273"; + let xml = "4273helloworld"; assert_eq!(xml, to_string(&val).unwrap()); assert_eq!(val, from_str(xml).unwrap()); }