Add support for serializing newtype structs
This enables formats like JSON to serialize newtype wrapper structs without the normal object wrapper. Consider types like: ```rust struct Point { x: u32, y: u32, } stuct MyPoint(Point); ``` Before this patch, `MyPoint(1,2)` would get serialized as `[{"x":1,"y":2}]`, but now it gets serialized as `{"x":1,"y"2}`.
This commit is contained in:
parent
6715fb6652
commit
97528b59cf
@ -208,8 +208,8 @@ pub trait Deserializer {
|
||||
self.visit(visitor)
|
||||
}
|
||||
|
||||
/// This method hints that the `Deserialize` type is expecting a named unit. This allows
|
||||
/// deserializers to a named unit that aren't tagged as a named unit.
|
||||
/// This method hints that the `Deserialize` type is expecting a unit struct. This allows
|
||||
/// deserializers to a unit struct that aren't tagged as a unit struct.
|
||||
#[inline]
|
||||
fn visit_unit_struct<V>(&mut self,
|
||||
_name: &'static str,
|
||||
@ -219,6 +219,17 @@ pub trait Deserializer {
|
||||
self.visit_unit(visitor)
|
||||
}
|
||||
|
||||
/// This method hints that the `Deserialize` type is expecting a newtype struct. This allows
|
||||
/// deserializers to a newtype struct that aren't tagged as a newtype struct.
|
||||
#[inline]
|
||||
fn visit_newtype_struct<V>(&mut self,
|
||||
name: &'static str,
|
||||
visitor: V) -> Result<V::Value, Self::Error>
|
||||
where V: Visitor,
|
||||
{
|
||||
self.visit_tuple_struct(name, 1, visitor)
|
||||
}
|
||||
|
||||
/// This method hints that the `Deserialize` type is expecting a tuple struct. This allows
|
||||
/// deserializers to parse sequences that aren't tagged as sequences.
|
||||
#[inline]
|
||||
@ -415,6 +426,12 @@ pub trait Visitor {
|
||||
Err(Error::syntax_error())
|
||||
}
|
||||
|
||||
fn visit_newtype_struct<D>(&mut self, _deserializer: &mut D) -> Result<Self::Value, D::Error>
|
||||
where D: Deserializer,
|
||||
{
|
||||
Err(Error::syntax_error())
|
||||
}
|
||||
|
||||
fn visit_seq<V>(&mut self, _visitor: V) -> Result<Self::Value, V::Error>
|
||||
where V: SeqVisitor,
|
||||
{
|
||||
|
@ -125,6 +125,18 @@ pub trait Serializer {
|
||||
self.visit_unit()
|
||||
}
|
||||
|
||||
/// The `visit_newtype_struct` allows a tuple struct with a single element, also known as a
|
||||
/// newtyped value, to be more efficiently serialized than a tuple struct with multiple items.
|
||||
/// By default it just serializes the value as a tuple struct sequence.
|
||||
#[inline]
|
||||
fn visit_newtype_struct<T>(&mut self,
|
||||
name: &'static str,
|
||||
value: T) -> Result<(), Self::Error>
|
||||
where T: Serialize,
|
||||
{
|
||||
self.visit_tuple_struct(name, Some(value))
|
||||
}
|
||||
|
||||
/// The `visit_newtype_variant` allows a variant with a single item to be more efficiently
|
||||
/// serialized than a variant with multiple items. By default it just serializes the value as a
|
||||
/// tuple variant sequence.
|
||||
|
@ -129,15 +129,24 @@ fn deserialize_item_struct(
|
||||
}
|
||||
}
|
||||
|
||||
match (named_fields.is_empty(), unnamed_fields == 0) {
|
||||
(true, true) => {
|
||||
match (named_fields.is_empty(), unnamed_fields) {
|
||||
(true, 0) => {
|
||||
deserialize_unit_struct(
|
||||
cx,
|
||||
&builder,
|
||||
item.ident,
|
||||
)
|
||||
}
|
||||
(true, false) => {
|
||||
(true, 1) => {
|
||||
deserialize_newtype_struct(
|
||||
cx,
|
||||
&builder,
|
||||
item.ident,
|
||||
impl_generics,
|
||||
ty,
|
||||
)
|
||||
}
|
||||
(true, _) => {
|
||||
deserialize_tuple_struct(
|
||||
cx,
|
||||
&builder,
|
||||
@ -147,7 +156,7 @@ fn deserialize_item_struct(
|
||||
unnamed_fields,
|
||||
)
|
||||
}
|
||||
(false, true) => {
|
||||
(false, 0) => {
|
||||
deserialize_struct(
|
||||
cx,
|
||||
&builder,
|
||||
@ -157,7 +166,7 @@ fn deserialize_item_struct(
|
||||
struct_def,
|
||||
)
|
||||
}
|
||||
(false, false) => {
|
||||
(false, _) => {
|
||||
cx.bug("struct has named and unnamed fields")
|
||||
}
|
||||
}
|
||||
@ -278,6 +287,58 @@ fn deserialize_unit_struct(
|
||||
})
|
||||
}
|
||||
|
||||
fn deserialize_newtype_struct(
|
||||
cx: &ExtCtxt,
|
||||
builder: &aster::AstBuilder,
|
||||
type_ident: Ident,
|
||||
impl_generics: &ast::Generics,
|
||||
ty: P<ast::Ty>,
|
||||
) -> P<ast::Expr> {
|
||||
let where_clause = &impl_generics.where_clause;
|
||||
|
||||
let (visitor_item, visitor_ty, visitor_expr, visitor_generics) =
|
||||
deserialize_visitor(
|
||||
builder,
|
||||
impl_generics,
|
||||
vec![deserializer_ty_param(builder)],
|
||||
vec![deserializer_ty_arg(builder)],
|
||||
);
|
||||
|
||||
let visit_seq_expr = deserialize_seq(
|
||||
cx,
|
||||
builder,
|
||||
builder.path().id(type_ident).build(),
|
||||
1,
|
||||
);
|
||||
|
||||
let type_name = builder.expr().str(type_ident);
|
||||
|
||||
quote_expr!(cx, {
|
||||
$visitor_item
|
||||
|
||||
impl $visitor_generics ::serde::de::Visitor for $visitor_ty $where_clause {
|
||||
type Value = $ty;
|
||||
|
||||
#[inline]
|
||||
fn visit_newtype_struct<D>(&mut self, deserializer: &mut D) -> Result<Self::Value, D::Error>
|
||||
where D: ::serde::de::Deserializer,
|
||||
{
|
||||
let value = try!(::serde::de::Deserialize::deserialize(deserializer));
|
||||
Ok($type_ident(value))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_seq<__V>(&mut self, mut visitor: __V) -> ::std::result::Result<$ty, __V::Error>
|
||||
where __V: ::serde::de::SeqVisitor,
|
||||
{
|
||||
$visit_seq_expr
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.visit_newtype_struct($type_name, $visitor_expr)
|
||||
})
|
||||
}
|
||||
|
||||
fn deserialize_tuple_struct(
|
||||
cx: &ExtCtxt,
|
||||
builder: &aster::AstBuilder,
|
||||
|
@ -124,15 +124,22 @@ fn serialize_item_struct(
|
||||
}
|
||||
}
|
||||
|
||||
match (named_fields.is_empty(), unnamed_fields == 0) {
|
||||
(true, true) => {
|
||||
match (named_fields.is_empty(), unnamed_fields) {
|
||||
(true, 0) => {
|
||||
serialize_unit_struct(
|
||||
cx,
|
||||
&builder,
|
||||
item.ident,
|
||||
)
|
||||
}
|
||||
(true, false) => {
|
||||
(true, 1) => {
|
||||
serialize_newtype_struct(
|
||||
cx,
|
||||
&builder,
|
||||
item.ident,
|
||||
)
|
||||
}
|
||||
(true, _) => {
|
||||
serialize_tuple_struct(
|
||||
cx,
|
||||
&builder,
|
||||
@ -142,7 +149,7 @@ fn serialize_item_struct(
|
||||
unnamed_fields,
|
||||
)
|
||||
}
|
||||
(false, true) => {
|
||||
(false, 0) => {
|
||||
serialize_struct(
|
||||
cx,
|
||||
&builder,
|
||||
@ -153,7 +160,7 @@ fn serialize_item_struct(
|
||||
named_fields,
|
||||
)
|
||||
}
|
||||
(false, false) => {
|
||||
(false, _) => {
|
||||
cx.bug("struct has named and unnamed fields")
|
||||
}
|
||||
}
|
||||
@ -169,6 +176,16 @@ fn serialize_unit_struct(
|
||||
quote_expr!(cx, serializer.visit_unit_struct($type_name))
|
||||
}
|
||||
|
||||
fn serialize_newtype_struct(
|
||||
cx: &ExtCtxt,
|
||||
builder: &aster::AstBuilder,
|
||||
type_ident: Ident
|
||||
) -> P<ast::Expr> {
|
||||
let type_name = builder.expr().str(type_ident);
|
||||
|
||||
quote_expr!(cx, serializer.visit_newtype_struct($type_name, &self.0))
|
||||
}
|
||||
|
||||
fn serialize_tuple_struct(
|
||||
cx: &ExtCtxt,
|
||||
builder: &aster::AstBuilder,
|
||||
|
@ -453,6 +453,15 @@ impl<Iter> de::Deserializer for Deserializer<Iter>
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_newtype_struct<V>(&mut self,
|
||||
_name: &str,
|
||||
mut visitor: V) -> Result<V::Value, Error>
|
||||
where V: de::Visitor,
|
||||
{
|
||||
visitor.visit_newtype_struct(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_enum<V>(&mut self,
|
||||
_name: &str,
|
||||
|
@ -158,6 +158,16 @@ impl<W, F> ser::Serializer for Serializer<W, F>
|
||||
self.writer.write_all(b"null")
|
||||
}
|
||||
|
||||
/// Override `visit_newtype_struct` to serialize newtypes without an object wrapper.
|
||||
#[inline]
|
||||
fn visit_newtype_struct<T>(&mut self,
|
||||
_name: &'static str,
|
||||
value: T) -> Result<(), Self::Error>
|
||||
where T: ser::Serialize,
|
||||
{
|
||||
value.serialize(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn visit_unit_variant(&mut self,
|
||||
_name: &str,
|
||||
|
@ -137,18 +137,17 @@ pub struct GenericStruct<T> {
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct GenericTupleStruct<T>(T);
|
||||
pub struct GenericNewtypeStruct<T>(T);
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum GenericEnumSeq<T, E> {
|
||||
Ok(T),
|
||||
Err(E),
|
||||
}
|
||||
pub struct GenericTupleStruct<T, U>(T, U);
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum GenericEnumMap<T, E> {
|
||||
Ok { x: T },
|
||||
Err { x: E },
|
||||
pub enum GenericEnum<T, U> {
|
||||
Unit,
|
||||
Newtype(T),
|
||||
Seq(T, U),
|
||||
Map { x: T, y: U },
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -496,28 +495,42 @@ fn test_lifetimes() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_generic() {
|
||||
macro_rules! declare_tests {
|
||||
($($ty:ty : $value:expr => $str:expr,)+) => {
|
||||
$({
|
||||
let value: $ty = $value;
|
||||
macro_rules! declare_tests {
|
||||
($($name:ident { $($ty:ty : $value:expr => $str:expr,)+ })+) => {
|
||||
$(
|
||||
#[test]
|
||||
fn $name() {
|
||||
$(
|
||||
let value: $ty = $value;
|
||||
|
||||
let string = serde_json::to_string(&value).unwrap();
|
||||
assert_eq!(string, $str);
|
||||
let string = serde_json::to_string(&value).unwrap();
|
||||
assert_eq!(string, $str);
|
||||
|
||||
let expected: $ty = serde_json::from_str(&string).unwrap();
|
||||
assert_eq!(value, expected);
|
||||
})+
|
||||
}
|
||||
let expected: $ty = serde_json::from_str(&string).unwrap();
|
||||
assert_eq!(value, expected);
|
||||
)+
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
|
||||
declare_tests! {
|
||||
test_generic_struct {
|
||||
GenericStruct<u32> : GenericStruct { x: 5 } => "{\"x\":5}",
|
||||
}
|
||||
test_generic_newtype_struct {
|
||||
GenericNewtypeStruct<u32> : GenericNewtypeStruct(5) => "5",
|
||||
}
|
||||
test_generic_tuple_struct {
|
||||
GenericTupleStruct<u32, u32> : GenericTupleStruct(5, 6) => "[5,6]",
|
||||
}
|
||||
test_generic_enum_newtype {
|
||||
GenericEnum<u32, u32> : GenericEnum::Newtype(5) => "{\"Newtype\":5}",
|
||||
}
|
||||
test_generic_enum_seq {
|
||||
GenericEnum<u32, u32> : GenericEnum::Seq(5, 6) => "{\"Seq\":[5,6]}",
|
||||
}
|
||||
test_generic_enum_map {
|
||||
GenericEnum<u32, u32> : GenericEnum::Map { x: 5, y: 6 } => "{\"Map\":{\"x\":5,\"y\":6}}",
|
||||
}
|
||||
|
||||
declare_tests!(
|
||||
GenericStruct<u32> : GenericStruct { x: 5 } => "{\"x\":5}",
|
||||
GenericTupleStruct<u32> : GenericTupleStruct(5) => "[5]",
|
||||
GenericEnumSeq<u32, u32> : GenericEnumSeq::Ok(5) => "{\"Ok\":5}",
|
||||
GenericEnumSeq<u32, u32> : GenericEnumSeq::Err(5) => "{\"Err\":5}",
|
||||
GenericEnumMap<u32, u32> : GenericEnumMap::Ok { x: 5 } => "{\"Ok\":{\"x\":5}}",
|
||||
GenericEnumMap<u32, u32> : GenericEnumMap::Err { x: 5 } => "{\"Err\":{\"x\":5}}",
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user