mirror of
https://gitlab.com/xmpp-rs/xmpp-rs.git
synced 2024-06-09 09:44:03 +02:00
parsers-macros: add support for custom defaulting
This adds support for using a custom function to create a default value during parsing instead of relying on [`std::default::Default`].
This commit is contained in:
parent
953de151c8
commit
eae3efc1de
|
@ -250,9 +250,11 @@ The following field kinds are available:
|
|||
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`: If set, the attribute's value will be defaulted using
|
||||
[`Default::default`] if the attribute is absent and none could be
|
||||
generated through [`FromOptionalXmlText`].
|
||||
- `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.
|
||||
|
@ -284,9 +286,13 @@ The following field kinds are available:
|
|||
completely.
|
||||
|
||||
This should often be combined with `default`.
|
||||
- `default`: If the child is not present, the field value will be created
|
||||
using [`Default::default`]. For `extract(..)`, this is even required when
|
||||
using `Option<..>` as a field type.
|
||||
- `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`: Extract an entire child element. The field type must
|
||||
implement [`FromXml`] (for [`FromXml`]) or `IntoXml` (for
|
||||
|
@ -331,9 +337,10 @@ The following field kinds are available:
|
|||
|
||||
- `name = ..` : The XML name of the element to match.
|
||||
- `namespace = ..`: The XML namespace of the element to match.
|
||||
- `default`: If the element cannot be found, initialize the field using
|
||||
[`std::default::Default`] instead of emitting an error. The type must
|
||||
implement `Default` for that (in addition to the other required traits).
|
||||
- `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.
|
||||
|
|
|
@ -5,7 +5,7 @@ use quote::quote;
|
|||
use syn::*;
|
||||
|
||||
use crate::error_message::{self, ParentRef};
|
||||
use crate::meta::{Flag, Name, NameRef};
|
||||
use crate::meta::{FlagOr, Name, NameRef};
|
||||
|
||||
use super::FieldParsePart;
|
||||
|
||||
|
@ -19,12 +19,12 @@ pub(crate) struct AttributeField {
|
|||
/// *Note:* Namespaced attributes are currently not supported.
|
||||
pub(super) name: Name,
|
||||
|
||||
/// Whether [`Default`] should be used to obtain a value f the attribute
|
||||
/// is missing.
|
||||
/// Whether [`Default`] or a given callable should be used to obtain a
|
||||
/// value if the attribute is missing.
|
||||
///
|
||||
/// If the flag is *not* set, an error is returned when parsing an element
|
||||
/// without the attribute.
|
||||
pub(super) default_on_missing: Flag,
|
||||
pub(super) default_: FlagOr<Path>,
|
||||
|
||||
/// The codec implementation to use.
|
||||
///
|
||||
|
@ -55,7 +55,7 @@ impl AttributeField {
|
|||
attr_span: &Span,
|
||||
field_ident: Option<&Ident>,
|
||||
name: Option<NameRef>,
|
||||
default_on_missing: Flag,
|
||||
default_: FlagOr<Path>,
|
||||
codec: Option<Type>,
|
||||
) -> Result<Self> {
|
||||
let name = name
|
||||
|
@ -67,7 +67,7 @@ impl AttributeField {
|
|||
|
||||
Ok(Self {
|
||||
name,
|
||||
default_on_missing,
|
||||
default_,
|
||||
codec,
|
||||
})
|
||||
}
|
||||
|
@ -86,15 +86,23 @@ impl AttributeField {
|
|||
) -> Result<FieldParsePart> {
|
||||
let missing_msg = error_message::on_missing_attribute(name, &ident);
|
||||
let name = self.name;
|
||||
let on_missing = if self.default_on_missing.is_set() {
|
||||
quote! {
|
||||
<#ty as ::std::default::Default>::default()
|
||||
let on_missing = match self.default_ {
|
||||
FlagOr::Absent => {
|
||||
quote! {
|
||||
return Err(::xmpp_parsers_core::error::Error::ParseError(
|
||||
#missing_msg,
|
||||
))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
return Err(::xmpp_parsers_core::error::Error::ParseError(
|
||||
#missing_msg,
|
||||
))
|
||||
FlagOr::Present(_) => {
|
||||
quote! {
|
||||
<#ty as ::std::default::Default>::default()
|
||||
}
|
||||
}
|
||||
FlagOr::Value { value, .. } => {
|
||||
quote! {
|
||||
#value()
|
||||
}
|
||||
}
|
||||
};
|
||||
let decode = match self.codec {
|
||||
|
|
|
@ -7,7 +7,7 @@ use syn::*;
|
|||
|
||||
use crate::compound::Compound;
|
||||
use crate::error_message::{self, ParentRef};
|
||||
use crate::meta::{Flag, NameRef, NamespaceRef, XmlFieldMeta};
|
||||
use crate::meta::{FlagOr, NameRef, NamespaceRef, XmlFieldMeta};
|
||||
|
||||
use super::{ChildMode, FieldDef, FieldNamespace, FieldParsePart};
|
||||
|
||||
|
@ -236,9 +236,9 @@ pub(crate) struct ChildField {
|
|||
extract: Option<ExtractDef>,
|
||||
|
||||
/// If set, the field's value will be generated using
|
||||
/// [`std::default::Default`] if no matching child can be found, instead
|
||||
/// of aborting parsing with an error.
|
||||
default_on_missing: Flag,
|
||||
/// [`std::default::Default`] or the given path if no matching child can
|
||||
/// be found, instead of aborting parsing with an error.
|
||||
default_: FlagOr<Path>,
|
||||
|
||||
/// If set, it must point to a function. That function will be called with
|
||||
/// an immutable reference to the field's value and must return a boolean.
|
||||
|
@ -259,8 +259,7 @@ impl ChildField {
|
|||
/// Otherwise, if no extract is intended, `namespace` and `name` must be
|
||||
/// `None` and `extract` must be empty.
|
||||
///
|
||||
/// The `default_on_missing` flag stored, see [`Self::default_on_missing`]
|
||||
/// for semantics.
|
||||
/// The `default_` flag stored, see [`Self::default_`] for semantics.
|
||||
///
|
||||
/// `field_type` must be the type of the field. It is used to configure
|
||||
/// the extract correctly, if it is specified and the mode is single.
|
||||
|
@ -274,7 +273,7 @@ impl ChildField {
|
|||
namespace: Option<NamespaceRef>,
|
||||
name: Option<NameRef>,
|
||||
extract: Vec<Box<XmlFieldMeta>>,
|
||||
default_on_missing: Flag,
|
||||
default_: FlagOr<Path>,
|
||||
skip_if: Option<Path>,
|
||||
field_type: &Type,
|
||||
) -> Result<Self> {
|
||||
|
@ -323,7 +322,7 @@ impl ChildField {
|
|||
single_extract_type,
|
||||
)?),
|
||||
skip_if,
|
||||
default_on_missing,
|
||||
default_,
|
||||
})
|
||||
} else {
|
||||
if let Some(namespace) = namespace {
|
||||
|
@ -341,7 +340,7 @@ impl ChildField {
|
|||
Ok(Self {
|
||||
mode,
|
||||
extract: None,
|
||||
default_on_missing,
|
||||
default_,
|
||||
skip_if,
|
||||
})
|
||||
}
|
||||
|
@ -379,13 +378,21 @@ impl ChildField {
|
|||
ChildMode::Single => {
|
||||
let missingerr = error_message::on_missing_child(name, &ident);
|
||||
let duperr = error_message::on_duplicate_child(name, &ident);
|
||||
let on_missing = if self.default_on_missing.is_set() {
|
||||
quote! {
|
||||
<#ty as ::std::default::Default>::default()
|
||||
let on_missing = match self.default_ {
|
||||
FlagOr::Absent => {
|
||||
quote! {
|
||||
return Err(::xmpp_parsers_core::error::Error::ParseError(#missingerr));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
return Err(::xmpp_parsers_core::error::Error::ParseError(#missingerr));
|
||||
FlagOr::Present(_) => {
|
||||
quote! {
|
||||
<#ty as ::std::default::Default>::default()
|
||||
}
|
||||
}
|
||||
FlagOr::Value { value, .. } => {
|
||||
quote! {
|
||||
#value()
|
||||
}
|
||||
}
|
||||
};
|
||||
match self.extract {
|
||||
|
|
|
@ -6,7 +6,7 @@ use quote::quote;
|
|||
use syn::*;
|
||||
|
||||
use crate::error_message::{self, ParentRef};
|
||||
use crate::meta::{Flag, Name, NameRef, NamespaceRef, StaticNamespace};
|
||||
use crate::meta::{FlagOr, Name, NameRef, NamespaceRef, StaticNamespace};
|
||||
|
||||
use super::FieldParsePart;
|
||||
|
||||
|
@ -23,10 +23,10 @@ pub(crate) struct ElementField {
|
|||
name: Name,
|
||||
|
||||
/// If set, the field value will be generated using
|
||||
/// [`std::default::Default`] if no matching child element is encountered
|
||||
/// during parsing. If unset, an error is generated instead and parsing of
|
||||
/// the parent element fails.
|
||||
default_on_missing: Flag,
|
||||
/// [`std::default::Default`] or the given callable if no matching child
|
||||
/// element is encountered during parsing. If unset, an error is generated
|
||||
/// instead and parsing of the parent element fails.
|
||||
default_: FlagOr<Path>,
|
||||
}
|
||||
|
||||
impl ElementField {
|
||||
|
@ -46,7 +46,7 @@ impl ElementField {
|
|||
attr_span: &Span,
|
||||
namespace: Option<NamespaceRef>,
|
||||
name: Option<NameRef>,
|
||||
default_on_missing: Flag,
|
||||
default_: FlagOr<Path>,
|
||||
) -> Result<Self> {
|
||||
let namespace = match namespace {
|
||||
None => {
|
||||
|
@ -81,7 +81,7 @@ impl ElementField {
|
|||
Ok(Self {
|
||||
namespace,
|
||||
name: name.into(),
|
||||
default_on_missing,
|
||||
default_,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -111,13 +111,21 @@ impl ElementField {
|
|||
let field_namespace = self.namespace;
|
||||
let missingerr = error_message::on_missing_child(name, &ident);
|
||||
let duperr = error_message::on_duplicate_child(name, &ident);
|
||||
let on_missing = if self.default_on_missing.is_set() {
|
||||
quote! {
|
||||
<#ty as ::std::default::Default>::default()
|
||||
let on_missing = match self.default_ {
|
||||
FlagOr::Absent => {
|
||||
quote! {
|
||||
return Err(::xmpp_parsers_core::error::Error::ParseError(#missingerr));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
return Err(::xmpp_parsers_core::error::Error::ParseError(#missingerr));
|
||||
FlagOr::Present(_) => {
|
||||
quote! {
|
||||
<#ty as ::std::default::Default>::default()
|
||||
}
|
||||
}
|
||||
FlagOr::Value { value, .. } => {
|
||||
quote! {
|
||||
#value()
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(FieldParsePart {
|
||||
|
|
|
@ -174,7 +174,7 @@ impl FieldKind {
|
|||
match meta {
|
||||
XmlFieldMeta::Attribute {
|
||||
name,
|
||||
default_on_missing,
|
||||
default_,
|
||||
codec,
|
||||
ty,
|
||||
} => {
|
||||
|
@ -185,7 +185,7 @@ impl FieldKind {
|
|||
span,
|
||||
field_ident,
|
||||
name,
|
||||
default_on_missing,
|
||||
default_,
|
||||
codec,
|
||||
)?))
|
||||
}
|
||||
|
@ -194,17 +194,10 @@ impl FieldKind {
|
|||
namespace,
|
||||
name,
|
||||
extract,
|
||||
default_on_missing,
|
||||
default_,
|
||||
skip_if,
|
||||
} => Ok(FieldKind::Child(ChildField::new(
|
||||
span,
|
||||
mode,
|
||||
namespace,
|
||||
name,
|
||||
extract,
|
||||
default_on_missing,
|
||||
skip_if,
|
||||
field_ty,
|
||||
span, mode, namespace, name, extract, default_, skip_if, field_ty,
|
||||
)?)),
|
||||
XmlFieldMeta::Text { codec, ty } => {
|
||||
if let Some(ty) = ty {
|
||||
|
@ -216,12 +209,9 @@ impl FieldKind {
|
|||
XmlFieldMeta::Element {
|
||||
namespace,
|
||||
name,
|
||||
default_on_missing,
|
||||
default_,
|
||||
} => Ok(FieldKind::Element(ElementField::new(
|
||||
span,
|
||||
namespace,
|
||||
name,
|
||||
default_on_missing,
|
||||
span, namespace, name, default_,
|
||||
)?)),
|
||||
XmlFieldMeta::Elements { namespace, name } => Ok(FieldKind::Elements(
|
||||
ElementsField::new(span, namespace, name)?,
|
||||
|
|
|
@ -102,6 +102,33 @@ impl<'a> MetaParse for ParseFlag<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Parse a [`FlagOr`] from a meta.
|
||||
struct ParseFlagOr<'a, T>(&'static str, &'a mut FlagOr<T>);
|
||||
|
||||
impl<'a, T: parse::Parse> MetaParse for ParseFlagOr<'a, T> {
|
||||
fn name(&self) -> &'static str {
|
||||
self.0
|
||||
}
|
||||
|
||||
fn force_parse_at_meta<'x>(&mut self, meta: meta::ParseNestedMeta<'x>) -> Result<()> {
|
||||
if self.1.is_set() {
|
||||
return Err(Error::new_spanned(
|
||||
meta.path,
|
||||
format!("flag {} is already set", self.name()),
|
||||
));
|
||||
}
|
||||
if meta.input.peek(Token![=]) {
|
||||
*self.1 = FlagOr::Value {
|
||||
span: meta.path.span(),
|
||||
value: meta.value()?.parse()?,
|
||||
};
|
||||
} else {
|
||||
*self.1 = FlagOr::Present(meta.path.span());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse any parseable value from a meta.
|
||||
struct ParseValue<'a, T: parse::Parse>(&'static str, &'a mut Option<T>);
|
||||
|
||||
|
@ -198,6 +225,61 @@ impl<T: Spanned> From<T> for Flag {
|
|||
}
|
||||
}
|
||||
|
||||
/// A flag with an optional value.
|
||||
#[derive(Clone)]
|
||||
#[cfg_attr(feature = "debug", derive(Debug))]
|
||||
pub(crate) enum FlagOr<T> {
|
||||
/// The flag is not set.
|
||||
Absent,
|
||||
|
||||
/// The flag was set.
|
||||
Present(
|
||||
/// The span of the syntax element which enabled the flag.
|
||||
///
|
||||
/// This is used to generate useful error messages by pointing at the
|
||||
/// specific place the flag was activated.
|
||||
Span,
|
||||
),
|
||||
|
||||
/// A value was assigned.
|
||||
Value {
|
||||
/// The span of the left hand side of the assignment.
|
||||
span: Span,
|
||||
|
||||
/// The actual value.
|
||||
value: T,
|
||||
},
|
||||
}
|
||||
|
||||
impl<T> FlagOr<T> {
|
||||
/// Return true if the flag is set, false otherwise.
|
||||
pub fn is_set(&self) -> bool {
|
||||
match self {
|
||||
Self::Absent => false,
|
||||
Self::Present(_) => true,
|
||||
Self::Value { .. } => true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Obtain the span of the path setting the flag or value.
|
||||
pub fn span(&self) -> Option<Span> {
|
||||
match self {
|
||||
Self::Absent => None,
|
||||
Self::Present(ref span) => Some(*span),
|
||||
Self::Value { ref span, .. } => Some(*span),
|
||||
}
|
||||
}
|
||||
|
||||
/// Obtain the span of the path setting the flag or value.
|
||||
pub fn into_span(self) -> Option<Span> {
|
||||
match self {
|
||||
Self::Absent => None,
|
||||
Self::Present(span) => Some(span),
|
||||
Self::Value { span, .. } => Some(span),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Type alias for a XML namespace setting.
|
||||
///
|
||||
/// This may in the future be replaced by an enum supporting both `Path` and
|
||||
|
@ -547,7 +629,7 @@ struct AttributeMeta {
|
|||
|
||||
/// The presence of the `default` flag a potentially nested `#[xml(..)]`,
|
||||
/// if any.
|
||||
default_on_missing: Flag,
|
||||
default_: FlagOr<Path>,
|
||||
|
||||
/// The value assigned to `type` inside a potentially nested
|
||||
/// `#[xml(..)]`, if any.
|
||||
|
@ -579,7 +661,7 @@ impl AttributeMeta {
|
|||
Ok(Self {
|
||||
name: Some(name),
|
||||
namespace: None,
|
||||
default_on_missing: Flag::Absent,
|
||||
default_: FlagOr::Absent,
|
||||
codec: None,
|
||||
ty: None,
|
||||
})
|
||||
|
@ -587,7 +669,7 @@ impl AttributeMeta {
|
|||
let mut name: Option<NameRef> = None;
|
||||
let mut namespace: Option<NamespaceRef> = None;
|
||||
let mut ty: Option<Type> = None;
|
||||
let mut default_on_missing: Flag = Flag::Absent;
|
||||
let mut default_: FlagOr<Path> = FlagOr::Absent;
|
||||
let mut codec: Option<Type> = None;
|
||||
meta.parse_nested_meta(|meta| {
|
||||
parse_meta!(
|
||||
|
@ -596,13 +678,13 @@ impl AttributeMeta {
|
|||
ParseValue("namespace", &mut namespace),
|
||||
ParseValue("type", &mut ty),
|
||||
ParseValue("codec", &mut codec),
|
||||
ParseFlag("default", &mut default_on_missing),
|
||||
ParseFlagOr("default", &mut default_),
|
||||
)
|
||||
})?;
|
||||
Ok(Self {
|
||||
namespace,
|
||||
name,
|
||||
default_on_missing,
|
||||
default_,
|
||||
codec,
|
||||
ty,
|
||||
})
|
||||
|
@ -610,7 +692,7 @@ impl AttributeMeta {
|
|||
Ok(Self {
|
||||
namespace: None,
|
||||
name: None,
|
||||
default_on_missing: Flag::Absent,
|
||||
default_: FlagOr::Absent,
|
||||
codec: None,
|
||||
ty: None,
|
||||
})
|
||||
|
@ -637,8 +719,8 @@ impl AttributeMeta {
|
|||
|
||||
/// Emit an error with the given message if the codec is set
|
||||
fn reject_default(&self, msg: impl Into<String>) -> Result<()> {
|
||||
if let Flag::Present(path) = &self.default_on_missing {
|
||||
Err(Error::new(*path, msg.into()))
|
||||
if let Some(span) = self.default_.span() {
|
||||
Err(Error::new(span, msg.into()))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
|
@ -672,8 +754,8 @@ pub(crate) enum XmlFieldMeta {
|
|||
/// `attribute` in the shorthand syntax.
|
||||
name: Option<NameRef>,
|
||||
|
||||
/// Presence of the `default` flag.
|
||||
default_on_missing: Flag,
|
||||
/// Contents of the `default` flag.
|
||||
default_: FlagOr<Path>,
|
||||
|
||||
/// Contents of the `codec = ..` option.
|
||||
codec: Option<Type>,
|
||||
|
@ -703,8 +785,8 @@ pub(crate) enum XmlFieldMeta {
|
|||
/// Contents of the `extract(..)` option.
|
||||
extract: Vec<Box<XmlFieldMeta>>,
|
||||
|
||||
/// Presence of the `default` flag.
|
||||
default_on_missing: Flag,
|
||||
/// Contents of the `default` flag.
|
||||
default_: FlagOr<Path>,
|
||||
|
||||
/// Contents of the `skip_if = ..` option.
|
||||
skip_if: Option<Path>,
|
||||
|
@ -752,8 +834,8 @@ pub(crate) enum XmlFieldMeta {
|
|||
/// Contents of the `name = ..` option.
|
||||
name: Option<NameRef>,
|
||||
|
||||
/// Presence of the `default` flag.
|
||||
default_on_missing: Flag,
|
||||
/// Contents of the `default` flag.
|
||||
default_: FlagOr<Path>,
|
||||
},
|
||||
|
||||
/// Maps the field to the presence of an empty XML child element.
|
||||
|
@ -789,7 +871,7 @@ impl XmlFieldMeta {
|
|||
}
|
||||
Ok(Self::Attribute {
|
||||
name: meta.name,
|
||||
default_on_missing: meta.default_on_missing,
|
||||
default_: meta.default_,
|
||||
codec: meta.codec,
|
||||
ty: meta.ty,
|
||||
})
|
||||
|
@ -803,7 +885,7 @@ impl XmlFieldMeta {
|
|||
Option<NamespaceRef>,
|
||||
Option<NameRef>,
|
||||
Vec<Box<XmlFieldMeta>>,
|
||||
Flag,
|
||||
FlagOr<Path>,
|
||||
Option<Path>,
|
||||
)> {
|
||||
if meta.input.peek(token::Paren) {
|
||||
|
@ -811,34 +893,33 @@ impl XmlFieldMeta {
|
|||
let mut namespace: Option<NamespaceRef> = None;
|
||||
let mut extract: Vec<Box<XmlFieldMeta>> = Vec::new();
|
||||
let mut skip_if: Option<Path> = None;
|
||||
let mut default_on_missing = Flag::Absent;
|
||||
let mut default_ = FlagOr::Absent;
|
||||
meta.parse_nested_meta(|meta| {
|
||||
parse_meta!(
|
||||
meta,
|
||||
ParseValue("name", &mut name),
|
||||
ParseValue("namespace", &mut namespace),
|
||||
ParseExtracts(&mut extract),
|
||||
ParseFlag("default", &mut default_on_missing),
|
||||
ParseFlagOr("default", &mut default_),
|
||||
ParseValue("skip_if", &mut skip_if),
|
||||
)
|
||||
})?;
|
||||
Ok((namespace, name, extract, default_on_missing, skip_if))
|
||||
Ok((namespace, name, extract, default_, skip_if))
|
||||
} else {
|
||||
Ok((None, None, Vec::new(), Flag::Absent, None))
|
||||
Ok((None, None, Vec::new(), FlagOr::Absent, None))
|
||||
}
|
||||
}
|
||||
|
||||
/// Processes a `#[xml(child)]` meta, creating a [`Self::Child`]
|
||||
/// variant with [`ChildMode::Single`].
|
||||
fn child_from_meta(meta: meta::ParseNestedMeta<'_>) -> Result<Self> {
|
||||
let (namespace, name, extract, default_on_missing, skip_if) =
|
||||
Self::child_common_from_meta(meta)?;
|
||||
let (namespace, name, extract, default_, skip_if) = Self::child_common_from_meta(meta)?;
|
||||
Ok(Self::Child {
|
||||
mode: ChildMode::Single,
|
||||
namespace,
|
||||
name,
|
||||
extract,
|
||||
default_on_missing,
|
||||
default_,
|
||||
skip_if,
|
||||
})
|
||||
}
|
||||
|
@ -846,17 +927,16 @@ impl XmlFieldMeta {
|
|||
/// Processes a `#[xml(children)]` meta, creating a [`Self::Child`]
|
||||
/// variant with [`ChildMode::Collection`].
|
||||
fn children_from_meta(meta: meta::ParseNestedMeta<'_>) -> Result<Self> {
|
||||
let (namespace, name, extract, default_on_missing, skip_if) =
|
||||
Self::child_common_from_meta(meta)?;
|
||||
if let Flag::Present(default_on_missing) = default_on_missing {
|
||||
return Err(syn::Error::new(default_on_missing, "default cannot be used on #[xml(children)] (it is implied, the default is the empty container)"));
|
||||
let (namespace, name, extract, default_, skip_if) = Self::child_common_from_meta(meta)?;
|
||||
if let Some(default_) = default_.into_span() {
|
||||
return Err(syn::Error::new(default_, "default cannot be used on #[xml(children)] (it is implied, the default is the empty container)"));
|
||||
}
|
||||
Ok(Self::Child {
|
||||
mode: ChildMode::Collection,
|
||||
namespace,
|
||||
name,
|
||||
extract,
|
||||
default_on_missing: Flag::Absent,
|
||||
default_: FlagOr::Absent,
|
||||
skip_if,
|
||||
})
|
||||
}
|
||||
|
@ -894,7 +974,7 @@ impl XmlFieldMeta {
|
|||
Ok(Self::Element {
|
||||
name: meta.name,
|
||||
namespace: meta.namespace,
|
||||
default_on_missing: meta.default_on_missing,
|
||||
default_: meta.default_,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -915,9 +995,9 @@ impl XmlFieldMeta {
|
|||
fn flag_from_meta(meta: meta::ParseNestedMeta<'_>) -> Result<Self> {
|
||||
let meta = AttributeMeta::parse_from_meta(meta)?;
|
||||
meta.reject_type("type = .. is not allowed on #[xml(flag)]")?;
|
||||
if let Flag::Present(default_on_missing) = meta.default_on_missing {
|
||||
if let Some(span) = meta.default_.into_span() {
|
||||
return Err(syn::Error::new(
|
||||
default_on_missing,
|
||||
span,
|
||||
"default cannot be used on #[xml(flag)] (it is implied, the default false)",
|
||||
));
|
||||
}
|
||||
|
|
|
@ -1000,3 +1000,27 @@ fn child_extract_skip_if_roundtrip_present() {
|
|||
"<skip-if xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'><foo>10</foo></skip-if>",
|
||||
);
|
||||
}
|
||||
|
||||
fn u8_255() -> u8 {
|
||||
255
|
||||
}
|
||||
|
||||
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
|
||||
#[xml(namespace = self::TEST_NS1, name = "default-func")]
|
||||
pub struct DefaultFunc {
|
||||
#[xml(attribute(default = u8_255))]
|
||||
pub attr: u8,
|
||||
|
||||
#[xml(child(namespace = self::TEST_NS1, name = "value", extract(text), default = u8_255))]
|
||||
pub child: u8,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_func_generates_values() {
|
||||
let v = crate::util::test::parse_str::<DefaultFunc>(
|
||||
"<default-func xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2'/>",
|
||||
)
|
||||
.expect("parse");
|
||||
assert_eq!(v.attr, 255);
|
||||
assert_eq!(v.child, 255);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user