mirror of
https://github.com/pezkuwichain/serde.git
synced 2026-04-22 21:48:02 +00:00
Untagged and internally tagged enums
This commit is contained in:
+308
-21
@@ -110,7 +110,8 @@ fn deserialize_body(
|
||||
impl_generics,
|
||||
ty,
|
||||
fields,
|
||||
&item.attrs)
|
||||
&item.attrs,
|
||||
None)
|
||||
}
|
||||
Body::Struct(Style::Tuple, ref fields) |
|
||||
Body::Struct(Style::Newtype, ref fields) => {
|
||||
@@ -124,7 +125,8 @@ fn deserialize_body(
|
||||
impl_generics,
|
||||
ty,
|
||||
fields,
|
||||
&item.attrs)
|
||||
&item.attrs,
|
||||
None)
|
||||
}
|
||||
Body::Struct(Style::Unit, _) => {
|
||||
deserialize_unit_struct(
|
||||
@@ -238,6 +240,7 @@ fn deserialize_tuple(
|
||||
ty: syn::Ty,
|
||||
fields: &[Field],
|
||||
item_attrs: &attr::Item,
|
||||
deserializer: Option<Tokens>,
|
||||
) -> Tokens {
|
||||
let where_clause = &impl_generics.where_clause;
|
||||
|
||||
@@ -274,7 +277,9 @@ fn deserialize_tuple(
|
||||
false,
|
||||
);
|
||||
|
||||
let dispatch = if is_enum {
|
||||
let dispatch = if let Some(deserializer) = deserializer {
|
||||
quote!(_serde::Deserializer::deserialize(#deserializer, #visitor_expr))
|
||||
} else if is_enum {
|
||||
quote!(_serde::de::VariantVisitor::visit_tuple(visitor, #nfields, #visitor_expr))
|
||||
} else if nfields == 1 {
|
||||
let type_name = item_attrs.name().deserialize_name();
|
||||
@@ -424,7 +429,11 @@ fn deserialize_struct(
|
||||
ty: syn::Ty,
|
||||
fields: &[Field],
|
||||
item_attrs: &attr::Item,
|
||||
deserializer: Option<Tokens>,
|
||||
) -> Tokens {
|
||||
let is_enum = variant_ident.is_some();
|
||||
let is_untagged = deserializer.is_some();
|
||||
|
||||
let where_clause = &impl_generics.where_clause;
|
||||
|
||||
let (visitor_item, visitor_ty, visitor_expr) = deserialize_visitor(impl_generics);
|
||||
@@ -454,8 +463,11 @@ fn deserialize_struct(
|
||||
item_attrs,
|
||||
);
|
||||
|
||||
let is_enum = variant_ident.is_some();
|
||||
let dispatch = if is_enum {
|
||||
let dispatch = if let Some(deserializer) = deserializer {
|
||||
quote! {
|
||||
_serde::Deserializer::deserialize(#deserializer, #visitor_expr)
|
||||
}
|
||||
} else if is_enum {
|
||||
quote! {
|
||||
_serde::de::VariantVisitor::visit_struct(visitor, FIELDS, #visitor_expr)
|
||||
}
|
||||
@@ -473,6 +485,20 @@ fn deserialize_struct(
|
||||
quote!(mut visitor)
|
||||
};
|
||||
|
||||
let visit_seq = if is_untagged {
|
||||
// untagged struct variants do not get a visit_seq method
|
||||
None
|
||||
} else {
|
||||
Some(quote! {
|
||||
#[inline]
|
||||
fn visit_seq<__V>(self, #visitor_var: __V) -> _serde::export::Result<#ty, __V::Error>
|
||||
where __V: _serde::de::SeqVisitor
|
||||
{
|
||||
#visit_seq
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
quote!({
|
||||
#field_visitor
|
||||
|
||||
@@ -485,12 +511,7 @@ fn deserialize_struct(
|
||||
_serde::export::fmt::Formatter::write_str(formatter, #expecting)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_seq<__V>(self, #visitor_var: __V) -> _serde::export::Result<#ty, __V::Error>
|
||||
where __V: _serde::de::SeqVisitor
|
||||
{
|
||||
#visit_seq
|
||||
}
|
||||
#visit_seq
|
||||
|
||||
#[inline]
|
||||
fn visit_map<__V>(self, mut visitor: __V) -> _serde::export::Result<#ty, __V::Error>
|
||||
@@ -512,6 +533,45 @@ fn deserialize_item_enum(
|
||||
ty: syn::Ty,
|
||||
variants: &[Variant],
|
||||
item_attrs: &attr::Item
|
||||
) -> Tokens {
|
||||
match *item_attrs.tag() {
|
||||
attr::EnumTag::External => {
|
||||
deserialize_externally_tagged_enum(
|
||||
type_ident,
|
||||
impl_generics,
|
||||
ty,
|
||||
variants,
|
||||
item_attrs,
|
||||
)
|
||||
}
|
||||
attr::EnumTag::Internal(ref tag) => {
|
||||
deserialize_internally_tagged_enum(
|
||||
type_ident,
|
||||
impl_generics,
|
||||
ty,
|
||||
variants,
|
||||
item_attrs,
|
||||
tag,
|
||||
)
|
||||
}
|
||||
attr::EnumTag::None => {
|
||||
deserialize_untagged_enum(
|
||||
type_ident,
|
||||
impl_generics,
|
||||
ty,
|
||||
variants,
|
||||
item_attrs,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_externally_tagged_enum(
|
||||
type_ident: &syn::Ident,
|
||||
impl_generics: &syn::Generics,
|
||||
ty: syn::Ty,
|
||||
variants: &[Variant],
|
||||
item_attrs: &attr::Item,
|
||||
) -> Tokens {
|
||||
let where_clause = &impl_generics.where_clause;
|
||||
|
||||
@@ -545,7 +605,7 @@ fn deserialize_item_enum(
|
||||
.map(|(i, variant)| {
|
||||
let variant_name = field_i(i);
|
||||
|
||||
let block = deserialize_variant(
|
||||
let block = deserialize_externally_tagged_variant(
|
||||
type_ident,
|
||||
impl_generics,
|
||||
ty.clone(),
|
||||
@@ -604,7 +664,111 @@ fn deserialize_item_enum(
|
||||
})
|
||||
}
|
||||
|
||||
fn deserialize_variant(
|
||||
fn deserialize_internally_tagged_enum(
|
||||
type_ident: &syn::Ident,
|
||||
impl_generics: &syn::Generics,
|
||||
ty: syn::Ty,
|
||||
variants: &[Variant],
|
||||
item_attrs: &attr::Item,
|
||||
tag: &str,
|
||||
) -> Tokens {
|
||||
let variant_names_idents: Vec<_> = variants.iter()
|
||||
.enumerate()
|
||||
.filter(|&(_, variant)| !variant.attrs.skip_deserializing())
|
||||
.map(|(i, variant)| (variant.attrs.name().deserialize_name(), field_i(i)))
|
||||
.collect();
|
||||
|
||||
let variants_stmt = {
|
||||
let variant_names = variant_names_idents.iter().map(|&(ref name, _)| name);
|
||||
quote! {
|
||||
const VARIANTS: &'static [&'static str] = &[ #(#variant_names),* ];
|
||||
}
|
||||
};
|
||||
|
||||
let variant_visitor = deserialize_field_visitor(
|
||||
variant_names_idents,
|
||||
item_attrs,
|
||||
true,
|
||||
);
|
||||
|
||||
// Match arms to extract a variant from a string
|
||||
let variant_arms = variants.iter()
|
||||
.enumerate()
|
||||
.filter(|&(_, variant)| !variant.attrs.skip_deserializing())
|
||||
.map(|(i, variant)| {
|
||||
let variant_name = field_i(i);
|
||||
|
||||
let block = deserialize_internally_tagged_variant(
|
||||
type_ident,
|
||||
impl_generics,
|
||||
ty.clone(),
|
||||
variant,
|
||||
item_attrs,
|
||||
quote!(_tagged.content),
|
||||
);
|
||||
|
||||
quote! {
|
||||
__Field::#variant_name => #block
|
||||
}
|
||||
});
|
||||
|
||||
quote!({
|
||||
#variant_visitor
|
||||
|
||||
#variants_stmt
|
||||
|
||||
let _tagged = try!(_serde::Deserializer::deserialize(
|
||||
deserializer,
|
||||
_serde::de::private::TaggedContentVisitor::<__Field, __D::Error>::new(#tag)));
|
||||
|
||||
match _tagged.tag {
|
||||
#(#variant_arms)*
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn deserialize_untagged_enum(
|
||||
type_ident: &syn::Ident,
|
||||
impl_generics: &syn::Generics,
|
||||
ty: syn::Ty,
|
||||
variants: &[Variant],
|
||||
item_attrs: &attr::Item,
|
||||
) -> Tokens {
|
||||
let attempts = variants.iter()
|
||||
.filter(|variant| !variant.attrs.skip_deserializing())
|
||||
.map(|variant| {
|
||||
deserialize_untagged_variant(
|
||||
type_ident,
|
||||
impl_generics,
|
||||
ty.clone(),
|
||||
variant,
|
||||
item_attrs,
|
||||
quote!(&_content),
|
||||
)
|
||||
});
|
||||
|
||||
// TODO this message could be better by saving the errors from the failed
|
||||
// attempts. The heuristic used by TOML was to count the number of fields
|
||||
// processed before an error, and use the error that happened after the
|
||||
// largest number of fields. I'm not sure I like that. Maybe it would be
|
||||
// better to save all the errors and combine them into one message that
|
||||
// explains why none of the variants matched.
|
||||
let fallthrough_msg = format!("data did not match any variant of untagged enum {}", type_ident);
|
||||
|
||||
quote!({
|
||||
let _content = try!(<_serde::de::private::Content<__D::Error> as _serde::Deserialize>::deserialize(deserializer));
|
||||
|
||||
#(
|
||||
if let _serde::export::Ok(ok) = #attempts {
|
||||
return _serde::export::Ok(ok);
|
||||
}
|
||||
)*
|
||||
|
||||
_serde::export::Err(_serde::de::Error::custom(#fallthrough_msg))
|
||||
})
|
||||
}
|
||||
|
||||
fn deserialize_externally_tagged_variant(
|
||||
type_ident: &syn::Ident,
|
||||
generics: &syn::Generics,
|
||||
ty: syn::Ty,
|
||||
@@ -621,7 +785,7 @@ fn deserialize_variant(
|
||||
})
|
||||
}
|
||||
Style::Newtype => {
|
||||
deserialize_newtype_variant(
|
||||
deserialize_externally_tagged_newtype_variant(
|
||||
type_ident,
|
||||
variant_ident,
|
||||
generics,
|
||||
@@ -636,6 +800,7 @@ fn deserialize_variant(
|
||||
ty,
|
||||
&variant.fields,
|
||||
item_attrs,
|
||||
None,
|
||||
)
|
||||
}
|
||||
Style::Struct => {
|
||||
@@ -646,22 +811,115 @@ fn deserialize_variant(
|
||||
ty,
|
||||
&variant.fields,
|
||||
item_attrs,
|
||||
None,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_newtype_variant(
|
||||
fn deserialize_internally_tagged_variant(
|
||||
type_ident: &syn::Ident,
|
||||
generics: &syn::Generics,
|
||||
ty: syn::Ty,
|
||||
variant: &Variant,
|
||||
item_attrs: &attr::Item,
|
||||
deserializer: Tokens,
|
||||
) -> Tokens {
|
||||
let variant_ident = &variant.ident;
|
||||
|
||||
match variant.style {
|
||||
Style::Unit => {
|
||||
let type_name = type_ident.as_ref();
|
||||
let variant_name = variant.ident.as_ref();
|
||||
quote!({
|
||||
try!(_serde::Deserializer::deserialize(#deserializer, _serde::de::private::InternallyTaggedUnitVisitor::new(#type_name, #variant_name)));
|
||||
_serde::export::Ok(#type_ident::#variant_ident)
|
||||
})
|
||||
}
|
||||
Style::Newtype | Style::Struct => {
|
||||
deserialize_untagged_variant(
|
||||
type_ident,
|
||||
generics,
|
||||
ty,
|
||||
variant,
|
||||
item_attrs,
|
||||
deserializer,
|
||||
)
|
||||
}
|
||||
Style::Tuple => unreachable!("checked in serde_codegen_internals"),
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_untagged_variant(
|
||||
type_ident: &syn::Ident,
|
||||
generics: &syn::Generics,
|
||||
ty: syn::Ty,
|
||||
variant: &Variant,
|
||||
item_attrs: &attr::Item,
|
||||
deserializer: Tokens,
|
||||
) -> Tokens {
|
||||
let variant_ident = &variant.ident;
|
||||
|
||||
match variant.style {
|
||||
Style::Unit => {
|
||||
let type_name = type_ident.as_ref();
|
||||
let variant_name = variant.ident.as_ref();
|
||||
quote! {
|
||||
_serde::export::Result::map(
|
||||
_serde::Deserializer::deserialize(
|
||||
#deserializer,
|
||||
_serde::de::private::UntaggedUnitVisitor::new(#type_name, #variant_name)
|
||||
),
|
||||
|()| #type_ident::#variant_ident)
|
||||
}
|
||||
}
|
||||
Style::Newtype => {
|
||||
deserialize_untagged_newtype_variant(
|
||||
type_ident,
|
||||
variant_ident,
|
||||
generics,
|
||||
&variant.fields[0],
|
||||
deserializer,
|
||||
)
|
||||
}
|
||||
Style::Tuple => {
|
||||
deserialize_tuple(
|
||||
type_ident,
|
||||
Some(variant_ident),
|
||||
generics,
|
||||
ty,
|
||||
&variant.fields,
|
||||
item_attrs,
|
||||
Some(deserializer),
|
||||
)
|
||||
}
|
||||
Style::Struct => {
|
||||
deserialize_struct(
|
||||
type_ident,
|
||||
Some(variant_ident),
|
||||
generics,
|
||||
ty,
|
||||
&variant.fields,
|
||||
item_attrs,
|
||||
Some(deserializer),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_externally_tagged_newtype_variant(
|
||||
type_ident: &syn::Ident,
|
||||
variant_ident: &syn::Ident,
|
||||
impl_generics: &syn::Generics,
|
||||
field: &Field,
|
||||
) -> Tokens {
|
||||
let visit = match field.attrs.deserialize_with() {
|
||||
match field.attrs.deserialize_with() {
|
||||
None => {
|
||||
let field_ty = &field.ty;
|
||||
quote! {
|
||||
try!(_serde::de::VariantVisitor::visit_newtype::<#field_ty>(visitor))
|
||||
_serde::export::Result::map(
|
||||
_serde::de::VariantVisitor::visit_newtype::<#field_ty>(visitor),
|
||||
#type_ident::#variant_ident),
|
||||
}
|
||||
}
|
||||
Some(path) => {
|
||||
@@ -670,12 +928,41 @@ fn deserialize_newtype_variant(
|
||||
quote!({
|
||||
#wrapper
|
||||
#wrapper_impl
|
||||
try!(_serde::de::VariantVisitor::visit_newtype::<#wrapper_ty>(visitor)).value
|
||||
_serde::export::Result::map(
|
||||
_serde::de::VariantVisitor::visit_newtype::<#wrapper_ty>(visitor),
|
||||
|_wrapper| #type_ident::#variant_ident(_wrapper.value))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_untagged_newtype_variant(
|
||||
type_ident: &syn::Ident,
|
||||
variant_ident: &syn::Ident,
|
||||
impl_generics: &syn::Generics,
|
||||
field: &Field,
|
||||
deserializer: Tokens,
|
||||
) -> Tokens {
|
||||
match field.attrs.deserialize_with() {
|
||||
None => {
|
||||
let field_ty = &field.ty;
|
||||
quote!({
|
||||
_serde::export::Result::map(
|
||||
<#field_ty as _serde::Deserialize>::deserialize(#deserializer),
|
||||
#type_ident::#variant_ident)
|
||||
})
|
||||
}
|
||||
Some(path) => {
|
||||
let (wrapper, wrapper_impl, wrapper_ty) = wrap_deserialize_with(
|
||||
type_ident, impl_generics, field.ty, path);
|
||||
quote!({
|
||||
#wrapper
|
||||
#wrapper_impl
|
||||
_serde::export::Result::map(
|
||||
<#wrapper_ty as _serde::Deserialize>::deserialize(#deserializer),
|
||||
|_wrapper| #type_ident::#variant_ident(_wrapper.value))
|
||||
})
|
||||
}
|
||||
};
|
||||
quote! {
|
||||
_serde::export::Ok(#type_ident::#variant_ident(#visit)),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+328
-89
@@ -251,14 +251,11 @@ fn serialize_variant(
|
||||
variant_index: usize,
|
||||
item_attrs: &attr::Item,
|
||||
) -> Tokens {
|
||||
let type_name = item_attrs.name().serialize_name();
|
||||
|
||||
let variant_ident = variant.ident.clone();
|
||||
let variant_name = variant.attrs.name().serialize_name();
|
||||
|
||||
if variant.attrs.skip_serializing() {
|
||||
let skipped_msg = format!("the enum variant {}::{} cannot be serialized",
|
||||
type_ident, variant_ident);
|
||||
type_ident, variant_ident);
|
||||
let skipped_err = quote! {
|
||||
_serde::export::Err(_serde::ser::Error::custom(#skipped_msg))
|
||||
};
|
||||
@@ -271,140 +268,351 @@ fn serialize_variant(
|
||||
#type_ident::#variant_ident #fields_pat => #skipped_err,
|
||||
}
|
||||
} else { // variant wasn't skipped
|
||||
match variant.style {
|
||||
let case = match variant.style {
|
||||
Style::Unit => {
|
||||
quote! {
|
||||
#type_ident::#variant_ident =>
|
||||
_serde::Serializer::serialize_unit_variant(
|
||||
_serializer,
|
||||
#type_name,
|
||||
#variant_index,
|
||||
#variant_name,
|
||||
),
|
||||
#type_ident::#variant_ident
|
||||
}
|
||||
},
|
||||
}
|
||||
Style::Newtype => {
|
||||
let block = serialize_newtype_variant(
|
||||
type_name,
|
||||
variant_index,
|
||||
variant_name,
|
||||
ty,
|
||||
generics,
|
||||
&variant.fields[0],
|
||||
);
|
||||
|
||||
quote! {
|
||||
#type_ident::#variant_ident(ref __simple_value) => #block,
|
||||
#type_ident::#variant_ident(ref __simple_value)
|
||||
}
|
||||
},
|
||||
}
|
||||
Style::Tuple => {
|
||||
let field_names = (0 .. variant.fields.len())
|
||||
.map(|i| Ident::new(format!("__field{}", i)));
|
||||
|
||||
let block = serialize_tuple_variant(
|
||||
type_name,
|
||||
variant_index,
|
||||
variant_name,
|
||||
generics,
|
||||
ty,
|
||||
&variant.fields,
|
||||
);
|
||||
|
||||
quote! {
|
||||
#type_ident::#variant_ident(#(ref #field_names),*) => { #block }
|
||||
#type_ident::#variant_ident(#(ref #field_names),*)
|
||||
}
|
||||
}
|
||||
Style::Struct => {
|
||||
let fields = variant.fields.iter()
|
||||
.map(|f| f.ident.clone().expect("struct variant has unnamed fields"));
|
||||
quote! {
|
||||
#type_ident::#variant_ident { #(ref #fields),* }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let block = serialize_struct_variant(
|
||||
variant_index,
|
||||
variant_name,
|
||||
let body = match *item_attrs.tag() {
|
||||
attr::EnumTag::External => {
|
||||
serialize_externally_tagged_variant(
|
||||
generics,
|
||||
ty,
|
||||
&variant.fields,
|
||||
variant,
|
||||
variant_index,
|
||||
item_attrs,
|
||||
);
|
||||
)
|
||||
}
|
||||
attr::EnumTag::Internal(ref tag) => {
|
||||
serialize_internally_tagged_variant(
|
||||
type_ident.as_ref(),
|
||||
variant_ident.as_ref(),
|
||||
generics,
|
||||
ty,
|
||||
variant,
|
||||
item_attrs,
|
||||
tag,
|
||||
)
|
||||
}
|
||||
attr::EnumTag::None => {
|
||||
serialize_untagged_variant(
|
||||
generics,
|
||||
ty,
|
||||
variant,
|
||||
item_attrs,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
quote! {
|
||||
#type_ident::#variant_ident { #(ref #fields),* } => { #block }
|
||||
}
|
||||
quote! {
|
||||
#case => #body
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_externally_tagged_variant(
|
||||
generics: &syn::Generics,
|
||||
ty: syn::Ty,
|
||||
variant: &Variant,
|
||||
variant_index: usize,
|
||||
item_attrs: &attr::Item,
|
||||
) -> Tokens {
|
||||
let type_name = item_attrs.name().serialize_name();
|
||||
let variant_name = variant.attrs.name().serialize_name();
|
||||
|
||||
match variant.style {
|
||||
Style::Unit => {
|
||||
quote! {
|
||||
_serde::Serializer::serialize_unit_variant(
|
||||
_serializer,
|
||||
#type_name,
|
||||
#variant_index,
|
||||
#variant_name,
|
||||
),
|
||||
}
|
||||
}
|
||||
Style::Newtype => {
|
||||
let field = &variant.fields[0];
|
||||
let mut field_expr = quote!(__simple_value);
|
||||
if let Some(path) = field.attrs.serialize_with() {
|
||||
field_expr = wrap_serialize_with(
|
||||
&ty, generics, field.ty, path, field_expr);
|
||||
}
|
||||
|
||||
quote! {
|
||||
_serde::Serializer::serialize_newtype_variant(
|
||||
_serializer,
|
||||
#type_name,
|
||||
#variant_index,
|
||||
#variant_name,
|
||||
#field_expr,
|
||||
),
|
||||
}
|
||||
}
|
||||
Style::Tuple => {
|
||||
let block = serialize_tuple_variant(
|
||||
TupleVariant::ExternallyTagged {
|
||||
type_name: type_name,
|
||||
variant_index: variant_index,
|
||||
variant_name: variant_name,
|
||||
},
|
||||
generics,
|
||||
ty,
|
||||
&variant.fields,
|
||||
);
|
||||
|
||||
quote! {
|
||||
{ #block }
|
||||
}
|
||||
}
|
||||
Style::Struct => {
|
||||
let block = serialize_struct_variant(
|
||||
StructVariant::ExternallyTagged {
|
||||
variant_index: variant_index,
|
||||
variant_name: variant_name,
|
||||
},
|
||||
generics,
|
||||
ty,
|
||||
&variant.fields,
|
||||
item_attrs,
|
||||
);
|
||||
|
||||
quote! {
|
||||
{ #block }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_newtype_variant(
|
||||
type_name: String,
|
||||
variant_index: usize,
|
||||
variant_name: String,
|
||||
item_ty: syn::Ty,
|
||||
fn serialize_internally_tagged_variant(
|
||||
type_ident: &str,
|
||||
variant_ident: &str,
|
||||
generics: &syn::Generics,
|
||||
field: &Field,
|
||||
ty: syn::Ty,
|
||||
variant: &Variant,
|
||||
item_attrs: &attr::Item,
|
||||
tag: &str,
|
||||
) -> Tokens {
|
||||
let mut field_expr = quote!(__simple_value);
|
||||
if let Some(path) = field.attrs.serialize_with() {
|
||||
field_expr = wrap_serialize_with(
|
||||
&item_ty, generics, field.ty, path, field_expr);
|
||||
}
|
||||
let type_name = item_attrs.name().serialize_name();
|
||||
let variant_name = variant.attrs.name().serialize_name();
|
||||
|
||||
quote! {
|
||||
_serde::Serializer::serialize_newtype_variant(
|
||||
_serializer,
|
||||
#type_name,
|
||||
#variant_index,
|
||||
#variant_name,
|
||||
#field_expr,
|
||||
)
|
||||
match variant.style {
|
||||
Style::Unit => {
|
||||
quote!({
|
||||
let mut __struct = try!(_serde::Serializer::serialize_struct(
|
||||
_serializer, #type_name, 1));
|
||||
try!(_serde::ser::SerializeStruct::serialize_field(
|
||||
&mut __struct, #tag, #variant_name));
|
||||
_serde::ser::SerializeStruct::end(__struct)
|
||||
})
|
||||
}
|
||||
Style::Newtype => {
|
||||
let field = &variant.fields[0];
|
||||
let mut field_expr = quote!(__simple_value);
|
||||
if let Some(path) = field.attrs.serialize_with() {
|
||||
field_expr = wrap_serialize_with(
|
||||
&ty, generics, field.ty, path, field_expr);
|
||||
}
|
||||
|
||||
quote! {
|
||||
_serde::ser::private::serialize_tagged_newtype(
|
||||
_serializer,
|
||||
#type_ident,
|
||||
#variant_ident,
|
||||
#tag,
|
||||
#variant_name,
|
||||
#field_expr,
|
||||
),
|
||||
}
|
||||
}
|
||||
Style::Struct => {
|
||||
let block = serialize_struct_variant(
|
||||
StructVariant::InternallyTagged {
|
||||
tag: tag,
|
||||
variant_name: variant_name,
|
||||
},
|
||||
generics,
|
||||
ty,
|
||||
&variant.fields,
|
||||
item_attrs,
|
||||
);
|
||||
|
||||
quote! {
|
||||
{ #block }
|
||||
}
|
||||
}
|
||||
Style::Tuple => unreachable!("checked in serde_codegen_internals"),
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_untagged_variant(
|
||||
generics: &syn::Generics,
|
||||
ty: syn::Ty,
|
||||
variant: &Variant,
|
||||
item_attrs: &attr::Item,
|
||||
) -> Tokens {
|
||||
match variant.style {
|
||||
Style::Unit => {
|
||||
quote! {
|
||||
_serde::Serializer::serialize_unit(_serializer),
|
||||
}
|
||||
}
|
||||
Style::Newtype => {
|
||||
let field = &variant.fields[0];
|
||||
let mut field_expr = quote!(__simple_value);
|
||||
if let Some(path) = field.attrs.serialize_with() {
|
||||
field_expr = wrap_serialize_with(
|
||||
&ty, generics, field.ty, path, field_expr);
|
||||
}
|
||||
|
||||
quote! {
|
||||
_serde::Serialize::serialize(#field_expr, _serializer),
|
||||
}
|
||||
}
|
||||
Style::Tuple => {
|
||||
let block = serialize_tuple_variant(
|
||||
TupleVariant::Untagged,
|
||||
generics,
|
||||
ty,
|
||||
&variant.fields,
|
||||
);
|
||||
|
||||
quote! {
|
||||
{ #block }
|
||||
}
|
||||
}
|
||||
Style::Struct => {
|
||||
let block = serialize_struct_variant(
|
||||
StructVariant::Untagged,
|
||||
generics,
|
||||
ty,
|
||||
&variant.fields,
|
||||
item_attrs,
|
||||
);
|
||||
|
||||
quote! {
|
||||
{ #block }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum TupleVariant {
|
||||
ExternallyTagged {
|
||||
type_name: String,
|
||||
variant_index: usize,
|
||||
variant_name: String,
|
||||
},
|
||||
Untagged,
|
||||
}
|
||||
|
||||
fn serialize_tuple_variant(
|
||||
type_name: String,
|
||||
variant_index: usize,
|
||||
variant_name: String,
|
||||
context: TupleVariant,
|
||||
generics: &syn::Generics,
|
||||
structure_ty: syn::Ty,
|
||||
fields: &[Field],
|
||||
) -> Tokens {
|
||||
let method = match context {
|
||||
TupleVariant::ExternallyTagged{..} => {
|
||||
quote!(_serde::ser::SerializeTupleVariant::serialize_field)
|
||||
}
|
||||
TupleVariant::Untagged => {
|
||||
quote!(_serde::ser::SerializeTuple::serialize_element)
|
||||
}
|
||||
};
|
||||
|
||||
let serialize_stmts = serialize_tuple_struct_visitor(
|
||||
structure_ty,
|
||||
fields,
|
||||
generics,
|
||||
true,
|
||||
quote!(_serde::ser::SerializeTupleVariant::serialize_field),
|
||||
method,
|
||||
);
|
||||
|
||||
let len = serialize_stmts.len();
|
||||
let let_mut = mut_if(len > 0);
|
||||
|
||||
quote! {
|
||||
let #let_mut __serde_state = try!(_serde::Serializer::serialize_tuple_variant(
|
||||
_serializer,
|
||||
#type_name,
|
||||
#variant_index,
|
||||
#variant_name,
|
||||
#len));
|
||||
#(#serialize_stmts)*
|
||||
_serde::ser::SerializeTupleVariant::end(__serde_state)
|
||||
match context {
|
||||
TupleVariant::ExternallyTagged { type_name, variant_index, variant_name } => {
|
||||
quote! {
|
||||
let #let_mut __serde_state = try!(_serde::Serializer::serialize_tuple_variant(
|
||||
_serializer,
|
||||
#type_name,
|
||||
#variant_index,
|
||||
#variant_name,
|
||||
#len));
|
||||
#(#serialize_stmts)*
|
||||
_serde::ser::SerializeTupleVariant::end(__serde_state)
|
||||
}
|
||||
}
|
||||
TupleVariant::Untagged => {
|
||||
quote! {
|
||||
let #let_mut __serde_state = try!(_serde::Serializer::serialize_tuple(
|
||||
_serializer,
|
||||
#len));
|
||||
#(#serialize_stmts)*
|
||||
_serde::ser::SerializeTuple::end(__serde_state)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn serialize_struct_variant(
|
||||
variant_index: usize,
|
||||
variant_name: String,
|
||||
enum StructVariant<'a> {
|
||||
ExternallyTagged {
|
||||
variant_index: usize,
|
||||
variant_name: String,
|
||||
},
|
||||
InternallyTagged {
|
||||
tag: &'a str,
|
||||
variant_name: String,
|
||||
},
|
||||
Untagged,
|
||||
}
|
||||
|
||||
fn serialize_struct_variant<'a>(
|
||||
context: StructVariant<'a>,
|
||||
generics: &syn::Generics,
|
||||
ty: syn::Ty,
|
||||
fields: &[Field],
|
||||
item_attrs: &attr::Item,
|
||||
) -> Tokens {
|
||||
let method = match context {
|
||||
StructVariant::ExternallyTagged{..} => {
|
||||
quote!(_serde::ser::SerializeStructVariant::serialize_field)
|
||||
}
|
||||
StructVariant::InternallyTagged{..} | StructVariant::Untagged => {
|
||||
quote!(_serde::ser::SerializeStruct::serialize_field)
|
||||
}
|
||||
};
|
||||
|
||||
let serialize_fields = serialize_struct_visitor(
|
||||
ty.clone(),
|
||||
fields,
|
||||
generics,
|
||||
true,
|
||||
quote!(_serde::ser::SerializeStructVariant::serialize_field),
|
||||
method,
|
||||
);
|
||||
|
||||
let item_name = item_attrs.name().serialize_name();
|
||||
@@ -426,16 +634,47 @@ fn serialize_struct_variant(
|
||||
})
|
||||
.fold(quote!(0), |sum, expr| quote!(#sum + #expr));
|
||||
|
||||
quote! {
|
||||
let #let_mut __serde_state = try!(_serde::Serializer::serialize_struct_variant(
|
||||
_serializer,
|
||||
#item_name,
|
||||
#variant_index,
|
||||
#variant_name,
|
||||
#len,
|
||||
));
|
||||
#(#serialize_fields)*
|
||||
_serde::ser::SerializeStructVariant::end(__serde_state)
|
||||
match context {
|
||||
StructVariant::ExternallyTagged { variant_index, variant_name } => {
|
||||
quote! {
|
||||
let #let_mut __serde_state = try!(_serde::Serializer::serialize_struct_variant(
|
||||
_serializer,
|
||||
#item_name,
|
||||
#variant_index,
|
||||
#variant_name,
|
||||
#len,
|
||||
));
|
||||
#(#serialize_fields)*
|
||||
_serde::ser::SerializeStructVariant::end(__serde_state)
|
||||
}
|
||||
}
|
||||
StructVariant::InternallyTagged { tag, variant_name } => {
|
||||
quote! {
|
||||
let mut __serde_state = try!(_serde::Serializer::serialize_struct(
|
||||
_serializer,
|
||||
#item_name,
|
||||
#len + 1,
|
||||
));
|
||||
try!(_serde::ser::SerializeStruct::serialize_field(
|
||||
&mut __serde_state,
|
||||
#tag,
|
||||
#variant_name,
|
||||
));
|
||||
#(#serialize_fields)*
|
||||
_serde::ser::SerializeStruct::end(__serde_state)
|
||||
}
|
||||
}
|
||||
StructVariant::Untagged => {
|
||||
quote! {
|
||||
let #let_mut __serde_state = try!(_serde::Serializer::serialize_struct(
|
||||
_serializer,
|
||||
#item_name,
|
||||
#len,
|
||||
));
|
||||
#(#serialize_fields)*
|
||||
_serde::ser::SerializeStruct::end(__serde_state)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user