xmpp-rs/xso/src/lib.rs

825 lines
29 KiB
Rust

#![deny(missing_docs)]
/*!
# Core types, macros and traits for parsing structs from XML
This crate provides the facilities for parsing XML data into Rust structs, and
vice versa. Think of it as an alternative[^serde-note] to serde, more suited to
XML.
To get started, use the [`FromXml`] and [`IntoXml`] derive macros
on your struct. See in particular the documentation of [`FromXml`] for
a full reference on the supported attributes.
[^serde-note]: Though it should be said that you can combine serde and this
crate on the same struct, no problem with that!
*/
pub mod error;
mod text;
#[doc(inline)]
pub use text::*;
/**
# Make a struct or enum parseable from XML
This macro generates the necessary trait implementations to convert a struct
from XML.
In order to work, structs, enums, enum variants and fields all must be
annotated using the `#[xml(..)]` meta. The insides of that meta are explained
below.
## Examples
```
# use xso::{FromXml};
static MY_NAMESPACE: &'static str = "urn:uuid:55c56882-3915-49de-a7ee-fd672d7a85cf";
#[derive(FromXml)]
#[xml(namespace = MY_NAMESPACE, name = "foo")]
struct Foo;
// parses <foo xmlns="urn:uuid:55c56882-3915-49de-a7ee-fd672d7a85cf"/>
```
## Field order
Field order **matters**. The fields are parsed in the order they are declared
(for children, anyway). If multiple fields match a given child element, the
first field which matches will be taken. The only exception is
`#[xml(elements)]` (without further arguments), which is always processed
last.
When XML is generated from a struct, the child elements are also generated
in the order of the fields. That means that passing an XML element through
FromXml and IntoXml may re-order some child elements.
Sorting order between elements which match the same field is generally
preserved, if the container preserves sort order on insertion.
## Struct attributes
- `namespace = ..`: This can be one of the following:
- `dyn`: allows dynamic namespace matching using the [`DynNamespaceEnum`]
trait. A struct with this namespace type needs to have exactly one field
with the `#[xml(namespace)]` annotation.
The type of that field must implement [`DynNamespaceEnum`] and will be
used to match the namespace of the XML elements.
- A path referring to a `&'static str` `static` which contains the
namespace URI of the XML element represented by the struct.
Required on non-`transparent` structs.
- `name = ..`: A string literal which contains the local XML name of
of the XML element represented by the struct.
Required on non-`transparent` structs.
- `validate = ..`: A path referring to a
`fn(&mut T) -> Result<(), xso::error::Error>`, where `T` is the
struct which is being defined. If set, the function will be called after
parsing has completed. If it returns an error, that error is returned
instead of the struct.
This attribute has no influence on [`IntoXml`].
- `prepare = ..`: A path referring to a
`fn(&mut T) -> ()`, where `T` is the struct which is being defined. If set,
the function will be called before the struct is converted into Element.
This attribute has no influence on [`FromXml`].
- `transparent`: Only allowed on tuple-like structs with exactly one field.
If set, the parsing is fully delegated to the inner field, which must in
turn implement `FromXml` (or `IntoXml` respectively).
Attributes on the single struct field are rejected.
`validate` is allowed and will be called.
- `element`, `element(..): Only allowed on tuple-like structs with exactly one
field. That field must be of type [`minidom::Element`].
Supports the following optional inner attributes:
- `namespace`: If given, restricts the namespace of the elements to parse.
Has no influence on XML generation: If the inner element has a different
namespace when the struct is serialized, that namespace will be used.
- `name`: If given, restricts the XML name of the elements to parse.
Has no influence on XML generation: If the inner element has a different
XML name when the struct is serialized, that namespace will be used.
`validate` is allowed and will be called.
- `on_unknown_child = ..` may be set to the identifier of a member of the
[`UnknownChildPolicy`] enum (i.e. for example `on_unknown_child = Ignore`).
This configures the behavior when unknown child elements are encountered.
See [`UnknownChildPolicy`] for details.
Has no effect on grandchildren.
- `on_unknown_attribute = ..` may be set to the identifier of a member of the
[`UnknownAttributePolicy`] enum (i.e. for example
`on_unknown_attribute = Ignore`). This configures the behavior when unknown
attributes are encountered. See [`UnknownAttributePolicy`] for details.
Has no effect on children.
- `wrapped_with(namespace = .., name = ..)`: If set, the struct will be wrapped
into an XML element with the given namespace and name. That means that
instead of `<inner/>`, on the wire, `<outer><inner/></outer>` (with the
corresponding XML names and namespaces) is expected and generated.
Other than the struct itself, the wrapping element must not have any
attributes or child elements, and it only supports static namespaces.
## Enums
Enums come in multiple flavors. All flavors have the following attributes:
- `validate = ..`: See struct attributes.
- `prepare = ..`: See struct attributes.
- `wrapped_with = ..`: See struct attributes.
The following flavors exist:
- fully dynamic: The variants must be each either transparent or specify
their `namespace` and `name`.
- XML name matched: The enum itself defines a namespace. Each variant must
then specify the name. The variant is picked based on the name of the XML
element.
- XML attribute matched: The enum itself defines a namespace, a name and an
attribute name. Each variant must specify the attribute value. The variant
is picked based on the value of the given attribute, provided that XML name
and namespace of the element itself match.
The flavor is determined based on the attributes of the enum declaration.
### Dynamic enums
No additional attributes are available on dynamic enumerations.
#### Dynamic enum variants
Dynamic enum variants work exactly like structs, except that the `prepare`
and `validate` attributes are not available.
### XML name matched enums
XML name matched enums support the following attributes:
- `namespace = ..` (required): This must be a path to a `&'static str`. It is
the namespace of the enumeration.
- `exhaustive` (flag): If present, the enum considers itself authoritative for
that namespace. If it encounters an element within the namespace which does
not match any variant, a fatal parsing error is returned.
This cannot be used if a variant is set as `fallback`.
This attribute has no relation to the Rust standard `#[non_exhaustive]`
attribute.
#### XML name matched enum variants
XML name matched enum variants support the following attributes:
- `name = ..` (required): String literal with the XML name to match against.
- `fallback` (flag): If present, the variant is parsed when no other variant
matches.
*Note:* When the enum is reserialized to XML, the XML name will be the one
declared in the `name` attribute of the variant; the original XML name is
lost.
### XML attribute matched enums
XML attribute matched enums support the following attributes:
- `namespace = ..` (required): This must be a path to a `&'static str`. It is
the namespace of the enumeration.
- `name = ..` (required): This must be a string literal containing the XML
name to match against.
- `attribute = ..` (required): This must be a string literal with the name of
the XML attribute to match against.
- `exhaustive` (flag): Must currently be set unless a variant is marked as
`fallback`. Support for non-exhaustive attribute-matched enums is not
implemented yet.
This cannot be used if a variant is set as `fallback`.
This attribute has no relation to the Rust standard `#[non_exhaustive]`
attribute.
- `normalize_with = ..`: Optional path to a thing which can be called with a
`&str` and which returns a [`std::borrow::Cow`]. If present, the attribute
value will be passed through that callable before it will be matched against
the enum variants.
#### XML attribute matched enum variants
XML attribute matched enum variants support the following attributes:
- `value = ..` (required): String literal with the attribute value to match
against.
- `fallback` (flag): If present, the variant is parsed when no other variant
matches.
*Note:* When the enum is reserialized to XML, the attribute value will be
the one declared in the `value` attribute of the variant; the original value
is lost.
## Field attributes
Field attributes are composed of a field kind, followed by a value or a list
of attributes. Examples:
```
# use xso::FromXml;
# static NS: &'static str = "urn:uuid:55c56882-3915-49de-a7ee-fd672d7a85cf";
# #[derive(FromXml)]
# #[xml(namespace = NS, name = "foo")]
# struct Foo {
#[xml(attribute)]
# f1: String,
#[xml(attribute = "foo")]
# f2: String,
#[xml(attribute(name = "foo"))]
# f3: String,
# }
```
If the `kind = ..` syntax is allowed, the attribute which is specified that
way will be marked as `default`.
The following field kinds are available:
- `attribute`, `attribute = name`, `attribute(..)`:
Extract a string from an XML attribute. The field type must
implement `FromOptionalXmlText` (for [`FromXml`]) or
`IntoOptionalXmlText` (for [`IntoXml`]), unless the `codec` option is
set.
- `name = ..` (default): The XML name of the attribute. If this is not
set, the field's identifier is used.
- `namespace = ..`: The XML namespace of the attribute. This is optional,
and if absent, only unnamespaced attributes are considered.
- `default`, `default = ..`: If set, a field value is generated if the
attribute is not present and [`FromOptionalXmlText`] did not create a
value from [`None`], instead of failing to parse. If the optional
argument is present, it must be the path to a callable which returns the
field's type. Otherwise, [`std::default::Default::default`] is used.
- `codec = ..`: Path to a type implementing [`TextCodec`] to use instead
of the [`FromOptionalXmlText`] / [`IntoOptionalXmlText`] implementation
of the field's type.
If set, you need to explicitly add the `default` flag to fields of type
`Option<_>`, because the default option logic of [`FromOptionalXmlText`]
is not present.
- `child(.., extract(..))`: Extract data from a child element.
- `name = ..` (required): The XML name of the child to match.
- `namespace = ..` (required): The XML namespace of the child to match. This
can be one of the following:
- A path referring to a `&'static str` `static` which contains the
namespace URI of the XML element represented by the struct.
- `super`: Only usable inside compounds with `#[xml(namespace = dyn)]`,
using `#[xml(namespace = super)]` on an extracted field allows to match
the field's child's namespace with the dynamically determined namespace
of the parent (both during serialisation and during deserialisation).
- `extract(..)` (required): Specification of data to extract. See below
for options.
- `skip_if`: If set, this must be the path to a callable. That callable is
invoked with a reference to the field's type at serialisation time. If
the callable returns true, the field is omitted from the output
completely.
This should often be combined with `default`.
- `default`, `default = ..`: If set, a field value is generated if the
child is not present instead of failing to parse. If the optional argument
is present, it must be the path to a callable which returns the field's
type. Otherwise, [`std::default::Default::default`] is used.
*Note:* When using `extract(..)`, this is required even when the field's
type is `Option<..>`.
- `child`, `child(..)` (without `extract(..)`): Extract an entire child
element. The field type must implement [`FromXml`] (for [`FromXml`])
or `IntoXml` (for [`IntoXml`]).
- `namespace = super`: If set, the field must also implement
[`DynNamespace`] and the compound the field is in must be set to be
`namespace = dyn`. In this case, the field's child is forced to be in the
same namespace as the parent during parsing.
- `skip_if`: If set, this must be the path to a callable. That callable is
invoked with a reference to the field's type at serialisation time. If
the callable returns true, the field is omitted from the output
completely.
This should often be combined with `default`.
- `default`, `default = ..`: If set, a field value is generated if the
child is not present instead of failing to parse. If the optional argument
is present, it must be the path to a callable which returns the field's
type. Otherwise, [`std::default::Default::default`] is used.
*Note:* When using `extract(..)`, this is required even when the field's
type is `Option<..>`.
Aside from `namespace = super`, matching of the XML namespace / name is
completely delegated to the [`FromXml`] implementation of the field's type
and thus the `namespace` and `name` attributes are not allowed.
- `children(.., extract(..))`: Like `child(.., extract(..))`, with the following
differences:
- More than one clause inside `extract(..)` are allowed.
- More than one matching child is allowed
- The field type must implement [`Default`][`std::default::Default`],
[`Extend<T>`][`std::iter::Extend`] and
[`IntoIterator<Item = T>`][`std::iter::IntoIterator`].
`T`, must be a tuple type matching the types provided by the
extracted parts.
- Extracts must specify their type, because it cannot be inferred through
the collection.
- `children`, `children(..)` (without `extract(..)`): Extract zero or more
entire child elements. The field type must implement
[`Default`][`std::default::Default`],
[`Extend<T>`][`std::iter::Extend`] and
[`IntoIterator<Item = T>`][`std::iter::IntoIterator`], where `T`
implements [`FromXml`] (and [`IntoXml`] for [`IntoXml`]).
- `skip_if`: If set, this must be the path to a callable. That callable is
invoked with a reference to the field's type at serialisation time. If
the callable returns true, the field is omitted from the output
completely.
This should often be combined with `default`.
The namespace and name to match are determined by the field type, thus it
is not allowed to specify them here. `namespace = super` is not supported.
- `text`, `text(..): Extract the element's text contents. The field type must
implement `FromXmlText` (for [`FromXml`]) or `IntoXmlText`
(for [`IntoXml`]), unless the `codec` option is set.
- `codec = ..`: Path to a type implementing [`TextCodec`] to use instead
of the [`FromXmlText`] / [`IntoXmlText`] implementation of the field's
type.
- `element`, `element(..)`: Collect a single element as [`minidom::Element`]
instead of attempting to destructure it. The field type must implement
`From<Element>` and `Into<Option<Element>>` ([`minidom::Element`] implements
both).
- `name = ..` (optional): The XML name of the element to match.
- `namespace = ..` (optional): The XML namespace of the element to match.
- `default`, `default = ..`: If set, a field value is generated if the
child is not present instead of failing to parse. If the optional argument
is present, it must be the path to a callable which returns the field's
type. Otherwise, [`std::default::Default::default`] is used.
If the field converts into `None` when invoking `Into<Option<Element>>`,
the element is omitted from the output altogether.
- `elements(..)`: Collect otherwise unknown children as [`minidom::Element`].
- `namespace = ..`: The XML namespace of the element to match.
- `name = ..` (optional): The XML name of the element to match. If omitted,
all elements from the given namespace are collected.
- `elements`: Collect all unknown children as [`minidom::Element`]. The field
type must be `Vec<Element>`.
- `namespace`: Represent the parent struct/enum variant's XML namespace. This
requires that the compound is declared with `#[xml(namespace = dyn)]`. The
field type must implement [`DynNamespaceEnum`].
- `ignore`: The field is not considered during parsing or serialisation. The
type must implement [`Default`].
### Extraction specification
Inside `extract(..)`, there must be a list of type annotations as used on
fields. All annotations can be used which can also be used on fields. Because
here there is no possibility to do any inferrence on the field type, the
following field attributes support an additional, optional `type` argument:
- `attribute`
- `text`
If the `extract(..)` contains exactly one part and the type of the extract
is not specified on that one part, it is assumed to be equal to the type of
the field the extract is used on.
Otherwise, the default is `String`, which is not going to work in many cases.
This limitation could be lifted, but we need a use case for it first :). So
if you run into this file an issue please!
*/
pub use xso_proc::FromXml;
/**
# Make a struct or enum convertible into XML
For all supported attributes, please see [`FromXml`].
*/
pub use xso_proc::IntoXml;
/**
# Make a struct fully dynamically namespaceable
For all supported attributes, please see [`FromXml`].
*/
pub use xso_proc::DynNamespace;
#[doc(hidden)]
pub mod exports {
pub use minidom;
}
use jid::{BareJid, FullJid, Jid};
macro_rules! from_text_via_parse {
($cons:path, $($t:ty,)+) => {
$(
impl FromXmlText for $t {
fn from_xml_text(s: &str) -> Result<Self, error::Error> {
s.parse().map_err($cons)
}
}
impl IntoXmlText for $t {
fn into_xml_text(self) -> String {
self.to_string()
}
}
)+
}
}
from_text_via_parse! {
error::Error::ParseIntError,
u8,
u16,
u32,
u64,
u128,
usize,
i8,
i16,
i32,
i64,
i128,
isize,
}
from_text_via_parse! {
error::Error::ParseAddrError,
std::net::IpAddr,
std::net::Ipv4Addr,
std::net::Ipv6Addr,
}
from_text_via_parse! {
error::Error::JidParseError,
Jid,
FullJid,
BareJid,
}
/// Convert XML text to a value.
pub trait FromXmlText: Sized {
/// Construct a value from XML text.
///
/// This is similar to [`std::str::FromStr`], but the error type is fixed.
fn from_xml_text(s: &str) -> Result<Self, error::Error>;
}
impl FromXmlText for String {
/// Copy the string from the source into the result.
fn from_xml_text(s: &str) -> Result<Self, error::Error> {
Ok(s.to_string())
}
}
/// Convert a value into XML text.
pub trait IntoXmlText {
/// Consume the value and return it as XML string.
fn into_xml_text(self) -> String;
}
impl IntoXmlText for String {
fn into_xml_text(self) -> String {
self
}
}
/// Provide construction of a value from optional XML text data.
///
/// Most likely, you don't need to implement this; implement [`FromXmlText`]
/// instead (which automatically provides a [`FromOptionalXmlText`]
/// implementation).
///
/// This trait has to exist to handle the special-ness of `Option<_>` when it
/// comes to values which don't always exist.
pub trait FromOptionalXmlText: Sized {
/// Convert the XML string to a value, maybe.
///
/// This should return `None` if and only if `s` is `None`, but in some
/// cases it makes sense to return `Some(..)` even for an input of
/// `None`, e.g. when a specific default is desired.
fn from_optional_xml_text(s: Option<&str>) -> Result<Option<Self>, error::Error>;
}
impl<T: FromXmlText> FromOptionalXmlText for T {
fn from_optional_xml_text(s: Option<&str>) -> Result<Option<Self>, error::Error> {
s.map(T::from_xml_text).transpose()
}
}
impl<T: FromOptionalXmlText> FromOptionalXmlText for Option<T> {
/// This implementation returns `Some(None)` for an input of `None`.
///
/// That way, absent attributes with a `Option<T>` field type effectively
/// default to `None`.
fn from_optional_xml_text(s: Option<&str>) -> Result<Option<Self>, error::Error> {
match s {
None => Ok(Some(None)),
Some(v) => Ok(Some(T::from_optional_xml_text(Some(v))?)),
}
}
}
/// Provide destruction of a value into optional XML text data.
///
/// Most likely, you don't need to implement this; implement [`IntoXmlText`]
/// instead (which automatically provides a [`IntoOptionalXmlText`]
/// implementation).
///
/// This trait has to exist to handle the special-ness of `Option<_>` when it
/// comes to values which don't always exist.
pub trait IntoOptionalXmlText {
/// Destruct the value into an optional string.
///
/// Returning `None` causes the resulting XML object (likely an attribute,
/// but maybe also an extracted child) to not appear in the output.
fn into_optional_xml_text(self) -> Option<String>;
}
impl<T: IntoXmlText> IntoOptionalXmlText for T {
fn into_optional_xml_text(self) -> Option<String> {
Some(self.into_xml_text())
}
}
impl<T: IntoOptionalXmlText> IntoOptionalXmlText for Option<T> {
fn into_optional_xml_text(self) -> Option<String> {
match self {
None => None,
Some(v) => v.into_optional_xml_text(),
}
}
}
/// Discard the value and do not emit a value.
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Discard;
impl FromXmlText for Discard {
fn from_xml_text(_: &str) -> Result<Self, error::Error> {
Ok(Self)
}
}
impl IntoOptionalXmlText for Discard {
fn into_optional_xml_text(self) -> Option<String> {
None
}
}
/// Provide construction of structs from XML (sub-)trees.
///
/// This trait is what is really implemented by the [`FromXml`] derive
/// macro.
pub trait FromXml: Sized {
/// Convert an XML subtree into a struct or fail with an error.
fn from_tree(tree: minidom::Element) -> Result<Self, error::Error>;
/// Provide an optional default if the element is absent.
///
/// This is used to automatically make `Option<T>` default to `None`.
fn absent() -> Option<Self>;
}
impl<T: FromXml> FromXml for Option<T> {
fn from_tree(tree: minidom::Element) -> Result<Self, error::Error> {
Ok(Some(T::from_tree(tree)?))
}
fn absent() -> Option<Self> {
Some(T::absent())
}
}
/// Convert a struct into an XML tree.
///
/// This trait is what is really implemented by the [`IntoXml`] derive
/// macro.
pub trait IntoXml {
/// Destruct the value into an optional [`minidom::Element`].
///
/// When returning `None`, no element will appear in the output. This
/// should only be used for values which can be constructed via
/// [`FromXml::absent`] in order to ensure that the result can be parsed
/// again.
fn into_tree(self) -> Option<minidom::Element>;
}
impl<T: IntoXml> IntoXml for Option<T> {
fn into_tree(self) -> Option<minidom::Element> {
self?.into_tree()
}
}
/// Enumeration of possible dynamically determined namespaces.
///
/// This trait must be implemented on types used on `#[xml(namespace)]`
/// fields.
///
/// It allows to specify XML elements which can be parsed in different
/// namespaces. This can be useful in weird protocols (looking at you, XMPP)
/// where the same element occurs in multiple different namespaces. The
/// namespace is then kept in the field and is thus available at serialisation
/// time.
///
/// This trait depends on `PartialEq<str>`; this is used to compare a value
/// against the value of an `xmlns` attribute without requiring to go through
/// (potentially costly and also error-inducing) [`Self::from_xml_text`].
pub trait DynNamespaceEnum: Sized + PartialEq<str> {
/// Parse the namespace from the `xmlns` attribute data.
///
/// This should return [`Mismatch`][`crate::error::DynNamespaceError`] for
/// namespaces which are not part of the enumeration of matching
/// namespaces. In such cases, the parser may attempt to parse using
/// another [`FromXml`] implementation.
///
/// You can also return [`Invalid`][`crate::error::DynNamespaceError`] to
/// abort parsing altogether; this is probably not what you want, though.
fn from_xml_text(s: &str) -> Result<Self, self::error::DynNamespaceError>;
/// Convert the namespace into the text value of an `xmlns` attribute.
fn into_xml_text(self) -> String;
}
/// Trait for structs implementing [`FromXml`] with `namespace = dyn`.
///
/// This allows to access the namespace of these structs without needing
/// knowledge about their internal structure.
pub trait DynNamespace {
/// The namespace enum for this struct.
type Namespace: DynNamespaceEnum;
/// Return a reference to the struct's namespace.
fn namespace(&self) -> &Self::Namespace;
/// Recursively set this struct's namespace.
///
/// This will set the namespace for the struct itself as well as for
/// fields referring to child structs with `namespace = super`.
fn set_namespace<T: Into<Self::Namespace>>(&mut self, ns: T);
}
/// Configure how unknown child elements are handled.
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum UnknownChildPolicy {
/// Fail parsing with a fatal error.
#[cfg_attr(not(feature = "disable-validation"), default)]
Fail,
/// Ignore and discard any unknown child elements.
#[cfg_attr(feature = "disable-validation", default)]
Ignore,
}
impl UnknownChildPolicy {
// TODO: once we have a more suitable error message structure, we should
// not pass the entire message down to this function.
/// Return a result describing the result of triggering the child policy.
///
/// In other words, this returns an error iff the policy is set to
/// `Fail`.
///
/// **Note:** This function is not to be considered part of the public
/// API! It is only marked `pub` because it needs to be called from
/// macro-generated code! Its signature and behavior may change without
/// notice and without major version bump at any time.
pub fn trigger(&self, msg: &'static str) -> Result<(), error::Error> {
match self {
Self::Fail => Err(error::Error::ParseError(msg)),
Self::Ignore => Ok(()),
}
}
}
/// Configure how unknown attributes are handled.
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum UnknownAttributePolicy {
/// Fail parsing with a fatal error.
#[cfg_attr(not(feature = "disable-validation"), default)]
Fail,
/// Ignore and discard any unknown attributes.
#[cfg_attr(feature = "disable-validation", default)]
Ignore,
}
impl UnknownAttributePolicy {
// TODO: once we have a more suitable error message structure, we should
// not pass the entire message down to this function.
/// Return a result describing the result of triggering the attribute
/// policy.
///
/// In other words, this returns an error iff the policy is set to
/// `Fail`.
///
/// **Note:** This function is not to be considered part of the public
/// API! It is only marked `pub` because it needs to be called from
/// macro-generated code! Its signature and behavior may change without
/// notice and without major version bump at any time.
pub fn trigger(&self, msg: &'static str) -> Result<(), error::Error> {
match self {
Self::Fail => Err(error::Error::ParseError(msg)),
Self::Ignore => Ok(()),
}
}
}
/// Trait to support destructuring of child structs beyond what the
/// `extract(..)` attribute can deliver.
///
/// This trait can only be sensibly implemented on types which implement both
/// `FromXml` and `IntoXml`. However, as there may be corner cases where only
/// one of these other traits is needed, they're not strictly included in the
/// trait bounds.
///
/// When used as value for `codec = ..` inside a `#[xml(child(..))]` or
/// `#[xml(children(..))]` field attribute, the field is destructured using
/// the trait implementations of `FromXml` / `IntoXml` and then converted
/// to the actual field's type by invoking the `ElementCodec<T>` methods, with
/// `T` being the field type.
pub trait ElementCodec<T> {
/// Transform the destructured value further toward the field type.
fn decode(value: Self) -> T;
/// Transform the field type back to something which can be structured
/// into XML.
fn encode(value: T) -> Self;
}
impl<T> ElementCodec<T> for T {
fn decode(value: Self) -> Self {
value
}
fn encode(value: Self) -> Self {
value
}
}
/// Trait for fallible extension.
///
/// You probably won't need to implement this---it is automatically implemented
/// for all containers which implement [`std::iter::Extend`].
///
/// The only useful case are containers where an extension may fail because of
/// data reasons, e.g. if you collect elements of multiple types but it can
/// only contain a single type at a time.
pub trait TryExtend<A> {
/// Attempt extending the container with the given iterator.
///
/// Upon error, the container may be partially extended.
fn try_extend<T: IntoIterator<Item = A>>(&mut self, iter: T) -> Result<(), self::error::Error>;
}
impl<A, T: Extend<A>> TryExtend<A> for T {
fn try_extend<I: IntoIterator<Item = A>>(&mut self, iter: I) -> Result<(), self::error::Error> {
self.extend(iter);
Ok(())
}
}