Merge pull request #1270 from serde-rs/transparent
Transparent attribute to specify that representation is the same as its only field
This commit is contained in:
commit
922fadf7e3
@ -15,7 +15,7 @@ use syn::{self, Ident, Index, Member};
|
|||||||
use bound;
|
use bound;
|
||||||
use fragment::{Expr, Fragment, Match, Stmts};
|
use fragment::{Expr, Fragment, Match, Stmts};
|
||||||
use internals::ast::{Container, Data, Field, Style, Variant};
|
use internals::ast::{Container, Data, Field, Style, Variant};
|
||||||
use internals::{attr, Ctxt};
|
use internals::{attr, Ctxt, Derive};
|
||||||
use pretend;
|
use pretend;
|
||||||
use try;
|
use try;
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ use std::collections::BTreeSet;
|
|||||||
|
|
||||||
pub fn expand_derive_deserialize(input: &syn::DeriveInput) -> Result<TokenStream, String> {
|
pub fn expand_derive_deserialize(input: &syn::DeriveInput) -> Result<TokenStream, String> {
|
||||||
let ctxt = Ctxt::new();
|
let ctxt = Ctxt::new();
|
||||||
let cont = Container::from_ast(&ctxt, input);
|
let cont = Container::from_ast(&ctxt, input, Derive::Deserialize);
|
||||||
precondition(&ctxt, &cont);
|
precondition(&ctxt, &cont);
|
||||||
try!(ctxt.check());
|
try!(ctxt.check());
|
||||||
|
|
||||||
@ -269,7 +269,9 @@ fn borrowed_lifetimes(cont: &Container) -> BorrowedLifetimes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_body(cont: &Container, params: &Parameters) -> Fragment {
|
fn deserialize_body(cont: &Container, params: &Parameters) -> Fragment {
|
||||||
if let Some(type_from) = cont.attrs.type_from() {
|
if cont.attrs.transparent() {
|
||||||
|
deserialize_transparent(cont, params)
|
||||||
|
} else if let Some(type_from) = cont.attrs.type_from() {
|
||||||
deserialize_from(type_from)
|
deserialize_from(type_from)
|
||||||
} else if let attr::Identifier::No = cont.attrs.identifier() {
|
} else if let attr::Identifier::No = cont.attrs.identifier() {
|
||||||
match cont.data {
|
match cont.data {
|
||||||
@ -298,7 +300,9 @@ fn deserialize_in_place_body(cont: &Container, params: &Parameters) -> Option<St
|
|||||||
// deserialize_in_place for remote derives.
|
// deserialize_in_place for remote derives.
|
||||||
assert!(!params.has_getter);
|
assert!(!params.has_getter);
|
||||||
|
|
||||||
if cont.attrs.type_from().is_some() || cont.attrs.identifier().is_some()
|
if cont.attrs.transparent()
|
||||||
|
|| cont.attrs.type_from().is_some()
|
||||||
|
|| cont.attrs.identifier().is_some()
|
||||||
|| cont
|
|| cont
|
||||||
.data
|
.data
|
||||||
.all_fields()
|
.all_fields()
|
||||||
@ -344,6 +348,41 @@ fn deserialize_in_place_body(_cont: &Container, _params: &Parameters) -> Option<
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn deserialize_transparent(cont: &Container, params: &Parameters) -> Fragment {
|
||||||
|
let fields = match cont.data {
|
||||||
|
Data::Struct(_, ref fields) => fields,
|
||||||
|
Data::Enum(_) => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let this = ¶ms.this;
|
||||||
|
let transparent_field = fields.iter().find(|f| f.attrs.transparent()).unwrap();
|
||||||
|
|
||||||
|
let path = match transparent_field.attrs.deserialize_with() {
|
||||||
|
Some(path) => quote!(#path),
|
||||||
|
None => quote!(_serde::Deserialize::deserialize),
|
||||||
|
};
|
||||||
|
|
||||||
|
let assign = fields.iter().map(|field| {
|
||||||
|
let member = &field.member;
|
||||||
|
if field as *const Field == transparent_field as *const Field {
|
||||||
|
quote!(#member: __transparent)
|
||||||
|
} else {
|
||||||
|
let value = match *field.attrs.default() {
|
||||||
|
attr::Default::Default => quote!(_serde::export::Default::default()),
|
||||||
|
attr::Default::Path(ref path) => quote!(#path()),
|
||||||
|
attr::Default::None => quote!(_serde::export::PhantomData),
|
||||||
|
};
|
||||||
|
quote!(#member: #value)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
quote_block! {
|
||||||
|
_serde::export::Result::map(
|
||||||
|
#path(__deserializer),
|
||||||
|
|__transparent| #this { #(#assign),* })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn deserialize_from(type_from: &syn::Type) -> Fragment {
|
fn deserialize_from(type_from: &syn::Type) -> Fragment {
|
||||||
quote_block! {
|
quote_block! {
|
||||||
_serde::export::Result::map(
|
_serde::export::Result::map(
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
use internals::attr;
|
use internals::attr;
|
||||||
use internals::check;
|
use internals::check;
|
||||||
use internals::Ctxt;
|
use internals::{Ctxt, Derive};
|
||||||
use syn;
|
use syn;
|
||||||
use syn::punctuated::Punctuated;
|
use syn::punctuated::Punctuated;
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ pub enum Style {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Container<'a> {
|
impl<'a> Container<'a> {
|
||||||
pub fn from_ast(cx: &Ctxt, item: &'a syn::DeriveInput) -> Container<'a> {
|
pub fn from_ast(cx: &Ctxt, item: &'a syn::DeriveInput, derive: Derive) -> Container<'a> {
|
||||||
let mut attrs = attr::Container::from_ast(cx, item);
|
let mut attrs = attr::Container::from_ast(cx, item);
|
||||||
|
|
||||||
let mut data = match item.data {
|
let mut data = match item.data {
|
||||||
@ -86,13 +86,13 @@ impl<'a> Container<'a> {
|
|||||||
attrs.mark_has_flatten();
|
attrs.mark_has_flatten();
|
||||||
}
|
}
|
||||||
|
|
||||||
let item = Container {
|
let mut item = Container {
|
||||||
ident: item.ident.clone(),
|
ident: item.ident.clone(),
|
||||||
attrs: attrs,
|
attrs: attrs,
|
||||||
data: data,
|
data: data,
|
||||||
generics: &item.generics,
|
generics: &item.generics,
|
||||||
};
|
};
|
||||||
check::check(cx, &item);
|
check::check(cx, &mut item, derive);
|
||||||
item
|
item
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -105,6 +105,7 @@ impl Name {
|
|||||||
/// Represents container (e.g. struct) attribute information
|
/// Represents container (e.g. struct) attribute information
|
||||||
pub struct Container {
|
pub struct Container {
|
||||||
name: Name,
|
name: Name,
|
||||||
|
transparent: bool,
|
||||||
deny_unknown_fields: bool,
|
deny_unknown_fields: bool,
|
||||||
default: Default,
|
default: Default,
|
||||||
rename_all: RenameRule,
|
rename_all: RenameRule,
|
||||||
@ -181,6 +182,7 @@ impl Container {
|
|||||||
pub fn from_ast(cx: &Ctxt, item: &syn::DeriveInput) -> Self {
|
pub fn from_ast(cx: &Ctxt, item: &syn::DeriveInput) -> Self {
|
||||||
let mut ser_name = Attr::none(cx, "rename");
|
let mut ser_name = Attr::none(cx, "rename");
|
||||||
let mut de_name = Attr::none(cx, "rename");
|
let mut de_name = Attr::none(cx, "rename");
|
||||||
|
let mut transparent = BoolAttr::none(cx, "transparent");
|
||||||
let mut deny_unknown_fields = BoolAttr::none(cx, "deny_unknown_fields");
|
let mut deny_unknown_fields = BoolAttr::none(cx, "deny_unknown_fields");
|
||||||
let mut default = Attr::none(cx, "default");
|
let mut default = Attr::none(cx, "default");
|
||||||
let mut rename_all = Attr::none(cx, "rename_all");
|
let mut rename_all = Attr::none(cx, "rename_all");
|
||||||
@ -228,6 +230,11 @@ impl Container {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parse `#[serde(transparent)]`
|
||||||
|
Meta(Word(ref word)) if word == "transparent" => {
|
||||||
|
transparent.set_true();
|
||||||
|
}
|
||||||
|
|
||||||
// Parse `#[serde(deny_unknown_fields)]`
|
// Parse `#[serde(deny_unknown_fields)]`
|
||||||
Meta(Word(ref word)) if word == "deny_unknown_fields" => {
|
Meta(Word(ref word)) if word == "deny_unknown_fields" => {
|
||||||
deny_unknown_fields.set_true();
|
deny_unknown_fields.set_true();
|
||||||
@ -376,6 +383,7 @@ impl Container {
|
|||||||
serialize: ser_name.get().unwrap_or_else(|| item.ident.to_string()),
|
serialize: ser_name.get().unwrap_or_else(|| item.ident.to_string()),
|
||||||
deserialize: de_name.get().unwrap_or_else(|| item.ident.to_string()),
|
deserialize: de_name.get().unwrap_or_else(|| item.ident.to_string()),
|
||||||
},
|
},
|
||||||
|
transparent: transparent.get(),
|
||||||
deny_unknown_fields: deny_unknown_fields.get(),
|
deny_unknown_fields: deny_unknown_fields.get(),
|
||||||
default: default.get().unwrap_or(Default::None),
|
default: default.get().unwrap_or(Default::None),
|
||||||
rename_all: rename_all.get().unwrap_or(RenameRule::None),
|
rename_all: rename_all.get().unwrap_or(RenameRule::None),
|
||||||
@ -398,6 +406,10 @@ impl Container {
|
|||||||
&self.rename_all
|
&self.rename_all
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn transparent(&self) -> bool {
|
||||||
|
self.transparent
|
||||||
|
}
|
||||||
|
|
||||||
pub fn deny_unknown_fields(&self) -> bool {
|
pub fn deny_unknown_fields(&self) -> bool {
|
||||||
self.deny_unknown_fields
|
self.deny_unknown_fields
|
||||||
}
|
}
|
||||||
@ -764,6 +776,7 @@ pub struct Field {
|
|||||||
borrowed_lifetimes: BTreeSet<syn::Lifetime>,
|
borrowed_lifetimes: BTreeSet<syn::Lifetime>,
|
||||||
getter: Option<syn::ExprPath>,
|
getter: Option<syn::ExprPath>,
|
||||||
flatten: bool,
|
flatten: bool,
|
||||||
|
transparent: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents the default to use for a field when deserializing.
|
/// Represents the default to use for a field when deserializing.
|
||||||
@ -777,7 +790,6 @@ pub enum Default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Default {
|
impl Default {
|
||||||
#[cfg(feature = "deserialize_in_place")]
|
|
||||||
pub fn is_none(&self) -> bool {
|
pub fn is_none(&self) -> bool {
|
||||||
match *self {
|
match *self {
|
||||||
Default::None => true,
|
Default::None => true,
|
||||||
@ -1066,6 +1078,7 @@ impl Field {
|
|||||||
borrowed_lifetimes: borrowed_lifetimes,
|
borrowed_lifetimes: borrowed_lifetimes,
|
||||||
getter: getter.get(),
|
getter: getter.get(),
|
||||||
flatten: flatten.get(),
|
flatten: flatten.get(),
|
||||||
|
transparent: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1125,6 +1138,14 @@ impl Field {
|
|||||||
pub fn flatten(&self) -> bool {
|
pub fn flatten(&self) -> bool {
|
||||||
self.flatten
|
self.flatten
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn transparent(&self) -> bool {
|
||||||
|
self.transparent
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mark_transparent(&mut self) {
|
||||||
|
self.transparent = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type SerAndDe<T> = (Option<T>, Option<T>);
|
type SerAndDe<T> = (Option<T>, Option<T>);
|
||||||
|
@ -8,18 +8,19 @@
|
|||||||
|
|
||||||
use internals::ast::{Container, Data, Field, Style};
|
use internals::ast::{Container, Data, Field, Style};
|
||||||
use internals::attr::{EnumTag, Identifier};
|
use internals::attr::{EnumTag, Identifier};
|
||||||
use internals::Ctxt;
|
use internals::{Ctxt, Derive};
|
||||||
use syn::Member;
|
use syn::{Member, Type};
|
||||||
|
|
||||||
/// Cross-cutting checks that require looking at more than a single attrs
|
/// Cross-cutting checks that require looking at more than a single attrs
|
||||||
/// object. Simpler checks should happen when parsing and building the attrs.
|
/// object. Simpler checks should happen when parsing and building the attrs.
|
||||||
pub fn check(cx: &Ctxt, cont: &Container) {
|
pub fn check(cx: &Ctxt, cont: &mut Container, derive: Derive) {
|
||||||
check_getter(cx, cont);
|
check_getter(cx, cont);
|
||||||
check_flatten(cx, cont);
|
check_flatten(cx, cont);
|
||||||
check_identifier(cx, cont);
|
check_identifier(cx, cont);
|
||||||
check_variant_skip_attrs(cx, cont);
|
check_variant_skip_attrs(cx, cont);
|
||||||
check_internal_tag_field_name_conflict(cx, cont);
|
check_internal_tag_field_name_conflict(cx, cont);
|
||||||
check_adjacent_tag_conflict(cx, cont);
|
check_adjacent_tag_conflict(cx, cont);
|
||||||
|
check_transparent(cx, cont, derive);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Getters are only allowed inside structs (not enums) with the `remote`
|
/// Getters are only allowed inside structs (not enums) with the `remote`
|
||||||
@ -278,9 +279,75 @@ fn check_adjacent_tag_conflict(cx: &Ctxt, cont: &Container) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Enums and unit structs cannot be transparent.
|
||||||
|
fn check_transparent(cx: &Ctxt, cont: &mut Container, derive: Derive) {
|
||||||
|
if !cont.attrs.transparent() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if cont.attrs.type_from().is_some() {
|
||||||
|
cx.error("#[serde(transparent)] is not allowed with #[serde(from = \"...\")]");
|
||||||
|
}
|
||||||
|
|
||||||
|
if cont.attrs.type_into().is_some() {
|
||||||
|
cx.error("#[serde(transparent)] is not allowed with #[serde(into = \"...\")]");
|
||||||
|
}
|
||||||
|
|
||||||
|
let fields = match cont.data {
|
||||||
|
Data::Enum(_) => {
|
||||||
|
cx.error("#[serde(transparent)] is not allowed on an enum");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Data::Struct(Style::Unit, _) => {
|
||||||
|
cx.error("#[serde(transparent)] is not allowed on a unit struct");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Data::Struct(_, ref mut fields) => fields,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut transparent_field = None;
|
||||||
|
|
||||||
|
for field in fields {
|
||||||
|
if allow_transparent(field, derive) {
|
||||||
|
if transparent_field.is_some() {
|
||||||
|
cx.error("#[serde(transparent)] requires struct to have at most one transparent field");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
transparent_field = Some(field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match transparent_field {
|
||||||
|
Some(transparent_field) => transparent_field.attrs.mark_transparent(),
|
||||||
|
None => match derive {
|
||||||
|
Derive::Serialize => {
|
||||||
|
cx.error("#[serde(transparent)] requires at least one field that is not skipped");
|
||||||
|
}
|
||||||
|
Derive::Deserialize => {
|
||||||
|
cx.error("#[serde(transparent)] requires at least one field that is neither skipped nor has a default");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn member_message(member: &Member) -> String {
|
fn member_message(member: &Member) -> String {
|
||||||
match *member {
|
match *member {
|
||||||
Member::Named(ref ident) => format!("`{}`", ident),
|
Member::Named(ref ident) => format!("`{}`", ident),
|
||||||
Member::Unnamed(ref i) => i.index.to_string(),
|
Member::Unnamed(ref i) => i.index.to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn allow_transparent(field: &Field, derive: Derive) -> bool {
|
||||||
|
if let Type::Path(ref ty) = *field.ty {
|
||||||
|
if let Some(seg) = ty.path.segments.last() {
|
||||||
|
if seg.into_value().ident == "PhantomData" {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match derive {
|
||||||
|
Derive::Serialize => !field.attrs.skip_serializing(),
|
||||||
|
Derive::Deserialize => !field.attrs.skip_deserializing() && field.attrs.default().is_none(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -14,3 +14,9 @@ pub use self::ctxt::Ctxt;
|
|||||||
|
|
||||||
mod case;
|
mod case;
|
||||||
mod check;
|
mod check;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum Derive {
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
}
|
||||||
|
@ -13,13 +13,13 @@ use syn::{self, Ident, Index, Member};
|
|||||||
use bound;
|
use bound;
|
||||||
use fragment::{Fragment, Match, Stmts};
|
use fragment::{Fragment, Match, Stmts};
|
||||||
use internals::ast::{Container, Data, Field, Style, Variant};
|
use internals::ast::{Container, Data, Field, Style, Variant};
|
||||||
use internals::{attr, Ctxt};
|
use internals::{attr, Ctxt, Derive};
|
||||||
use pretend;
|
use pretend;
|
||||||
use try;
|
use try;
|
||||||
|
|
||||||
pub fn expand_derive_serialize(input: &syn::DeriveInput) -> Result<TokenStream, String> {
|
pub fn expand_derive_serialize(input: &syn::DeriveInput) -> Result<TokenStream, String> {
|
||||||
let ctxt = Ctxt::new();
|
let ctxt = Ctxt::new();
|
||||||
let cont = Container::from_ast(&ctxt, input);
|
let cont = Container::from_ast(&ctxt, input, Derive::Serialize);
|
||||||
precondition(&ctxt, &cont);
|
precondition(&ctxt, &cont);
|
||||||
try!(ctxt.check());
|
try!(ctxt.check());
|
||||||
|
|
||||||
@ -166,7 +166,9 @@ fn needs_serialize_bound(field: &attr::Field, variant: Option<&attr::Variant>) -
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_body(cont: &Container, params: &Parameters) -> Fragment {
|
fn serialize_body(cont: &Container, params: &Parameters) -> Fragment {
|
||||||
if let Some(type_into) = cont.attrs.type_into() {
|
if cont.attrs.transparent() {
|
||||||
|
serialize_transparent(cont, params)
|
||||||
|
} else if let Some(type_into) = cont.attrs.type_into() {
|
||||||
serialize_into(params, type_into)
|
serialize_into(params, type_into)
|
||||||
} else {
|
} else {
|
||||||
match cont.data {
|
match cont.data {
|
||||||
@ -185,6 +187,26 @@ fn serialize_body(cont: &Container, params: &Parameters) -> Fragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn serialize_transparent(cont: &Container, params: &Parameters) -> Fragment {
|
||||||
|
let fields = match cont.data {
|
||||||
|
Data::Struct(_, ref fields) => fields,
|
||||||
|
Data::Enum(_) => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let self_var = ¶ms.self_var;
|
||||||
|
let transparent_field = fields.iter().find(|f| f.attrs.transparent()).unwrap();
|
||||||
|
let member = &transparent_field.member;
|
||||||
|
|
||||||
|
let path = match transparent_field.attrs.serialize_with() {
|
||||||
|
Some(path) => quote!(#path),
|
||||||
|
None => quote!(_serde::Serialize::serialize),
|
||||||
|
};
|
||||||
|
|
||||||
|
quote_block! {
|
||||||
|
#path(&#self_var.#member, __serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn serialize_into(params: &Parameters, type_into: &syn::Type) -> Fragment {
|
fn serialize_into(params: &Parameters, type_into: &syn::Type) -> Fragment {
|
||||||
let self_var = ¶ms.self_var;
|
let self_var = ¶ms.self_var;
|
||||||
quote_block! {
|
quote_block! {
|
||||||
|
20
test_suite/tests/compile-fail/transparent/at_most_one.rs
Normal file
20
test_suite/tests/compile-fail/transparent/at_most_one.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// Copyright 2018 Serde Developers
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate serde_derive;
|
||||||
|
|
||||||
|
#[derive(Serialize)] //~ ERROR: proc-macro derive panicked
|
||||||
|
#[serde(transparent)]
|
||||||
|
struct S {
|
||||||
|
//~^^^ HELP: #[serde(transparent)] requires struct to have at most one transparent field
|
||||||
|
a: u8,
|
||||||
|
b: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
22
test_suite/tests/compile-fail/transparent/de_at_least_one.rs
Normal file
22
test_suite/tests/compile-fail/transparent/de_at_least_one.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// Copyright 2018 Serde Developers
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate serde_derive;
|
||||||
|
|
||||||
|
#[derive(Deserialize)] //~ ERROR: proc-macro derive panicked
|
||||||
|
#[serde(transparent)]
|
||||||
|
struct S {
|
||||||
|
//~^^^ HELP: #[serde(transparent)] requires at least one field that is neither skipped nor has a default
|
||||||
|
#[serde(skip)]
|
||||||
|
a: u8,
|
||||||
|
#[serde(default)]
|
||||||
|
b: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
@ -0,0 +1,20 @@
|
|||||||
|
// Copyright 2018 Serde Developers
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
// option. This file may not be copied, modified, or distributed
|
||||||
|
// except according to those terms.
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate serde_derive;
|
||||||
|
|
||||||
|
#[derive(Serialize)] //~ ERROR: proc-macro derive panicked
|
||||||
|
#[serde(transparent)]
|
||||||
|
struct S {
|
||||||
|
//~^^^ HELP: #[serde(transparent)] requires at least one field that is not skipped
|
||||||
|
#[serde(skip)]
|
||||||
|
a: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
@ -15,6 +15,7 @@ extern crate serde;
|
|||||||
use self::serde::de::{self, Unexpected};
|
use self::serde::de::{self, Unexpected};
|
||||||
use self::serde::{Deserialize, Deserializer, Serialize, Serializer};
|
use self::serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
extern crate serde_test;
|
extern crate serde_test;
|
||||||
use self::serde_test::{
|
use self::serde_test::{
|
||||||
@ -2161,3 +2162,41 @@ fn test_flatten_option() {
|
|||||||
&[Token::Map { len: None }, Token::MapEnd],
|
&[Token::Map { len: None }, Token::MapEnd],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_transparent_struct() {
|
||||||
|
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
struct Transparent {
|
||||||
|
#[serde(skip)]
|
||||||
|
a: bool,
|
||||||
|
b: u32,
|
||||||
|
#[serde(skip)]
|
||||||
|
c: bool,
|
||||||
|
d: PhantomData<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_tokens(
|
||||||
|
&Transparent {
|
||||||
|
a: false,
|
||||||
|
b: 1,
|
||||||
|
c: false,
|
||||||
|
d: PhantomData,
|
||||||
|
},
|
||||||
|
&[Token::U32(1)],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_transparent_tuple_struct() {
|
||||||
|
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
struct Transparent(
|
||||||
|
#[serde(skip)] bool,
|
||||||
|
u32,
|
||||||
|
#[serde(skip)] bool,
|
||||||
|
PhantomData<()>,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_tokens(&Transparent(false, 1, false, PhantomData), &[Token::U32(1)]);
|
||||||
|
}
|
||||||
|
@ -653,6 +653,14 @@ fn test_gen() {
|
|||||||
X,
|
X,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
struct TransparentWith {
|
||||||
|
#[serde(serialize_with = "ser_x")]
|
||||||
|
#[serde(deserialize_with = "de_x")]
|
||||||
|
x: X,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
Loading…
x
Reference in New Issue
Block a user