Add support for rename annotations
This commit is contained in:
parent
f64634155e
commit
fb7570056d
|
@ -1,5 +1,5 @@
|
|||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use quote::quote;
|
||||
use quote::{quote, ToTokens};
|
||||
|
||||
use super::{discard_lifetimes, ContainerMeta, FieldMeta, Namespace};
|
||||
|
||||
|
@ -76,7 +76,10 @@ pub(crate) fn from_xml(input: &syn::DeriveInput) -> TokenStream {
|
|||
let attributes_names = attributes_tokens.names;
|
||||
let attr_type_match = attributes_tokens.r#match;
|
||||
|
||||
let name = ident.to_string();
|
||||
let name = match &container_meta.rename {
|
||||
Some(name) => quote!(#name),
|
||||
None => ident.to_string().into_token_stream(),
|
||||
};
|
||||
quote!(
|
||||
impl #xml_impl_generics FromXml<'xml> for #ident #ty_generics #where_clause {
|
||||
fn deserialize<'cx>(deserializer: &'cx mut ::instant_xml::Deserializer<'cx, 'xml>) -> Result<Self, ::instant_xml::Error> {
|
||||
|
@ -160,9 +163,13 @@ fn process_field(
|
|||
field_meta: FieldMeta,
|
||||
container_meta: &ContainerMeta,
|
||||
) {
|
||||
let field_var = field.ident.as_ref().unwrap();
|
||||
let field_var_str = field_var.to_string();
|
||||
let const_field_var_str = Ident::new(&field_var_str.to_uppercase(), Span::call_site());
|
||||
let field_name = field.ident.as_ref().unwrap();
|
||||
let field_tag = match field_meta.rename {
|
||||
Some(name) => quote!(#name),
|
||||
None => field_name.to_string().into_token_stream(),
|
||||
};
|
||||
|
||||
let const_field_var_str = Ident::new(&field_name.to_string().to_uppercase(), Span::call_site());
|
||||
let mut no_lifetime_type = field.ty.clone();
|
||||
discard_lifetimes(&mut no_lifetime_type);
|
||||
|
||||
|
@ -182,7 +189,7 @@ fn process_field(
|
|||
|
||||
tokens.consts.extend(quote!(
|
||||
const #const_field_var_str: Id<'static> = <#no_lifetime_type as FromXml<'_>>::KIND.name(
|
||||
Id { ns: #ns, name: #field_var_str }
|
||||
Id { ns: #ns, name: #field_tag }
|
||||
);
|
||||
));
|
||||
|
||||
|
@ -225,7 +232,7 @@ fn process_field(
|
|||
}
|
||||
|
||||
return_val.extend(quote!(
|
||||
#field_var: match #enum_name {
|
||||
#field_name: match #enum_name {
|
||||
Some(v) => v,
|
||||
None => <#no_lifetime_type>::missing_value()?,
|
||||
},
|
||||
|
|
|
@ -24,9 +24,10 @@ pub fn from_xml(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
|||
proc_macro::TokenStream::from(de::from_xml(&ast))
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Debug, Default)]
|
||||
struct ContainerMeta {
|
||||
ns: NamespaceMeta,
|
||||
rename: Option<Literal>,
|
||||
}
|
||||
|
||||
impl ContainerMeta {
|
||||
|
@ -36,16 +37,18 @@ impl ContainerMeta {
|
|||
match item {
|
||||
MetaItem::Attribute => panic!("attribute key invalid in container xml attribute"),
|
||||
MetaItem::Ns(ns) => meta.ns = ns,
|
||||
MetaItem::Rename(lit) => meta.rename = Some(lit),
|
||||
}
|
||||
}
|
||||
meta
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Debug, Default)]
|
||||
struct FieldMeta {
|
||||
attribute: bool,
|
||||
ns: NamespaceMeta,
|
||||
rename: Option<Literal>,
|
||||
}
|
||||
|
||||
impl FieldMeta {
|
||||
|
@ -55,13 +58,14 @@ impl FieldMeta {
|
|||
match item {
|
||||
MetaItem::Attribute => meta.attribute = true,
|
||||
MetaItem::Ns(ns) => meta.ns = ns,
|
||||
MetaItem::Rename(lit) => meta.rename = Some(lit),
|
||||
}
|
||||
}
|
||||
meta
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Debug, Default)]
|
||||
struct NamespaceMeta {
|
||||
uri: Option<Namespace>,
|
||||
prefixes: BTreeMap<String, Namespace>,
|
||||
|
@ -346,6 +350,8 @@ fn meta_items(attrs: &[syn::Attribute]) -> Vec<MetaItem> {
|
|||
MetaState::Comma
|
||||
} else if id == "ns" {
|
||||
MetaState::Ns
|
||||
} else if id == "rename" {
|
||||
MetaState::Rename
|
||||
} else {
|
||||
panic!("unexpected key in xml attribute");
|
||||
}
|
||||
|
@ -359,6 +365,13 @@ fn meta_items(attrs: &[syn::Attribute]) -> Vec<MetaItem> {
|
|||
items.push(MetaItem::Ns(NamespaceMeta::from_tokens(group)));
|
||||
MetaState::Comma
|
||||
}
|
||||
(MetaState::Rename, TokenTree::Punct(punct)) if punct.as_char() == '=' => {
|
||||
MetaState::RenameValue
|
||||
}
|
||||
(MetaState::RenameValue, TokenTree::Literal(lit)) => {
|
||||
items.push(MetaItem::Rename(lit));
|
||||
MetaState::Comma
|
||||
}
|
||||
(state, tree) => {
|
||||
panic!(
|
||||
"invalid state transition while parsing xml attribute ({}, {tree})",
|
||||
|
@ -376,6 +389,8 @@ enum MetaState {
|
|||
Start,
|
||||
Comma,
|
||||
Ns,
|
||||
Rename,
|
||||
RenameValue,
|
||||
}
|
||||
|
||||
impl MetaState {
|
||||
|
@ -384,6 +399,8 @@ impl MetaState {
|
|||
MetaState::Start => "Start",
|
||||
MetaState::Comma => "Comma",
|
||||
MetaState::Ns => "Ns",
|
||||
MetaState::Rename => "Rename",
|
||||
MetaState::RenameValue => "RenameValue",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -438,9 +455,11 @@ impl NsState {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum MetaItem {
|
||||
Ns(NamespaceMeta),
|
||||
Attribute,
|
||||
Ns(NamespaceMeta),
|
||||
Rename(Literal),
|
||||
}
|
||||
|
||||
enum Namespace {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use quote::{quote, ToTokens};
|
||||
use syn::spanned::Spanned;
|
||||
|
||||
use crate::Namespace;
|
||||
|
@ -42,8 +42,11 @@ pub fn to_xml(input: &syn::DeriveInput) -> proc_macro2::TokenStream {
|
|||
}
|
||||
|
||||
let ident = &input.ident;
|
||||
let root_name = ident.to_string();
|
||||
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||
let tag = match &meta.rename {
|
||||
Some(rename) => quote!(#rename),
|
||||
None => ident.to_string().into_token_stream(),
|
||||
};
|
||||
|
||||
quote!(
|
||||
impl #impl_generics ToXml for #ident #ty_generics #where_clause {
|
||||
|
@ -52,7 +55,7 @@ pub fn to_xml(input: &syn::DeriveInput) -> proc_macro2::TokenStream {
|
|||
serializer: &mut instant_xml::Serializer<W>,
|
||||
) -> Result<(), instant_xml::Error> {
|
||||
// Start tag
|
||||
let prefix = serializer.write_start(#root_name, #default_namespace, false)?;
|
||||
let prefix = serializer.write_start(#tag, #default_namespace, false)?;
|
||||
debug_assert_eq!(prefix, None);
|
||||
|
||||
// Set up element context, this will also emit namespace declarations
|
||||
|
@ -66,7 +69,7 @@ pub fn to_xml(input: &syn::DeriveInput) -> proc_macro2::TokenStream {
|
|||
#body
|
||||
|
||||
// Close tag
|
||||
serializer.write_close(prefix, #root_name)?;
|
||||
serializer.write_close(prefix, #tag)?;
|
||||
serializer.pop(old);
|
||||
|
||||
Ok(())
|
||||
|
@ -74,7 +77,7 @@ pub fn to_xml(input: &syn::DeriveInput) -> proc_macro2::TokenStream {
|
|||
|
||||
const KIND: ::instant_xml::Kind = ::instant_xml::Kind::Element(::instant_xml::Id {
|
||||
ns: #default_namespace,
|
||||
name: #root_name,
|
||||
name: #tag,
|
||||
});
|
||||
};
|
||||
)
|
||||
|
@ -86,9 +89,12 @@ fn process_named_field(
|
|||
attributes: &mut TokenStream,
|
||||
meta: &ContainerMeta,
|
||||
) {
|
||||
let name = field.ident.as_ref().unwrap().to_string();
|
||||
let field_value = field.ident.as_ref().unwrap();
|
||||
let field_name = field.ident.as_ref().unwrap();
|
||||
let field_meta = FieldMeta::from_field(field);
|
||||
let tag = match &field_meta.rename {
|
||||
Some(rename) => quote!(#rename),
|
||||
None => field_name.to_string().into_token_stream(),
|
||||
};
|
||||
|
||||
let default_ns = &meta.ns.uri;
|
||||
if field_meta.attribute {
|
||||
|
@ -130,7 +136,7 @@ fn process_named_field(
|
|||
|
||||
attributes.extend(quote!(
|
||||
#error
|
||||
serializer.write_attr(#name, #ns, &self.#field_value)?;
|
||||
serializer.write_attr(#tag, #ns, &self.#field_name)?;
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
@ -148,13 +154,13 @@ fn process_named_field(
|
|||
body.extend(quote!(
|
||||
match <#no_lifetime_type as ToXml>::KIND {
|
||||
::instant_xml::Kind::Element(_) => {
|
||||
self.#field_value.serialize(serializer)?;
|
||||
self.#field_name.serialize(serializer)?;
|
||||
}
|
||||
::instant_xml::Kind::Scalar => {
|
||||
let prefix = serializer.write_start(#name, #ns, true)?;
|
||||
let prefix = serializer.write_start(#tag, #ns, true)?;
|
||||
serializer.end_start()?;
|
||||
self.#field_value.serialize(serializer)?;
|
||||
serializer.write_close(prefix, #name)?;
|
||||
self.#field_name.serialize(serializer)?;
|
||||
serializer.write_close(prefix, #tag)?;
|
||||
}
|
||||
}
|
||||
));
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
use similar_asserts::assert_eq;
|
||||
|
||||
use instant_xml::{from_str, to_string, FromXml, ToXml};
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, FromXml, ToXml)]
|
||||
#[xml(rename = "renamed")]
|
||||
struct Renamed {
|
||||
#[xml(attribute, rename = "renamed")]
|
||||
flag: bool,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn renamed() {
|
||||
assert_eq!(
|
||||
from_str::<Renamed>("<renamed renamed=\"true\"></renamed>"),
|
||||
Ok(Renamed { flag: true })
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
to_string(&Renamed { flag: true }).unwrap(),
|
||||
"<renamed renamed=\"true\"></renamed>"
|
||||
);
|
||||
}
|
Loading…
Reference in New Issue