Implement transparent mode for serialization
This commit is contained in:
parent
b276ee173f
commit
be7902925e
|
@ -54,7 +54,7 @@ impl<'input> ContainerMeta<'input> {
|
|||
}
|
||||
MetaItem::Mode(new) => match mode {
|
||||
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 modes")),
|
||||
},
|
||||
_ => {
|
||||
return Err(syn::Error::new(
|
||||
|
@ -302,6 +302,7 @@ fn discard_path_lifetimes(
|
|||
enum Mode {
|
||||
Forward,
|
||||
Scalar,
|
||||
Transparent,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -334,7 +335,7 @@ mod tests {
|
|||
}
|
||||
})
|
||||
.to_string())
|
||||
.find("compile_error ! { \"missing enum mode\" }")
|
||||
.find("compile_error ! { \"missing mode\" }")
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
|
|
|
@ -298,6 +298,9 @@ pub(crate) fn meta_items(attrs: &[syn::Attribute]) -> Vec<(MetaItem, Span)> {
|
|||
} else if id == "direct" {
|
||||
items.push((MetaItem::Direct, span));
|
||||
MetaState::Comma
|
||||
} else if id == "transparent" {
|
||||
items.push((MetaItem::Mode(Mode::Transparent), span));
|
||||
MetaState::Comma
|
||||
} else if id == "ns" {
|
||||
MetaState::Ns
|
||||
} else if id == "rename" {
|
||||
|
|
|
@ -15,13 +15,23 @@ pub fn to_xml(input: &syn::DeriveInput) -> proc_macro2::TokenStream {
|
|||
|
||||
match (&input.data, meta.mode) {
|
||||
(syn::Data::Struct(data), None) => serialize_struct(input, data, meta),
|
||||
(syn::Data::Struct(data), Some(Mode::Transparent)) => {
|
||||
serialize_inline_struct(input, data, meta)
|
||||
}
|
||||
(syn::Data::Enum(data), Some(Mode::Scalar)) => serialize_scalar_enum(input, data, meta),
|
||||
(syn::Data::Enum(data), Some(Mode::Forward)) => serialize_forward_enum(input, data, meta),
|
||||
(syn::Data::Struct(_), _) => {
|
||||
syn::Error::new(input.span(), "enum mode not allowed on struct type").to_compile_error()
|
||||
}
|
||||
(syn::Data::Enum(_), _) => {
|
||||
syn::Error::new(input.span(), "missing enum mode").to_compile_error()
|
||||
(syn::Data::Struct(_), Some(mode)) => syn::Error::new(
|
||||
input.span(),
|
||||
format_args!("{mode:?} mode not allowed on struct type"),
|
||||
)
|
||||
.to_compile_error(),
|
||||
(syn::Data::Enum(_), Some(mode)) => syn::Error::new(
|
||||
input.span(),
|
||||
format_args!("{mode:?} mode not allowed on enum type"),
|
||||
)
|
||||
.to_compile_error(),
|
||||
(syn::Data::Enum(_), None) => {
|
||||
syn::Error::new(input.span(), "missing mode").to_compile_error()
|
||||
}
|
||||
_ => todo!(),
|
||||
}
|
||||
|
@ -233,6 +243,82 @@ fn serialize_struct(
|
|||
)
|
||||
}
|
||||
|
||||
fn serialize_inline_struct(
|
||||
input: &syn::DeriveInput,
|
||||
data: &syn::DataStruct,
|
||||
meta: ContainerMeta,
|
||||
) -> proc_macro2::TokenStream {
|
||||
if !meta.ns.prefixes.is_empty() {
|
||||
return syn::Error::new(
|
||||
input.span(),
|
||||
"inline structs cannot have namespace declarations",
|
||||
)
|
||||
.to_compile_error();
|
||||
} else if let Some(ns) = meta.ns.uri {
|
||||
return syn::Error::new(
|
||||
ns.span(),
|
||||
"inline structs cannot have namespace declarations",
|
||||
)
|
||||
.to_compile_error();
|
||||
} else if let Some(rename) = meta.rename {
|
||||
return syn::Error::new(rename.span(), "inline structs cannot be renamed")
|
||||
.to_compile_error();
|
||||
}
|
||||
|
||||
let mut body = TokenStream::new();
|
||||
let mut attributes = TokenStream::new();
|
||||
let mut borrowed = BTreeSet::new();
|
||||
match &data.fields {
|
||||
syn::Fields::Named(fields) => {
|
||||
for field in &fields.named {
|
||||
if let Err(err) =
|
||||
named_field(field, &mut body, &mut attributes, &mut borrowed, &meta)
|
||||
{
|
||||
return err.to_compile_error();
|
||||
}
|
||||
|
||||
if !attributes.is_empty() {
|
||||
return syn::Error::new(
|
||||
input.span(),
|
||||
"no attributes allowed on inline structs",
|
||||
)
|
||||
.to_compile_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
syn::Fields::Unnamed(fields) => {
|
||||
for (index, field) in fields.unnamed.iter().enumerate() {
|
||||
if let Err(err) = unnamed_field(field, index, &mut body, &mut borrowed) {
|
||||
return err.to_compile_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
syn::Fields::Unit => body.extend(quote!(serializer.end_empty()?;)),
|
||||
}
|
||||
|
||||
let mut generics = input.generics.clone();
|
||||
for param in generics.type_params_mut() {
|
||||
param
|
||||
.bounds
|
||||
.push(syn::parse_str("::instant_xml::ToXml").unwrap());
|
||||
}
|
||||
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
let ident = &input.ident;
|
||||
quote!(
|
||||
impl #impl_generics ToXml for #ident #ty_generics #where_clause {
|
||||
fn serialize<W: ::core::fmt::Write + ?::core::marker::Sized>(
|
||||
&self,
|
||||
field: Option<::instant_xml::Id<'_>>,
|
||||
serializer: &mut instant_xml::Serializer<W>,
|
||||
) -> Result<(), instant_xml::Error> {
|
||||
#body
|
||||
Ok(())
|
||||
}
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
fn named_field(
|
||||
field: &syn::Field,
|
||||
body: &mut TokenStream,
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
use instant_xml::{to_string, ToXml};
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, ToXml)]
|
||||
struct Wrapper {
|
||||
inline: Inline,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, ToXml)]
|
||||
#[xml(transparent)]
|
||||
struct Inline {
|
||||
foo: Foo,
|
||||
bar: Bar,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, ToXml)]
|
||||
struct Foo {
|
||||
i: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, ToXml)]
|
||||
struct Bar {
|
||||
s: String,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inline() {
|
||||
let v = Wrapper {
|
||||
inline: Inline {
|
||||
foo: Foo { i: 42 },
|
||||
bar: Bar {
|
||||
s: "hello".to_string(),
|
||||
},
|
||||
},
|
||||
};
|
||||
let xml = r#"<Wrapper><Foo><i>42</i></Foo><Bar><s>hello</s></Bar></Wrapper>"#;
|
||||
assert_eq!(xml, to_string(&v).unwrap());
|
||||
}
|
Loading…
Reference in New Issue