mirror of
https://github.com/serde-rs/serde.git
synced 2025-10-02 07:21:12 +00:00
Reduce dependence on type inference
This commit is contained in:
parent
8378267b9b
commit
f4414bfc14
@ -180,7 +180,7 @@ pub struct FieldAttrs {
|
|||||||
skip_serializing_field_if: Option<P<ast::Expr>>,
|
skip_serializing_field_if: Option<P<ast::Expr>>,
|
||||||
default_expr_if_missing: Option<P<ast::Expr>>,
|
default_expr_if_missing: Option<P<ast::Expr>>,
|
||||||
serialize_with: Option<P<ast::Expr>>,
|
serialize_with: Option<P<ast::Expr>>,
|
||||||
deserialize_with: P<ast::Expr>,
|
deserialize_with: Option<ast::Path>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FieldAttrs {
|
impl FieldAttrs {
|
||||||
@ -197,8 +197,6 @@ impl FieldAttrs {
|
|||||||
None => { cx.span_bug(field.span, "struct field has no name?") }
|
None => { cx.span_bug(field.span, "struct field has no name?") }
|
||||||
};
|
};
|
||||||
|
|
||||||
let identity = quote_expr!(cx, |x| x);
|
|
||||||
|
|
||||||
let mut field_attrs = FieldAttrs {
|
let mut field_attrs = FieldAttrs {
|
||||||
name: Name::new(field_ident),
|
name: Name::new(field_ident),
|
||||||
skip_serializing_field: false,
|
skip_serializing_field: false,
|
||||||
@ -206,7 +204,7 @@ impl FieldAttrs {
|
|||||||
skip_serializing_field_if: None,
|
skip_serializing_field_if: None,
|
||||||
default_expr_if_missing: None,
|
default_expr_if_missing: None,
|
||||||
serialize_with: None,
|
serialize_with: None,
|
||||||
deserialize_with: identity,
|
deserialize_with: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
for meta_items in field.attrs.iter().filter_map(get_serde_meta_items) {
|
for meta_items in field.attrs.iter().filter_map(get_serde_meta_items) {
|
||||||
@ -287,14 +285,8 @@ impl FieldAttrs {
|
|||||||
|
|
||||||
// Parse `#[serde(deserialize_with="...")]`
|
// Parse `#[serde(deserialize_with="...")]`
|
||||||
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"deserialize_with" => {
|
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"deserialize_with" => {
|
||||||
let expr = wrap_deserialize_with(
|
let path = try!(parse_lit_into_path(cx, name, lit));
|
||||||
cx,
|
field_attrs.deserialize_with = Some(path);
|
||||||
&field.ty,
|
|
||||||
generics,
|
|
||||||
try!(parse_lit_into_path(cx, name, lit)),
|
|
||||||
);
|
|
||||||
|
|
||||||
field_attrs.deserialize_with = expr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
@ -348,8 +340,8 @@ impl FieldAttrs {
|
|||||||
self.serialize_with.as_ref()
|
self.serialize_with.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deserialize_with(&self) -> &P<ast::Expr> {
|
pub fn deserialize_with(&self) -> Option<&ast::Path> {
|
||||||
&self.deserialize_with
|
self.deserialize_with.as_ref()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -612,36 +604,3 @@ fn wrap_serialize_with(cx: &ExtCtxt,
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function wraps the expression in `#[serde(deserialize_with="...")]` in a trait to prevent
|
|
||||||
/// it from accessing the internal `Deserialize` state.
|
|
||||||
fn wrap_deserialize_with(cx: &ExtCtxt,
|
|
||||||
field_ty: &P<ast::Ty>,
|
|
||||||
generics: &ast::Generics,
|
|
||||||
path: ast::Path) -> P<ast::Expr> {
|
|
||||||
// Quasi-quoting doesn't do a great job of expanding generics into paths, so manually build it.
|
|
||||||
let ty_path = AstBuilder::new().path()
|
|
||||||
.segment("__SerdeDeserializeWithStruct")
|
|
||||||
.with_generics(generics.clone())
|
|
||||||
.build()
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let where_clause = &generics.where_clause;
|
|
||||||
|
|
||||||
quote_expr!(cx, ({
|
|
||||||
struct __SerdeDeserializeWithStruct $generics $where_clause {
|
|
||||||
value: $field_ty,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl $generics _serde::de::Deserialize for $ty_path $where_clause {
|
|
||||||
fn deserialize<D>(deserializer: &mut D) -> ::std::result::Result<Self, D::Error>
|
|
||||||
where D: _serde::de::Deserializer
|
|
||||||
{
|
|
||||||
let value = try!($path(deserializer));
|
|
||||||
Ok(__SerdeDeserializeWithStruct { value: value })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|visit: $ty_path| visit.value
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
@ -509,12 +509,14 @@ fn deserialize_seq(
|
|||||||
fn deserialize_struct_as_seq(
|
fn deserialize_struct_as_seq(
|
||||||
cx: &ExtCtxt,
|
cx: &ExtCtxt,
|
||||||
builder: &aster::AstBuilder,
|
builder: &aster::AstBuilder,
|
||||||
|
type_ident: Ident,
|
||||||
struct_path: ast::Path,
|
struct_path: ast::Path,
|
||||||
|
impl_generics: &ast::Generics,
|
||||||
fields: &[(&ast::StructField, attr::FieldAttrs)],
|
fields: &[(&ast::StructField, attr::FieldAttrs)],
|
||||||
) -> Result<P<ast::Expr>, Error> {
|
) -> Result<P<ast::Expr>, Error> {
|
||||||
let let_values: Vec<_> = fields.iter()
|
let let_values: Vec<_> = fields.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, &(_, ref attrs))| {
|
.map(|(i, &(field, ref attrs))| {
|
||||||
let name = builder.id(format!("__field{}", i));
|
let name = builder.id(format!("__field{}", i));
|
||||||
if attrs.skip_deserializing_field() {
|
if attrs.skip_deserializing_field() {
|
||||||
let default = attrs.expr_is_missing();
|
let default = attrs.expr_is_missing();
|
||||||
@ -522,10 +524,24 @@ fn deserialize_struct_as_seq(
|
|||||||
let $name = $default;
|
let $name = $default;
|
||||||
).unwrap()
|
).unwrap()
|
||||||
} else {
|
} else {
|
||||||
let deserialize_with = attrs.deserialize_with();
|
let visit = match attrs.deserialize_with() {
|
||||||
|
None => {
|
||||||
|
let field_ty = &field.ty;
|
||||||
|
quote_expr!(cx, try!(visitor.visit::<$field_ty>()))
|
||||||
|
}
|
||||||
|
Some(path) => {
|
||||||
|
let (wrapper, wrapper_impl, wrapper_ty) = wrap_deserialize_with(
|
||||||
|
cx, builder, type_ident, impl_generics, &field.ty, path);
|
||||||
|
quote_expr!(cx, {
|
||||||
|
$wrapper
|
||||||
|
$wrapper_impl
|
||||||
|
try!(visitor.visit::<$wrapper_ty>()).map(|wrap| wrap.value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
quote_stmt!(cx,
|
quote_stmt!(cx,
|
||||||
let $name = match try!(visitor.visit()) {
|
let $name = match $visit {
|
||||||
Some(value) => { $deserialize_with(value) },
|
Some(value) => { value },
|
||||||
None => {
|
None => {
|
||||||
return Err(_serde::de::Error::end_of_stream());
|
return Err(_serde::de::Error::end_of_stream());
|
||||||
}
|
}
|
||||||
@ -587,14 +603,18 @@ fn deserialize_struct(
|
|||||||
let visit_seq_expr = try!(deserialize_struct_as_seq(
|
let visit_seq_expr = try!(deserialize_struct_as_seq(
|
||||||
cx,
|
cx,
|
||||||
builder,
|
builder,
|
||||||
|
type_ident,
|
||||||
type_path.clone(),
|
type_path.clone(),
|
||||||
|
impl_generics,
|
||||||
&fields_with_attrs,
|
&fields_with_attrs,
|
||||||
));
|
));
|
||||||
|
|
||||||
let (field_visitor, fields_stmt, visit_map_expr) = try!(deserialize_struct_visitor(
|
let (field_visitor, fields_stmt, visit_map_expr) = try!(deserialize_struct_visitor(
|
||||||
cx,
|
cx,
|
||||||
builder,
|
builder,
|
||||||
|
type_ident,
|
||||||
type_path.clone(),
|
type_path.clone(),
|
||||||
|
impl_generics,
|
||||||
&fields_with_attrs,
|
&fields_with_attrs,
|
||||||
container_attrs,
|
container_attrs,
|
||||||
));
|
));
|
||||||
@ -843,14 +863,18 @@ fn deserialize_struct_variant(
|
|||||||
let visit_seq_expr = try!(deserialize_struct_as_seq(
|
let visit_seq_expr = try!(deserialize_struct_as_seq(
|
||||||
cx,
|
cx,
|
||||||
builder,
|
builder,
|
||||||
|
type_ident,
|
||||||
type_path.clone(),
|
type_path.clone(),
|
||||||
|
generics,
|
||||||
&fields_with_attrs,
|
&fields_with_attrs,
|
||||||
));
|
));
|
||||||
|
|
||||||
let (field_visitor, fields_stmt, field_expr) = try!(deserialize_struct_visitor(
|
let (field_visitor, fields_stmt, field_expr) = try!(deserialize_struct_visitor(
|
||||||
cx,
|
cx,
|
||||||
builder,
|
builder,
|
||||||
|
type_ident,
|
||||||
type_path,
|
type_path,
|
||||||
|
generics,
|
||||||
&fields_with_attrs,
|
&fields_with_attrs,
|
||||||
container_attrs,
|
container_attrs,
|
||||||
));
|
));
|
||||||
@ -1011,10 +1035,8 @@ fn deserialize_field_visitor(
|
|||||||
fn deserialize<D>(deserializer: &mut D) -> ::std::result::Result<__Field, D::Error>
|
fn deserialize<D>(deserializer: &mut D) -> ::std::result::Result<__Field, D::Error>
|
||||||
where D: _serde::de::Deserializer,
|
where D: _serde::de::Deserializer,
|
||||||
{
|
{
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
struct __FieldVisitor<D> {
|
struct __FieldVisitor<D> {
|
||||||
phantom: PhantomData<D>
|
phantom: ::std::marker::PhantomData<D>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<__D> _serde::de::Visitor for __FieldVisitor<__D>
|
impl<__D> _serde::de::Visitor for __FieldVisitor<__D>
|
||||||
@ -1041,7 +1063,11 @@ fn deserialize_field_visitor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deserializer.deserialize_struct_field(__FieldVisitor::<D>{ phantom: PhantomData })
|
deserializer.deserialize_struct_field(
|
||||||
|
__FieldVisitor::<D>{
|
||||||
|
phantom: ::std::marker::PhantomData
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
).unwrap();
|
).unwrap();
|
||||||
@ -1052,7 +1078,9 @@ fn deserialize_field_visitor(
|
|||||||
fn deserialize_struct_visitor(
|
fn deserialize_struct_visitor(
|
||||||
cx: &ExtCtxt,
|
cx: &ExtCtxt,
|
||||||
builder: &aster::AstBuilder,
|
builder: &aster::AstBuilder,
|
||||||
|
type_ident: Ident,
|
||||||
struct_path: ast::Path,
|
struct_path: ast::Path,
|
||||||
|
impl_generics: &ast::Generics,
|
||||||
fields: &[(&ast::StructField, attr::FieldAttrs)],
|
fields: &[(&ast::StructField, attr::FieldAttrs)],
|
||||||
container_attrs: &attr::ContainerAttrs,
|
container_attrs: &attr::ContainerAttrs,
|
||||||
) -> Result<(Vec<P<ast::Item>>, ast::Stmt, P<ast::Expr>), Error> {
|
) -> Result<(Vec<P<ast::Item>>, ast::Stmt, P<ast::Expr>), Error> {
|
||||||
@ -1071,7 +1099,9 @@ fn deserialize_struct_visitor(
|
|||||||
let visit_map_expr = try!(deserialize_map(
|
let visit_map_expr = try!(deserialize_map(
|
||||||
cx,
|
cx,
|
||||||
builder,
|
builder,
|
||||||
|
type_ident,
|
||||||
struct_path,
|
struct_path,
|
||||||
|
impl_generics,
|
||||||
fields,
|
fields,
|
||||||
container_attrs,
|
container_attrs,
|
||||||
));
|
));
|
||||||
@ -1100,7 +1130,9 @@ fn deserialize_struct_visitor(
|
|||||||
fn deserialize_map(
|
fn deserialize_map(
|
||||||
cx: &ExtCtxt,
|
cx: &ExtCtxt,
|
||||||
builder: &aster::AstBuilder,
|
builder: &aster::AstBuilder,
|
||||||
|
type_ident: Ident,
|
||||||
struct_path: ast::Path,
|
struct_path: ast::Path,
|
||||||
|
impl_generics: &ast::Generics,
|
||||||
fields: &[(&ast::StructField, attr::FieldAttrs)],
|
fields: &[(&ast::StructField, attr::FieldAttrs)],
|
||||||
container_attrs: &attr::ContainerAttrs,
|
container_attrs: &attr::ContainerAttrs,
|
||||||
) -> Result<P<ast::Expr>, Error> {
|
) -> Result<P<ast::Expr>, Error> {
|
||||||
@ -1114,17 +1146,34 @@ fn deserialize_map(
|
|||||||
// Declare each field that will be deserialized.
|
// Declare each field that will be deserialized.
|
||||||
let let_values: Vec<ast::Stmt> = fields_attrs_names.iter()
|
let let_values: Vec<ast::Stmt> = fields_attrs_names.iter()
|
||||||
.filter(|&&(_, ref attrs, _)| !attrs.skip_deserializing_field())
|
.filter(|&&(_, ref attrs, _)| !attrs.skip_deserializing_field())
|
||||||
.map(|&(_, _, name)| quote_stmt!(cx, let mut $name = None;).unwrap())
|
.map(|&(field, _, name)| {
|
||||||
|
let field_ty = &field.ty;
|
||||||
|
quote_stmt!(cx, let mut $name: Option<$field_ty> = None;).unwrap()
|
||||||
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// Match arms to extract a value for a field.
|
// Match arms to extract a value for a field.
|
||||||
let value_arms = fields_attrs_names.iter()
|
let value_arms = fields_attrs_names.iter()
|
||||||
.filter(|&&(_, ref attrs, _)| !attrs.skip_deserializing_field())
|
.filter(|&&(_, ref attrs, _)| !attrs.skip_deserializing_field())
|
||||||
.map(|&(_, ref attrs, name)| {
|
.map(|&(field, ref attrs, name)| {
|
||||||
let deserialize_with = attrs.deserialize_with();
|
let visit = match attrs.deserialize_with() {
|
||||||
|
None => {
|
||||||
|
let field_ty = &field.ty;
|
||||||
|
quote_expr!(cx, try!(visitor.visit_value::<$field_ty>()))
|
||||||
|
}
|
||||||
|
Some(path) => {
|
||||||
|
let (wrapper, wrapper_impl, wrapper_ty) = wrap_deserialize_with(
|
||||||
|
cx, builder, type_ident, impl_generics, &field.ty, path);
|
||||||
|
quote_expr!(cx, ({
|
||||||
|
$wrapper
|
||||||
|
$wrapper_impl
|
||||||
|
try!(visitor.visit_value::<$wrapper_ty>()).value
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
};
|
||||||
quote_arm!(cx,
|
quote_arm!(cx,
|
||||||
__Field::$name => {
|
__Field::$name => {
|
||||||
$name = Some($deserialize_with(try!(visitor.visit_value())));
|
$name = Some($visit);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -1192,7 +1241,7 @@ fn deserialize_map(
|
|||||||
Ok(quote_expr!(cx, {
|
Ok(quote_expr!(cx, {
|
||||||
$let_values
|
$let_values
|
||||||
|
|
||||||
while let Some(key) = try!(visitor.visit_key()) {
|
while let Some(key) = try!(visitor.visit_key::<__Field>()) {
|
||||||
match key {
|
match key {
|
||||||
$value_arms
|
$value_arms
|
||||||
$skipped_arms
|
$skipped_arms
|
||||||
@ -1222,3 +1271,55 @@ fn fields_with_attrs<'a>(
|
|||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This function wraps the expression in `#[serde(deserialize_with="...")]` in
|
||||||
|
/// a trait to prevent it from accessing the internal `Deserialize` state.
|
||||||
|
fn wrap_deserialize_with(
|
||||||
|
cx: &ExtCtxt,
|
||||||
|
builder: &aster::AstBuilder,
|
||||||
|
type_ident: Ident,
|
||||||
|
impl_generics: &ast::Generics,
|
||||||
|
field_ty: &P<ast::Ty>,
|
||||||
|
deserialize_with: &ast::Path,
|
||||||
|
) -> (ast::Stmt, ast::Stmt, ast::Path) {
|
||||||
|
// Quasi-quoting doesn't do a great job of expanding generics into paths,
|
||||||
|
// so manually build it.
|
||||||
|
let wrapper_ty = builder.path()
|
||||||
|
.segment("__SerdeDeserializeWithStruct")
|
||||||
|
.with_generics(impl_generics.clone())
|
||||||
|
.build()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let where_clause = &impl_generics.where_clause;
|
||||||
|
|
||||||
|
let phantom_ty = builder.path()
|
||||||
|
.segment(type_ident)
|
||||||
|
.with_generics(builder.from_generics(impl_generics.clone())
|
||||||
|
.strip_ty_params()
|
||||||
|
.build())
|
||||||
|
.build()
|
||||||
|
.build();
|
||||||
|
|
||||||
|
(
|
||||||
|
quote_stmt!(cx,
|
||||||
|
struct __SerdeDeserializeWithStruct $impl_generics $where_clause {
|
||||||
|
value: $field_ty,
|
||||||
|
phantom: ::std::marker::PhantomData<$phantom_ty>,
|
||||||
|
}
|
||||||
|
).unwrap(),
|
||||||
|
quote_stmt!(cx,
|
||||||
|
impl $impl_generics _serde::de::Deserialize for $wrapper_ty $where_clause {
|
||||||
|
fn deserialize<D>(__d: &mut D) -> ::std::result::Result<Self, D::Error>
|
||||||
|
where D: _serde::de::Deserializer
|
||||||
|
{
|
||||||
|
let value = try!($deserialize_with(__d));
|
||||||
|
Ok(__SerdeDeserializeWithStruct {
|
||||||
|
value: value,
|
||||||
|
phantom: ::std::marker::PhantomData,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).unwrap(),
|
||||||
|
wrapper_ty,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -6,5 +6,6 @@ mod token;
|
|||||||
mod test_annotations;
|
mod test_annotations;
|
||||||
mod test_bytes;
|
mod test_bytes;
|
||||||
mod test_de;
|
mod test_de;
|
||||||
|
mod test_gen;
|
||||||
mod test_macros;
|
mod test_macros;
|
||||||
mod test_ser;
|
mod test_ser;
|
||||||
|
@ -3,7 +3,7 @@ use std::net;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
use self::serde::de::{Deserializer, Visitor};
|
use self::serde::de::Deserializer;
|
||||||
|
|
||||||
use token::{
|
use token::{
|
||||||
Error,
|
Error,
|
||||||
|
56
serde_tests/tests/test_gen.rs
Normal file
56
serde_tests/tests/test_gen.rs
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
// These just test that serde_codegen is able to produce code that compiles
|
||||||
|
// successfully when there are a variety of generics involved.
|
||||||
|
|
||||||
|
extern crate serde;
|
||||||
|
use self::serde::ser::{Serialize, Serializer};
|
||||||
|
use self::serde::de::{Deserialize, Deserializer};
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct With<T> {
|
||||||
|
t: T,
|
||||||
|
#[serde(serialize_with="ser_i32", deserialize_with="de_i32")]
|
||||||
|
i: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct WithRef<'a, T: 'a> {
|
||||||
|
#[serde(skip_deserializing)]
|
||||||
|
t: Option<&'a T>,
|
||||||
|
#[serde(serialize_with="ser_i32", deserialize_with="de_i32")]
|
||||||
|
i: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct Bounds<T: Serialize + Deserialize> {
|
||||||
|
t: T,
|
||||||
|
option: Option<T>,
|
||||||
|
boxed: Box<T>,
|
||||||
|
option_boxed: Option<Box<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct NoBounds<T> {
|
||||||
|
t: T,
|
||||||
|
option: Option<T>,
|
||||||
|
boxed: Box<T>,
|
||||||
|
option_boxed: Option<Box<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
enum EnumWith<T> {
|
||||||
|
A(
|
||||||
|
#[serde(serialize_with="ser_i32", deserialize_with="de_i32")]
|
||||||
|
i32),
|
||||||
|
B {
|
||||||
|
t: T,
|
||||||
|
#[serde(serialize_with="ser_i32", deserialize_with="de_i32")]
|
||||||
|
i: i32 },
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
fn ser_i32<S: Serializer>(_: &i32, _: &mut S) -> Result<(), S::Error> { panic!() }
|
||||||
|
|
||||||
|
fn de_i32<D: Deserializer>(_: &mut D) -> Result<i32, D::Error> { panic!() }
|
Loading…
x
Reference in New Issue
Block a user