Add support for unnamed fields

This commit is contained in:
Dirkjan Ochtman 2022-11-26 12:53:57 -08:00
parent 69eca9c2a5
commit 894da25f8b
4 changed files with 199 additions and 29 deletions

View File

@ -13,7 +13,11 @@ pub(crate) fn from_xml(input: &syn::DeriveInput) -> TokenStream {
};
match (&input.data, meta.mode) {
(syn::Data::Struct(data), None) => deserialize_struct(input, data, meta),
(syn::Data::Struct(data), None) => match &data.fields {
syn::Fields::Named(fields) => deserialize_struct(input, fields, meta),
syn::Fields::Unnamed(fields) => deserialize_tuple_struct(input, fields, meta),
syn::Fields::Unit => deserialize_unit_struct(input, &meta),
},
(syn::Data::Enum(data), Some(Mode::Scalar)) => deserialize_scalar_enum(input, data, meta),
(syn::Data::Enum(data), Some(Mode::Wrapped)) => deserialize_wrapped_enum(input, data, meta),
(syn::Data::Struct(_), _) => {
@ -172,7 +176,7 @@ fn deserialize_wrapped_enum(
fn deserialize_struct(
input: &syn::DeriveInput,
data: &syn::DataStruct,
fields: &syn::FieldsNamed,
container_meta: ContainerMeta,
) -> TokenStream {
let mut namespaces_map = quote!(let mut namespaces_map = std::collections::HashMap::new(););
@ -190,33 +194,27 @@ fn deserialize_struct(
let mut declare_values = TokenStream::new();
let mut return_val = TokenStream::new();
match &data.fields {
syn::Fields::Named(fields) => {
for (index, field) in fields.named.iter().enumerate() {
let field_meta = match FieldMeta::from_field(field, &container_meta) {
Ok(meta) => meta,
Err(err) => return err.into_compile_error(),
};
for (index, field) in fields.named.iter().enumerate() {
let field_meta = match FieldMeta::from_field(field, &container_meta) {
Ok(meta) => meta,
Err(err) => return err.into_compile_error(),
};
let tokens = match field_meta.attribute {
true => &mut attributes_tokens,
false => &mut elements_tokens,
};
let tokens = match field_meta.attribute {
true => &mut attributes_tokens,
false => &mut elements_tokens,
};
named_field(
field,
index,
&mut declare_values,
&mut return_val,
tokens,
field_meta,
&container_meta,
);
}
}
syn::Fields::Unnamed(_) => panic!("unamed"),
syn::Fields::Unit => {}
};
named_field(
field,
index,
&mut declare_values,
&mut return_val,
tokens,
field_meta,
&container_meta,
);
}
// Elements
let elements_enum = elements_tokens.r#enum;
@ -377,6 +375,134 @@ fn named_field(
));
}
fn deserialize_tuple_struct(
input: &syn::DeriveInput,
fields: &syn::FieldsUnnamed,
container_meta: ContainerMeta,
) -> TokenStream {
let mut namespaces_map = quote!(let mut namespaces_map = std::collections::HashMap::new(););
for (k, v) in container_meta.ns.prefixes.iter() {
namespaces_map.extend(quote!(
namespaces_map.insert(#k, #v);
))
}
// Varying values
let mut declare_values = TokenStream::new();
let mut return_val = TokenStream::new();
for (index, field) in fields.unnamed.iter().enumerate() {
if !field.attrs.is_empty() {
return syn::Error::new(
field.span(),
"attributes not allowed on tuple struct fields",
)
.to_compile_error();
}
unnamed_field(field, index, &mut declare_values, &mut return_val);
}
let ident = &input.ident;
let name = container_meta.tag();
let default_namespace = container_meta.default_namespace();
let generics = container_meta.xml_generics();
let (xml_impl_generics, _, _) = generics.split_for_impl();
let (_, ty_generics, where_clause) = input.generics.split_for_impl();
quote!(
impl #xml_impl_generics FromXml<'xml> for #ident #ty_generics #where_clause {
fn deserialize<'cx>(
deserializer: &'cx mut ::instant_xml::Deserializer<'cx, 'xml>,
into: &mut Option<Self>,
) -> Result<(), ::instant_xml::Error> {
use ::instant_xml::de::Node;
use ::instant_xml::{Error, Id};
#declare_values
*into = Some(Self(#return_val));
Ok(())
}
const KIND: ::instant_xml::Kind<'static> = ::instant_xml::Kind::Element(::instant_xml::Id {
ns: #default_namespace,
name: #name,
});
}
)
}
#[allow(clippy::too_many_arguments)]
fn unnamed_field(
field: &syn::Field,
index: usize,
declare_values: &mut TokenStream,
return_val: &mut TokenStream,
) {
let mut no_lifetime_type = field.ty.clone();
discard_lifetimes(&mut no_lifetime_type);
let name = Ident::new(&format!("v{index}"), Span::call_site());
declare_values.extend(quote!(
let node = match deserializer.next() {
Some(result) => result?,
None => return Err(Error::MissingValue(&<#no_lifetime_type as FromXml>::KIND)),
};
let #name = match node {
Node::Open(data) => {
let mut value: Option<#no_lifetime_type> = None;
<#no_lifetime_type>::deserialize(deserializer, &mut value)?;
value
}
Node::Text(data) => {
deserializer.push_front(Node::Text(data));
let mut value: Option<#no_lifetime_type> = None;
<#no_lifetime_type>::deserialize(deserializer, &mut value)?;
value
}
node => return Err(Error::UnexpectedNode(format!("{:?}", node))),
};
));
return_val.extend(quote!(
match #name {
Some(v) => v,
None => <#no_lifetime_type>::missing_value()?,
},
));
}
fn deserialize_unit_struct(input: &syn::DeriveInput, meta: &ContainerMeta) -> TokenStream {
let ident = &input.ident;
let name = meta.tag();
let default_namespace = meta.default_namespace();
let generics = meta.xml_generics();
let (xml_impl_generics, _, _) = generics.split_for_impl();
let (_, ty_generics, where_clause) = input.generics.split_for_impl();
quote!(
impl #xml_impl_generics FromXml<'xml> for #ident #ty_generics #where_clause {
fn deserialize<'cx>(
deserializer: &'cx mut ::instant_xml::Deserializer<'cx, 'xml>,
into: &mut Option<Self>,
) -> Result<(), ::instant_xml::Error> {
deserializer.ignore()?;
*into = Some(Self);
Ok(())
}
const KIND: ::instant_xml::Kind<'static> = ::instant_xml::Kind::Element(::instant_xml::Id {
ns: #default_namespace,
name: #name,
});
}
)
}
#[derive(Default)]
struct Tokens {
r#enum: TokenStream,

View File

@ -188,7 +188,13 @@ fn serialize_struct(
}
}
}
syn::Fields::Unnamed(_) => todo!(),
syn::Fields::Unnamed(fields) => {
for (index, field) in fields.unnamed.iter().enumerate() {
if let Err(err) = unnamed_field(field, index, &mut body) {
return err.to_compile_error();
}
}
}
syn::Fields::Unit => {}
};
@ -340,3 +346,25 @@ fn named_field(
Ok(())
}
fn unnamed_field(
field: &syn::Field,
index: usize,
body: &mut TokenStream,
) -> Result<(), syn::Error> {
if !field.attrs.is_empty() {
return Err(syn::Error::new(
field.span(),
"unnamed fields cannot have attributes",
));
}
let mut no_lifetime_type = field.ty.clone();
discard_lifetimes(&mut no_lifetime_type);
let index = syn::Index::from(index);
body.extend(quote!(
self.#index.serialize(None, serializer)?;
));
Ok(())
}

View File

@ -2,7 +2,7 @@ use std::collections::{BTreeMap, VecDeque};
use xmlparser::{ElementEnd, Token, Tokenizer};
use crate::{Kind, Error, Id};
use crate::{Error, Id, Kind};
pub struct Deserializer<'cx, 'xml> {
pub(crate) local: &'xml str,
@ -90,6 +90,10 @@ impl<'cx, 'xml> Deserializer<'cx, 'xml> {
}
}
pub fn push_front(&mut self, node: Node<'xml>) {
self.context.records.push_front(node);
}
#[inline]
pub fn element_id(&self, element: &Element<'xml>) -> Result<Id<'xml>, Error> {
self.context.element_id(element)

View File

@ -0,0 +1,12 @@
use instant_xml::{from_str, to_string, FromXml, ToXml};
#[derive(Debug, Eq, FromXml, PartialEq, ToXml)]
struct OneNumber(i32);
#[test]
fn one_number() {
let v = OneNumber(42);
let xml = r#"<OneNumber>42</OneNumber>"#;
assert_eq!(xml, to_string(&v).unwrap());
assert_eq!(v, from_str(xml).unwrap());
}