xml_struct 1.0.0

Simple, low-boilerplate serialization of Rust data structures to XML.
Documentation
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! This module provides implementations of serialization for common types from
//! the standard library.

use quick_xml::{
    events::{BytesText, Event},
    Writer,
};

use crate::{Error, XmlSerialize, XmlSerializeAttr};

/// Serializes a string as a text content node.
impl XmlSerialize for str {
    fn serialize_child_nodes<W>(&self, writer: &mut Writer<W>) -> Result<(), Error>
    where
        W: std::io::Write,
    {
        writer.write_event(Event::Text(BytesText::new(self)))?;

        Ok(())
    }
}

/// Serializes a string as a text content node.
impl XmlSerialize for String {
    fn serialize_child_nodes<W>(&self, writer: &mut Writer<W>) -> Result<(), Error>
    where
        W: std::io::Write,
    {
        writer.write_event(Event::Text(BytesText::new(self.as_str())))?;

        Ok(())
    }
}

/// Serializes a string as a text content node.
impl XmlSerialize for &str {
    fn serialize_child_nodes<W>(&self, writer: &mut Writer<W>) -> Result<(), Error>
    where
        W: std::io::Write,
    {
        writer.write_event(Event::Text(BytesText::new(self)))?;

        Ok(())
    }
}

/// Serializes a boolean as a text content node.
///
/// `true` is serialized as the string value "true", while `false` is serialized
/// as the string value "false".
impl XmlSerialize for bool {
    fn serialize_child_nodes<W>(&self, writer: &mut Writer<W>) -> Result<(), Error>
    where
        W: std::io::Write,
    {
        let content = if *self { "true" } else { "false" };

        writer.write_event(Event::Text(BytesText::new(content)))?;

        Ok(())
    }
}

/// Serializes the contents of an `Option<T>` as content nodes.
///
/// `Some(t)` is serialized identically to `t`, while `None` produces no output.
impl<T> XmlSerialize for Option<T>
where
    T: XmlSerialize,
{
    fn serialize_as_element<W>(&self, writer: &mut Writer<W>, name: &str) -> Result<(), Error>
    where
        W: std::io::Write,
    {
        match self {
            Some(value) => <T as XmlSerialize>::serialize_as_element(value, writer, name),
            None => Ok(()),
        }
    }

    fn serialize_child_nodes<W>(&self, writer: &mut Writer<W>) -> Result<(), Error>
    where
        W: std::io::Write,
    {
        match self {
            Some(value) => <T as XmlSerialize>::serialize_child_nodes(value, writer),
            None => Ok(()),
        }
    }
}

/// Serializes the contents of a `Box<T>` using the implementation from the referenced type.
impl<T> XmlSerialize for Box<T>
where
    T: XmlSerialize,
{
    fn serialize_as_element<W>(&self, writer: &mut Writer<W>, name: &str) -> Result<(), Error>
    where
        W: std::io::Write,
    {
        <T as XmlSerialize>::serialize_as_element(self, writer, name)
    }

    fn serialize_child_nodes<W>(&self, writer: &mut Writer<W>) -> Result<(), Error>
    where
        W: std::io::Write,
    {
        <T as XmlSerialize>::serialize_child_nodes(self, writer)
    }
}

/// Serializes the contents of a `Vec<T>` as content nodes.
///
/// Each element of the `Vec` is serialized via its `serialize_child_nodes()`
/// implementation. If the `Vec` is empty, no output is produced.
impl<T> XmlSerialize for Vec<T>
where
    T: XmlSerialize,
{
    fn serialize_child_nodes<W>(&self, writer: &mut Writer<W>) -> Result<(), Error>
    where
        W: std::io::Write,
    {
        if self.is_empty() {
            return Ok(());
        }

        for value in self {
            <T as XmlSerialize>::serialize_child_nodes(value, writer)?;
        }

        Ok(())
    }
}

/// Serializes a `&T` using the implementation for the non-borrowed type.
impl<T> XmlSerialize for &T
where
    T: XmlSerialize,
{
    fn serialize_child_nodes<W>(&self, writer: &mut Writer<W>) -> Result<(), Error>
    where
        W: std::io::Write,
    {
        T::serialize_child_nodes(self, writer)
    }

    fn serialize_as_element<W>(&self, writer: &mut Writer<W>, name: &str) -> Result<(), Error>
    where
        W: std::io::Write,
    {
        // The default implementation for `serialize_as_element` creates a
        // top-level element with `name` and no attribute. This means top-level
        // proc-macro attributes such as `default_ns` get ignored. Instead, we
        // want to forward the call to the implementation for `T`, which may be
        // the default one OR one generated with a `#[derive()]` macro, which
        // might include additional information.
        T::serialize_as_element(self, writer, name)
    }
}

/// Serializes a string as an XML attribute value.
impl XmlSerializeAttr for str {
    fn serialize_as_attribute(&self, start_tag: &mut quick_xml::events::BytesStart, name: &str) {
        start_tag.push_attribute((name, self));
    }
}

/// Serializes a string as an XML attribute value.
impl XmlSerializeAttr for String {
    fn serialize_as_attribute(&self, start_tag: &mut quick_xml::events::BytesStart, name: &str) {
        start_tag.push_attribute((name, self.as_str()));
    }
}

/// Serializes a string as an XML attribute value.
impl XmlSerializeAttr for &str {
    fn serialize_as_attribute(&self, start_tag: &mut quick_xml::events::BytesStart, name: &str) {
        start_tag.push_attribute((name, *self));
    }
}

/// Serializes a boolean as an XML attribute value.
///
/// `true` is serialized as the string value "true", while `false` is serialized
/// as the string value "false".
impl XmlSerializeAttr for bool {
    fn serialize_as_attribute(&self, start_tag: &mut quick_xml::events::BytesStart, name: &str) {
        let content = if *self { "true" } else { "false" };

        start_tag.push_attribute((name, content));
    }
}

/// Serializes the contents of an `Option<T>` as an XML attribute value.
///
/// `Some(t)` is serialized identically to `t`, while `None` produces no output.
impl<T> XmlSerializeAttr for Option<T>
where
    T: XmlSerializeAttr,
{
    fn serialize_as_attribute(&self, start_tag: &mut quick_xml::events::BytesStart, name: &str) {
        if let Some(value) = self {
            value.serialize_as_attribute(start_tag, name)
        }
    }
}

/// Serializes a `&T` as an attribute using the implementation for the
/// non-borrowed type.
impl<T> XmlSerializeAttr for &T
where
    T: XmlSerializeAttr,
{
    fn serialize_as_attribute(&self, start_tag: &mut quick_xml::events::BytesStart, name: &str) {
        T::serialize_as_attribute(self, start_tag, name);
    }
}

/// Implements serialization of a type as either an XML text node or attribute
/// value.
///
/// This is a convenience macro intended for implementing basic serialization of
/// primitive/standard library types. This is done per-type rather than
/// wholesale for `ToString` in order to avoid requiring that `Display` and
/// `XmlSerialize`/`XmlSerializeAttr` share a form.
macro_rules! impl_as_text_for {
    ($( $ty:ty ),*) => {
        $(
        /// Serializes an integer as a text content node.
        impl XmlSerialize for $ty {
            fn serialize_child_nodes<W>(&self, writer: &mut Writer<W>) -> Result<(), Error>
            where
                W: std::io::Write,
            {
                let string = self.to_string();
                writer.write_event(Event::Text(BytesText::new(&string)))?;

                Ok(())
            }
        }

        /// Serializes an integer as an XML attribute value.
        impl XmlSerializeAttr for $ty {
            fn serialize_as_attribute(
                &self,
                start_tag: &mut quick_xml::events::BytesStart,
                name: &str,
            ) {
                start_tag.push_attribute((name, self.to_string().as_str()));
            }
        })*
    };
}

impl_as_text_for!(i8, u8, i16, u16, i32, u32, i64, u64, usize);