xso_proc: allow `#[xml(element)]` without any selector

This commit is contained in:
Jonas Schäfer 2024-03-31 15:17:29 +02:00
parent b0d9f57160
commit d4bce2e959
3 changed files with 24 additions and 35 deletions

View File

@ -7,6 +7,7 @@ use syn::*;
use crate::error_message::{self, ParentRef};
use crate::meta::{FlagOr, Name, NameRef, NamespaceRef, StaticNamespace};
use crate::structs::ElementSelector;
use super::FieldParsePart;
@ -16,11 +17,8 @@ use super::FieldParsePart;
/// Maps to `#[xml(element)]`.
#[cfg_attr(feature = "debug", derive(Debug))]
pub(crate) struct ElementField {
/// The XML namespace of the child element to collect.
namespace: StaticNamespace,
/// The XML name of the child element to collect.
name: Name,
/// Logic to select matching child elements.
selector: ElementSelector,
/// If set, the field value will be generated using
/// [`std::default::Default`] or the given callable if no matching child
@ -43,19 +41,14 @@ impl ElementField {
/// can be constructed. This should point at the `#[xml(..)]` meta of the
/// field or another closely-related object.
pub(super) fn new(
attr_span: &Span,
_attr_span: &Span,
namespace: Option<NamespaceRef>,
name: Option<NameRef>,
default_: FlagOr<Path>,
) -> Result<Self> {
let namespace = match namespace {
None => {
return Err(Error::new(
attr_span.clone(),
"#[xml(element)] requires namespace attribute",
))
}
Some(NamespaceRef::Static(ns)) => ns,
None => None,
Some(NamespaceRef::Static(ns)) => Some(ns),
Some(NamespaceRef::Dyn(ns)) => {
return Err(Error::new_spanned(
ns,
@ -69,20 +62,14 @@ impl ElementField {
))
}
};
let name = match name {
None => {
return Err(Error::new(
attr_span.clone(),
"#[xml(element)] requires name attribute",
))
}
Some(name) => name,
let name = name.map(Name::from);
let selector = match (namespace, name) {
(Some(namespace), Some(name)) => ElementSelector::Qualified { namespace, name },
(Some(namespace), None) => ElementSelector::ByNamespace(namespace),
(None, Some(name)) => ElementSelector::ByName(name),
(None, None) => ElementSelector::Any,
};
Ok(Self {
namespace,
name: name.into(),
default_,
})
Ok(Self { selector, default_ })
}
/// Construct the code necessary to parse the child element from a
@ -107,8 +94,9 @@ impl ElementField {
ident: Member,
ty: Type,
) -> Result<FieldParsePart> {
let field_name = self.name;
let field_namespace = self.namespace;
let test = self
.selector
.build_test(&Ident::new("residual", Span::call_site()));
let missingerr = error_message::on_missing_child(name, &ident);
let duperr = error_message::on_duplicate_child(name, &ident);
let on_missing = match self.default_ {
@ -133,7 +121,7 @@ impl ElementField {
let mut #tempname: Option<::xso::exports::minidom::Element> = None;
},
childiter: quote! {
residual = if residual.is(#field_name, #field_namespace) {
residual = if #test {
if #tempname.is_some() {
return Err(::xso::error::Error::ParseError(#duperr));
}

View File

@ -109,7 +109,7 @@ impl ElementSelector {
/// If the `minidom::Element` in `residual` matches the selector, the
/// token stream will evaluate to true. Otherwise, it will evaluate to
/// false.
fn build_test(self, residual: &Ident) -> TokenStream {
pub(crate) fn build_test(self, residual: &Ident) -> TokenStream {
match self {
Self::Any => quote! { true },
Self::ByName(name) => quote! {

View File

@ -331,12 +331,13 @@ The following field kinds are available:
of the [`FromXmlText`] / [`IntoXmlText`] implementation of the field's
type.
- `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).
- `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 = ..` : The XML name of the element to match.
- `namespace = ..`: The XML namespace of the element to match.
- `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