xso-proc: ensure that all meta keys are handled
See inline comments for the rationale.
This commit is contained in:
parent
4845715add
commit
cf617e4d7e
3 changed files with 95 additions and 30 deletions
|
|
@ -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(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue