Add support for direct fields
This commit is contained in:
parent
d2605977aa
commit
f8b4364acd
|
@ -196,9 +196,15 @@ fn deserialize_struct(
|
||||||
// Common values
|
// Common 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 direct = TokenStream::new();
|
||||||
|
|
||||||
let mut borrowed = BTreeSet::new();
|
let mut borrowed = BTreeSet::new();
|
||||||
for (index, field) in fields.named.iter().enumerate() {
|
for (index, field) in fields.named.iter().enumerate() {
|
||||||
|
if !direct.is_empty() {
|
||||||
|
return syn::Error::new(field.span(), "direct field must be the last")
|
||||||
|
.into_compile_error();
|
||||||
|
}
|
||||||
|
|
||||||
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,
|
||||||
Err(err) => return err.into_compile_error(),
|
Err(err) => return err.into_compile_error(),
|
||||||
|
@ -216,6 +222,7 @@ fn deserialize_struct(
|
||||||
&mut return_val,
|
&mut return_val,
|
||||||
tokens,
|
tokens,
|
||||||
&mut borrowed,
|
&mut borrowed,
|
||||||
|
&mut direct,
|
||||||
field_meta,
|
field_meta,
|
||||||
&container_meta,
|
&container_meta,
|
||||||
);
|
);
|
||||||
|
@ -299,6 +306,7 @@ fn deserialize_struct(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#direct
|
||||||
node => return Err(Error::UnexpectedNode(format!("{:?}", node))),
|
node => return Err(Error::UnexpectedNode(format!("{:?}", node))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -323,6 +331,7 @@ fn named_field(
|
||||||
return_val: &mut TokenStream,
|
return_val: &mut TokenStream,
|
||||||
tokens: &mut Tokens,
|
tokens: &mut Tokens,
|
||||||
borrowed: &mut BTreeSet<syn::Lifetime>,
|
borrowed: &mut BTreeSet<syn::Lifetime>,
|
||||||
|
direct: &mut TokenStream,
|
||||||
mut field_meta: FieldMeta,
|
mut field_meta: FieldMeta,
|
||||||
container_meta: &ContainerMeta,
|
container_meta: &ContainerMeta,
|
||||||
) -> Result<(), syn::Error> {
|
) -> Result<(), syn::Error> {
|
||||||
|
@ -353,19 +362,21 @@ fn named_field(
|
||||||
discard_lifetimes(&mut no_lifetime_type, borrowed, field_meta.borrow, true);
|
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,));
|
if !field_meta.direct {
|
||||||
|
tokens.r#enum.extend(quote!(#enum_name,));
|
||||||
|
|
||||||
if !tokens.branches.is_empty() {
|
if !tokens.branches.is_empty() {
|
||||||
tokens.branches.extend(quote!(else));
|
tokens.branches.extend(quote!(else));
|
||||||
|
}
|
||||||
|
tokens.branches.extend(quote!(
|
||||||
|
if <#no_lifetime_type as FromXml>::KIND.matches(id, Id { ns: #ns, name: #field_tag })
|
||||||
|
));
|
||||||
|
|
||||||
|
tokens.branches.extend(match field_meta.attribute {
|
||||||
|
true => quote!({ __Attributes::#enum_name }),
|
||||||
|
false => quote!({ __Elements::#enum_name }),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
tokens.branches.extend(quote!(
|
|
||||||
if <#no_lifetime_type as FromXml>::KIND.matches(id, Id { ns: #ns, name: #field_tag })
|
|
||||||
));
|
|
||||||
|
|
||||||
tokens.branches.extend(match field_meta.attribute {
|
|
||||||
true => quote!({ __Attributes::#enum_name }),
|
|
||||||
false => quote!({ __Elements::#enum_name }),
|
|
||||||
});
|
|
||||||
|
|
||||||
declare_values.extend(quote!(
|
declare_values.extend(quote!(
|
||||||
let mut #enum_name: Option<#no_lifetime_type> = None;
|
let mut #enum_name: Option<#no_lifetime_type> = None;
|
||||||
|
@ -386,12 +397,26 @@ fn named_field(
|
||||||
|
|
||||||
if !field_meta.attribute {
|
if !field_meta.attribute {
|
||||||
if let Some(with) = deserialize_with {
|
if let Some(with) = deserialize_with {
|
||||||
|
if field_meta.direct {
|
||||||
|
return Err(syn::Error::new(
|
||||||
|
field.span(),
|
||||||
|
"direct attribute is not supported deserialization functions",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
tokens.r#match.extend(quote!(
|
tokens.r#match.extend(quote!(
|
||||||
__Elements::#enum_name => {
|
__Elements::#enum_name => {
|
||||||
let mut nested = deserializer.nested(data);
|
let mut nested = deserializer.nested(data);
|
||||||
#with(&mut nested, &mut #enum_name)?;
|
#with(&mut nested, &mut #enum_name)?;
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
|
} else if field_meta.direct {
|
||||||
|
direct.extend(quote!(
|
||||||
|
Node::Text(text) => {
|
||||||
|
let mut nested = deserializer.for_node(Node::Text(text));
|
||||||
|
FromXml::deserialize(&mut nested, &mut #enum_name)?;
|
||||||
|
}
|
||||||
|
));
|
||||||
} else {
|
} else {
|
||||||
tokens.r#match.extend(quote!(
|
tokens.r#match.extend(quote!(
|
||||||
__Elements::#enum_name => match <#no_lifetime_type as FromXml>::KIND {
|
__Elements::#enum_name => match <#no_lifetime_type as FromXml>::KIND {
|
||||||
|
@ -408,6 +433,13 @@ fn named_field(
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if field_meta.direct {
|
||||||
|
return Err(syn::Error::new(
|
||||||
|
field.span(),
|
||||||
|
"direct attribute is not supported for attribute fields",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(with) = deserialize_with {
|
if let Some(with) = deserialize_with {
|
||||||
tokens.r#match.extend(quote!(
|
tokens.r#match.extend(quote!(
|
||||||
__Attributes::#enum_name => {
|
__Attributes::#enum_name => {
|
||||||
|
|
|
@ -108,6 +108,7 @@ impl<'input> ContainerMeta<'input> {
|
||||||
struct FieldMeta {
|
struct FieldMeta {
|
||||||
attribute: bool,
|
attribute: bool,
|
||||||
borrow: bool,
|
borrow: bool,
|
||||||
|
direct: bool,
|
||||||
ns: NamespaceMeta,
|
ns: NamespaceMeta,
|
||||||
tag: TokenStream,
|
tag: TokenStream,
|
||||||
serialize_with: Option<Literal>,
|
serialize_with: Option<Literal>,
|
||||||
|
@ -129,6 +130,7 @@ impl FieldMeta {
|
||||||
match item {
|
match item {
|
||||||
MetaItem::Attribute => meta.attribute = true,
|
MetaItem::Attribute => meta.attribute = true,
|
||||||
MetaItem::Borrow => meta.borrow = true,
|
MetaItem::Borrow => meta.borrow = true,
|
||||||
|
MetaItem::Direct => meta.direct = 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),
|
||||||
|
|
|
@ -295,6 +295,9 @@ pub(crate) fn meta_items(attrs: &[syn::Attribute]) -> Vec<(MetaItem, Span)> {
|
||||||
} else if id == "borrow" {
|
} else if id == "borrow" {
|
||||||
items.push((MetaItem::Borrow, span));
|
items.push((MetaItem::Borrow, span));
|
||||||
MetaState::Comma
|
MetaState::Comma
|
||||||
|
} else if id == "direct" {
|
||||||
|
items.push((MetaItem::Direct, span));
|
||||||
|
MetaState::Comma
|
||||||
} else if id == "ns" {
|
} else if id == "ns" {
|
||||||
MetaState::Ns
|
MetaState::Ns
|
||||||
} else if id == "rename" {
|
} else if id == "rename" {
|
||||||
|
@ -477,6 +480,7 @@ impl fmt::Debug for Namespace {
|
||||||
pub(crate) enum MetaItem {
|
pub(crate) enum MetaItem {
|
||||||
Attribute,
|
Attribute,
|
||||||
Borrow,
|
Borrow,
|
||||||
|
Direct,
|
||||||
Ns(NamespaceMeta),
|
Ns(NamespaceMeta),
|
||||||
Rename(Literal),
|
Rename(Literal),
|
||||||
Mode(Mode),
|
Mode(Mode),
|
||||||
|
|
|
@ -277,19 +277,6 @@ fn named_field(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(with) = field_meta.serialize_with {
|
|
||||||
let path = with.to_string();
|
|
||||||
let path = syn::parse_str::<syn::Path>(path.trim_matches('"')).map_err(|err| {
|
|
||||||
syn::Error::new(
|
|
||||||
with.span(),
|
|
||||||
format!("failed to parse serialize_with as path: {err}"),
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
|
|
||||||
body.extend(quote!(#path(&self.#field_name, serializer)?;));
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
let tag = field_meta.tag;
|
let tag = field_meta.tag;
|
||||||
let default_ns = match &meta.ns.uri {
|
let default_ns = match &meta.ns.uri {
|
||||||
Some(ns) => quote!(#ns),
|
Some(ns) => quote!(#ns),
|
||||||
|
@ -297,6 +284,13 @@ fn named_field(
|
||||||
};
|
};
|
||||||
|
|
||||||
if field_meta.attribute {
|
if field_meta.attribute {
|
||||||
|
if field_meta.direct {
|
||||||
|
return Err(syn::Error::new(
|
||||||
|
field.span(),
|
||||||
|
"direct attribute is not supported on attributes",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let (ns, error) = match &field_meta.ns.uri {
|
let (ns, error) = match &field_meta.ns.uri {
|
||||||
Some(Namespace::Path(path)) => match path.get_ident() {
|
Some(Namespace::Path(path)) => match path.get_ident() {
|
||||||
Some(prefix) => match &meta.ns.prefixes.get(&prefix.to_string()) {
|
Some(prefix) => match &meta.ns.prefixes.get(&prefix.to_string()) {
|
||||||
|
@ -344,9 +338,33 @@ 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, borrowed, false, true);
|
discard_lifetimes(&mut no_lifetime_type, borrowed, false, true);
|
||||||
body.extend(quote!(
|
if let Some(with) = field_meta.serialize_with {
|
||||||
self.#field_name.serialize(Some(::instant_xml::Id { ns: #ns, name: #tag }), serializer)?;
|
if field_meta.direct {
|
||||||
));
|
return Err(syn::Error::new(
|
||||||
|
field.span(),
|
||||||
|
"direct serialization is not supported with `serialize_with`",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let path = with.to_string();
|
||||||
|
let path = syn::parse_str::<syn::Path>(path.trim_matches('"')).map_err(|err| {
|
||||||
|
syn::Error::new(
|
||||||
|
with.span(),
|
||||||
|
format!("failed to parse serialize_with as path: {err}"),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
body.extend(quote!(#path(&self.#field_name, serializer)?;));
|
||||||
|
return Ok(());
|
||||||
|
} else if field_meta.direct {
|
||||||
|
body.extend(quote!(
|
||||||
|
self.#field_name.serialize(None, serializer)?;
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
body.extend(quote!(
|
||||||
|
self.#field_name.serialize(Some(::instant_xml::Id { ns: #ns, name: #tag }), serializer)?;
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
use similar_asserts::assert_eq;
|
||||||
|
|
||||||
|
use instant_xml::{from_str, to_string, FromXml, ToXml};
|
||||||
|
|
||||||
|
#[derive(Debug, Eq, FromXml, PartialEq, ToXml)]
|
||||||
|
struct Foo {
|
||||||
|
#[xml(attribute)]
|
||||||
|
flag: bool,
|
||||||
|
#[xml(direct)]
|
||||||
|
inner: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn direct() {
|
||||||
|
let v = Foo {
|
||||||
|
flag: true,
|
||||||
|
inner: "hello".to_string(),
|
||||||
|
};
|
||||||
|
let xml = "<Foo flag=\"true\">hello</Foo>";
|
||||||
|
|
||||||
|
assert_eq!(to_string(&v).unwrap(), xml);
|
||||||
|
assert_eq!(from_str::<Foo>(xml), Ok(v));
|
||||||
|
}
|
Loading…
Reference in New Issue