diff --git a/xso-proc/src/enums.rs b/xso-proc/src/enums.rs index 20ac67ae6f71ca7cac0870d7792fd66c8746cea2..b756faee4e00f0cecd2b4003983401f140c087f3 100644 --- a/xso-proc/src/enums.rs +++ b/xso-proc/src/enums.rs @@ -15,7 +15,7 @@ use syn::*; use crate::common::{AsXmlParts, FromXmlParts, ItemDef}; use crate::compound::Compound; use crate::error_message::ParentRef; -use crate::meta::{reject_key, Flag, NameRef, NamespaceRef, XmlCompoundMeta}; +use crate::meta::{reject_key, Flag, NameRef, NamespaceRef, QNameRef, XmlCompoundMeta}; use crate::state::{AsItemsStateMachine, FromEventsStateMachine}; /// The definition of an enum variant, switched on the XML element's name. @@ -38,8 +38,7 @@ impl NameVariant { // an error if they are present. let XmlCompoundMeta { span: meta_span, - namespace, - name, + qname: QNameRef { namespace, name }, exhaustive, debug, builder, @@ -172,8 +171,7 @@ impl EnumDef { // an error if they are present. let XmlCompoundMeta { span: meta_span, - namespace, - name, + qname: QNameRef { namespace, name }, exhaustive, debug, builder, diff --git a/xso-proc/src/field.rs b/xso-proc/src/field.rs index e8cc5caa2d8730c2bd9c8bdcb3274b03f54b01c3..2b480090a2c106b9aa8b0ed212abeb78c0a40456 100644 --- a/xso-proc/src/field.rs +++ b/xso-proc/src/field.rs @@ -13,7 +13,7 @@ use syn::{spanned::Spanned, *}; use rxml_validation::NcName; use crate::error_message::{self, ParentRef}; -use crate::meta::{AmountConstraint, Flag, NameRef, NamespaceRef, XmlFieldMeta}; +use crate::meta::{AmountConstraint, Flag, NameRef, NamespaceRef, QNameRef, XmlFieldMeta}; use crate::scope::{AsItemsScope, FromEventsScope}; use crate::types::{ as_optional_xml_text_fn, as_xml_iter_fn, as_xml_text_fn, default_fn, extend_fn, from_events_fn, @@ -172,8 +172,7 @@ impl FieldKind { match meta { XmlFieldMeta::Attribute { span, - namespace, - name, + qname: QNameRef { namespace, name }, default_, } => { let xml_name = match name { diff --git a/xso-proc/src/meta.rs b/xso-proc/src/meta.rs index c2d1e78880b722aa2db50a96734f8f9e3807cede..24dbfcb3d222d1044ae17019c60e8088f3786699 100644 --- a/xso-proc/src/meta.rs +++ b/xso-proc/src/meta.rs @@ -260,6 +260,63 @@ impl From for Flag { } } +/// A pair of `namespace` and `name` keys. +#[derive(Debug, Default)] +pub(crate) struct QNameRef { + /// The XML namespace supplied. + pub(crate) namespace: Option, + + /// The XML name supplied. + pub(crate) name: Option, +} + +impl QNameRef { + /// Attempt to incrementally parse this QNameRef. + /// + /// If `meta` contains either `namespace` or `name` keys, they are + /// processed and either `Ok(None)` or an error is returned. + /// + /// If no matching key is found, `Ok(Some(meta))` is returned for further + /// processing. + fn parse_incremental_from_meta<'x>( + &mut self, + meta: ParseNestedMeta<'x>, + ) -> Result>> { + if meta.path.is_ident("name") { + if self.name.is_some() { + return Err(Error::new_spanned(meta.path, "duplicate `name` key")); + } + let value = meta.value()?; + let name_span = value.span(); + let (new_namespace, new_name) = parse_prefixed_name(value)?; + if let Some(new_namespace) = new_namespace { + if let Some(namespace) = self.namespace.as_ref() { + let mut error = Error::new( + name_span, + "cannot combine `namespace` key with prefixed `name`", + ); + error.combine(Error::new_spanned(namespace, "`namespace` was set here")); + return Err(error); + } + self.namespace = Some(new_namespace); + } + self.name = Some(new_name); + Ok(None) + } else if meta.path.is_ident("namespace") { + if self.namespace.is_some() { + return Err(Error::new_spanned( + meta.path, + "duplicate `namespace` key or `name` key has prefix", + )); + } + self.namespace = Some(meta.value()?.parse()?); + Ok(None) + } else { + Ok(Some(meta)) + } + } +} + /// Contents of an `#[xml(..)]` attribute on a struct, enum variant, or enum. #[derive(Debug)] pub(crate) struct XmlCompoundMeta { @@ -268,11 +325,9 @@ pub(crate) struct XmlCompoundMeta { /// This is useful for error messages. pub(crate) span: Span, - /// The value assigned to `namespace` inside `#[xml(..)]`, if any. - pub(crate) namespace: Option, - - /// The value assigned to `name` inside `#[xml(..)]`, if any. - pub(crate) name: Option, + /// The value assigned to `namespace` and `name` fields inside + /// `#[xml(..)]`, if any. + pub(crate) qname: QNameRef, /// The debug flag. pub(crate) debug: Flag, @@ -293,27 +348,14 @@ impl XmlCompoundMeta { /// Undefined options or options with incompatible values are rejected /// with an appropriate compile-time error. fn parse_from_attribute(attr: &Attribute) -> Result { - let mut namespace = None; - let mut name = None; + let mut qname = QNameRef::default(); let mut builder = None; let mut iterator = None; let mut debug = Flag::Absent; let mut exhaustive = Flag::Absent; attr.parse_nested_meta(|meta| { - if meta.path.is_ident("name") { - if name.is_some() { - return Err(Error::new_spanned(meta.path, "duplicate `name` key")); - } - name = Some(meta.value()?.parse()?); - Ok(()) - } else if meta.path.is_ident("namespace") { - if namespace.is_some() { - return Err(Error::new_spanned(meta.path, "duplicate `namespace` key")); - } - namespace = Some(meta.value()?.parse()?); - Ok(()) - } else if meta.path.is_ident("debug") { + if meta.path.is_ident("debug") { if debug.is_set() { return Err(Error::new_spanned(meta.path, "duplicate `debug` key")); } @@ -338,14 +380,16 @@ impl XmlCompoundMeta { exhaustive = (&meta.path).into(); Ok(()) } else { - Err(Error::new_spanned(meta.path, "unsupported key")) + match qname.parse_incremental_from_meta(meta)? { + None => Ok(()), + Some(meta) => Err(Error::new_spanned(meta.path, "unsupported key")), + } } })?; Ok(Self { span: attr.span(), - namespace, - name, + qname, debug, builder, iterator, @@ -585,11 +629,8 @@ pub(crate) enum XmlFieldMeta { /// This is useful for error messages. span: Span, - /// The XML namespace supplied. - namespace: Option, - - /// The XML name supplied. - name: Option, + /// The namespace/name keys. + qname: QNameRef, /// The `default` flag. default_: Flag, @@ -624,65 +665,40 @@ impl XmlFieldMeta { let (namespace, name) = parse_prefixed_name(meta.value()?)?; Ok(Self::Attribute { span: meta.path.span(), - name: Some(name), - namespace, + qname: QNameRef { + name: Some(name), + namespace, + }, default_: Flag::Absent, }) } else if meta.input.peek(syn::token::Paren) { // full syntax - let mut name: Option = None; - let mut namespace: Option = None; + let mut qname = QNameRef::default(); let mut default_ = Flag::Absent; meta.parse_nested_meta(|meta| { - if meta.path.is_ident("name") { - if name.is_some() { - return Err(Error::new_spanned(meta.path, "duplicate `name` key")); - } - let value = meta.value()?; - let name_span = value.span(); - let (new_namespace, new_name) = parse_prefixed_name(value)?; - if let Some(new_namespace) = new_namespace { - if namespace.is_some() { - return Err(Error::new( - name_span, - "cannot combine `namespace` key with prefixed `name`", - )); - } - namespace = Some(new_namespace); - } - name = Some(new_name); - Ok(()) - } else if meta.path.is_ident("namespace") { - if namespace.is_some() { - return Err(Error::new_spanned( - meta.path, - "duplicate `namespace` key or `name` key has prefix", - )); - } - namespace = Some(meta.value()?.parse()?); - Ok(()) - } else if meta.path.is_ident("default") { + if meta.path.is_ident("default") { if default_.is_set() { return Err(Error::new_spanned(meta.path, "duplicate `default` key")); } default_ = (&meta.path).into(); Ok(()) } else { - Err(Error::new_spanned(meta.path, "unsupported key")) + match qname.parse_incremental_from_meta(meta)? { + None => Ok(()), + Some(meta) => Err(Error::new_spanned(meta.path, "unsupported key")), + } } })?; Ok(Self::Attribute { span: meta.path.span(), - name, - namespace, + qname, default_, }) } else { // argument-less syntax Ok(Self::Attribute { span: meta.path.span(), - name: None, - namespace: None, + qname: QNameRef::default(), default_: Flag::Absent, }) } diff --git a/xso-proc/src/structs.rs b/xso-proc/src/structs.rs index 7d6c28c9daa162c57f1ceb852a35bbd3df53328a..ebdd28dee21e7a98c80edc5106a754a1c7be1a07 100644 --- a/xso-proc/src/structs.rs +++ b/xso-proc/src/structs.rs @@ -12,7 +12,7 @@ use syn::*; use crate::common::{AsXmlParts, FromXmlParts, ItemDef}; use crate::compound::Compound; -use crate::meta::{reject_key, Flag, NameRef, NamespaceRef, XmlCompoundMeta}; +use crate::meta::{reject_key, Flag, NameRef, NamespaceRef, QNameRef, XmlCompoundMeta}; /// Definition of a struct and how to parse it. pub(crate) struct StructDef { @@ -46,8 +46,7 @@ impl StructDef { // an error if they are present. let XmlCompoundMeta { span: meta_span, - namespace, - name, + qname: QNameRef { namespace, name }, exhaustive, debug, builder,