Add support for deserialize_with and borrow
This commit is contained in:
parent
aab73952a1
commit
20f73b7e01
|
@ -1,4 +1,6 @@
|
||||||
use proc_macro2::{Ident, Span, TokenStream};
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
|
use proc_macro2::{Ident, Literal, Span, TokenStream};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
|
|
||||||
|
@ -49,7 +51,7 @@ fn deserialize_scalar_enum(
|
||||||
variants.extend(quote!(Ok(#serialize_as) => #ident::#v_ident,));
|
variants.extend(quote!(Ok(#serialize_as) => #ident::#v_ident,));
|
||||||
}
|
}
|
||||||
|
|
||||||
let generics = meta.xml_generics();
|
let generics = meta.xml_generics(BTreeSet::new());
|
||||||
let (impl_generics, _, _) = generics.split_for_impl();
|
let (impl_generics, _, _) = generics.split_for_impl();
|
||||||
let (_, ty_generics, where_clause) = input.generics.split_for_impl();
|
let (_, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||||
|
|
||||||
|
@ -90,6 +92,7 @@ fn deserialize_wrapped_enum(
|
||||||
|
|
||||||
let ident = &input.ident;
|
let ident = &input.ident;
|
||||||
let mut variants = TokenStream::new();
|
let mut variants = TokenStream::new();
|
||||||
|
let mut borrowed = BTreeSet::new();
|
||||||
for variant in data.variants.iter() {
|
for variant in data.variants.iter() {
|
||||||
let field = match &variant.fields {
|
let field = match &variant.fields {
|
||||||
syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
|
syn::Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
|
||||||
|
@ -113,7 +116,7 @@ fn deserialize_wrapped_enum(
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut no_lifetime_type = field.ty.clone();
|
let mut no_lifetime_type = field.ty.clone();
|
||||||
discard_lifetimes(&mut no_lifetime_type);
|
discard_lifetimes(&mut no_lifetime_type, &mut borrowed, false, true);
|
||||||
|
|
||||||
if !variants.is_empty() {
|
if !variants.is_empty() {
|
||||||
variants.extend(quote!(else));
|
variants.extend(quote!(else));
|
||||||
|
@ -132,7 +135,7 @@ fn deserialize_wrapped_enum(
|
||||||
|
|
||||||
let name = meta.tag();
|
let name = meta.tag();
|
||||||
let default_namespace = meta.default_namespace();
|
let default_namespace = meta.default_namespace();
|
||||||
let generics = meta.xml_generics();
|
let generics = meta.xml_generics(borrowed);
|
||||||
let (xml_impl_generics, _, _) = generics.split_for_impl();
|
let (xml_impl_generics, _, _) = generics.split_for_impl();
|
||||||
let (_, ty_generics, where_clause) = input.generics.split_for_impl();
|
let (_, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||||
quote!(
|
quote!(
|
||||||
|
@ -194,6 +197,7 @@ 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();
|
||||||
|
|
||||||
|
let mut borrowed = BTreeSet::new();
|
||||||
for (index, field) in fields.named.iter().enumerate() {
|
for (index, field) in fields.named.iter().enumerate() {
|
||||||
let field_meta = match FieldMeta::from_field(field, &container_meta) {
|
let field_meta = match FieldMeta::from_field(field, &container_meta) {
|
||||||
Ok(meta) => meta,
|
Ok(meta) => meta,
|
||||||
|
@ -205,15 +209,20 @@ fn deserialize_struct(
|
||||||
false => &mut elements_tokens,
|
false => &mut elements_tokens,
|
||||||
};
|
};
|
||||||
|
|
||||||
named_field(
|
let result = named_field(
|
||||||
field,
|
field,
|
||||||
index,
|
index,
|
||||||
&mut declare_values,
|
&mut declare_values,
|
||||||
&mut return_val,
|
&mut return_val,
|
||||||
tokens,
|
tokens,
|
||||||
|
&mut borrowed,
|
||||||
field_meta,
|
field_meta,
|
||||||
&container_meta,
|
&container_meta,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if let Err(err) = result {
|
||||||
|
return err.into_compile_error();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Elements
|
// Elements
|
||||||
|
@ -237,7 +246,7 @@ fn deserialize_struct(
|
||||||
let ident = &input.ident;
|
let ident = &input.ident;
|
||||||
let name = container_meta.tag();
|
let name = container_meta.tag();
|
||||||
let default_namespace = container_meta.default_namespace();
|
let default_namespace = container_meta.default_namespace();
|
||||||
let generics = container_meta.xml_generics();
|
let generics = container_meta.xml_generics(borrowed);
|
||||||
|
|
||||||
let (xml_impl_generics, _, _) = generics.split_for_impl();
|
let (xml_impl_generics, _, _) = generics.split_for_impl();
|
||||||
let (_, ty_generics, where_clause) = input.generics.split_for_impl();
|
let (_, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||||
|
@ -313,9 +322,10 @@ fn named_field(
|
||||||
declare_values: &mut TokenStream,
|
declare_values: &mut TokenStream,
|
||||||
return_val: &mut TokenStream,
|
return_val: &mut TokenStream,
|
||||||
tokens: &mut Tokens,
|
tokens: &mut Tokens,
|
||||||
field_meta: FieldMeta,
|
borrowed: &mut BTreeSet<syn::Lifetime>,
|
||||||
|
mut field_meta: FieldMeta,
|
||||||
container_meta: &ContainerMeta,
|
container_meta: &ContainerMeta,
|
||||||
) {
|
) -> Result<(), syn::Error> {
|
||||||
let field_name = field.ident.as_ref().unwrap();
|
let field_name = field.ident.as_ref().unwrap();
|
||||||
let field_tag = field_meta.tag;
|
let field_tag = field_meta.tag;
|
||||||
let default_ns = match &field_meta.ns.uri {
|
let default_ns = match &field_meta.ns.uri {
|
||||||
|
@ -329,8 +339,18 @@ fn named_field(
|
||||||
None => quote!(""),
|
None => quote!(""),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if field_meta.borrow && field_meta.deserialize_with.is_none() {
|
||||||
|
if is_cow(&field.ty, is_str) {
|
||||||
|
field_meta.deserialize_with =
|
||||||
|
Some(Literal::string("::instant_xml::de::borrow_cow_str"));
|
||||||
|
} else if is_cow(&field.ty, is_slice_u8) {
|
||||||
|
field_meta.deserialize_with =
|
||||||
|
Some(Literal::string("::instant_xml::de::borrow_cow_slice_u8"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut no_lifetime_type = field.ty.clone();
|
let mut no_lifetime_type = field.ty.clone();
|
||||||
discard_lifetimes(&mut no_lifetime_type);
|
discard_lifetimes(&mut no_lifetime_type, borrowed, field_meta.borrow, true);
|
||||||
|
|
||||||
let enum_name = Ident::new(&format!("__Value{index}"), Span::call_site());
|
let enum_name = Ident::new(&format!("__Value{index}"), Span::call_site());
|
||||||
tokens.r#enum.extend(quote!(#enum_name,));
|
tokens.r#enum.extend(quote!(#enum_name,));
|
||||||
|
@ -351,27 +371,58 @@ fn named_field(
|
||||||
let mut #enum_name: Option<#no_lifetime_type> = None;
|
let mut #enum_name: Option<#no_lifetime_type> = None;
|
||||||
));
|
));
|
||||||
|
|
||||||
|
let deserialize_with = field_meta
|
||||||
|
.deserialize_with
|
||||||
|
.map(|with| {
|
||||||
|
let path = with.to_string();
|
||||||
|
syn::parse_str::<syn::Path>(path.trim_matches('"')).map_err(|err| {
|
||||||
|
syn::Error::new(
|
||||||
|
with.span(),
|
||||||
|
format!("failed to parse deserialize_with as path: {err}"),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.transpose()?;
|
||||||
|
|
||||||
if !field_meta.attribute {
|
if !field_meta.attribute {
|
||||||
tokens.r#match.extend(quote!(
|
if let Some(with) = deserialize_with {
|
||||||
__Elements::#enum_name => match <#no_lifetime_type as FromXml>::KIND {
|
tokens.r#match.extend(quote!(
|
||||||
Kind::Element(_) => {
|
__Elements::#enum_name => {
|
||||||
let mut nested = deserializer.nested(data);
|
let mut nested = deserializer.nested(data);
|
||||||
FromXml::deserialize(&mut nested, &mut #enum_name)?;
|
#with(&mut nested, &mut #enum_name)?;
|
||||||
}
|
},
|
||||||
Kind::Scalar => {
|
));
|
||||||
let mut nested = deserializer.nested(data);
|
} else {
|
||||||
FromXml::deserialize(&mut nested, &mut #enum_name)?;
|
tokens.r#match.extend(quote!(
|
||||||
nested.ignore()?;
|
__Elements::#enum_name => match <#no_lifetime_type as FromXml>::KIND {
|
||||||
}
|
Kind::Element(_) => {
|
||||||
},
|
let mut nested = deserializer.nested(data);
|
||||||
));
|
FromXml::deserialize(&mut nested, &mut #enum_name)?;
|
||||||
|
}
|
||||||
|
Kind::Scalar => {
|
||||||
|
let mut nested = deserializer.nested(data);
|
||||||
|
FromXml::deserialize(&mut nested, &mut #enum_name)?;
|
||||||
|
nested.ignore()?;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
tokens.r#match.extend(quote!(
|
if let Some(with) = deserialize_with {
|
||||||
__Attributes::#enum_name => {
|
tokens.r#match.extend(quote!(
|
||||||
let mut nested = deserializer.for_node(Node::AttributeValue(attr.value));
|
__Attributes::#enum_name => {
|
||||||
let new = <#no_lifetime_type>::deserialize(&mut nested, &mut #enum_name)?;
|
let mut nested = deserializer.nested(data);
|
||||||
},
|
#with(&mut nested, &mut #enum_name)?;
|
||||||
));
|
},
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
tokens.r#match.extend(quote!(
|
||||||
|
__Attributes::#enum_name => {
|
||||||
|
let mut nested = deserializer.for_node(Node::AttributeValue(attr.value));
|
||||||
|
let new = <#no_lifetime_type>::deserialize(&mut nested, &mut #enum_name)?;
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return_val.extend(quote!(
|
return_val.extend(quote!(
|
||||||
|
@ -380,6 +431,8 @@ fn named_field(
|
||||||
None => <#no_lifetime_type>::missing_value()?,
|
None => <#no_lifetime_type>::missing_value()?,
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_tuple_struct(
|
fn deserialize_tuple_struct(
|
||||||
|
@ -397,7 +450,7 @@ fn deserialize_tuple_struct(
|
||||||
// Varying values
|
// Varying values
|
||||||
let mut declare_values = TokenStream::new();
|
let mut declare_values = TokenStream::new();
|
||||||
let mut return_val = TokenStream::new();
|
let mut return_val = TokenStream::new();
|
||||||
|
let mut borrowed = BTreeSet::new();
|
||||||
for (index, field) in fields.unnamed.iter().enumerate() {
|
for (index, field) in fields.unnamed.iter().enumerate() {
|
||||||
if !field.attrs.is_empty() {
|
if !field.attrs.is_empty() {
|
||||||
return syn::Error::new(
|
return syn::Error::new(
|
||||||
|
@ -407,13 +460,19 @@ fn deserialize_tuple_struct(
|
||||||
.to_compile_error();
|
.to_compile_error();
|
||||||
}
|
}
|
||||||
|
|
||||||
unnamed_field(field, index, &mut declare_values, &mut return_val);
|
unnamed_field(
|
||||||
|
field,
|
||||||
|
index,
|
||||||
|
&mut declare_values,
|
||||||
|
&mut return_val,
|
||||||
|
&mut borrowed,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let ident = &input.ident;
|
let ident = &input.ident;
|
||||||
let name = container_meta.tag();
|
let name = container_meta.tag();
|
||||||
let default_namespace = container_meta.default_namespace();
|
let default_namespace = container_meta.default_namespace();
|
||||||
let generics = container_meta.xml_generics();
|
let generics = container_meta.xml_generics(borrowed);
|
||||||
|
|
||||||
let (xml_impl_generics, _, _) = generics.split_for_impl();
|
let (xml_impl_generics, _, _) = generics.split_for_impl();
|
||||||
let (_, ty_generics, where_clause) = input.generics.split_for_impl();
|
let (_, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||||
|
@ -448,9 +507,10 @@ fn unnamed_field(
|
||||||
index: usize,
|
index: usize,
|
||||||
declare_values: &mut TokenStream,
|
declare_values: &mut TokenStream,
|
||||||
return_val: &mut TokenStream,
|
return_val: &mut TokenStream,
|
||||||
|
borrowed: &mut BTreeSet<syn::Lifetime>,
|
||||||
) {
|
) {
|
||||||
let mut no_lifetime_type = field.ty.clone();
|
let mut no_lifetime_type = field.ty.clone();
|
||||||
discard_lifetimes(&mut no_lifetime_type);
|
discard_lifetimes(&mut no_lifetime_type, borrowed, false, true);
|
||||||
|
|
||||||
let name = Ident::new(&format!("v{index}"), Span::call_site());
|
let name = Ident::new(&format!("v{index}"), Span::call_site());
|
||||||
declare_values.extend(quote!(
|
declare_values.extend(quote!(
|
||||||
|
@ -487,7 +547,7 @@ fn deserialize_unit_struct(input: &syn::DeriveInput, meta: &ContainerMeta) -> To
|
||||||
let ident = &input.ident;
|
let ident = &input.ident;
|
||||||
let name = meta.tag();
|
let name = meta.tag();
|
||||||
let default_namespace = meta.default_namespace();
|
let default_namespace = meta.default_namespace();
|
||||||
let generics = meta.xml_generics();
|
let generics = meta.xml_generics(BTreeSet::new());
|
||||||
|
|
||||||
let (xml_impl_generics, _, _) = generics.split_for_impl();
|
let (xml_impl_generics, _, _) = generics.split_for_impl();
|
||||||
let (_, ty_generics, where_clause) = input.generics.split_for_impl();
|
let (_, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||||
|
@ -511,6 +571,68 @@ fn deserialize_unit_struct(input: &syn::DeriveInput, meta: &ContainerMeta) -> To
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_cow(ty: &syn::Type, elem: fn(&syn::Type) -> bool) -> bool {
|
||||||
|
let path = match ungroup(ty) {
|
||||||
|
syn::Type::Path(ty) => &ty.path,
|
||||||
|
_ => {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let seg = match path.segments.last() {
|
||||||
|
Some(seg) => seg,
|
||||||
|
None => {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let args = match &seg.arguments {
|
||||||
|
syn::PathArguments::AngleBracketed(bracketed) => &bracketed.args,
|
||||||
|
_ => {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
seg.ident == "Cow"
|
||||||
|
&& args.len() == 2
|
||||||
|
&& match (&args[0], &args[1]) {
|
||||||
|
(syn::GenericArgument::Lifetime(_), syn::GenericArgument::Type(arg)) => elem(arg),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_str(ty: &syn::Type) -> bool {
|
||||||
|
is_primitive_type(ty, "str")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_slice_u8(ty: &syn::Type) -> bool {
|
||||||
|
match ungroup(ty) {
|
||||||
|
syn::Type::Slice(ty) => is_primitive_type(&ty.elem, "u8"),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_primitive_type(ty: &syn::Type, primitive: &str) -> bool {
|
||||||
|
match ungroup(ty) {
|
||||||
|
syn::Type::Path(ty) => ty.qself.is_none() && is_primitive_path(&ty.path, primitive),
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_primitive_path(path: &syn::Path, primitive: &str) -> bool {
|
||||||
|
path.leading_colon.is_none()
|
||||||
|
&& path.segments.len() == 1
|
||||||
|
&& path.segments[0].ident == primitive
|
||||||
|
&& path.segments[0].arguments.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ungroup(mut ty: &syn::Type) -> &syn::Type {
|
||||||
|
while let syn::Type::Group(group) = ty {
|
||||||
|
ty = &group.elem;
|
||||||
|
}
|
||||||
|
ty
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct Tokens {
|
struct Tokens {
|
||||||
r#enum: TokenStream,
|
r#enum: TokenStream,
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
extern crate proc_macro;
|
extern crate proc_macro;
|
||||||
|
|
||||||
|
use std::collections::BTreeSet;
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
use proc_macro2::{Literal, Span, TokenStream};
|
use proc_macro2::{Literal, Span, TokenStream};
|
||||||
use quote::{quote, ToTokens};
|
use quote::{quote, ToTokens};
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
|
@ -71,11 +74,10 @@ impl<'input> ContainerMeta<'input> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn xml_generics(&self) -> Generics {
|
fn xml_generics<'a>(&self, borrowed: BTreeSet<syn::Lifetime>) -> Generics {
|
||||||
let mut xml_generics = self.input.generics.clone();
|
let mut xml_generics = self.input.generics.clone();
|
||||||
let mut xml = syn::LifetimeDef::new(syn::Lifetime::new("'xml", Span::call_site()));
|
let mut xml = syn::LifetimeDef::new(syn::Lifetime::new("'xml", Span::call_site()));
|
||||||
xml.bounds
|
xml.bounds.extend(borrowed.into_iter());
|
||||||
.extend(xml_generics.lifetimes().map(|lt| lt.lifetime.clone()));
|
|
||||||
xml_generics.params.push(xml.into());
|
xml_generics.params.push(xml.into());
|
||||||
|
|
||||||
for param in xml_generics.type_params_mut() {
|
for param in xml_generics.type_params_mut() {
|
||||||
|
@ -105,9 +107,11 @@ impl<'input> ContainerMeta<'input> {
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
struct FieldMeta {
|
struct FieldMeta {
|
||||||
attribute: bool,
|
attribute: bool,
|
||||||
|
borrow: bool,
|
||||||
ns: NamespaceMeta,
|
ns: NamespaceMeta,
|
||||||
tag: TokenStream,
|
tag: TokenStream,
|
||||||
serialize_with: Option<Literal>,
|
serialize_with: Option<Literal>,
|
||||||
|
deserialize_with: Option<Literal>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FieldMeta {
|
impl FieldMeta {
|
||||||
|
@ -124,9 +128,11 @@ impl FieldMeta {
|
||||||
for (item, span) in meta_items(&input.attrs) {
|
for (item, span) in meta_items(&input.attrs) {
|
||||||
match item {
|
match item {
|
||||||
MetaItem::Attribute => meta.attribute = true,
|
MetaItem::Attribute => meta.attribute = true,
|
||||||
|
MetaItem::Borrow => meta.borrow = true,
|
||||||
MetaItem::Ns(ns) => meta.ns = ns,
|
MetaItem::Ns(ns) => meta.ns = ns,
|
||||||
MetaItem::Rename(lit) => meta.tag = quote!(#lit),
|
MetaItem::Rename(lit) => meta.tag = quote!(#lit),
|
||||||
MetaItem::SerializeWith(lit) => meta.serialize_with = Some(lit),
|
MetaItem::SerializeWith(lit) => meta.serialize_with = Some(lit),
|
||||||
|
MetaItem::DeserializeWith(lit) => meta.deserialize_with = Some(lit),
|
||||||
MetaItem::RenameAll(_) => {
|
MetaItem::RenameAll(_) => {
|
||||||
return Err(syn::Error::new(
|
return Err(syn::Error::new(
|
||||||
span,
|
span,
|
||||||
|
@ -216,20 +222,51 @@ impl VariantMeta {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn discard_lifetimes(ty: &mut syn::Type) {
|
fn discard_lifetimes(
|
||||||
|
ty: &mut syn::Type,
|
||||||
|
borrowed: &mut BTreeSet<syn::Lifetime>,
|
||||||
|
borrow: bool,
|
||||||
|
top: bool,
|
||||||
|
) {
|
||||||
match ty {
|
match ty {
|
||||||
syn::Type::Path(ty) => discard_path_lifetimes(ty),
|
syn::Type::Path(ty) => discard_path_lifetimes(ty, borrowed, borrow),
|
||||||
syn::Type::Reference(ty) => {
|
syn::Type::Reference(ty) => {
|
||||||
ty.lifetime = None;
|
if top {
|
||||||
discard_lifetimes(&mut ty.elem);
|
// If at the top level, we'll want to borrow from `&'a str` and `&'a [u8]`.
|
||||||
|
match &*ty.elem {
|
||||||
|
syn::Type::Path(inner) if top && inner.path.is_ident("str") => {
|
||||||
|
if let Some(lt) = ty.lifetime.take() {
|
||||||
|
borrowed.insert(lt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
syn::Type::Slice(inner) if top => match &*inner.elem {
|
||||||
|
syn::Type::Path(inner) if inner.path.is_ident("u8") => {
|
||||||
|
borrowed.extend(ty.lifetime.take().into_iter());
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
},
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
} else if borrow {
|
||||||
|
// Otherwise, only borrow if the user has requested it.
|
||||||
|
borrowed.extend(ty.lifetime.take().into_iter());
|
||||||
|
} else {
|
||||||
|
ty.lifetime = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
discard_lifetimes(&mut ty.elem, borrowed, borrow, false);
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn discard_path_lifetimes(path: &mut syn::TypePath) {
|
fn discard_path_lifetimes(
|
||||||
|
path: &mut syn::TypePath,
|
||||||
|
borrowed: &mut BTreeSet<syn::Lifetime>,
|
||||||
|
borrow: bool,
|
||||||
|
) {
|
||||||
if let Some(q) = &mut path.qself {
|
if let Some(q) = &mut path.qself {
|
||||||
discard_lifetimes(&mut q.ty);
|
discard_lifetimes(&mut q.ty, borrowed, borrow, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
for segment in &mut path.path.segments {
|
for segment in &mut path.path.segments {
|
||||||
|
@ -238,17 +275,23 @@ fn discard_path_lifetimes(path: &mut syn::TypePath) {
|
||||||
syn::PathArguments::AngleBracketed(args) => {
|
syn::PathArguments::AngleBracketed(args) => {
|
||||||
args.args.iter_mut().for_each(|arg| match arg {
|
args.args.iter_mut().for_each(|arg| match arg {
|
||||||
syn::GenericArgument::Lifetime(lt) => {
|
syn::GenericArgument::Lifetime(lt) => {
|
||||||
*lt = syn::Lifetime::new("'_", Span::call_site())
|
let lt = mem::replace(lt, syn::Lifetime::new("'_", Span::call_site()));
|
||||||
|
if borrow {
|
||||||
|
borrowed.insert(lt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
syn::GenericArgument::Type(ty) => {
|
||||||
|
discard_lifetimes(ty, borrowed, borrow, false)
|
||||||
}
|
}
|
||||||
syn::GenericArgument::Type(ty) => discard_lifetimes(ty),
|
|
||||||
syn::GenericArgument::Binding(_)
|
syn::GenericArgument::Binding(_)
|
||||||
| syn::GenericArgument::Constraint(_)
|
| syn::GenericArgument::Constraint(_)
|
||||||
| syn::GenericArgument::Const(_) => {}
|
| syn::GenericArgument::Const(_) => {}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
syn::PathArguments::Parenthesized(args) => {
|
syn::PathArguments::Parenthesized(args) => args
|
||||||
args.inputs.iter_mut().for_each(discard_lifetimes)
|
.inputs
|
||||||
}
|
.iter_mut()
|
||||||
|
.for_each(|ty| discard_lifetimes(ty, borrowed, borrow, false)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -292,6 +292,9 @@ pub(crate) fn meta_items(attrs: &[syn::Attribute]) -> Vec<(MetaItem, Span)> {
|
||||||
if id == "attribute" {
|
if id == "attribute" {
|
||||||
items.push((MetaItem::Attribute, span));
|
items.push((MetaItem::Attribute, span));
|
||||||
MetaState::Comma
|
MetaState::Comma
|
||||||
|
} else if id == "borrow" {
|
||||||
|
items.push((MetaItem::Borrow, span));
|
||||||
|
MetaState::Comma
|
||||||
} else if id == "ns" {
|
} else if id == "ns" {
|
||||||
MetaState::Ns
|
MetaState::Ns
|
||||||
} else if id == "rename" {
|
} else if id == "rename" {
|
||||||
|
@ -306,6 +309,8 @@ pub(crate) fn meta_items(attrs: &[syn::Attribute]) -> Vec<(MetaItem, Span)> {
|
||||||
MetaState::Comma
|
MetaState::Comma
|
||||||
} else if id == "serialize_with" {
|
} else if id == "serialize_with" {
|
||||||
MetaState::SerializeWith
|
MetaState::SerializeWith
|
||||||
|
} else if id == "deserialize_with" {
|
||||||
|
MetaState::DeserializeWith
|
||||||
} else {
|
} else {
|
||||||
panic!("unexpected key in xml attribute");
|
panic!("unexpected key in xml attribute");
|
||||||
}
|
}
|
||||||
|
@ -340,6 +345,13 @@ pub(crate) fn meta_items(attrs: &[syn::Attribute]) -> Vec<(MetaItem, Span)> {
|
||||||
items.push((MetaItem::SerializeWith(lit), span));
|
items.push((MetaItem::SerializeWith(lit), span));
|
||||||
MetaState::Comma
|
MetaState::Comma
|
||||||
}
|
}
|
||||||
|
(MetaState::DeserializeWith, TokenTree::Punct(punct)) if punct.as_char() == '=' => {
|
||||||
|
MetaState::DeserializeWithValue
|
||||||
|
}
|
||||||
|
(MetaState::DeserializeWithValue, TokenTree::Literal(lit)) => {
|
||||||
|
items.push((MetaItem::DeserializeWith(lit), span));
|
||||||
|
MetaState::Comma
|
||||||
|
}
|
||||||
(state, tree) => {
|
(state, tree) => {
|
||||||
panic!(
|
panic!(
|
||||||
"invalid state transition while parsing xml attribute ({}, {tree})",
|
"invalid state transition while parsing xml attribute ({}, {tree})",
|
||||||
|
@ -363,6 +375,8 @@ enum MetaState {
|
||||||
RenameAllValue,
|
RenameAllValue,
|
||||||
SerializeWith,
|
SerializeWith,
|
||||||
SerializeWithValue,
|
SerializeWithValue,
|
||||||
|
DeserializeWith,
|
||||||
|
DeserializeWithValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MetaState {
|
impl MetaState {
|
||||||
|
@ -377,6 +391,8 @@ impl MetaState {
|
||||||
MetaState::RenameAllValue => "RenameAllValue",
|
MetaState::RenameAllValue => "RenameAllValue",
|
||||||
MetaState::SerializeWith => "SerializeWith",
|
MetaState::SerializeWith => "SerializeWith",
|
||||||
MetaState::SerializeWithValue => "SerializeWithValue",
|
MetaState::SerializeWithValue => "SerializeWithValue",
|
||||||
|
MetaState::DeserializeWith => "DeserializeWith",
|
||||||
|
MetaState::DeserializeWithValue => "DeserializeWithValue",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -460,9 +476,11 @@ impl fmt::Debug for Namespace {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) enum MetaItem {
|
pub(crate) enum MetaItem {
|
||||||
Attribute,
|
Attribute,
|
||||||
|
Borrow,
|
||||||
Ns(NamespaceMeta),
|
Ns(NamespaceMeta),
|
||||||
Rename(Literal),
|
Rename(Literal),
|
||||||
Mode(Mode),
|
Mode(Mode),
|
||||||
RenameAll(Literal),
|
RenameAll(Literal),
|
||||||
SerializeWith(Literal),
|
SerializeWith(Literal),
|
||||||
|
DeserializeWith(Literal),
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
|
@ -180,12 +182,14 @@ fn serialize_struct(
|
||||||
let tag = meta.tag();
|
let tag = meta.tag();
|
||||||
let mut body = TokenStream::new();
|
let mut body = TokenStream::new();
|
||||||
let mut attributes = TokenStream::new();
|
let mut attributes = TokenStream::new();
|
||||||
|
let mut borrowed = BTreeSet::new();
|
||||||
match &data.fields {
|
match &data.fields {
|
||||||
syn::Fields::Named(fields) => {
|
syn::Fields::Named(fields) => {
|
||||||
body.extend(quote!(serializer.end_start()?;));
|
body.extend(quote!(serializer.end_start()?;));
|
||||||
for field in &fields.named {
|
for field in &fields.named {
|
||||||
if let Err(err) = named_field(field, &mut body, &mut attributes, &meta) {
|
if let Err(err) =
|
||||||
|
named_field(field, &mut body, &mut attributes, &mut borrowed, &meta)
|
||||||
|
{
|
||||||
return err.to_compile_error();
|
return err.to_compile_error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -194,7 +198,7 @@ fn serialize_struct(
|
||||||
syn::Fields::Unnamed(fields) => {
|
syn::Fields::Unnamed(fields) => {
|
||||||
body.extend(quote!(serializer.end_start()?;));
|
body.extend(quote!(serializer.end_start()?;));
|
||||||
for (index, field) in fields.unnamed.iter().enumerate() {
|
for (index, field) in fields.unnamed.iter().enumerate() {
|
||||||
if let Err(err) = unnamed_field(field, index, &mut body) {
|
if let Err(err) = unnamed_field(field, index, &mut body, &mut borrowed) {
|
||||||
return err.to_compile_error();
|
return err.to_compile_error();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -261,6 +265,7 @@ fn named_field(
|
||||||
field: &syn::Field,
|
field: &syn::Field,
|
||||||
body: &mut TokenStream,
|
body: &mut TokenStream,
|
||||||
attributes: &mut TokenStream,
|
attributes: &mut TokenStream,
|
||||||
|
borrowed: &mut BTreeSet<syn::Lifetime>,
|
||||||
meta: &ContainerMeta,
|
meta: &ContainerMeta,
|
||||||
) -> Result<(), syn::Error> {
|
) -> Result<(), syn::Error> {
|
||||||
let field_name = field.ident.as_ref().unwrap();
|
let field_name = field.ident.as_ref().unwrap();
|
||||||
|
@ -338,7 +343,7 @@ fn named_field(
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut no_lifetime_type = field.ty.clone();
|
let mut no_lifetime_type = field.ty.clone();
|
||||||
discard_lifetimes(&mut no_lifetime_type);
|
discard_lifetimes(&mut no_lifetime_type, borrowed, false, true);
|
||||||
body.extend(quote!(
|
body.extend(quote!(
|
||||||
self.#field_name.serialize(Some(::instant_xml::Id { ns: #ns, name: #tag }), serializer)?;
|
self.#field_name.serialize(Some(::instant_xml::Id { ns: #ns, name: #tag }), serializer)?;
|
||||||
));
|
));
|
||||||
|
@ -350,6 +355,7 @@ fn unnamed_field(
|
||||||
field: &syn::Field,
|
field: &syn::Field,
|
||||||
index: usize,
|
index: usize,
|
||||||
body: &mut TokenStream,
|
body: &mut TokenStream,
|
||||||
|
borrowed: &mut BTreeSet<syn::Lifetime>,
|
||||||
) -> Result<(), syn::Error> {
|
) -> Result<(), syn::Error> {
|
||||||
if !field.attrs.is_empty() {
|
if !field.attrs.is_empty() {
|
||||||
return Err(syn::Error::new(
|
return Err(syn::Error::new(
|
||||||
|
@ -359,7 +365,7 @@ fn unnamed_field(
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut no_lifetime_type = field.ty.clone();
|
let mut no_lifetime_type = field.ty.clone();
|
||||||
discard_lifetimes(&mut no_lifetime_type);
|
discard_lifetimes(&mut no_lifetime_type, borrowed, false, true);
|
||||||
let index = syn::Index::from(index);
|
let index = syn::Index::from(index);
|
||||||
body.extend(quote!(
|
body.extend(quote!(
|
||||||
self.#index.serialize(None, serializer)?;
|
self.#index.serialize(None, serializer)?;
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
use std::borrow::Cow;
|
||||||
use std::collections::{BTreeMap, VecDeque};
|
use std::collections::{BTreeMap, VecDeque};
|
||||||
|
|
||||||
use xmlparser::{ElementEnd, Token, Tokenizer};
|
use xmlparser::{ElementEnd, Token, Tokenizer};
|
||||||
|
|
||||||
|
use crate::impls::decode;
|
||||||
use crate::{Error, Id, Kind};
|
use crate::{Error, Id, Kind};
|
||||||
|
|
||||||
pub struct Deserializer<'cx, 'xml> {
|
pub struct Deserializer<'cx, 'xml> {
|
||||||
|
@ -320,6 +322,38 @@ impl<'xml> Iterator for Context<'xml> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn borrow_cow_str<'xml>(
|
||||||
|
deserializer: &mut Deserializer<'_, 'xml>,
|
||||||
|
into: &mut Option<Cow<'xml, str>>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
if into.is_some() {
|
||||||
|
return Err(Error::DuplicateValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = deserializer.take_str()?;
|
||||||
|
*into = Some(decode(value));
|
||||||
|
deserializer.ignore()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn borrow_cow_slice_u8<'xml>(
|
||||||
|
deserializer: &mut Deserializer<'_, 'xml>,
|
||||||
|
into: &mut Option<Cow<'xml, [u8]>>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
if into.is_some() {
|
||||||
|
return Err(Error::DuplicateValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = deserializer.take_str()?;
|
||||||
|
*into = Some(match decode(value) {
|
||||||
|
Cow::Borrowed(v) => Cow::Borrowed(v.as_bytes()),
|
||||||
|
Cow::Owned(v) => Cow::Owned(v.into_bytes()),
|
||||||
|
});
|
||||||
|
|
||||||
|
deserializer.ignore()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Node<'xml> {
|
pub enum Node<'xml> {
|
||||||
Attribute(Attribute<'xml>),
|
Attribute(Attribute<'xml>),
|
||||||
|
|
|
@ -176,15 +176,9 @@ impl<'xml> FromXml<'xml> for String {
|
||||||
return Err(Error::DuplicateValue);
|
return Err(Error::DuplicateValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut value = None;
|
let value = deserializer.take_str()?;
|
||||||
<Cow<'xml, str> as FromXml<'xml>>::deserialize(deserializer, &mut value)?;
|
*into = Some(decode(value).into_owned());
|
||||||
match value {
|
Ok(())
|
||||||
Some(value) => {
|
|
||||||
*into = Some(value.into_owned());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
None => Err(Error::MissingValue(&Kind::Scalar)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const KIND: Kind<'static> = Kind::Scalar;
|
const KIND: Kind<'static> = Kind::Scalar;
|
||||||
|
@ -199,24 +193,27 @@ impl<'xml> FromXml<'xml> for &'xml str {
|
||||||
return Err(Error::DuplicateValue);
|
return Err(Error::DuplicateValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut value = None;
|
let value = deserializer.take_str()?;
|
||||||
<Cow<'xml, str> as FromXml<'xml>>::deserialize(deserializer, &mut value)?;
|
match decode(value) {
|
||||||
match value {
|
Cow::Borrowed(str) => *into = Some(str),
|
||||||
Some(Cow::Borrowed(s)) => {
|
Cow::Owned(_) => {
|
||||||
*into = Some(s);
|
return Err(Error::UnexpectedValue(
|
||||||
Ok(())
|
"string with escape characters cannot be deserialized as &str",
|
||||||
|
))
|
||||||
}
|
}
|
||||||
Some(Cow::Owned(_)) => Err(Error::UnexpectedValue(
|
|
||||||
"string with escape characters cannot be deserialized as &str",
|
|
||||||
)),
|
|
||||||
None => Err(Error::MissingValue(&Kind::Scalar)),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
const KIND: Kind<'static> = Kind::Scalar;
|
const KIND: Kind<'static> = Kind::Scalar;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'xml> FromXml<'xml> for Cow<'xml, str> {
|
impl<'xml, 'a, T: ?Sized> FromXml<'xml> for Cow<'a, T>
|
||||||
|
where
|
||||||
|
T: ToOwned,
|
||||||
|
T::Owned: FromXml<'xml>,
|
||||||
|
{
|
||||||
fn deserialize(
|
fn deserialize(
|
||||||
deserializer: &mut Deserializer<'_, 'xml>,
|
deserializer: &mut Deserializer<'_, 'xml>,
|
||||||
into: &mut Option<Self>,
|
into: &mut Option<Self>,
|
||||||
|
@ -225,9 +222,15 @@ impl<'xml> FromXml<'xml> for Cow<'xml, str> {
|
||||||
return Err(Error::DuplicateValue);
|
return Err(Error::DuplicateValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
let value = deserializer.take_str()?;
|
let mut value = None;
|
||||||
*into = Some(decode(value));
|
T::Owned::deserialize(deserializer, &mut value)?;
|
||||||
Ok(())
|
match value {
|
||||||
|
Some(value) => {
|
||||||
|
*into = Some(Cow::Owned(value));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
None => Err(Error::MissingValue(&Kind::Scalar)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const KIND: Kind<'static> = Kind::Scalar;
|
const KIND: Kind<'static> = Kind::Scalar;
|
||||||
|
@ -386,7 +389,7 @@ fn encode(input: &str) -> Result<Cow<'_, str>, Error> {
|
||||||
Ok(Cow::Owned(result))
|
Ok(Cow::Owned(result))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode(input: &str) -> Cow<'_, str> {
|
pub(crate) fn decode(input: &str) -> Cow<'_, str> {
|
||||||
let mut result = String::with_capacity(input.len());
|
let mut result = String::with_capacity(input.len());
|
||||||
let input_len = input.len();
|
let input_len = input.len();
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ use instant_xml::{from_str, to_string, Error, FromXml, ToXml};
|
||||||
struct StructSpecialEntities<'a> {
|
struct StructSpecialEntities<'a> {
|
||||||
string: String,
|
string: String,
|
||||||
str: &'a str,
|
str: &'a str,
|
||||||
|
#[xml(borrow)]
|
||||||
cow: Cow<'a, str>,
|
cow: Cow<'a, str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue