mirror of https://gitlab.com/xmpp-rs/xmpp-rs.git
825 lines
29 KiB
Rust
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(())
|
|
}
|
|
}
|