xso-proc: ensure that all meta keys are handled

See inline comments for the rationale.
This commit is contained in:
Jonas Schäfer 2024-07-10 16:34:48 +02:00
commit cf617e4d7e
3 changed files with 95 additions and 30 deletions

View file

@ -15,7 +15,7 @@ use syn::*;
use crate::common::{AsXmlParts, FromXmlParts, ItemDef}; use crate::common::{AsXmlParts, FromXmlParts, ItemDef};
use crate::compound::Compound; use crate::compound::Compound;
use crate::error_message::ParentRef; 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}; use crate::state::{AsItemsStateMachine, FromEventsStateMachine};
/// The definition of an enum variant, switched on the XML element's name. /// The definition of an enum variant, switched on the XML element's name.
@ -33,17 +33,25 @@ struct NameVariant {
impl NameVariant { impl NameVariant {
/// Construct a new name-selected variant from its declaration. /// Construct a new name-selected variant from its declaration.
fn new(decl: &Variant) -> Result<Self> { fn new(decl: &Variant) -> Result<Self> {
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 { reject_key!(debug flag not on "enum variants" only on "enums and structs");
return Err(Error::new_spanned( reject_key!(namespace not on "enum variants" only on "enums and structs");
namespace, reject_key!(builder not on "enum variants" only on "enums and structs");
"`namespace` is not allowed 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 { let Some(name) = name else {
return Err(Error::new(meta.span, "`name` is required on enum variants")); return Err(Error::new(meta_span, "`name` is required on enum variants"));
}; };
Ok(Self { Ok(Self {
@ -154,15 +162,22 @@ impl EnumDef {
meta: XmlCompoundMeta, meta: XmlCompoundMeta,
variant_iter: I, variant_iter: I,
) -> Result<Self> { ) -> Result<Self> {
if let Some(name) = meta.name { // We destructure here so that we get informed when new fields are
return Err(Error::new_spanned( // added and can handle them, either by processing them or raising
name, // an error if they are present.
"`name` is not allowed on enums (only on their variants)", let XmlCompoundMeta {
)); span: meta_span,
} namespace,
name,
debug,
builder,
iterator,
} = meta;
let Some(namespace) = meta.namespace else { reject_key!(name not on "enums" only on "their variants");
return Err(Error::new(meta.span, "`namespace` is required on enums"));
let Some(namespace) = namespace else {
return Err(Error::new(meta_span, "`namespace` is required on enums"));
}; };
let mut variants = Vec::new(); let mut variants = Vec::new();
@ -182,12 +197,12 @@ impl EnumDef {
variants.push(variant); variants.push(variant);
} }
let builder_ty_ident = match meta.builder { let builder_ty_ident = match builder {
Some(v) => v, Some(v) => v,
None => quote::format_ident!("{}FromXmlBuilder", ident.to_string()), 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, Some(v) => v,
None => quote::format_ident!("{}AsXmlIterator", ident.to_string()), None => quote::format_ident!("{}AsXmlIterator", ident.to_string()),
}; };
@ -198,7 +213,7 @@ impl EnumDef {
target_ty_ident: ident.clone(), target_ty_ident: ident.clone(),
builder_ty_ident, builder_ty_ident,
item_iter_ty_ident, item_iter_ty_ident,
debug: meta.debug.is_set(), debug: debug.is_set(),
}) })
} }
} }

View file

@ -22,6 +22,44 @@ pub const XMLNS_XML: &str = "http://www.w3.org/XML/1998/namespace";
/// XML namespace URI (for the `xmlns:` prefix) /// XML namespace URI (for the `xmlns:` prefix)
pub const XMLNS_XMLNS: &str = "http://www.w3.org/2000/xmlns/"; 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. /// Value for the `#[xml(namespace = ..)]` attribute.
#[derive(Debug)] #[derive(Debug)]
pub(crate) enum NamespaceRef { pub(crate) enum NamespaceRef {

View file

@ -41,20 +41,32 @@ pub(crate) struct StructDef {
impl StructDef { impl StructDef {
/// Create a new struct from its name, meta, and fields. /// Create a new struct from its name, meta, and fields.
pub(crate) fn new(ident: &Ident, meta: XmlCompoundMeta, fields: &Fields) -> Result<Self> { pub(crate) fn new(ident: &Ident, meta: XmlCompoundMeta, fields: &Fields) -> Result<Self> {
let Some(namespace) = meta.namespace else { // We destructure here so that we get informed when new fields are
return Err(Error::new(meta.span, "`namespace` is required on structs")); // 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 { let Some(name) = name else {
return Err(Error::new(meta.span, "`name` is required on structs")); 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, Some(v) => v,
None => quote::format_ident!("{}FromXmlBuilder", ident.to_string()), 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, Some(v) => v,
None => quote::format_ident!("{}AsXmlIterator", ident.to_string()), None => quote::format_ident!("{}AsXmlIterator", ident.to_string()),
}; };
@ -66,7 +78,7 @@ impl StructDef {
target_ty_ident: ident.clone(), target_ty_ident: ident.clone(),
builder_ty_ident, builder_ty_ident,
item_iter_ty_ident, item_iter_ty_ident,
debug: meta.debug.is_set(), debug: debug.is_set(),
}) })
} }
} }