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) {
|
for (item, span) in meta_items(&input.attrs) {
|
||||||
match item {
|
match item {
|
||||||
MetaItem::Attribute => {
|
|
||||||
return Err(syn::Error::new(
|
|
||||||
span,
|
|
||||||
"attribute key invalid in container xml attribute",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
MetaItem::Ns(namespace) => ns = namespace,
|
MetaItem::Ns(namespace) => ns = namespace,
|
||||||
MetaItem::Rename(lit) => rename = Some(lit),
|
MetaItem::Rename(lit) => rename = Some(lit),
|
||||||
MetaItem::RenameAll(lit) => {
|
MetaItem::RenameAll(lit) => {
|
||||||
|
@ -63,6 +57,12 @@ impl<'input> ContainerMeta<'input> {
|
||||||
None => mode = Some(new),
|
None => mode = Some(new),
|
||||||
Some(_) => return Err(syn::Error::new(span, "cannot have two enum modes")),
|
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,
|
attribute: bool,
|
||||||
ns: NamespaceMeta,
|
ns: NamespaceMeta,
|
||||||
tag: TokenStream,
|
tag: TokenStream,
|
||||||
|
serialize_with: Option<Literal>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FieldMeta {
|
impl FieldMeta {
|
||||||
|
@ -129,6 +130,7 @@ impl FieldMeta {
|
||||||
MetaItem::Attribute => meta.attribute = true,
|
MetaItem::Attribute => meta.attribute = 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::RenameAll(_) => {
|
MetaItem::RenameAll(_) => {
|
||||||
return Err(syn::Error::new(
|
return Err(syn::Error::new(
|
||||||
span,
|
span,
|
||||||
|
@ -514,6 +516,8 @@ fn meta_items(attrs: &[syn::Attribute]) -> Vec<(MetaItem, Span)> {
|
||||||
} else if id == "wrapped" {
|
} else if id == "wrapped" {
|
||||||
items.push((MetaItem::Mode(Mode::Wrapped), span));
|
items.push((MetaItem::Mode(Mode::Wrapped), span));
|
||||||
MetaState::Comma
|
MetaState::Comma
|
||||||
|
} else if id == "serialize_with" {
|
||||||
|
MetaState::SerializeWith
|
||||||
} else {
|
} else {
|
||||||
panic!("unexpected key in xml attribute");
|
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));
|
items.push((MetaItem::RenameAll(lit), span));
|
||||||
MetaState::Comma
|
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) => {
|
(state, tree) => {
|
||||||
panic!(
|
panic!(
|
||||||
"invalid state transition while parsing xml attribute ({}, {tree})",
|
"invalid state transition while parsing xml attribute ({}, {tree})",
|
||||||
|
@ -562,6 +573,8 @@ enum MetaState {
|
||||||
RenameValue,
|
RenameValue,
|
||||||
RenameAll,
|
RenameAll,
|
||||||
RenameAllValue,
|
RenameAllValue,
|
||||||
|
SerializeWith,
|
||||||
|
SerializeWithValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MetaState {
|
impl MetaState {
|
||||||
|
@ -574,6 +587,8 @@ impl MetaState {
|
||||||
MetaState::RenameValue => "RenameValue",
|
MetaState::RenameValue => "RenameValue",
|
||||||
MetaState::RenameAll => "RenameAll",
|
MetaState::RenameAll => "RenameAll",
|
||||||
MetaState::RenameAllValue => "RenameAllValue",
|
MetaState::RenameAllValue => "RenameAllValue",
|
||||||
|
MetaState::SerializeWith => "SerializeWith",
|
||||||
|
MetaState::SerializeWithValue => "SerializeWithValue",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -635,6 +650,7 @@ enum MetaItem {
|
||||||
Rename(Literal),
|
Rename(Literal),
|
||||||
Mode(Mode),
|
Mode(Mode),
|
||||||
RenameAll(Literal),
|
RenameAll(Literal),
|
||||||
|
SerializeWith(Literal),
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Namespace {
|
enum Namespace {
|
||||||
|
|
|
@ -166,9 +166,11 @@ fn serialize_struct(
|
||||||
|
|
||||||
match data.fields {
|
match data.fields {
|
||||||
syn::Fields::Named(ref fields) => {
|
syn::Fields::Named(ref fields) => {
|
||||||
fields.named.iter().for_each(|field| {
|
for field in &fields.named {
|
||||||
process_named_field(field, &mut body, &mut attributes, &meta);
|
if let Err(err) = process_named_field(field, &mut body, &mut attributes, &meta) {
|
||||||
});
|
return err.to_compile_error();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
syn::Fields::Unnamed(_) => todo!(),
|
syn::Fields::Unnamed(_) => todo!(),
|
||||||
syn::Fields::Unit => {}
|
syn::Fields::Unit => {}
|
||||||
|
@ -238,16 +240,29 @@ fn process_named_field(
|
||||||
body: &mut TokenStream,
|
body: &mut TokenStream,
|
||||||
attributes: &mut TokenStream,
|
attributes: &mut TokenStream,
|
||||||
meta: &ContainerMeta,
|
meta: &ContainerMeta,
|
||||||
) {
|
) -> Result<(), syn::Error> {
|
||||||
let field_name = field.ident.as_ref().unwrap();
|
let field_name = field.ident.as_ref().unwrap();
|
||||||
let field_meta = match FieldMeta::from_field(field, meta) {
|
let field_meta = match FieldMeta::from_field(field, meta) {
|
||||||
Ok(meta) => meta,
|
Ok(meta) => meta,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
body.extend(err.into_compile_error());
|
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 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),
|
||||||
|
@ -292,7 +307,7 @@ fn process_named_field(
|
||||||
#error
|
#error
|
||||||
serializer.write_attr(#tag, #ns, &self.#field_name)?;
|
serializer.write_attr(#tag, #ns, &self.#field_name)?;
|
||||||
));
|
));
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let ns = match field_meta.ns.uri {
|
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