mirror of
https://github.com/instant-labs/instant-xml.git
synced 2025-02-16 06:52:15 +00:00
Add support for unnamed fields
This commit is contained in:
parent
69eca9c2a5
commit
894da25f8b
@ -13,7 +13,11 @@ pub(crate) fn from_xml(input: &syn::DeriveInput) -> TokenStream {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match (&input.data, meta.mode) {
|
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::Scalar)) => deserialize_scalar_enum(input, data, meta),
|
||||||
(syn::Data::Enum(data), Some(Mode::Wrapped)) => deserialize_wrapped_enum(input, data, meta),
|
(syn::Data::Enum(data), Some(Mode::Wrapped)) => deserialize_wrapped_enum(input, data, meta),
|
||||||
(syn::Data::Struct(_), _) => {
|
(syn::Data::Struct(_), _) => {
|
||||||
@ -172,7 +176,7 @@ fn deserialize_wrapped_enum(
|
|||||||
|
|
||||||
fn deserialize_struct(
|
fn deserialize_struct(
|
||||||
input: &syn::DeriveInput,
|
input: &syn::DeriveInput,
|
||||||
data: &syn::DataStruct,
|
fields: &syn::FieldsNamed,
|
||||||
container_meta: ContainerMeta,
|
container_meta: ContainerMeta,
|
||||||
) -> TokenStream {
|
) -> TokenStream {
|
||||||
let mut namespaces_map = quote!(let mut namespaces_map = std::collections::HashMap::new(););
|
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 declare_values = TokenStream::new();
|
||||||
let mut return_val = TokenStream::new();
|
let mut return_val = TokenStream::new();
|
||||||
|
|
||||||
match &data.fields {
|
for (index, field) in fields.named.iter().enumerate() {
|
||||||
syn::Fields::Named(fields) => {
|
let field_meta = match FieldMeta::from_field(field, &container_meta) {
|
||||||
for (index, field) in fields.named.iter().enumerate() {
|
Ok(meta) => meta,
|
||||||
let field_meta = match FieldMeta::from_field(field, &container_meta) {
|
Err(err) => return err.into_compile_error(),
|
||||||
Ok(meta) => meta,
|
};
|
||||||
Err(err) => return err.into_compile_error(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let tokens = match field_meta.attribute {
|
let tokens = match field_meta.attribute {
|
||||||
true => &mut attributes_tokens,
|
true => &mut attributes_tokens,
|
||||||
false => &mut elements_tokens,
|
false => &mut elements_tokens,
|
||||||
};
|
};
|
||||||
|
|
||||||
named_field(
|
named_field(
|
||||||
field,
|
field,
|
||||||
index,
|
index,
|
||||||
&mut declare_values,
|
&mut declare_values,
|
||||||
&mut return_val,
|
&mut return_val,
|
||||||
tokens,
|
tokens,
|
||||||
field_meta,
|
field_meta,
|
||||||
&container_meta,
|
&container_meta,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
syn::Fields::Unnamed(_) => panic!("unamed"),
|
|
||||||
syn::Fields::Unit => {}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Elements
|
// Elements
|
||||||
let elements_enum = elements_tokens.r#enum;
|
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)]
|
#[derive(Default)]
|
||||||
struct Tokens {
|
struct Tokens {
|
||||||
r#enum: TokenStream,
|
r#enum: TokenStream,
|
||||||
|
@ -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 => {}
|
syn::Fields::Unit => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -340,3 +346,25 @@ fn named_field(
|
|||||||
|
|
||||||
Ok(())
|
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(())
|
||||||
|
}
|
||||||
|
@ -2,7 +2,7 @@ use std::collections::{BTreeMap, VecDeque};
|
|||||||
|
|
||||||
use xmlparser::{ElementEnd, Token, Tokenizer};
|
use xmlparser::{ElementEnd, Token, Tokenizer};
|
||||||
|
|
||||||
use crate::{Kind, Error, Id};
|
use crate::{Error, Id, Kind};
|
||||||
|
|
||||||
pub struct Deserializer<'cx, 'xml> {
|
pub struct Deserializer<'cx, 'xml> {
|
||||||
pub(crate) local: &'xml str,
|
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]
|
#[inline]
|
||||||
pub fn element_id(&self, element: &Element<'xml>) -> Result<Id<'xml>, Error> {
|
pub fn element_id(&self, element: &Element<'xml>) -> Result<Id<'xml>, Error> {
|
||||||
self.context.element_id(element)
|
self.context.element_id(element)
|
||||||
|
12
instant-xml/tests/unnamed.rs
Normal file
12
instant-xml/tests/unnamed.rs
Normal 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());
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user