xso-proc: provide FromXml::xml_name_matcher on derived impls

That way, derived implementations can also make use of the performance
benefits.
This commit is contained in:
Jonas Schäfer 2025-05-01 16:26:32 +02:00 • committed by Link Mauve
commit 59af49c691
5 changed files with 166 additions and 3 deletions

View file

@ -12,7 +12,7 @@ use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens};
use syn::*;
use crate::common::{AsXmlParts, FromXmlParts, ItemDef};
use crate::common::{AsXmlParts, FromXmlParts, ItemDef, XmlNameMatcher};
use crate::compound::Compound;
use crate::error_message::ParentRef;
use crate::meta::{reject_key, Flag, NameRef, NamespaceRef, QNameRef, XmlCompoundMeta};
@ -203,6 +203,16 @@ impl NameSwitchedEnum {
})
}
/// Provide the `XmlNameMatcher` template for this enum.
///
/// Name-switched enums always return a matcher in the namespace of the
/// elements they match against.
fn xml_name_matcher(&self) -> Result<XmlNameMatcher> {
Ok(XmlNameMatcher::InNamespace(
self.namespace.to_token_stream(),
))
}
/// Build the deserialisation statemachine for the name-switched enum.
fn make_from_events_statemachine(
&self,
@ -461,6 +471,17 @@ impl AttributeSwitchedEnum {
})
}
/// Provide the `XmlNameMatcher` template for this enum.
///
/// Attribute-switched enums always return a matcher specific to the
/// element they operate on.
fn xml_name_matcher(&self) -> Result<XmlNameMatcher> {
Ok(XmlNameMatcher::Specific(
self.elem_namespace.to_token_stream(),
self.elem_name.to_token_stream(),
))
}
/// Build the deserialisation statemachine for the attribute-switched enum.
fn make_from_events_statemachine(
&self,
@ -613,6 +634,31 @@ impl DynamicEnum {
Ok(Self { variants })
}
/// Provide the `XmlNameMatcher` template for this dynamic enum.
///
/// Dynamic enums return the superset of the matchers of their variants,
/// which in many cases will be XmlNameMatcher::Any.
fn xml_name_matcher(&self) -> Result<XmlNameMatcher> {
let mut iter = self.variants.iter();
let Some(first) = iter.next() else {
// Since the enum has no variants, it cannot match anything. We
// use an empty namespace and an empty name, which will never
// match.
return Ok(XmlNameMatcher::Custom(quote! {
::xso::fromxml::XmlNameMatcher::<'static>::Specific("", "")
}));
};
let first_matcher = first.inner.xml_name_matcher()?;
let mut composition = quote! { #first_matcher };
for variant in iter {
let next_matcher = variant.inner.xml_name_matcher()?;
composition.extend(quote! { .superset(#next_matcher) });
}
Ok(XmlNameMatcher::Custom(composition))
}
/// Build the deserialisation statemachine for the dynamic enum.
fn make_from_events_statemachine(
&self,
@ -781,6 +827,15 @@ impl EnumInner {
}
}
/// Provide the `XmlNameMatcher` template for this enum.
fn xml_name_matcher(&self) -> Result<XmlNameMatcher> {
match self {
Self::NameSwitched(ref inner) => inner.xml_name_matcher(),
Self::AttributeSwitched(ref inner) => inner.xml_name_matcher(),
Self::Dynamic(ref inner) => inner.xml_name_matcher(),
}
}
/// Build the deserialisation statemachine for the enum.
fn make_from_events_statemachine(
&self,
@ -911,6 +966,7 @@ impl ItemDef for EnumDef {
#builder_ty_ident::new(#name_ident, #attrs_ident, ctx)
},
builder_ty_ident: builder_ty_ident.clone(),
name_matcher: self.inner.xml_name_matcher()?,
})
}