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)
|
self.visit(visitor)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This method hints that the `Deserialize` type is expecting a named unit. This allows
|
/// This method hints that the `Deserialize` type is expecting a unit struct. This allows
|
||||||
/// deserializers to a named unit that aren't tagged as a named unit.
|
/// deserializers to a unit struct that aren't tagged as a unit struct.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn visit_unit_struct<V>(&mut self,
|
fn visit_unit_struct<V>(&mut self,
|
||||||
_name: &'static str,
|
_name: &'static str,
|
||||||
@ -219,6 +219,17 @@ pub trait Deserializer {
|
|||||||
self.visit_unit(visitor)
|
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
|
/// This method hints that the `Deserialize` type is expecting a tuple struct. This allows
|
||||||
/// deserializers to parse sequences that aren't tagged as sequences.
|
/// deserializers to parse sequences that aren't tagged as sequences.
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -415,6 +426,12 @@ pub trait Visitor {
|
|||||||
Err(Error::syntax_error())
|
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>
|
fn visit_seq<V>(&mut self, _visitor: V) -> Result<Self::Value, V::Error>
|
||||||
where V: SeqVisitor,
|
where V: SeqVisitor,
|
||||||
{
|
{
|
||||||
|
@ -125,6 +125,18 @@ pub trait Serializer {
|
|||||||
self.visit_unit()
|
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
|
/// 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
|
/// serialized than a variant with multiple items. By default it just serializes the value as a
|
||||||
/// tuple variant sequence.
|
/// tuple variant sequence.
|
||||||
|
@ -129,15 +129,24 @@ fn deserialize_item_struct(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match (named_fields.is_empty(), unnamed_fields == 0) {
|
match (named_fields.is_empty(), unnamed_fields) {
|
||||||
(true, true) => {
|
(true, 0) => {
|
||||||
deserialize_unit_struct(
|
deserialize_unit_struct(
|
||||||
cx,
|
cx,
|
||||||
&builder,
|
&builder,
|
||||||
item.ident,
|
item.ident,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
(true, false) => {
|
(true, 1) => {
|
||||||
|
deserialize_newtype_struct(
|
||||||
|
cx,
|
||||||
|
&builder,
|
||||||
|
item.ident,
|
||||||
|
impl_generics,
|
||||||
|
ty,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
(true, _) => {
|
||||||
deserialize_tuple_struct(
|
deserialize_tuple_struct(
|
||||||
cx,
|
cx,
|
||||||
&builder,
|
&builder,
|
||||||
@ -147,7 +156,7 @@ fn deserialize_item_struct(
|
|||||||
unnamed_fields,
|
unnamed_fields,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
(false, true) => {
|
(false, 0) => {
|
||||||
deserialize_struct(
|
deserialize_struct(
|
||||||
cx,
|
cx,
|
||||||
&builder,
|
&builder,
|
||||||
@ -157,7 +166,7 @@ fn deserialize_item_struct(
|
|||||||
struct_def,
|
struct_def,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
(false, false) => {
|
(false, _) => {
|
||||||
cx.bug("struct has named and unnamed fields")
|
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(
|
fn deserialize_tuple_struct(
|
||||||
cx: &ExtCtxt,
|
cx: &ExtCtxt,
|
||||||
builder: &aster::AstBuilder,
|
builder: &aster::AstBuilder,
|
||||||
|
@ -124,15 +124,22 @@ fn serialize_item_struct(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match (named_fields.is_empty(), unnamed_fields == 0) {
|
match (named_fields.is_empty(), unnamed_fields) {
|
||||||
(true, true) => {
|
(true, 0) => {
|
||||||
serialize_unit_struct(
|
serialize_unit_struct(
|
||||||
cx,
|
cx,
|
||||||
&builder,
|
&builder,
|
||||||
item.ident,
|
item.ident,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
(true, false) => {
|
(true, 1) => {
|
||||||
|
serialize_newtype_struct(
|
||||||
|
cx,
|
||||||
|
&builder,
|
||||||
|
item.ident,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
(true, _) => {
|
||||||
serialize_tuple_struct(
|
serialize_tuple_struct(
|
||||||
cx,
|
cx,
|
||||||
&builder,
|
&builder,
|
||||||
@ -142,7 +149,7 @@ fn serialize_item_struct(
|
|||||||
unnamed_fields,
|
unnamed_fields,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
(false, true) => {
|
(false, 0) => {
|
||||||
serialize_struct(
|
serialize_struct(
|
||||||
cx,
|
cx,
|
||||||
&builder,
|
&builder,
|
||||||
@ -153,7 +160,7 @@ fn serialize_item_struct(
|
|||||||
named_fields,
|
named_fields,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
(false, false) => {
|
(false, _) => {
|
||||||
cx.bug("struct has named and unnamed fields")
|
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))
|
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(
|
fn serialize_tuple_struct(
|
||||||
cx: &ExtCtxt,
|
cx: &ExtCtxt,
|
||||||
builder: &aster::AstBuilder,
|
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]
|
#[inline]
|
||||||
fn visit_enum<V>(&mut self,
|
fn visit_enum<V>(&mut self,
|
||||||
_name: &str,
|
_name: &str,
|
||||||
|
@ -158,6 +158,16 @@ impl<W, F> ser::Serializer for Serializer<W, F>
|
|||||||
self.writer.write_all(b"null")
|
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]
|
#[inline]
|
||||||
fn visit_unit_variant(&mut self,
|
fn visit_unit_variant(&mut self,
|
||||||
_name: &str,
|
_name: &str,
|
||||||
|
@ -137,18 +137,17 @@ pub struct GenericStruct<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct GenericTupleStruct<T>(T);
|
pub struct GenericNewtypeStruct<T>(T);
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum GenericEnumSeq<T, E> {
|
pub struct GenericTupleStruct<T, U>(T, U);
|
||||||
Ok(T),
|
|
||||||
Err(E),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum GenericEnumMap<T, E> {
|
pub enum GenericEnum<T, U> {
|
||||||
Ok { x: T },
|
Unit,
|
||||||
Err { x: E },
|
Newtype(T),
|
||||||
|
Seq(T, U),
|
||||||
|
Map { x: T, y: U },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -496,28 +495,42 @@ fn test_lifetimes() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
macro_rules! declare_tests {
|
||||||
fn test_generic() {
|
($($name:ident { $($ty:ty : $value:expr => $str:expr,)+ })+) => {
|
||||||
macro_rules! declare_tests {
|
$(
|
||||||
($($ty:ty : $value:expr => $str:expr,)+) => {
|
#[test]
|
||||||
$({
|
fn $name() {
|
||||||
let value: $ty = $value;
|
$(
|
||||||
|
let value: $ty = $value;
|
||||||
|
|
||||||
let string = serde_json::to_string(&value).unwrap();
|
let string = serde_json::to_string(&value).unwrap();
|
||||||
assert_eq!(string, $str);
|
assert_eq!(string, $str);
|
||||||
|
|
||||||
let expected: $ty = serde_json::from_str(&string).unwrap();
|
let expected: $ty = serde_json::from_str(&string).unwrap();
|
||||||
assert_eq!(value, expected);
|
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