Add support for direct fields
This commit is contained in:
parent
d2605977aa
commit
f8b4364acd
|
@ -196,9 +196,15 @@ fn deserialize_struct(
|
|||
// Common values
|
||||
let mut declare_values = TokenStream::new();
|
||||
let mut return_val = TokenStream::new();
|
||||
let mut direct = TokenStream::new();
|
||||
|
||||
let mut borrowed = BTreeSet::new();
|
||||
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) {
|
||||
Ok(meta) => meta,
|
||||
Err(err) => return err.into_compile_error(),
|
||||
|
@ -216,6 +222,7 @@ fn deserialize_struct(
|
|||
&mut return_val,
|
||||
tokens,
|
||||
&mut borrowed,
|
||||
&mut direct,
|
||||
field_meta,
|
||||
&container_meta,
|
||||
);
|
||||
|
@ -299,6 +306,7 @@ fn deserialize_struct(
|
|||
}
|
||||
}
|
||||
}
|
||||
#direct
|
||||
node => return Err(Error::UnexpectedNode(format!("{:?}", node))),
|
||||
}
|
||||
}
|
||||
|
@ -323,6 +331,7 @@ fn named_field(
|
|||
return_val: &mut TokenStream,
|
||||
tokens: &mut Tokens,
|
||||
borrowed: &mut BTreeSet<syn::Lifetime>,
|
||||
direct: &mut TokenStream,
|
||||
mut field_meta: FieldMeta,
|
||||
container_meta: &ContainerMeta,
|
||||
) -> Result<(), syn::Error> {
|
||||
|
@ -353,6 +362,7 @@ fn named_field(
|
|||
discard_lifetimes(&mut no_lifetime_type, borrowed, field_meta.borrow, true);
|
||||
|
||||
let enum_name = Ident::new(&format!("__Value{index}"), Span::call_site());
|
||||
if !field_meta.direct {
|
||||
tokens.r#enum.extend(quote!(#enum_name,));
|
||||
|
||||
if !tokens.branches.is_empty() {
|
||||
|
@ -366,6 +376,7 @@ fn named_field(
|
|||
true => quote!({ __Attributes::#enum_name }),
|
||||
false => quote!({ __Elements::#enum_name }),
|
||||
});
|
||||
}
|
||||
|
||||
declare_values.extend(quote!(
|
||||
let mut #enum_name: Option<#no_lifetime_type> = None;
|
||||
|
@ -386,12 +397,26 @@ fn named_field(
|
|||
|
||||
if !field_meta.attribute {
|
||||
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!(
|
||||
__Elements::#enum_name => {
|
||||
let mut nested = deserializer.nested(data);
|
||||
#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 {
|
||||
tokens.r#match.extend(quote!(
|
||||
__Elements::#enum_name => match <#no_lifetime_type as FromXml>::KIND {
|
||||
|
@ -408,6 +433,13 @@ fn named_field(
|
|||
));
|
||||
}
|
||||
} 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 {
|
||||
tokens.r#match.extend(quote!(
|
||||
__Attributes::#enum_name => {
|
||||
|
|
|
@ -108,6 +108,7 @@ impl<'input> ContainerMeta<'input> {
|
|||
struct FieldMeta {
|
||||
attribute: bool,
|
||||
borrow: bool,
|
||||
direct: bool,
|
||||
ns: NamespaceMeta,
|
||||
tag: TokenStream,
|
||||
serialize_with: Option<Literal>,
|
||||
|
@ -129,6 +130,7 @@ impl FieldMeta {
|
|||
match item {
|
||||
MetaItem::Attribute => meta.attribute = true,
|
||||
MetaItem::Borrow => meta.borrow = true,
|
||||
MetaItem::Direct => meta.direct = true,
|
||||
MetaItem::Ns(ns) => meta.ns = ns,
|
||||
MetaItem::Rename(lit) => meta.tag = quote!(#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" {
|
||||
items.push((MetaItem::Borrow, span));
|
||||
MetaState::Comma
|
||||
} else if id == "direct" {
|
||||
items.push((MetaItem::Direct, span));
|
||||
MetaState::Comma
|
||||
} else if id == "ns" {
|
||||
MetaState::Ns
|
||||
} else if id == "rename" {
|
||||
|
@ -477,6 +480,7 @@ impl fmt::Debug for Namespace {
|
|||
pub(crate) enum MetaItem {
|
||||
Attribute,
|
||||
Borrow,
|
||||
Direct,
|
||||
Ns(NamespaceMeta),
|
||||
Rename(Literal),
|
||||
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 default_ns = match &meta.ns.uri {
|
||||
Some(ns) => quote!(#ns),
|
||||
|
@ -297,6 +284,13 @@ fn named_field(
|
|||
};
|
||||
|
||||
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 {
|
||||
Some(Namespace::Path(path)) => match path.get_ident() {
|
||||
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();
|
||||
discard_lifetimes(&mut no_lifetime_type, borrowed, false, true);
|
||||
if let Some(with) = field_meta.serialize_with {
|
||||
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(())
|
||||
}
|
||||
|
|
|
@ -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