xso_proc: implement normalize_with option on attribute enums

Needed for the `http_upload::Header` enum, because HTTP headers are
case-insensitive.
This commit is contained in:
Jonas Schäfer 2024-03-29 09:25:36 +01:00
parent cac4e08484
commit 57b72e5a27
5 changed files with 77 additions and 1 deletions

View File

@ -1,3 +1,4 @@
use std::borrow::Cow;
use std::collections::BTreeMap;
use crate::core::{
@ -837,3 +838,28 @@ fn attribute_switched_enum_fallback() {
other => panic!("unexpected result: {:?}", other),
}
}
fn to_lower(v: &str) -> Cow<'_, str> {
let mut v = v.to_owned();
v.make_ascii_lowercase();
v.into()
}
#[derive(FromXml, IntoXml, PartialEq, Clone, Debug)]
#[xml(namespace = self::TEST_NS1, name = "attr-switched-enum-norm", attribute = "key", exhaustive, normalize_with = to_lower)]
pub enum AttributeSwitchedEnumNormalized {
#[xml(value = "variant-1")]
Variant1,
#[xml(value = "variant-2")]
Variant2,
}
#[test]
fn attribute_switched_enum_normalized() {
match crate::util::test::parse_str::<AttributeSwitchedEnumNormalized>(
"<attr-switched-enum-norm xmlns='urn:uuid:41854041-fa04-4e2b-94ae-ffaefb6b24e2' key='VarIaNT-2'/>",
) {
Ok(v) => assert_eq!(v, AttributeSwitchedEnumNormalized::Variant2),
other => panic!("unexpected result: {:?}", other),
}
}

View File

@ -453,6 +453,10 @@ struct XmlAttributeSwitched {
/// The name of the XML attribute to read.
attribute_name: LitStr,
/// Function, if any, to normalize the attribute value with, before
/// matching.
normalize_with: Option<Path>,
/// The enum's variants.
variants: Vec<StrMatchedVariant>,
}
@ -472,6 +476,7 @@ impl XmlAttributeSwitched {
namespace: StaticNamespace,
name: Name,
attribute_name: LitStr,
normalize_with: Option<Path>,
input: I,
) -> Result<Self> {
let mut variants = Vec::with_capacity(input.size_hint().1.unwrap_or(0));
@ -507,6 +512,7 @@ impl XmlAttributeSwitched {
namespace,
name,
attribute_name,
normalize_with,
variants,
})
}
@ -584,9 +590,19 @@ impl XmlAttributeSwitched {
attribute_name, enum_ident,
);
let normalize = match self.normalize_with {
Some(normalize_with) => quote! {
let attr_value = attr_value.map(|value| #normalize_with(value));
let attr_value = attr_value.as_ref().map(|value| ::std::borrow::Borrow::<str>::borrow(value));
},
None => quote! {},
};
Ok(quote! {
if #residual.is(#xml_name, #xml_namespace) {
match #residual.attr(#attribute_name) {
let attr_value = #residual.attr(#attribute_name);
#normalize
match attr_value {
Some(v) => match v {
#iter
#fallback
@ -886,6 +902,13 @@ impl EnumDef {
));
}
if let Some(normalize_with) = meta.normalize_with {
return Err(syn::Error::new_spanned(
normalize_with,
"`normalize_with` option is only allowed on attribute value switched enums",
));
};
return Ok(Self {
validate,
prepare,
@ -912,6 +935,12 @@ impl EnumDef {
let name = match meta.name {
None => {
if let Some(normalize_with) = meta.normalize_with {
return Err(syn::Error::new_spanned(
normalize_with,
"`normalize_with` option is only allowed on attribute value switched enums",
));
};
return Ok(Self {
prepare,
validate,
@ -940,6 +969,7 @@ impl EnumDef {
namespace,
name.into(),
attribute_name,
meta.normalize_with,
input,
)?),
})

View File

@ -351,6 +351,9 @@ pub(crate) struct XmlCompoundMeta {
/// The value assigned to `prepare` inside `#[xml(..)]`, if any.
pub(crate) prepare: Option<Path>,
/// The value assigned to `normalize_with` inside `#[xml(..)]`, if any.
pub(crate) normalize_with: Option<Path>,
/// Flag indicating the presence of `debug` inside `#[xml(..)]`
pub(crate) debug: Flag,
}
@ -371,6 +374,7 @@ impl XmlCompoundMeta {
let mut debug = Flag::Absent;
let mut validate: Option<Path> = None;
let mut prepare: Option<Path> = None;
let mut normalize_with: Option<Path> = None;
attr.parse_nested_meta(|meta| {
parse_meta!(
@ -385,6 +389,8 @@ impl XmlCompoundMeta {
ParseFlag("debug", &mut debug),
ParseValue("validate", &mut validate),
ParseValue("prepare", &mut prepare),
ParseValue("normalize_with", &mut normalize_with),
ParseValue("normalise_with", &mut normalize_with),
)
})?;
@ -408,6 +414,7 @@ impl XmlCompoundMeta {
exhaustive,
prepare,
debug,
normalize_with,
})
}

View File

@ -91,6 +91,7 @@ impl StructInner {
assert!(!meta.exhaustive.is_set());
assert!(meta.validate.is_none());
assert!(meta.prepare.is_none());
assert!(meta.normalize_with.is_none());
assert!(!meta.debug.is_set());
assert!(!meta.fallback.is_set());
assert!(meta.attribute.is_none());
@ -449,6 +450,13 @@ impl StructDef {
));
}
if let Some(normalize_with) = meta.normalize_with.take() {
return Err(syn::Error::new_spanned(
normalize_with,
"`normalize_with` is not allowed on structs",
));
}
let validate = meta.validate.take();
let prepare = meta.prepare.take();
let debug = meta.debug.take();

View File

@ -181,6 +181,11 @@ XML attribute matched enums support the following attributes:
This attribute has no relation to the Rust standard `#[non_exhaustive]`
attribute.
- `normalize_with = ..`: Optional path to a thing which can be called with a
`&str` and which returns a [`std::borrow::Cow`]. If present, the attribute
value will be passed through that callable before it will be matched against
the enum variants.
#### XML attribute matched enum variants
XML attribute matched enum variants support the following attributes: