xso: add support for dynamic enums
This commit is contained in:
parent
b36d1d0d4c
commit
fb80bb7532
5 changed files with 329 additions and 46 deletions
|
|
@ -1600,3 +1600,36 @@ fn transparent_struct_named_roundtrip() {
|
||||||
};
|
};
|
||||||
roundtrip_full::<TransparentStructNamed>("<attr xmlns='urn:example:ns1' foo='bar'/>");
|
roundtrip_full::<TransparentStructNamed>("<attr xmlns='urn:example:ns1' foo='bar'/>");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(FromXml, AsXml, PartialEq, Debug, Clone)]
|
||||||
|
#[xml()]
|
||||||
|
enum DynamicEnum {
|
||||||
|
#[xml(transparent)]
|
||||||
|
A(RequiredAttribute),
|
||||||
|
|
||||||
|
#[xml(namespace = NS2, name = "b")]
|
||||||
|
B {
|
||||||
|
#[xml(text)]
|
||||||
|
contents: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dynamic_enum_roundtrip_a() {
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use std::{
|
||||||
|
option::Option::{None, Some},
|
||||||
|
result::Result::{Err, Ok},
|
||||||
|
};
|
||||||
|
roundtrip_full::<DynamicEnum>("<attr xmlns='urn:example:ns1' foo='bar'/>");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dynamic_enum_roundtrip_b() {
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use std::{
|
||||||
|
option::Option::{None, Some},
|
||||||
|
result::Result::{Err, Ok},
|
||||||
|
};
|
||||||
|
roundtrip_full::<DynamicEnum>("<b xmlns='urn:example:ns2'>hello world</b>");
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,9 +17,11 @@ use crate::compound::Compound;
|
||||||
use crate::error_message::ParentRef;
|
use crate::error_message::ParentRef;
|
||||||
use crate::meta::{reject_key, Flag, NameRef, NamespaceRef, QNameRef, XmlCompoundMeta};
|
use crate::meta::{reject_key, Flag, NameRef, NamespaceRef, QNameRef, XmlCompoundMeta};
|
||||||
use crate::state::{AsItemsStateMachine, FromEventsStateMachine};
|
use crate::state::{AsItemsStateMachine, FromEventsStateMachine};
|
||||||
|
use crate::structs::StructInner;
|
||||||
use crate::types::{ref_ty, ty_from_ident};
|
use crate::types::{ref_ty, ty_from_ident};
|
||||||
|
|
||||||
/// The definition of an enum variant, switched on the XML element's name.
|
/// The definition of an enum variant, switched on the XML element's name,
|
||||||
|
/// inside a [`NameSwitchedEnum`].
|
||||||
struct NameVariant {
|
struct NameVariant {
|
||||||
/// The XML name of the element to map the enum variant to.
|
/// The XML name of the element to map the enum variant to.
|
||||||
name: NameRef,
|
name: NameRef,
|
||||||
|
|
@ -140,6 +142,8 @@ impl NameVariant {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The definition of a enum which switches based on the XML element name,
|
||||||
|
/// with the XML namespace fixed.
|
||||||
struct NameSwitchedEnum {
|
struct NameSwitchedEnum {
|
||||||
/// The XML namespace of the element to map the enum to.
|
/// The XML namespace of the element to map the enum to.
|
||||||
namespace: NamespaceRef,
|
namespace: NamespaceRef,
|
||||||
|
|
@ -153,36 +157,10 @@ struct NameSwitchedEnum {
|
||||||
|
|
||||||
impl NameSwitchedEnum {
|
impl NameSwitchedEnum {
|
||||||
fn new<'x, I: IntoIterator<Item = &'x Variant>>(
|
fn new<'x, I: IntoIterator<Item = &'x Variant>>(
|
||||||
meta: XmlCompoundMeta,
|
namespace: NamespaceRef,
|
||||||
|
exhaustive: Flag,
|
||||||
variant_iter: I,
|
variant_iter: I,
|
||||||
) -> Result<Self> {
|
) -> Result<Self> {
|
||||||
// We destructure here so that we get informed when new fields are
|
|
||||||
// added and can handle them, either by processing them or raising
|
|
||||||
// an error if they are present.
|
|
||||||
let XmlCompoundMeta {
|
|
||||||
span: meta_span,
|
|
||||||
qname: QNameRef { namespace, name },
|
|
||||||
exhaustive,
|
|
||||||
debug,
|
|
||||||
builder,
|
|
||||||
iterator,
|
|
||||||
transparent,
|
|
||||||
} = meta;
|
|
||||||
|
|
||||||
// These must've been cleared by the caller. Because these being set
|
|
||||||
// is a programming error (in xso-proc) and not a usage error, we
|
|
||||||
// assert here instead of using reject_key!.
|
|
||||||
assert!(builder.is_none());
|
|
||||||
assert!(iterator.is_none());
|
|
||||||
assert!(!debug.is_set());
|
|
||||||
|
|
||||||
reject_key!(name not on "enums" only on "their variants");
|
|
||||||
reject_key!(transparent flag not on "enums" only on "structs");
|
|
||||||
|
|
||||||
let Some(namespace) = namespace else {
|
|
||||||
return Err(Error::new(meta_span, "`namespace` is required on enums"));
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut variants = Vec::new();
|
let mut variants = Vec::new();
|
||||||
let mut seen_names = HashMap::new();
|
let mut seen_names = HashMap::new();
|
||||||
for variant in variant_iter {
|
for variant in variant_iter {
|
||||||
|
|
@ -207,6 +185,7 @@ impl NameSwitchedEnum {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Build the deserialisation statemachine for the name-switched enum.
|
||||||
fn make_from_events_statemachine(
|
fn make_from_events_statemachine(
|
||||||
&self,
|
&self,
|
||||||
target_ty_ident: &Ident,
|
target_ty_ident: &Ident,
|
||||||
|
|
@ -241,6 +220,7 @@ impl NameSwitchedEnum {
|
||||||
Ok(statemachine)
|
Ok(statemachine)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Build the serialisation statemachine for the name-switched enum.
|
||||||
fn make_as_item_iter_statemachine(
|
fn make_as_item_iter_statemachine(
|
||||||
&self,
|
&self,
|
||||||
target_ty_ident: &Ident,
|
target_ty_ident: &Ident,
|
||||||
|
|
@ -261,10 +241,211 @@ impl NameSwitchedEnum {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The definition of an enum variant in a [`DynamicEnum`].
|
||||||
|
struct DynamicVariant {
|
||||||
|
/// The identifier of the enum variant.
|
||||||
|
ident: Ident,
|
||||||
|
|
||||||
|
/// The definition of the struct-like which resembles the enum variant.
|
||||||
|
inner: StructInner,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DynamicVariant {
|
||||||
|
fn new(variant: &Variant) -> Result<Self> {
|
||||||
|
let ident = variant.ident.clone();
|
||||||
|
let meta = XmlCompoundMeta::parse_from_attributes(&variant.attrs)?;
|
||||||
|
|
||||||
|
// We destructure here so that we get informed when new fields are
|
||||||
|
// added and can handle them, either by processing them or raising
|
||||||
|
// an error if they are present.
|
||||||
|
let XmlCompoundMeta {
|
||||||
|
span: _,
|
||||||
|
qname: _, // used by StructInner
|
||||||
|
ref exhaustive,
|
||||||
|
ref debug,
|
||||||
|
ref builder,
|
||||||
|
ref iterator,
|
||||||
|
transparent: _, // used by StructInner
|
||||||
|
} = meta;
|
||||||
|
|
||||||
|
reject_key!(debug flag not on "enum variants" only on "enums and structs");
|
||||||
|
reject_key!(exhaustive flag not on "enum variants" only on "enums");
|
||||||
|
reject_key!(builder not on "enum variants" only on "enums and structs");
|
||||||
|
reject_key!(iterator not on "enum variants" only on "enums and structs");
|
||||||
|
|
||||||
|
let inner = StructInner::new(meta, &variant.fields)?;
|
||||||
|
Ok(Self { ident, inner })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The definition of an enum where each variant is a completely unrelated
|
||||||
|
/// possible XML subtree.
|
||||||
|
struct DynamicEnum {
|
||||||
|
/// The enum variants.
|
||||||
|
variants: Vec<DynamicVariant>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DynamicEnum {
|
||||||
|
fn new<'x, I: IntoIterator<Item = &'x Variant>>(variant_iter: I) -> Result<Self> {
|
||||||
|
let mut variants = Vec::new();
|
||||||
|
for variant in variant_iter {
|
||||||
|
variants.push(DynamicVariant::new(variant)?);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self { variants })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build the deserialisation statemachine for the dynamic enum.
|
||||||
|
fn make_from_events_statemachine(
|
||||||
|
&self,
|
||||||
|
target_ty_ident: &Ident,
|
||||||
|
state_ty_ident: &Ident,
|
||||||
|
) -> Result<FromEventsStateMachine> {
|
||||||
|
let mut statemachine = FromEventsStateMachine::new();
|
||||||
|
for variant in self.variants.iter() {
|
||||||
|
let submachine = variant.inner.make_from_events_statemachine(
|
||||||
|
state_ty_ident,
|
||||||
|
&ParentRef::Named(Path {
|
||||||
|
leading_colon: None,
|
||||||
|
segments: [
|
||||||
|
PathSegment::from(target_ty_ident.clone()),
|
||||||
|
variant.ident.clone().into(),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
}),
|
||||||
|
&variant.ident.to_string(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
statemachine.merge(submachine.compile());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(statemachine)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build the serialisation statemachine for the dynamic enum.
|
||||||
|
fn make_as_item_iter_statemachine(
|
||||||
|
&self,
|
||||||
|
target_ty_ident: &Ident,
|
||||||
|
state_ty_ident: &Ident,
|
||||||
|
item_iter_ty_lifetime: &Lifetime,
|
||||||
|
) -> Result<AsItemsStateMachine> {
|
||||||
|
let mut statemachine = AsItemsStateMachine::new();
|
||||||
|
for variant in self.variants.iter() {
|
||||||
|
let submachine = variant.inner.make_as_item_iter_statemachine(
|
||||||
|
&ParentRef::Named(Path {
|
||||||
|
leading_colon: None,
|
||||||
|
segments: [
|
||||||
|
PathSegment::from(target_ty_ident.clone()),
|
||||||
|
variant.ident.clone().into(),
|
||||||
|
]
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
}),
|
||||||
|
state_ty_ident,
|
||||||
|
&variant.ident.to_string(),
|
||||||
|
item_iter_ty_lifetime,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
statemachine.merge(submachine.compile());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(statemachine)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The definition of an enum.
|
||||||
|
enum EnumInner {
|
||||||
|
/// The enum switches based on the XML name of the element, with the XML
|
||||||
|
/// namespace fixed.
|
||||||
|
NameSwitched(NameSwitchedEnum),
|
||||||
|
|
||||||
|
/// The enum consists of variants with entirely unrelated XML structures.
|
||||||
|
Dynamic(DynamicEnum),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EnumInner {
|
||||||
|
fn new<'x, I: IntoIterator<Item = &'x Variant>>(
|
||||||
|
meta: XmlCompoundMeta,
|
||||||
|
variant_iter: I,
|
||||||
|
) -> Result<Self> {
|
||||||
|
// We destructure here so that we get informed when new fields are
|
||||||
|
// added and can handle them, either by processing them or raising
|
||||||
|
// an error if they are present.
|
||||||
|
let XmlCompoundMeta {
|
||||||
|
span: _,
|
||||||
|
qname: QNameRef { namespace, name },
|
||||||
|
exhaustive,
|
||||||
|
debug,
|
||||||
|
builder,
|
||||||
|
iterator,
|
||||||
|
transparent,
|
||||||
|
} = meta;
|
||||||
|
|
||||||
|
// These must've been cleared by the caller. Because these being set
|
||||||
|
// is a programming error (in xso-proc) and not a usage error, we
|
||||||
|
// assert here instead of using reject_key!.
|
||||||
|
assert!(builder.is_none());
|
||||||
|
assert!(iterator.is_none());
|
||||||
|
assert!(!debug.is_set());
|
||||||
|
|
||||||
|
reject_key!(name not on "enums" only on "their variants");
|
||||||
|
reject_key!(transparent flag not on "enums" only on "structs");
|
||||||
|
|
||||||
|
if let Some(namespace) = namespace {
|
||||||
|
Ok(Self::NameSwitched(NameSwitchedEnum::new(
|
||||||
|
namespace,
|
||||||
|
exhaustive,
|
||||||
|
variant_iter,
|
||||||
|
)?))
|
||||||
|
} else {
|
||||||
|
reject_key!(exhaustive flag not on "dynamic enums" only on "name-switched enums");
|
||||||
|
Ok(Self::Dynamic(DynamicEnum::new(variant_iter)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build the deserialisation statemachine for the enum.
|
||||||
|
fn make_from_events_statemachine(
|
||||||
|
&self,
|
||||||
|
target_ty_ident: &Ident,
|
||||||
|
state_ty_ident: &Ident,
|
||||||
|
) -> Result<FromEventsStateMachine> {
|
||||||
|
match self {
|
||||||
|
Self::NameSwitched(ref inner) => {
|
||||||
|
inner.make_from_events_statemachine(target_ty_ident, state_ty_ident)
|
||||||
|
}
|
||||||
|
Self::Dynamic(ref inner) => {
|
||||||
|
inner.make_from_events_statemachine(target_ty_ident, state_ty_ident)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Build the serialisation statemachine for the enum.
|
||||||
|
fn make_as_item_iter_statemachine(
|
||||||
|
&self,
|
||||||
|
target_ty_ident: &Ident,
|
||||||
|
state_ty_ident: &Ident,
|
||||||
|
item_iter_ty_lifetime: &Lifetime,
|
||||||
|
) -> Result<AsItemsStateMachine> {
|
||||||
|
match self {
|
||||||
|
Self::NameSwitched(ref inner) => inner.make_as_item_iter_statemachine(
|
||||||
|
target_ty_ident,
|
||||||
|
state_ty_ident,
|
||||||
|
item_iter_ty_lifetime,
|
||||||
|
),
|
||||||
|
Self::Dynamic(ref inner) => inner.make_as_item_iter_statemachine(
|
||||||
|
target_ty_ident,
|
||||||
|
state_ty_ident,
|
||||||
|
item_iter_ty_lifetime,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Definition of an enum and how to parse it.
|
/// Definition of an enum and how to parse it.
|
||||||
pub(crate) struct EnumDef {
|
pub(crate) struct EnumDef {
|
||||||
/// Implementation of the enum itself
|
/// Implementation of the enum itself
|
||||||
inner: NameSwitchedEnum,
|
inner: EnumInner,
|
||||||
|
|
||||||
/// Name of the target type.
|
/// Name of the target type.
|
||||||
target_ty_ident: Ident,
|
target_ty_ident: Ident,
|
||||||
|
|
@ -299,7 +480,7 @@ impl EnumDef {
|
||||||
let debug = meta.debug.take().is_set();
|
let debug = meta.debug.take().is_set();
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
inner: NameSwitchedEnum::new(meta, variant_iter)?,
|
inner: EnumInner::new(meta, variant_iter)?,
|
||||||
target_ty_ident: ident.clone(),
|
target_ty_ident: ident.clone(),
|
||||||
builder_ty_ident,
|
builder_ty_ident,
|
||||||
item_iter_ty_ident,
|
item_iter_ty_ident,
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ pub const XMLNS_XMLNS: &str = "http://www.w3.org/2000/xmlns/";
|
||||||
|
|
||||||
macro_rules! reject_key {
|
macro_rules! reject_key {
|
||||||
($key:ident not on $not_allowed_on:literal $(only on $only_allowed_on:literal)?) => {
|
($key:ident not on $not_allowed_on:literal $(only on $only_allowed_on:literal)?) => {
|
||||||
if let Some($key) = $key {
|
if let Some(ref $key) = $key {
|
||||||
return Err(Error::new_spanned(
|
return Err(Error::new_spanned(
|
||||||
$key,
|
$key,
|
||||||
concat!(
|
concat!(
|
||||||
|
|
@ -43,9 +43,9 @@ macro_rules! reject_key {
|
||||||
};
|
};
|
||||||
|
|
||||||
($key:ident flag not on $not_allowed_on:literal $(only on $only_allowed_on:literal)?) => {
|
($key:ident flag not on $not_allowed_on:literal $(only on $only_allowed_on:literal)?) => {
|
||||||
if let Flag::Present($key) = $key {
|
if let Flag::Present(ref $key) = $key {
|
||||||
return Err(Error::new(
|
return Err(Error::new(
|
||||||
$key,
|
*$key,
|
||||||
concat!(
|
concat!(
|
||||||
"`",
|
"`",
|
||||||
stringify!($key),
|
stringify!($key),
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ use crate::types::{
|
||||||
/// The inner parts of the struct.
|
/// The inner parts of the struct.
|
||||||
///
|
///
|
||||||
/// This contains all data necessary for the matching logic.
|
/// This contains all data necessary for the matching logic.
|
||||||
enum StructInner {
|
pub(crate) enum StructInner {
|
||||||
/// Single-field struct declared with `#[xml(transparent)]`.
|
/// Single-field struct declared with `#[xml(transparent)]`.
|
||||||
///
|
///
|
||||||
/// Transparent struct delegate all parsing and serialising to their
|
/// Transparent struct delegate all parsing and serialising to their
|
||||||
|
|
@ -58,7 +58,7 @@ enum StructInner {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StructInner {
|
impl StructInner {
|
||||||
fn new(meta: XmlCompoundMeta, fields: &Fields) -> Result<Self> {
|
pub(crate) fn new(meta: XmlCompoundMeta, fields: &Fields) -> Result<Self> {
|
||||||
// We destructure here so that we get informed when new fields are
|
// We destructure here so that we get informed when new fields are
|
||||||
// added and can handle them, either by processing them or raising
|
// added and can handle them, either by processing them or raising
|
||||||
// an error if they are present.
|
// an error if they are present.
|
||||||
|
|
@ -150,7 +150,7 @@ impl StructInner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_from_events_statemachine(
|
pub(crate) fn make_from_events_statemachine(
|
||||||
&self,
|
&self,
|
||||||
state_ty_ident: &Ident,
|
state_ty_ident: &Ident,
|
||||||
output_name: &ParentRef,
|
output_name: &ParentRef,
|
||||||
|
|
@ -225,7 +225,7 @@ impl StructInner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_as_item_iter_statemachine(
|
pub(crate) fn make_as_item_iter_statemachine(
|
||||||
&self,
|
&self,
|
||||||
input_name: &ParentRef,
|
input_name: &ParentRef,
|
||||||
state_ty_ident: &Ident,
|
state_ty_ident: &Ident,
|
||||||
|
|
|
||||||
|
|
@ -81,9 +81,27 @@ implement [`FromXml`] in order to derive `FromXml` and [`AsXml`] in order to
|
||||||
derive `AsXml`. The struct will be (de-)serialised exactly like the type of
|
derive `AsXml`. The struct will be (de-)serialised exactly like the type of
|
||||||
that single field. This allows a newtype-like pattern for XSO structs.
|
that single field. This allows a newtype-like pattern for XSO structs.
|
||||||
|
|
||||||
### Enum meta
|
### Enums
|
||||||
|
|
||||||
The following keys are defined on enums:
|
Two different `enum` flavors are supported:
|
||||||
|
|
||||||
|
1. [**Name-switched enums**](#name-switched-enum-meta) have a fixed XML
|
||||||
|
namespace they match on and each variant corresponds to a different XML
|
||||||
|
element name within that namespace.
|
||||||
|
|
||||||
|
2. [**Dynamic enums**](#dynamic-enum-meta) have entirely unrelated variants.
|
||||||
|
|
||||||
|
At the source-code level, they are distinguished by the meta keys which are
|
||||||
|
present on the `enum`: The different variants have different sets of mandatory
|
||||||
|
keys and can thus be uniquely identified.
|
||||||
|
|
||||||
|
#### Name-switched enum meta
|
||||||
|
|
||||||
|
Name-switched enums match a fixed XML namespace and then select the enum
|
||||||
|
variant based on the XML element's name. Name-switched enums are declared by
|
||||||
|
setting the `namespace` key on a `enum` item.
|
||||||
|
|
||||||
|
The following keys are defined on name-switched enums:
|
||||||
|
|
||||||
| Key | Value type | Description |
|
| Key | Value type | Description |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
|
|
@ -92,10 +110,10 @@ The following keys are defined on enums:
|
||||||
| `iterator` | optional *ident* | The name to use for the generated iterator type. |
|
| `iterator` | optional *ident* | The name to use for the generated iterator type. |
|
||||||
| `exhaustive` | *flag* | If present, the enum considers itself authoritative for its namespace; unknown elements within the namespace are rejected instead of treated as mismatch. |
|
| `exhaustive` | *flag* | If present, the enum considers itself authoritative for its namespace; unknown elements within the namespace are rejected instead of treated as mismatch. |
|
||||||
|
|
||||||
All variants of an enum live within the same namespace and are distinguished
|
All variants of a name-switched enum live within the same namespace and are
|
||||||
exclusively by their XML name within that namespace. The contents of the XML
|
distinguished exclusively by their XML name within that namespace. The
|
||||||
element (including attributes) is not inspected before selecting the variant
|
contents of the XML element (including attributes) is not inspected before
|
||||||
when parsing XML.
|
selecting the variant when parsing XML.
|
||||||
|
|
||||||
If *exhaustive* is set and an element is encountered which matches the
|
If *exhaustive* is set and an element is encountered which matches the
|
||||||
namespace of the enum, but matches none of its variants, parsing will fail
|
namespace of the enum, but matches none of its variants, parsing will fail
|
||||||
|
|
@ -109,7 +127,7 @@ Note that the *exhaustive* flag is orthogonal to the Rust attribute
|
||||||
For details on `builder` and `iterator`, see the [Struct meta](#struct-meta)
|
For details on `builder` and `iterator`, see the [Struct meta](#struct-meta)
|
||||||
documentation above.
|
documentation above.
|
||||||
|
|
||||||
#### Enum variant meta
|
##### Name-switched enum variant meta
|
||||||
|
|
||||||
| Key | Value type | Description |
|
| Key | Value type | Description |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
|
|
@ -119,7 +137,7 @@ Note that the `name` value must be a valid XML element name, without colons.
|
||||||
The namespace prefix, if any, is assigned automatically at serialisation time
|
The namespace prefix, if any, is assigned automatically at serialisation time
|
||||||
and cannot be overridden.
|
and cannot be overridden.
|
||||||
|
|
||||||
#### Example
|
##### Example
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# use xso::FromXml;
|
# use xso::FromXml;
|
||||||
|
|
@ -145,6 +163,57 @@ let foo: Foo = xso::from_bytes(b"<b xmlns='urn:example' bar='hello'/>").unwrap()
|
||||||
assert_eq!(foo, Foo::Variant2 { bar: "hello".to_string() });
|
assert_eq!(foo, Foo::Variant2 { bar: "hello".to_string() });
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Dynamic enum meta
|
||||||
|
|
||||||
|
Dynamic enums select their variants by attempting to match them in declaration
|
||||||
|
order. Dynamic enums are declared by not setting the `namespace` key on an
|
||||||
|
`enum` item.
|
||||||
|
|
||||||
|
The following keys are defined on dynamic enums:
|
||||||
|
|
||||||
|
| Key | Value type | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `builder` | optional *ident* | The name to use for the generated builder type. |
|
||||||
|
| `iterator` | optional *ident* | The name to use for the generated iterator type. |
|
||||||
|
|
||||||
|
For details on `builder` and `iterator`, see the [Struct meta](#struct-meta)
|
||||||
|
documentation above.
|
||||||
|
|
||||||
|
##### Dynamic enum variant meta
|
||||||
|
|
||||||
|
Dynamic enum variants are completely independent of one another and thus use
|
||||||
|
the same meta structure as structs. See [Struct meta](#struct-meta) for
|
||||||
|
details.
|
||||||
|
|
||||||
|
The `builder`, `iterator` and `debug` keys cannot be used on dynmaic enum
|
||||||
|
variants.
|
||||||
|
|
||||||
|
##### Example
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# use xso::FromXml;
|
||||||
|
#[derive(FromXml, Debug, PartialEq)]
|
||||||
|
#[xml()]
|
||||||
|
enum Foo {
|
||||||
|
#[xml(namespace = "urn:example:ns1", name = "a")]
|
||||||
|
Variant1 {
|
||||||
|
#[xml(attribute)]
|
||||||
|
foo: String,
|
||||||
|
},
|
||||||
|
#[xml(namespace = "urn:example:ns2", name = "b")]
|
||||||
|
Variant2 {
|
||||||
|
#[xml(attribute)]
|
||||||
|
bar: String,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
let foo: Foo = xso::from_bytes(b"<a xmlns='urn:example:ns1' foo='hello'/>").unwrap();
|
||||||
|
assert_eq!(foo, Foo::Variant1 { foo: "hello".to_string() });
|
||||||
|
|
||||||
|
let foo: Foo = xso::from_bytes(b"<b xmlns='urn:example:ns2' bar='hello'/>").unwrap();
|
||||||
|
assert_eq!(foo, Foo::Variant2 { bar: "hello".to_string() });
|
||||||
|
```
|
||||||
|
|
||||||
### Field meta
|
### Field meta
|
||||||
|
|
||||||
For fields, the *meta* consists of a nested meta inside the `#[xml(..)]` meta,
|
For fields, the *meta* consists of a nested meta inside the `#[xml(..)]` meta,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue