Allowed serde(tag="...") on structs
Added test test_internally_tagged_struct Renamed EnumTag to TagType as it now also used for structs Modified serialize_struct_as_struct
This commit is contained in:
parent
5c24f0f0f3
commit
414fd694c0
@ -1145,15 +1145,15 @@ fn deserialize_enum(
|
|||||||
cattrs: &attr::Container,
|
cattrs: &attr::Container,
|
||||||
) -> Fragment {
|
) -> Fragment {
|
||||||
match *cattrs.tag() {
|
match *cattrs.tag() {
|
||||||
attr::EnumTag::External => deserialize_externally_tagged_enum(params, variants, cattrs),
|
attr::TagType::External => deserialize_externally_tagged_enum(params, variants, cattrs),
|
||||||
attr::EnumTag::Internal { ref tag } => {
|
attr::TagType::Internal { ref tag } => {
|
||||||
deserialize_internally_tagged_enum(params, variants, cattrs, tag)
|
deserialize_internally_tagged_enum(params, variants, cattrs, tag)
|
||||||
}
|
}
|
||||||
attr::EnumTag::Adjacent {
|
attr::TagType::Adjacent {
|
||||||
ref tag,
|
ref tag,
|
||||||
ref content,
|
ref content,
|
||||||
} => deserialize_adjacently_tagged_enum(params, variants, cattrs, tag, content),
|
} => deserialize_adjacently_tagged_enum(params, variants, cattrs, tag, content),
|
||||||
attr::EnumTag::None => deserialize_untagged_enum(params, variants, cattrs),
|
attr::TagType::None => deserialize_untagged_enum(params, variants, cattrs),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,7 +120,7 @@ pub struct Container {
|
|||||||
rename_all: RenameRule,
|
rename_all: RenameRule,
|
||||||
ser_bound: Option<Vec<syn::WherePredicate>>,
|
ser_bound: Option<Vec<syn::WherePredicate>>,
|
||||||
de_bound: Option<Vec<syn::WherePredicate>>,
|
de_bound: Option<Vec<syn::WherePredicate>>,
|
||||||
tag: EnumTag,
|
tag: TagType,
|
||||||
type_from: Option<syn::Type>,
|
type_from: Option<syn::Type>,
|
||||||
type_into: Option<syn::Type>,
|
type_into: Option<syn::Type>,
|
||||||
remote: Option<syn::Path>,
|
remote: Option<syn::Path>,
|
||||||
@ -129,7 +129,7 @@ pub struct Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Styles of representing an enum.
|
/// Styles of representing an enum.
|
||||||
pub enum EnumTag {
|
pub enum TagType {
|
||||||
/// The default.
|
/// The default.
|
||||||
///
|
///
|
||||||
/// ```json
|
/// ```json
|
||||||
@ -358,17 +358,10 @@ impl Container {
|
|||||||
Meta(NameValue(ref m)) if m.ident == "tag" => {
|
Meta(NameValue(ref m)) if m.ident == "tag" => {
|
||||||
if let Ok(s) = get_lit_str(cx, &m.ident, &m.ident, &m.lit) {
|
if let Ok(s) = get_lit_str(cx, &m.ident, &m.ident, &m.lit) {
|
||||||
match item.data {
|
match item.data {
|
||||||
syn::Data::Enum(_) => {
|
syn::Data::Enum(_) |
|
||||||
|
syn::Data::Struct(_, ..) => {
|
||||||
internal_tag.set(&m.ident, s.value());
|
internal_tag.set(&m.ident, s.value());
|
||||||
}
|
}
|
||||||
syn::Data::Struct(syn::DataStruct {
|
|
||||||
ref struct_token, ..
|
|
||||||
}) => {
|
|
||||||
cx.error_spanned_by(
|
|
||||||
struct_token,
|
|
||||||
"#[serde(tag = \"...\")] can only be used on enums",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
syn::Data::Union(syn::DataUnion {
|
syn::Data::Union(syn::DataUnion {
|
||||||
ref union_token, ..
|
ref union_token, ..
|
||||||
}) => {
|
}) => {
|
||||||
@ -505,7 +498,7 @@ impl Container {
|
|||||||
self.de_bound.as_ref().map(|vec| &vec[..])
|
self.de_bound.as_ref().map(|vec| &vec[..])
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn tag(&self) -> &EnumTag {
|
pub fn tag(&self) -> &TagType {
|
||||||
&self.tag
|
&self.tag
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -540,14 +533,14 @@ fn decide_tag(
|
|||||||
untagged: BoolAttr,
|
untagged: BoolAttr,
|
||||||
internal_tag: Attr<String>,
|
internal_tag: Attr<String>,
|
||||||
content: Attr<String>,
|
content: Attr<String>,
|
||||||
) -> EnumTag {
|
) -> TagType {
|
||||||
match (
|
match (
|
||||||
untagged.0.get_with_tokens(),
|
untagged.0.get_with_tokens(),
|
||||||
internal_tag.get_with_tokens(),
|
internal_tag.get_with_tokens(),
|
||||||
content.get_with_tokens(),
|
content.get_with_tokens(),
|
||||||
) {
|
) {
|
||||||
(None, None, None) => EnumTag::External,
|
(None, None, None) => TagType::External,
|
||||||
(Some(_), None, None) => EnumTag::None,
|
(Some(_), None, None) => TagType::None,
|
||||||
(None, Some((_, tag)), None) => {
|
(None, Some((_, tag)), None) => {
|
||||||
// Check that there are no tuple variants.
|
// Check that there are no tuple variants.
|
||||||
if let syn::Data::Enum(ref data) = item.data {
|
if let syn::Data::Enum(ref data) = item.data {
|
||||||
@ -567,7 +560,7 @@ fn decide_tag(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EnumTag::Internal { tag: tag }
|
TagType::Internal { tag: tag }
|
||||||
}
|
}
|
||||||
(Some((untagged_tokens, _)), Some((tag_tokens, _)), None) => {
|
(Some((untagged_tokens, _)), Some((tag_tokens, _)), None) => {
|
||||||
cx.error_spanned_by(
|
cx.error_spanned_by(
|
||||||
@ -578,14 +571,14 @@ fn decide_tag(
|
|||||||
tag_tokens,
|
tag_tokens,
|
||||||
"enum cannot be both untagged and internally tagged",
|
"enum cannot be both untagged and internally tagged",
|
||||||
);
|
);
|
||||||
EnumTag::External // doesn't matter, will error
|
TagType::External // doesn't matter, will error
|
||||||
}
|
}
|
||||||
(None, None, Some((content_tokens, _))) => {
|
(None, None, Some((content_tokens, _))) => {
|
||||||
cx.error_spanned_by(
|
cx.error_spanned_by(
|
||||||
content_tokens,
|
content_tokens,
|
||||||
"#[serde(tag = \"...\", content = \"...\")] must be used together",
|
"#[serde(tag = \"...\", content = \"...\")] must be used together",
|
||||||
);
|
);
|
||||||
EnumTag::External
|
TagType::External
|
||||||
}
|
}
|
||||||
(Some((untagged_tokens, _)), None, Some((content_tokens, _))) => {
|
(Some((untagged_tokens, _)), None, Some((content_tokens, _))) => {
|
||||||
cx.error_spanned_by(
|
cx.error_spanned_by(
|
||||||
@ -596,9 +589,9 @@ fn decide_tag(
|
|||||||
content_tokens,
|
content_tokens,
|
||||||
"untagged enum cannot have #[serde(content = \"...\")]",
|
"untagged enum cannot have #[serde(content = \"...\")]",
|
||||||
);
|
);
|
||||||
EnumTag::External
|
TagType::External
|
||||||
}
|
}
|
||||||
(None, Some((_, tag)), Some((_, content))) => EnumTag::Adjacent {
|
(None, Some((_, tag)), Some((_, content))) => TagType::Adjacent {
|
||||||
tag: tag,
|
tag: tag,
|
||||||
content: content,
|
content: content,
|
||||||
},
|
},
|
||||||
@ -615,7 +608,7 @@ fn decide_tag(
|
|||||||
content_tokens,
|
content_tokens,
|
||||||
"untagged enum cannot have #[serde(tag = \"...\", content = \"...\")]",
|
"untagged enum cannot have #[serde(tag = \"...\", content = \"...\")]",
|
||||||
);
|
);
|
||||||
EnumTag::External
|
TagType::External
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use internals::ast::{Container, Data, Field, Style};
|
use internals::ast::{Container, Data, Field, Style};
|
||||||
use internals::attr::{EnumTag, Identifier};
|
use internals::attr::{TagType, Identifier};
|
||||||
use internals::{Ctxt, Derive};
|
use internals::{Ctxt, Derive};
|
||||||
use syn::{Member, Type};
|
use syn::{Member, Type};
|
||||||
|
|
||||||
@ -127,7 +127,7 @@ fn check_identifier(cx: &Ctxt, cont: &Container) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Variant with `other` attribute cannot appear in untagged enum
|
// Variant with `other` attribute cannot appear in untagged enum
|
||||||
(_, Identifier::No, true, &EnumTag::None) => {
|
(_, Identifier::No, true, &TagType::None) => {
|
||||||
cx.error_spanned_by(
|
cx.error_spanned_by(
|
||||||
variant.original,
|
variant.original,
|
||||||
"#[serde(other)] cannot appear on untagged enum",
|
"#[serde(other)] cannot appear on untagged enum",
|
||||||
@ -276,8 +276,8 @@ fn check_internal_tag_field_name_conflict(cx: &Ctxt, cont: &Container) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let tag = match *cont.attrs.tag() {
|
let tag = match *cont.attrs.tag() {
|
||||||
EnumTag::Internal { ref tag } => tag.as_str(),
|
TagType::Internal { ref tag } => tag.as_str(),
|
||||||
EnumTag::External | EnumTag::Adjacent { .. } | EnumTag::None => return,
|
TagType::External | TagType::Adjacent { .. } | TagType::None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
let diagnose_conflict = || {
|
let diagnose_conflict = || {
|
||||||
@ -312,11 +312,11 @@ fn check_internal_tag_field_name_conflict(cx: &Ctxt, cont: &Container) {
|
|||||||
/// contents tag must differ, for the same reason.
|
/// contents tag must differ, for the same reason.
|
||||||
fn check_adjacent_tag_conflict(cx: &Ctxt, cont: &Container) {
|
fn check_adjacent_tag_conflict(cx: &Ctxt, cont: &Container) {
|
||||||
let (type_tag, content_tag) = match *cont.attrs.tag() {
|
let (type_tag, content_tag) = match *cont.attrs.tag() {
|
||||||
EnumTag::Adjacent {
|
TagType::Adjacent {
|
||||||
ref tag,
|
ref tag,
|
||||||
ref content,
|
ref content,
|
||||||
} => (tag, content),
|
} => (tag, content),
|
||||||
EnumTag::Internal { .. } | EnumTag::External | EnumTag::None => return,
|
TagType::Internal { .. } | TagType::External | TagType::None => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
if type_tag == content_tag {
|
if type_tag == content_tag {
|
||||||
|
@ -311,11 +311,23 @@ fn serialize_struct_as_struct(
|
|||||||
fields: &[Field],
|
fields: &[Field],
|
||||||
cattrs: &attr::Container,
|
cattrs: &attr::Container,
|
||||||
) -> Fragment {
|
) -> Fragment {
|
||||||
let serialize_fields =
|
let mut serialize_fields =
|
||||||
serialize_struct_visitor(fields, params, false, &StructTrait::SerializeStruct);
|
serialize_struct_visitor(fields, params, false, &StructTrait::SerializeStruct);
|
||||||
|
|
||||||
let type_name = cattrs.name().serialize_name();
|
let type_name = cattrs.name().serialize_name();
|
||||||
|
|
||||||
|
let additional_field_count: usize = match cattrs.tag() {
|
||||||
|
attr::TagType::Internal{ref tag} => {
|
||||||
|
let func = StructTrait::SerializeStruct.serialize_field(Span::call_site());
|
||||||
|
serialize_fields.insert(0, quote! {
|
||||||
|
try!(#func(&mut __serde_state, #tag, #type_name));
|
||||||
|
});
|
||||||
|
|
||||||
|
1
|
||||||
|
}
|
||||||
|
_ => 0
|
||||||
|
};
|
||||||
|
|
||||||
let mut serialized_fields = fields
|
let mut serialized_fields = fields
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|&field| !field.attrs.skip_serializing())
|
.filter(|&field| !field.attrs.skip_serializing())
|
||||||
@ -331,7 +343,7 @@ fn serialize_struct_as_struct(
|
|||||||
quote!(if #path(#field_expr) { 0 } else { 1 })
|
quote!(if #path(#field_expr) { 0 } else { 1 })
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.fold(quote!(0), |sum, expr| quote!(#sum + #expr));
|
.fold(quote!(#additional_field_count), |sum, expr| quote!(#sum + #expr));
|
||||||
|
|
||||||
quote_block! {
|
quote_block! {
|
||||||
let #let_mut __serde_state = try!(_serde::Serializer::serialize_struct(__serializer, #type_name, #len));
|
let #let_mut __serde_state = try!(_serde::Serializer::serialize_struct(__serializer, #type_name, #len));
|
||||||
@ -452,17 +464,17 @@ fn serialize_variant(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let body = Match(match *cattrs.tag() {
|
let body = Match(match *cattrs.tag() {
|
||||||
attr::EnumTag::External => {
|
attr::TagType::External => {
|
||||||
serialize_externally_tagged_variant(params, variant, variant_index, cattrs)
|
serialize_externally_tagged_variant(params, variant, variant_index, cattrs)
|
||||||
}
|
}
|
||||||
attr::EnumTag::Internal { ref tag } => {
|
attr::TagType::Internal { ref tag } => {
|
||||||
serialize_internally_tagged_variant(params, variant, cattrs, tag)
|
serialize_internally_tagged_variant(params, variant, cattrs, tag)
|
||||||
}
|
}
|
||||||
attr::EnumTag::Adjacent {
|
attr::TagType::Adjacent {
|
||||||
ref tag,
|
ref tag,
|
||||||
ref content,
|
ref content,
|
||||||
} => serialize_adjacently_tagged_variant(params, variant, cattrs, tag, content),
|
} => serialize_adjacently_tagged_variant(params, variant, cattrs, tag, content),
|
||||||
attr::EnumTag::None => serialize_untagged_variant(params, variant, cattrs),
|
attr::TagType::None => serialize_untagged_variant(params, variant, cattrs),
|
||||||
});
|
});
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
|
@ -1376,6 +1376,44 @@ fn test_enum_in_internally_tagged_enum() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_internally_tagged_struct() {
|
||||||
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
#[serde(tag="type")]
|
||||||
|
pub struct Struct {
|
||||||
|
a: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_tokens(
|
||||||
|
&Struct{ a: 1 },
|
||||||
|
&[
|
||||||
|
Token::Struct {
|
||||||
|
name: "Struct",
|
||||||
|
len: 2,
|
||||||
|
},
|
||||||
|
Token::Str("type"),
|
||||||
|
Token::Str("Struct"),
|
||||||
|
Token::Str("a"),
|
||||||
|
Token::U8(1),
|
||||||
|
Token::StructEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_de_tokens(
|
||||||
|
&Struct { a: 1 },
|
||||||
|
&[
|
||||||
|
Token::Struct {
|
||||||
|
name: "Struct",
|
||||||
|
len: 1,
|
||||||
|
},
|
||||||
|
Token::Str("a"),
|
||||||
|
Token::U8(1),
|
||||||
|
Token::StructEnd,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_enum_in_untagged_enum() {
|
fn test_enum_in_untagged_enum() {
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user