mirror of
https://gitlab.com/xmpp-rs/xmpp-rs.git
synced 2024-07-12 22:21:53 +00:00
xso-proc: add support for renaming attributes
This is akin to `#[serde(rename = ..)]` and thus useful.
This commit is contained in:
parent
0bae5d3346
commit
219d682295
4 changed files with 112 additions and 27 deletions
|
@ -228,3 +228,20 @@ fn required_attribute_missing() {
|
||||||
other => panic!("unexpected result: {:?}", other),
|
other => panic!("unexpected result: {:?}", other),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(FromXml, IntoXml, PartialEq, Debug, Clone)]
|
||||||
|
#[xml(namespace = NS1, name = "attr")]
|
||||||
|
struct RenamedAttribute {
|
||||||
|
#[xml(attribute = "a1")]
|
||||||
|
foo: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn renamed_attribute_roundtrip() {
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
use std::{
|
||||||
|
option::Option::{None, Some},
|
||||||
|
result::Result::{Err, Ok},
|
||||||
|
};
|
||||||
|
roundtrip_full::<RenamedAttribute>("<attr xmlns='urn:example:ns1' a1='bar'/>");
|
||||||
|
}
|
||||||
|
|
|
@ -73,30 +73,30 @@ impl FieldKind {
|
||||||
/// it is not specified explicitly.
|
/// it is not specified explicitly.
|
||||||
fn from_meta(meta: XmlFieldMeta, field_ident: Option<&Ident>) -> Result<Self> {
|
fn from_meta(meta: XmlFieldMeta, field_ident: Option<&Ident>) -> Result<Self> {
|
||||||
match meta {
|
match meta {
|
||||||
XmlFieldMeta::Attribute { span } => {
|
XmlFieldMeta::Attribute { span, name } => {
|
||||||
let Some(field_ident) = field_ident else {
|
let xml_name = match name {
|
||||||
return Err(Error::new(
|
Some(v) => v,
|
||||||
span,
|
None => match field_ident {
|
||||||
"attribute extraction not supported on unnamed fields",
|
None => return Err(Error::new(
|
||||||
));
|
span,
|
||||||
};
|
"attribute name must be explicitly specified using `#[xml(attribute = ..)] on unnamed fields",
|
||||||
|
)),
|
||||||
let xml_name = match NcName::try_from(field_ident.to_string()) {
|
Some(field_ident) => match NcName::try_from(field_ident.to_string()) {
|
||||||
Ok(v) => v,
|
Ok(value) => NameRef::Literal {
|
||||||
Err(e) => {
|
span: field_ident.span(),
|
||||||
return Err(Error::new(
|
value,
|
||||||
field_ident.span(),
|
},
|
||||||
format!("invalid XML attribute name: {}", e),
|
Err(e) => {
|
||||||
))
|
return Err(Error::new(
|
||||||
|
field_ident.span(),
|
||||||
|
format!("invalid XML attribute name: {}", e),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Self::Attribute {
|
Ok(Self::Attribute { xml_name })
|
||||||
xml_name: NameRef::Literal {
|
|
||||||
span: field_ident.span(),
|
|
||||||
value: xml_name,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -203,15 +203,51 @@ pub(crate) enum XmlFieldMeta {
|
||||||
///
|
///
|
||||||
/// This is useful for error messages.
|
/// This is useful for error messages.
|
||||||
span: Span,
|
span: Span,
|
||||||
|
|
||||||
|
/// The XML name supplied.
|
||||||
|
name: Option<NameRef>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl XmlFieldMeta {
|
impl XmlFieldMeta {
|
||||||
/// Parse a `#[xml(attribute(..))]` meta.
|
/// Parse a `#[xml(attribute(..))]` meta.
|
||||||
|
///
|
||||||
|
/// That meta can have three distinct syntax styles:
|
||||||
|
/// - argument-less: `#[xml(attribute)]`
|
||||||
|
/// - shorthand: `#[xml(attribute = ..)]`
|
||||||
|
/// - full: `#[xml(attribute(..))]`
|
||||||
fn attribute_from_meta(meta: ParseNestedMeta<'_>) -> Result<Self> {
|
fn attribute_from_meta(meta: ParseNestedMeta<'_>) -> Result<Self> {
|
||||||
Ok(Self::Attribute {
|
if meta.input.peek(Token![=]) {
|
||||||
span: meta.path.span(),
|
// shorthand syntax
|
||||||
})
|
Ok(Self::Attribute {
|
||||||
|
span: meta.path.span(),
|
||||||
|
name: Some(meta.value()?.parse()?),
|
||||||
|
})
|
||||||
|
} else if meta.input.peek(syn::token::Paren) {
|
||||||
|
// full syntax
|
||||||
|
let mut name: Option<NameRef> = None;
|
||||||
|
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"));
|
||||||
|
}
|
||||||
|
name = Some(meta.value()?.parse()?);
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error::new_spanned(meta.path, "unsupported key"))
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
Ok(Self::Attribute {
|
||||||
|
span: meta.path.span(),
|
||||||
|
name,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// argument-less syntax
|
||||||
|
Ok(Self::Attribute {
|
||||||
|
span: meta.path.span(),
|
||||||
|
name: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse [`Self`] from a nestd meta, switching on the identifier
|
/// Parse [`Self`] from a nestd meta, switching on the identifier
|
||||||
|
|
|
@ -62,6 +62,38 @@ The following mapping types are defined:
|
||||||
|
|
||||||
#### `attribute` meta
|
#### `attribute` meta
|
||||||
|
|
||||||
The `attribute` meta does not support additional parameters. The field it is
|
The `attribute` meta causes the field to be mapped to an XML attribute of the
|
||||||
used on is mapped to an XML attribute of the same name and must be of type
|
same name. The field must be of type [`String`].
|
||||||
[`String`].
|
|
||||||
|
The following keys can be used inside the `#[xml(attribute(..))]` meta:
|
||||||
|
|
||||||
|
| Key | Value type | Description |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `name` | *string literal* or *path* | The name of the XML attribute to match. If it is a *path*, it must point at a `&'static NcNameStr`. |
|
||||||
|
|
||||||
|
The `attribute` meta also supports a shorthand syntax,
|
||||||
|
`#[xml(attribute = ..)]`, where the value is treated as the value for the
|
||||||
|
`name` key.
|
||||||
|
|
||||||
|
##### Example
|
||||||
|
|
||||||
|
```rust
|
||||||
|
# use xso::FromXml;
|
||||||
|
#[derive(FromXml, Debug, PartialEq)]
|
||||||
|
#[xml(namespace = "urn:example", name = "foo")]
|
||||||
|
struct Foo {
|
||||||
|
#[xml(attribute)]
|
||||||
|
a: String,
|
||||||
|
#[xml(attribute = "bar")]
|
||||||
|
b: String,
|
||||||
|
#[xml(attribute(name = "baz"))]
|
||||||
|
c: String,
|
||||||
|
};
|
||||||
|
|
||||||
|
let foo: Foo = xso::from_bytes(b"<foo xmlns='urn:example' a='1' bar='2' baz='3'/>").unwrap();
|
||||||
|
assert_eq!(foo, Foo {
|
||||||
|
a: "1".to_string(),
|
||||||
|
b: "2".to_string(),
|
||||||
|
c: "3".to_string(),
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
Loading…
Reference in a new issue