Add support for deserialize_with and borrow

This commit is contained in:
Dirkjan Ochtman 2022-11-29 13:07:20 +01:00
parent aab73952a1
commit 20f73b7e01
7 changed files with 302 additions and 75 deletions

View File

@ -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,

View File

@ -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)),
} }
} }
} }

View File

@ -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),
} }

View File

@ -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)?;

View File

@ -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>),

View File

@ -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();

View File

@ -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>,
} }