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 {
|
MetaItem::Mode(new) => match mode {
|
||||||
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 modes")),
|
||||||
},
|
},
|
||||||
_ => {
|
_ => {
|
||||||
return Err(syn::Error::new(
|
return Err(syn::Error::new(
|
||||||
|
@ -302,6 +302,7 @@ fn discard_path_lifetimes(
|
||||||
enum Mode {
|
enum Mode {
|
||||||
Forward,
|
Forward,
|
||||||
Scalar,
|
Scalar,
|
||||||
|
Transparent,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -334,7 +335,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.to_string())
|
.to_string())
|
||||||
.find("compile_error ! { \"missing enum mode\" }")
|
.find("compile_error ! { \"missing mode\" }")
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -298,6 +298,9 @@ pub(crate) fn meta_items(attrs: &[syn::Attribute]) -> Vec<(MetaItem, Span)> {
|
||||||
} else if id == "direct" {
|
} else if id == "direct" {
|
||||||
items.push((MetaItem::Direct, span));
|
items.push((MetaItem::Direct, span));
|
||||||
MetaState::Comma
|
MetaState::Comma
|
||||||
|
} else if id == "transparent" {
|
||||||
|
items.push((MetaItem::Mode(Mode::Transparent), span));
|
||||||
|
MetaState::Comma
|
||||||
} else if id == "ns" {
|
} else if id == "ns" {
|
||||||
MetaState::Ns
|
MetaState::Ns
|
||||||
} else if id == "rename" {
|
} else if id == "rename" {
|
||||||
|
|
|
@ -15,13 +15,23 @@ pub fn to_xml(input: &syn::DeriveInput) -> proc_macro2::TokenStream {
|
||||||
|
|
||||||
match (&input.data, meta.mode) {
|
match (&input.data, meta.mode) {
|
||||||
(syn::Data::Struct(data), None) => serialize_struct(input, data, meta),
|
(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::Scalar)) => serialize_scalar_enum(input, data, meta),
|
||||||
(syn::Data::Enum(data), Some(Mode::Forward)) => serialize_forward_enum(input, data, meta),
|
(syn::Data::Enum(data), Some(Mode::Forward)) => serialize_forward_enum(input, data, meta),
|
||||||
(syn::Data::Struct(_), _) => {
|
(syn::Data::Struct(_), Some(mode)) => syn::Error::new(
|
||||||
syn::Error::new(input.span(), "enum mode not allowed on struct type").to_compile_error()
|
input.span(),
|
||||||
}
|
format_args!("{mode:?} mode not allowed on struct type"),
|
||||||
(syn::Data::Enum(_), _) => {
|
)
|
||||||
syn::Error::new(input.span(), "missing enum mode").to_compile_error()
|
.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!(),
|
_ => 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(
|
fn named_field(
|
||||||
field: &syn::Field,
|
field: &syn::Field,
|
||||||
body: &mut TokenStream,
|
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