From cf617e4d7e27da3ff84f2daeaa6ea3dfc4a5d3b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Sch=C3=A4fer?= Date: Wed, 10 Jul 2024 16:34:48 +0200 Subject: [PATCH] xso-proc: ensure that all meta keys are handled See inline comments for the rationale. --- xso-proc/src/enums.rs | 57 ++++++++++++++++++++++++++--------------- xso-proc/src/meta.rs | 38 +++++++++++++++++++++++++++ xso-proc/src/structs.rs | 26 ++++++++++++++----- 3 files changed, 93 insertions(+), 28 deletions(-) diff --git a/xso-proc/src/enums.rs b/xso-proc/src/enums.rs index 2ee4fd37..dcad8451 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::{NameRef, NamespaceRef, XmlCompoundMeta}; +use crate::meta::{reject_key, Flag, NameRef, NamespaceRef, XmlCompoundMeta}; use crate::state::{AsItemsStateMachine, FromEventsStateMachine}; /// The definition of an enum variant, switched on the XML element's name. @@ -33,17 +33,25 @@ struct NameVariant { impl NameVariant { /// Construct a new name-selected variant from its declaration. fn new(decl: &Variant) -> Result { - let meta = XmlCompoundMeta::parse_from_attributes(&decl.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: meta_span, + namespace, + name, + debug, + builder, + iterator, + } = XmlCompoundMeta::parse_from_attributes(&decl.attrs)?; - if let Some(namespace) = meta.namespace { - return Err(Error::new_spanned( - namespace, - "`namespace` is not allowed on enum variants (only on enums and structs)", - )); - } + reject_key!(debug flag not on "enum variants" only on "enums and structs"); + reject_key!(namespace not on "enum variants" only on "enums and structs"); + 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 Some(name) = meta.name else { - return Err(Error::new(meta.span, "`name` is required on enum variants")); + let Some(name) = name else { + return Err(Error::new(meta_span, "`name` is required on enum variants")); }; Ok(Self { @@ -154,15 +162,22 @@ impl EnumDef { meta: XmlCompoundMeta, variant_iter: I, ) -> Result { - if let Some(name) = meta.name { - return Err(Error::new_spanned( - name, - "`name` is not allowed on enums (only on their variants)", - )); - } + // 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, + namespace, + name, + debug, + builder, + iterator, + } = meta; - let Some(namespace) = meta.namespace else { - return Err(Error::new(meta.span, "`namespace` is required on enums")); + reject_key!(name not on "enums" only on "their variants"); + + let Some(namespace) = namespace else { + return Err(Error::new(meta_span, "`namespace` is required on enums")); }; let mut variants = Vec::new(); @@ -182,12 +197,12 @@ impl EnumDef { variants.push(variant); } - let builder_ty_ident = match meta.builder { + let builder_ty_ident = match builder { Some(v) => v, None => quote::format_ident!("{}FromXmlBuilder", ident.to_string()), }; - let item_iter_ty_ident = match meta.iterator { + let item_iter_ty_ident = match iterator { Some(v) => v, None => quote::format_ident!("{}AsXmlIterator", ident.to_string()), }; @@ -198,7 +213,7 @@ impl EnumDef { target_ty_ident: ident.clone(), builder_ty_ident, item_iter_ty_ident, - debug: meta.debug.is_set(), + debug: debug.is_set(), }) } } diff --git a/xso-proc/src/meta.rs b/xso-proc/src/meta.rs index e954ae2d..8496d513 100644 --- a/xso-proc/src/meta.rs +++ b/xso-proc/src/meta.rs @@ -22,6 +22,44 @@ pub const XMLNS_XML: &str = "http://www.w3.org/XML/1998/namespace"; /// XML namespace URI (for the `xmlns:` prefix) pub const XMLNS_XMLNS: &str = "http://www.w3.org/2000/xmlns/"; +macro_rules! reject_key { + ($key:ident not on $not_allowed_on:literal only on $only_allowed_on:literal) => { + if let Some($key) = $key { + return Err(Error::new_spanned( + $key, + concat!( + "`", + stringify!($key), + "` is not allowed on ", + $not_allowed_on, + " (only on ", + $only_allowed_on, + ")" + ), + )); + } + }; + + ($key:ident flag not on $not_allowed_on:literal only on $only_allowed_on:literal) => { + if let Flag::Present($key) = $key { + return Err(Error::new( + $key, + concat!( + "`", + stringify!($key), + "` is not allowed on ", + $not_allowed_on, + " (only on ", + $only_allowed_on, + ")" + ), + )); + } + }; +} + +pub(crate) use reject_key; + /// Value for the `#[xml(namespace = ..)]` attribute. #[derive(Debug)] pub(crate) enum NamespaceRef { diff --git a/xso-proc/src/structs.rs b/xso-proc/src/structs.rs index 99417d02..74f95564 100644 --- a/xso-proc/src/structs.rs +++ b/xso-proc/src/structs.rs @@ -41,20 +41,32 @@ pub(crate) struct StructDef { impl StructDef { /// Create a new struct from its name, meta, and fields. pub(crate) fn new(ident: &Ident, meta: XmlCompoundMeta, fields: &Fields) -> Result { - let Some(namespace) = meta.namespace else { - return Err(Error::new(meta.span, "`namespace` is required on structs")); + // 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, + namespace, + name, + debug, + builder, + iterator, + } = meta; + + let Some(namespace) = namespace else { + return Err(Error::new(meta_span, "`namespace` is required on structs")); }; - let Some(name) = meta.name else { - return Err(Error::new(meta.span, "`name` is required on structs")); + let Some(name) = name else { + return Err(Error::new(meta_span, "`name` is required on structs")); }; - let builder_ty_ident = match meta.builder { + let builder_ty_ident = match builder { Some(v) => v, None => quote::format_ident!("{}FromXmlBuilder", ident.to_string()), }; - let item_iter_ty_ident = match meta.iterator { + let item_iter_ty_ident = match iterator { Some(v) => v, None => quote::format_ident!("{}AsXmlIterator", ident.to_string()), }; @@ -66,7 +78,7 @@ impl StructDef { target_ty_ident: ident.clone(), builder_ty_ident, item_iter_ty_ident, - debug: meta.debug.is_set(), + debug: debug.is_set(), }) } }