Move user-facing serialization interface out of trait

This commit is contained in:
Dirkjan Ochtman 2022-09-05 13:10:27 +02:00
parent 2827bd404e
commit 9577aace57
9 changed files with 67 additions and 42 deletions

View File

@ -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<W: std::fmt::Write>(&self, serializer: &mut instant_xml::Serializer<W>) -> Result<(), instant_xml::Error> {
fn serialize<W: ::core::fmt::Write + ?::core::marker::Sized>(
&self,
serializer: &mut instant_xml::Serializer<W>,
) -> Result<(), instant_xml::Error> {
let _ = serializer.consume_field_context();
let mut field_context = instant_xml::ser::FieldContext {
name: #root_name,

View File

@ -54,10 +54,10 @@ impl<'a, T> ToXml for DisplayToXml<'a, T>
where
T: fmt::Display,
{
fn serialize<W>(&self, serializer: &mut Serializer<W>) -> Result<(), Error>
where
W: fmt::Write,
{
fn serialize<W: fmt::Write + ?Sized>(
&self,
serializer: &mut Serializer<W>,
) -> 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<W: fmt::Write>(
fn serialize<W: fmt::Write + ?Sized>(
&self,
serializer: &mut Serializer<W>,
) -> Result<(), Error> {
@ -321,7 +321,10 @@ to_xml_for_number!(f32);
to_xml_for_number!(f64);
impl ToXml for bool {
fn serialize<W: fmt::Write>(&self, serializer: &mut Serializer<W>) -> Result<(), Error> {
fn serialize<W: fmt::Write + ?Sized>(
&self,
serializer: &mut Serializer<W>,
) -> Result<(), Error> {
let value = match self {
true => "true",
false => "false",
@ -332,32 +335,47 @@ impl ToXml for bool {
}
impl ToXml for String {
fn serialize<W: fmt::Write>(&self, serializer: &mut Serializer<W>) -> Result<(), Error> {
fn serialize<W: fmt::Write + ?Sized>(
&self,
serializer: &mut Serializer<W>,
) -> Result<(), Error> {
DisplayToXml(&escape(self)?).serialize(serializer)
}
}
impl ToXml for char {
fn serialize<W: fmt::Write>(&self, serializer: &mut Serializer<W>) -> Result<(), Error> {
fn serialize<W: fmt::Write + ?Sized>(
&self,
serializer: &mut Serializer<W>,
) -> Result<(), Error> {
let mut tmp = [0u8; 4];
DisplayToXml(&escape(&*self.encode_utf8(&mut tmp))?).serialize(serializer)
}
}
impl ToXml for &str {
fn serialize<W: fmt::Write>(&self, serializer: &mut Serializer<W>) -> Result<(), Error> {
fn serialize<W: fmt::Write + ?Sized>(
&self,
serializer: &mut Serializer<W>,
) -> Result<(), Error> {
DisplayToXml(&escape(self)?).serialize(serializer)
}
}
impl ToXml for Cow<'_, str> {
fn serialize<W: fmt::Write>(&self, serializer: &mut Serializer<W>) -> Result<(), Error> {
fn serialize<W: fmt::Write + ?Sized>(
&self,
serializer: &mut Serializer<W>,
) -> Result<(), Error> {
DisplayToXml(&escape(self)?).serialize(serializer)
}
}
impl<T: ToXml> ToXml for Option<T> {
fn serialize<W: fmt::Write>(&self, serializer: &mut Serializer<W>) -> Result<(), Error> {
fn serialize<W: fmt::Write + ?Sized>(
&self,
serializer: &mut Serializer<W>,
) -> Result<(), Error> {
match self {
Some(v) => v.serialize(serializer),
None => Ok(()),

View File

@ -13,14 +13,10 @@ pub mod ser;
pub use ser::Serializer;
pub trait ToXml {
fn to_xml(&self) -> Result<String, Error> {
let mut output = String::new();
let mut serializer = Serializer::new(&mut output);
self.serialize(&mut serializer)?;
Ok(output)
}
fn serialize<W: fmt::Write>(&self, serializer: &mut Serializer<W>) -> Result<(), Error>;
fn serialize<W: fmt::Write + ?Sized>(
&self,
serializer: &mut Serializer<W>,
) -> Result<(), Error>;
}
pub enum FieldAttribute<'xml> {
@ -45,6 +41,19 @@ pub fn from_str<'xml, T: FromXml<'xml>>(input: &'xml str) -> Result<T, Error> {
T::deserialize(&mut Deserializer::new(input))
}
pub fn to_string(value: &(impl ToXml + ?Sized)) -> Result<String, Error> {
let mut output = String::new();
to_writer(value, &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>),

View File

@ -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<FieldContext<'xml>>,
}
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(),

View File

@ -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(),
"<StructSpecialEntities xmlns=\"URI\"><string>&amp;&quot;&lt;&gt;&apos;aa</string><str>&amp;&quot;&lt;&gt;&apos;bb</str><cow>&amp;&quot;&lt;&gt;&apos;cc</cow></StructSpecialEntities>"
}).unwrap(),
"<StructSpecialEntities xmlns=\"URI\"><string>&amp;&quot;&lt;&gt;&apos;aa</string><str>&amp;&quot;&lt;&gt;&apos;bb</str><cow>&amp;&quot;&lt;&gt;&apos;cc</cow></StructSpecialEntities>",
);
}

View File

@ -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(),
"<StructChildNamespaces xmlns=\"URI\" xmlns:bar=\"BAZ\" xmlns:foo=\"BAR\"><NestedDifferentNamespace xmlns=\"\" xmlns:internal=\"INTERNAL\"><bar:flag_parent_prefix>true</bar:flag_parent_prefix><internal:flag_internal_prefix>false</internal:flag_internal_prefix></NestedDifferentNamespace><Nested xmlns:internal=\"INTERNAL\"><bar:flag_parent_prefix>true</bar:flag_parent_prefix><internal:flag_internal_prefix>false</internal:flag_internal_prefix></Nested></StructChildNamespaces>"
);

View File

@ -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(),
"<StructWithNamedFields xmlns:bar=\"BAZ\" xmlns:foo=\"BAR\"><flag>true</flag><bar:string>test</bar:string><number xmlns=\"typo\">1</number></StructWithNamedFields>"
);

View File

@ -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(),
"<StructWithCustomField xmlns=\"URI\" xmlns:bar=\"BAZ\" xmlns:foo=\"BAR\" int_attribute=\"42\"><flag_direct_namespace_same_the_same_as_prefix xmlns=\"BAZ\">true</flag_direct_namespace_same_the_same_as_prefix><bar:flag_prefix>false</bar:flag_prefix><flag_direct_namespace xmlns=\"DIFFERENT\">true</flag_direct_namespace><Nested xmlns:internal=\"INTERNAL\"><bar:flag_parent_prefix>true</bar:flag_parent_prefix><internal:flag_internal_prefix>false</internal:flag_internal_prefix></Nested></StructWithCustomField>"
);

View File

@ -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(), "<Unit></Unit>");
assert_eq!(to_string(&Unit).unwrap(), "<Unit></Unit>");
//assert_eq!(Unit::from_xml("<Unit/>").unwrap(), Unit);
}