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 syn::spanned::Spanned;
|
||||
|
||||
|
@ -49,7 +51,7 @@ fn deserialize_scalar_enum(
|
|||
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 (_, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||
|
||||
|
@ -90,6 +92,7 @@ fn deserialize_wrapped_enum(
|
|||
|
||||
let ident = &input.ident;
|
||||
let mut variants = TokenStream::new();
|
||||
let mut borrowed = BTreeSet::new();
|
||||
for variant in data.variants.iter() {
|
||||
let field = match &variant.fields {
|
||||
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();
|
||||
discard_lifetimes(&mut no_lifetime_type);
|
||||
discard_lifetimes(&mut no_lifetime_type, &mut borrowed, false, true);
|
||||
|
||||
if !variants.is_empty() {
|
||||
variants.extend(quote!(else));
|
||||
|
@ -132,7 +135,7 @@ fn deserialize_wrapped_enum(
|
|||
|
||||
let name = meta.tag();
|
||||
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 (_, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||
quote!(
|
||||
|
@ -194,6 +197,7 @@ fn deserialize_struct(
|
|||
let mut declare_values = TokenStream::new();
|
||||
let mut return_val = TokenStream::new();
|
||||
|
||||
let mut borrowed = BTreeSet::new();
|
||||
for (index, field) in fields.named.iter().enumerate() {
|
||||
let field_meta = match FieldMeta::from_field(field, &container_meta) {
|
||||
Ok(meta) => meta,
|
||||
|
@ -205,15 +209,20 @@ fn deserialize_struct(
|
|||
false => &mut elements_tokens,
|
||||
};
|
||||
|
||||
named_field(
|
||||
let result = named_field(
|
||||
field,
|
||||
index,
|
||||
&mut declare_values,
|
||||
&mut return_val,
|
||||
tokens,
|
||||
&mut borrowed,
|
||||
field_meta,
|
||||
&container_meta,
|
||||
);
|
||||
|
||||
if let Err(err) = result {
|
||||
return err.into_compile_error();
|
||||
}
|
||||
}
|
||||
|
||||
// Elements
|
||||
|
@ -237,7 +246,7 @@ fn deserialize_struct(
|
|||
let ident = &input.ident;
|
||||
let name = container_meta.tag();
|
||||
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 (_, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||
|
@ -313,9 +322,10 @@ fn named_field(
|
|||
declare_values: &mut TokenStream,
|
||||
return_val: &mut TokenStream,
|
||||
tokens: &mut Tokens,
|
||||
field_meta: FieldMeta,
|
||||
borrowed: &mut BTreeSet<syn::Lifetime>,
|
||||
mut field_meta: FieldMeta,
|
||||
container_meta: &ContainerMeta,
|
||||
) {
|
||||
) -> Result<(), syn::Error> {
|
||||
let field_name = field.ident.as_ref().unwrap();
|
||||
let field_tag = field_meta.tag;
|
||||
let default_ns = match &field_meta.ns.uri {
|
||||
|
@ -329,8 +339,18 @@ fn named_field(
|
|||
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();
|
||||
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());
|
||||
tokens.r#enum.extend(quote!(#enum_name,));
|
||||
|
@ -351,27 +371,58 @@ fn named_field(
|
|||
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 {
|
||||
tokens.r#match.extend(quote!(
|
||||
__Elements::#enum_name => match <#no_lifetime_type as FromXml>::KIND {
|
||||
Kind::Element(_) => {
|
||||
if let Some(with) = deserialize_with {
|
||||
tokens.r#match.extend(quote!(
|
||||
__Elements::#enum_name => {
|
||||
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()?;
|
||||
}
|
||||
},
|
||||
));
|
||||
#with(&mut nested, &mut #enum_name)?;
|
||||
},
|
||||
));
|
||||
} else {
|
||||
tokens.r#match.extend(quote!(
|
||||
__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 {
|
||||
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)?;
|
||||
},
|
||||
));
|
||||
if let Some(with) = deserialize_with {
|
||||
tokens.r#match.extend(quote!(
|
||||
__Attributes::#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!(
|
||||
|
@ -380,6 +431,8 @@ fn named_field(
|
|||
None => <#no_lifetime_type>::missing_value()?,
|
||||
},
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize_tuple_struct(
|
||||
|
@ -397,7 +450,7 @@ fn deserialize_tuple_struct(
|
|||
// Varying values
|
||||
let mut declare_values = TokenStream::new();
|
||||
let mut return_val = TokenStream::new();
|
||||
|
||||
let mut borrowed = BTreeSet::new();
|
||||
for (index, field) in fields.unnamed.iter().enumerate() {
|
||||
if !field.attrs.is_empty() {
|
||||
return syn::Error::new(
|
||||
|
@ -407,13 +460,19 @@ fn deserialize_tuple_struct(
|
|||
.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 name = container_meta.tag();
|
||||
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 (_, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||
|
@ -448,9 +507,10 @@ fn unnamed_field(
|
|||
index: usize,
|
||||
declare_values: &mut TokenStream,
|
||||
return_val: &mut TokenStream,
|
||||
borrowed: &mut BTreeSet<syn::Lifetime>,
|
||||
) {
|
||||
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());
|
||||
declare_values.extend(quote!(
|
||||
|
@ -487,7 +547,7 @@ fn deserialize_unit_struct(input: &syn::DeriveInput, meta: &ContainerMeta) -> To
|
|||
let ident = &input.ident;
|
||||
let name = meta.tag();
|
||||
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 (_, 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)]
|
||||
struct Tokens {
|
||||
r#enum: TokenStream,
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
extern crate proc_macro;
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
use std::mem;
|
||||
|
||||
use proc_macro2::{Literal, Span, TokenStream};
|
||||
use quote::{quote, ToTokens};
|
||||
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 = syn::LifetimeDef::new(syn::Lifetime::new("'xml", Span::call_site()));
|
||||
xml.bounds
|
||||
.extend(xml_generics.lifetimes().map(|lt| lt.lifetime.clone()));
|
||||
xml.bounds.extend(borrowed.into_iter());
|
||||
xml_generics.params.push(xml.into());
|
||||
|
||||
for param in xml_generics.type_params_mut() {
|
||||
|
@ -105,9 +107,11 @@ impl<'input> ContainerMeta<'input> {
|
|||
#[derive(Debug, Default)]
|
||||
struct FieldMeta {
|
||||
attribute: bool,
|
||||
borrow: bool,
|
||||
ns: NamespaceMeta,
|
||||
tag: TokenStream,
|
||||
serialize_with: Option<Literal>,
|
||||
deserialize_with: Option<Literal>,
|
||||
}
|
||||
|
||||
impl FieldMeta {
|
||||
|
@ -124,9 +128,11 @@ impl FieldMeta {
|
|||
for (item, span) in meta_items(&input.attrs) {
|
||||
match item {
|
||||
MetaItem::Attribute => meta.attribute = true,
|
||||
MetaItem::Borrow => meta.borrow = true,
|
||||
MetaItem::Ns(ns) => meta.ns = ns,
|
||||
MetaItem::Rename(lit) => meta.tag = quote!(#lit),
|
||||
MetaItem::SerializeWith(lit) => meta.serialize_with = Some(lit),
|
||||
MetaItem::DeserializeWith(lit) => meta.deserialize_with = Some(lit),
|
||||
MetaItem::RenameAll(_) => {
|
||||
return Err(syn::Error::new(
|
||||
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 {
|
||||
syn::Type::Path(ty) => discard_path_lifetimes(ty),
|
||||
syn::Type::Path(ty) => discard_path_lifetimes(ty, borrowed, borrow),
|
||||
syn::Type::Reference(ty) => {
|
||||
ty.lifetime = None;
|
||||
discard_lifetimes(&mut ty.elem);
|
||||
if top {
|
||||
// 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 {
|
||||
discard_lifetimes(&mut q.ty);
|
||||
discard_lifetimes(&mut q.ty, borrowed, borrow, false);
|
||||
}
|
||||
|
||||
for segment in &mut path.path.segments {
|
||||
|
@ -238,17 +275,23 @@ fn discard_path_lifetimes(path: &mut syn::TypePath) {
|
|||
syn::PathArguments::AngleBracketed(args) => {
|
||||
args.args.iter_mut().for_each(|arg| match arg {
|
||||
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::Constraint(_)
|
||||
| syn::GenericArgument::Const(_) => {}
|
||||
})
|
||||
}
|
||||
syn::PathArguments::Parenthesized(args) => {
|
||||
args.inputs.iter_mut().for_each(discard_lifetimes)
|
||||
}
|
||||
syn::PathArguments::Parenthesized(args) => args
|
||||
.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" {
|
||||
items.push((MetaItem::Attribute, span));
|
||||
MetaState::Comma
|
||||
} else if id == "borrow" {
|
||||
items.push((MetaItem::Borrow, span));
|
||||
MetaState::Comma
|
||||
} else if id == "ns" {
|
||||
MetaState::Ns
|
||||
} else if id == "rename" {
|
||||
|
@ -306,6 +309,8 @@ pub(crate) fn meta_items(attrs: &[syn::Attribute]) -> Vec<(MetaItem, Span)> {
|
|||
MetaState::Comma
|
||||
} else if id == "serialize_with" {
|
||||
MetaState::SerializeWith
|
||||
} else if id == "deserialize_with" {
|
||||
MetaState::DeserializeWith
|
||||
} else {
|
||||
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));
|
||||
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) => {
|
||||
panic!(
|
||||
"invalid state transition while parsing xml attribute ({}, {tree})",
|
||||
|
@ -363,6 +375,8 @@ enum MetaState {
|
|||
RenameAllValue,
|
||||
SerializeWith,
|
||||
SerializeWithValue,
|
||||
DeserializeWith,
|
||||
DeserializeWithValue,
|
||||
}
|
||||
|
||||
impl MetaState {
|
||||
|
@ -377,6 +391,8 @@ impl MetaState {
|
|||
MetaState::RenameAllValue => "RenameAllValue",
|
||||
MetaState::SerializeWith => "SerializeWith",
|
||||
MetaState::SerializeWithValue => "SerializeWithValue",
|
||||
MetaState::DeserializeWith => "DeserializeWith",
|
||||
MetaState::DeserializeWithValue => "DeserializeWithValue",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -460,9 +476,11 @@ impl fmt::Debug for Namespace {
|
|||
#[derive(Debug)]
|
||||
pub(crate) enum MetaItem {
|
||||
Attribute,
|
||||
Borrow,
|
||||
Ns(NamespaceMeta),
|
||||
Rename(Literal),
|
||||
Mode(Mode),
|
||||
RenameAll(Literal),
|
||||
SerializeWith(Literal),
|
||||
DeserializeWith(Literal),
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::collections::BTreeSet;
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::spanned::Spanned;
|
||||
|
@ -180,12 +182,14 @@ fn serialize_struct(
|
|||
let tag = meta.tag();
|
||||
let mut body = TokenStream::new();
|
||||
let mut attributes = TokenStream::new();
|
||||
|
||||
let mut borrowed = BTreeSet::new();
|
||||
match &data.fields {
|
||||
syn::Fields::Named(fields) => {
|
||||
body.extend(quote!(serializer.end_start()?;));
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -194,7 +198,7 @@ fn serialize_struct(
|
|||
syn::Fields::Unnamed(fields) => {
|
||||
body.extend(quote!(serializer.end_start()?;));
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -261,6 +265,7 @@ fn named_field(
|
|||
field: &syn::Field,
|
||||
body: &mut TokenStream,
|
||||
attributes: &mut TokenStream,
|
||||
borrowed: &mut BTreeSet<syn::Lifetime>,
|
||||
meta: &ContainerMeta,
|
||||
) -> Result<(), syn::Error> {
|
||||
let field_name = field.ident.as_ref().unwrap();
|
||||
|
@ -338,7 +343,7 @@ fn named_field(
|
|||
};
|
||||
|
||||
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!(
|
||||
self.#field_name.serialize(Some(::instant_xml::Id { ns: #ns, name: #tag }), serializer)?;
|
||||
));
|
||||
|
@ -350,6 +355,7 @@ fn unnamed_field(
|
|||
field: &syn::Field,
|
||||
index: usize,
|
||||
body: &mut TokenStream,
|
||||
borrowed: &mut BTreeSet<syn::Lifetime>,
|
||||
) -> Result<(), syn::Error> {
|
||||
if !field.attrs.is_empty() {
|
||||
return Err(syn::Error::new(
|
||||
|
@ -359,7 +365,7 @@ fn unnamed_field(
|
|||
}
|
||||
|
||||
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);
|
||||
body.extend(quote!(
|
||||
self.#index.serialize(None, serializer)?;
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use std::borrow::Cow;
|
||||
use std::collections::{BTreeMap, VecDeque};
|
||||
|
||||
use xmlparser::{ElementEnd, Token, Tokenizer};
|
||||
|
||||
use crate::impls::decode;
|
||||
use crate::{Error, Id, Kind};
|
||||
|
||||
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)]
|
||||
pub enum Node<'xml> {
|
||||
Attribute(Attribute<'xml>),
|
||||
|
|
|
@ -176,15 +176,9 @@ impl<'xml> FromXml<'xml> for String {
|
|||
return Err(Error::DuplicateValue);
|
||||
}
|
||||
|
||||
let mut value = None;
|
||||
<Cow<'xml, str> as FromXml<'xml>>::deserialize(deserializer, &mut value)?;
|
||||
match value {
|
||||
Some(value) => {
|
||||
*into = Some(value.into_owned());
|
||||
Ok(())
|
||||
}
|
||||
None => Err(Error::MissingValue(&Kind::Scalar)),
|
||||
}
|
||||
let value = deserializer.take_str()?;
|
||||
*into = Some(decode(value).into_owned());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
const KIND: Kind<'static> = Kind::Scalar;
|
||||
|
@ -199,24 +193,27 @@ impl<'xml> FromXml<'xml> for &'xml str {
|
|||
return Err(Error::DuplicateValue);
|
||||
}
|
||||
|
||||
let mut value = None;
|
||||
<Cow<'xml, str> as FromXml<'xml>>::deserialize(deserializer, &mut value)?;
|
||||
match value {
|
||||
Some(Cow::Borrowed(s)) => {
|
||||
*into = Some(s);
|
||||
Ok(())
|
||||
let value = deserializer.take_str()?;
|
||||
match decode(value) {
|
||||
Cow::Borrowed(str) => *into = Some(str),
|
||||
Cow::Owned(_) => {
|
||||
return Err(Error::UnexpectedValue(
|
||||
"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;
|
||||
}
|
||||
|
||||
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(
|
||||
deserializer: &mut Deserializer<'_, 'xml>,
|
||||
into: &mut Option<Self>,
|
||||
|
@ -225,9 +222,15 @@ impl<'xml> FromXml<'xml> for Cow<'xml, str> {
|
|||
return Err(Error::DuplicateValue);
|
||||
}
|
||||
|
||||
let value = deserializer.take_str()?;
|
||||
*into = Some(decode(value));
|
||||
Ok(())
|
||||
let mut value = None;
|
||||
T::Owned::deserialize(deserializer, &mut value)?;
|
||||
match value {
|
||||
Some(value) => {
|
||||
*into = Some(Cow::Owned(value));
|
||||
Ok(())
|
||||
}
|
||||
None => Err(Error::MissingValue(&Kind::Scalar)),
|
||||
}
|
||||
}
|
||||
|
||||
const KIND: Kind<'static> = Kind::Scalar;
|
||||
|
@ -386,7 +389,7 @@ fn encode(input: &str) -> Result<Cow<'_, str>, Error> {
|
|||
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 input_len = input.len();
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ use instant_xml::{from_str, to_string, Error, FromXml, ToXml};
|
|||
struct StructSpecialEntities<'a> {
|
||||
string: String,
|
||||
str: &'a str,
|
||||
#[xml(borrow)]
|
||||
cow: Cow<'a, str>,
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue