Add support for serialize_with attribute
This commit is contained in:
parent
61df3a7835
commit
f6e22b3e31
|
@ -45,12 +45,6 @@ impl<'input> ContainerMeta<'input> {
|
|||
|
||||
for (item, span) in meta_items(&input.attrs) {
|
||||
match item {
|
||||
MetaItem::Attribute => {
|
||||
return Err(syn::Error::new(
|
||||
span,
|
||||
"attribute key invalid in container xml attribute",
|
||||
))
|
||||
}
|
||||
MetaItem::Ns(namespace) => ns = namespace,
|
||||
MetaItem::Rename(lit) => rename = Some(lit),
|
||||
MetaItem::RenameAll(lit) => {
|
||||
|
@ -63,6 +57,12 @@ impl<'input> ContainerMeta<'input> {
|
|||
None => mode = Some(new),
|
||||
Some(_) => return Err(syn::Error::new(span, "cannot have two enum modes")),
|
||||
},
|
||||
_ => {
|
||||
return Err(syn::Error::new(
|
||||
span,
|
||||
"invalid field in container xml attribute",
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,6 +111,7 @@ struct FieldMeta {
|
|||
attribute: bool,
|
||||
ns: NamespaceMeta,
|
||||
tag: TokenStream,
|
||||
serialize_with: Option<Literal>,
|
||||
}
|
||||
|
||||
impl FieldMeta {
|
||||
|
@ -129,6 +130,7 @@ impl FieldMeta {
|
|||
MetaItem::Attribute => meta.attribute = true,
|
||||
MetaItem::Ns(ns) => meta.ns = ns,
|
||||
MetaItem::Rename(lit) => meta.tag = quote!(#lit),
|
||||
MetaItem::SerializeWith(lit) => meta.serialize_with = Some(lit),
|
||||
MetaItem::RenameAll(_) => {
|
||||
return Err(syn::Error::new(
|
||||
span,
|
||||
|
@ -514,6 +516,8 @@ fn meta_items(attrs: &[syn::Attribute]) -> Vec<(MetaItem, Span)> {
|
|||
} else if id == "wrapped" {
|
||||
items.push((MetaItem::Mode(Mode::Wrapped), span));
|
||||
MetaState::Comma
|
||||
} else if id == "serialize_with" {
|
||||
MetaState::SerializeWith
|
||||
} else {
|
||||
panic!("unexpected key in xml attribute");
|
||||
}
|
||||
|
@ -541,6 +545,13 @@ fn meta_items(attrs: &[syn::Attribute]) -> Vec<(MetaItem, Span)> {
|
|||
items.push((MetaItem::RenameAll(lit), span));
|
||||
MetaState::Comma
|
||||
}
|
||||
(MetaState::SerializeWith, TokenTree::Punct(punct)) if punct.as_char() == '=' => {
|
||||
MetaState::SerializeWithValue
|
||||
}
|
||||
(MetaState::SerializeWithValue, TokenTree::Literal(lit)) => {
|
||||
items.push((MetaItem::SerializeWith(lit), span));
|
||||
MetaState::Comma
|
||||
}
|
||||
(state, tree) => {
|
||||
panic!(
|
||||
"invalid state transition while parsing xml attribute ({}, {tree})",
|
||||
|
@ -562,6 +573,8 @@ enum MetaState {
|
|||
RenameValue,
|
||||
RenameAll,
|
||||
RenameAllValue,
|
||||
SerializeWith,
|
||||
SerializeWithValue,
|
||||
}
|
||||
|
||||
impl MetaState {
|
||||
|
@ -574,6 +587,8 @@ impl MetaState {
|
|||
MetaState::RenameValue => "RenameValue",
|
||||
MetaState::RenameAll => "RenameAll",
|
||||
MetaState::RenameAllValue => "RenameAllValue",
|
||||
MetaState::SerializeWith => "SerializeWith",
|
||||
MetaState::SerializeWithValue => "SerializeWithValue",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -635,6 +650,7 @@ enum MetaItem {
|
|||
Rename(Literal),
|
||||
Mode(Mode),
|
||||
RenameAll(Literal),
|
||||
SerializeWith(Literal),
|
||||
}
|
||||
|
||||
enum Namespace {
|
||||
|
|
|
@ -166,9 +166,11 @@ fn serialize_struct(
|
|||
|
||||
match data.fields {
|
||||
syn::Fields::Named(ref fields) => {
|
||||
fields.named.iter().for_each(|field| {
|
||||
process_named_field(field, &mut body, &mut attributes, &meta);
|
||||
});
|
||||
for field in &fields.named {
|
||||
if let Err(err) = process_named_field(field, &mut body, &mut attributes, &meta) {
|
||||
return err.to_compile_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
syn::Fields::Unnamed(_) => todo!(),
|
||||
syn::Fields::Unit => {}
|
||||
|
@ -238,16 +240,29 @@ fn process_named_field(
|
|||
body: &mut TokenStream,
|
||||
attributes: &mut TokenStream,
|
||||
meta: &ContainerMeta,
|
||||
) {
|
||||
) -> Result<(), syn::Error> {
|
||||
let field_name = field.ident.as_ref().unwrap();
|
||||
let field_meta = match FieldMeta::from_field(field, meta) {
|
||||
Ok(meta) => meta,
|
||||
Err(err) => {
|
||||
body.extend(err.into_compile_error());
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
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),
|
||||
|
@ -292,7 +307,7 @@ fn process_named_field(
|
|||
#error
|
||||
serializer.write_attr(#tag, #ns, &self.#field_name)?;
|
||||
));
|
||||
return;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let ns = match field_meta.ns.uri {
|
||||
|
@ -315,4 +330,6 @@ fn process_named_field(
|
|||
}
|
||||
}
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
use std::fmt;
|
||||
|
||||
use instant_xml::{to_string, Error, Serializer, ToXml};
|
||||
|
||||
#[derive(ToXml)]
|
||||
struct Foo {
|
||||
#[xml(serialize_with = "serialize_foo")]
|
||||
foo: u8,
|
||||
}
|
||||
|
||||
fn serialize_foo<W: fmt::Write + ?Sized>(
|
||||
value: &u8,
|
||||
serializer: &mut Serializer<'_, W>,
|
||||
) -> Result<(), Error> {
|
||||
serializer.write_str(&format_args!("foo: {value}"))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_with() {
|
||||
let v = Foo { foo: 42 };
|
||||
let xml = r#"<Foo>foo: 42</Foo>"#;
|
||||
assert_eq!(xml, to_string(&v).unwrap());
|
||||
}
|
Loading…
Reference in New Issue