mirror of
https://github.com/pezkuwichain/serde.git
synced 2026-04-23 15:18:01 +00:00
Compare commits
53 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4de20bd48d | |||
| 922fadf7e3 | |||
| 6bbc415fdf | |||
| b13875dd97 | |||
| ac1b25e91d | |||
| 1335f85213 | |||
| 0a4d536253 | |||
| 7dba1e303d | |||
| 0ea9d73fdf | |||
| a64aaeeb3b | |||
| 320897679b | |||
| 3d5141a2f1 | |||
| 656ea96c65 | |||
| 5302482596 | |||
| 7ada27014d | |||
| 4fa2a50f62 | |||
| 0c5f20c148 | |||
| aa2bbb4704 | |||
| 16d1265e17 | |||
| f09320b293 | |||
| 3b4803115b | |||
| fa5f0f4541 | |||
| 4b7f55bd42 | |||
| 593bcb087d | |||
| f58000cb41 | |||
| 01b86d5ce4 | |||
| c80f9238d7 | |||
| 62850bf832 | |||
| 9f114548f4 | |||
| 8890061f82 | |||
| 2c05518810 | |||
| 4aeb0df88f | |||
| 6550231a51 | |||
| ea0012fc5a | |||
| d6b62b9417 | |||
| 2ee347c5a5 | |||
| 4305260174 | |||
| 35aae92b56 | |||
| f3f26796c7 | |||
| d1460e1f0d | |||
| dfd81323d5 | |||
| 368961e961 | |||
| f9c6f0ab62 | |||
| b2b36e1764 | |||
| 4ad140ea70 | |||
| 67777eb585 | |||
| b4e51fcc77 | |||
| be7fe2a5eb | |||
| b4076f4577 | |||
| c4181f46be | |||
| 8c0efc3d77 | |||
| 7e3efaf6c5 | |||
| 12fe42ed45 |
@@ -0,0 +1,7 @@
|
||||
---
|
||||
name: Anything else!
|
||||
about: Whatever is on your mind
|
||||
|
||||
---
|
||||
|
||||
|
||||
+3
-7
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "serde"
|
||||
version = "1.0.50" # remember to update html_root_url
|
||||
version = "1.0.59" # remember to update html_root_url
|
||||
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
|
||||
license = "MIT/Apache-2.0"
|
||||
description = "A generic serialization/deserialization framework"
|
||||
@@ -10,7 +10,8 @@ documentation = "https://docs.serde.rs/serde/"
|
||||
keywords = ["serde", "serialization", "no_std"]
|
||||
categories = ["encoding"]
|
||||
readme = "README.md"
|
||||
include = ["Cargo.toml", "src/**/*.rs", "README.md", "LICENSE-APACHE", "LICENSE-MIT"]
|
||||
include = ["Cargo.toml", "build.rs", "src/**/*.rs", "README.md", "LICENSE-APACHE", "LICENSE-MIT"]
|
||||
build = "build.rs"
|
||||
|
||||
[badges]
|
||||
travis-ci = { repository = "serde-rs/serde" }
|
||||
@@ -61,8 +62,3 @@ alloc = ["unstable"]
|
||||
# does not preserve identity and may result in multiple copies of the same data.
|
||||
# Be sure that this is what you want before enabling this feature.
|
||||
rc = []
|
||||
|
||||
# Get serde_derive picked up by the Integer 32 playground. Not public API.
|
||||
#
|
||||
# http://play.integer32.com/
|
||||
playground = ["serde_derive"]
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
use std::env;
|
||||
use std::process::Command;
|
||||
use std::str::{self, FromStr};
|
||||
|
||||
fn main() {
|
||||
let rustc = match env::var_os("RUSTC") {
|
||||
Some(rustc) => rustc,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let output = match Command::new(rustc).arg("--version").output() {
|
||||
Ok(output) => output,
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
let version = match str::from_utf8(&output.stdout) {
|
||||
Ok(version) => version,
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
let mut pieces = version.split('.');
|
||||
if pieces.next() != Some("rustc 1") {
|
||||
return;
|
||||
}
|
||||
|
||||
let next = match pieces.next() {
|
||||
Some(next) => next,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let minor = match u32::from_str(next) {
|
||||
Ok(minor) => minor,
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
// 128-bit integers stabilized in Rust 1.26:
|
||||
// https://blog.rust-lang.org/2018/05/10/Rust-1.26.html
|
||||
if minor >= 26 {
|
||||
println!("cargo:rustc-cfg=integer128");
|
||||
}
|
||||
}
|
||||
@@ -39,12 +39,10 @@ macro_rules! uint_to {
|
||||
}
|
||||
|
||||
pub trait FromPrimitive: Sized {
|
||||
fn from_isize(n: isize) -> Option<Self>;
|
||||
fn from_i8(n: i8) -> Option<Self>;
|
||||
fn from_i16(n: i16) -> Option<Self>;
|
||||
fn from_i32(n: i32) -> Option<Self>;
|
||||
fn from_i64(n: i64) -> Option<Self>;
|
||||
fn from_usize(n: usize) -> Option<Self>;
|
||||
fn from_u8(n: u8) -> Option<Self>;
|
||||
fn from_u16(n: u16) -> Option<Self>;
|
||||
fn from_u32(n: u32) -> Option<Self>;
|
||||
@@ -54,10 +52,6 @@ pub trait FromPrimitive: Sized {
|
||||
macro_rules! impl_from_primitive_for_int {
|
||||
($t:ident) => {
|
||||
impl FromPrimitive for $t {
|
||||
#[inline]
|
||||
fn from_isize(n: isize) -> Option<Self> {
|
||||
int_to_int!($t, n)
|
||||
}
|
||||
#[inline]
|
||||
fn from_i8(n: i8) -> Option<Self> {
|
||||
int_to_int!($t, n)
|
||||
@@ -75,10 +69,6 @@ macro_rules! impl_from_primitive_for_int {
|
||||
int_to_int!($t, n)
|
||||
}
|
||||
#[inline]
|
||||
fn from_usize(n: usize) -> Option<Self> {
|
||||
uint_to!($t, n)
|
||||
}
|
||||
#[inline]
|
||||
fn from_u8(n: u8) -> Option<Self> {
|
||||
uint_to!($t, n)
|
||||
}
|
||||
@@ -101,10 +91,6 @@ macro_rules! impl_from_primitive_for_int {
|
||||
macro_rules! impl_from_primitive_for_uint {
|
||||
($t:ident) => {
|
||||
impl FromPrimitive for $t {
|
||||
#[inline]
|
||||
fn from_isize(n: isize) -> Option<Self> {
|
||||
int_to_uint!($t, n)
|
||||
}
|
||||
#[inline]
|
||||
fn from_i8(n: i8) -> Option<Self> {
|
||||
int_to_uint!($t, n)
|
||||
@@ -122,10 +108,6 @@ macro_rules! impl_from_primitive_for_uint {
|
||||
int_to_uint!($t, n)
|
||||
}
|
||||
#[inline]
|
||||
fn from_usize(n: usize) -> Option<Self> {
|
||||
uint_to!($t, n)
|
||||
}
|
||||
#[inline]
|
||||
fn from_u8(n: u8) -> Option<Self> {
|
||||
uint_to!($t, n)
|
||||
}
|
||||
@@ -148,10 +130,6 @@ macro_rules! impl_from_primitive_for_uint {
|
||||
macro_rules! impl_from_primitive_for_float {
|
||||
($t:ident) => {
|
||||
impl FromPrimitive for $t {
|
||||
#[inline]
|
||||
fn from_isize(n: isize) -> Option<Self> {
|
||||
Some(n as Self)
|
||||
}
|
||||
#[inline]
|
||||
fn from_i8(n: i8) -> Option<Self> {
|
||||
Some(n as Self)
|
||||
@@ -169,10 +147,6 @@ macro_rules! impl_from_primitive_for_float {
|
||||
Some(n as Self)
|
||||
}
|
||||
#[inline]
|
||||
fn from_usize(n: usize) -> Option<Self> {
|
||||
Some(n as Self)
|
||||
}
|
||||
#[inline]
|
||||
fn from_u8(n: u8) -> Option<Self> {
|
||||
Some(n as Self)
|
||||
}
|
||||
|
||||
+10
-19
@@ -529,6 +529,14 @@ where
|
||||
{
|
||||
T::deserialize(deserializer).map(Some)
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn __private_visit_untagged_option<D>(self, deserializer: D) -> Result<Self::Value, ()>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Ok(T::deserialize(deserializer).ok())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de, T> Deserialize<'de> for Option<T>
|
||||
@@ -1403,7 +1411,8 @@ impl<'de> Visitor<'de> for OsStringVisitor {
|
||||
use std::os::windows::ffi::OsStringExt;
|
||||
|
||||
match try!(data.variant()) {
|
||||
(OsStringKind::Windows, v) => v.newtype_variant::<Vec<u16>>()
|
||||
(OsStringKind::Windows, v) => v
|
||||
.newtype_variant::<Vec<u16>>()
|
||||
.map(|vec| OsString::from_wide(&vec)),
|
||||
(OsStringKind::Unix, _) => Err(Error::custom(
|
||||
"cannot deserialize Unix OS string on Windows",
|
||||
@@ -1997,24 +2006,6 @@ where
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
#[allow(deprecated)]
|
||||
impl<'de, T> Deserialize<'de> for NonZero<T>
|
||||
where
|
||||
T: Deserialize<'de> + Zeroable,
|
||||
{
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let value = try!(Deserialize::deserialize(deserializer));
|
||||
match NonZero::new(value) {
|
||||
Some(nonzero) => Ok(nonzero),
|
||||
None => Err(Error::custom("expected a non-zero value")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! nonzero_integers {
|
||||
( $( $T: ty, )+ ) => {
|
||||
$(
|
||||
|
||||
+9
-14
@@ -84,7 +84,6 @@
|
||||
//! - LinkedList\<T\>
|
||||
//! - VecDeque\<T\>
|
||||
//! - Vec\<T\>
|
||||
//! - EnumSet\<T\> (unstable)
|
||||
//! - **Zero-copy types**:
|
||||
//! - &str
|
||||
//! - &[u8]
|
||||
@@ -98,7 +97,6 @@
|
||||
//! - Path
|
||||
//! - PathBuf
|
||||
//! - Range\<T\>
|
||||
//! - NonZero\<T\> (unstable, deprecated)
|
||||
//! - num::NonZero* (unstable)
|
||||
//! - **Net types**:
|
||||
//! - IpAddr
|
||||
@@ -1132,18 +1130,6 @@ pub trait Deserializer<'de>: Sized {
|
||||
fn is_human_readable(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
// Not public API.
|
||||
#[doc(hidden)]
|
||||
fn private_deserialize_internally_tagged_enum<V>(
|
||||
self,
|
||||
visitor: V,
|
||||
) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
self.deserialize_any(visitor)
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -1541,6 +1527,15 @@ pub trait Visitor<'de>: Sized {
|
||||
let _ = data;
|
||||
Err(Error::invalid_type(Unexpected::Enum, &self))
|
||||
}
|
||||
|
||||
// Used when deserializing a flattened Option field. Not public API.
|
||||
#[doc(hidden)]
|
||||
fn __private_visit_untagged_option<D>(self, _: D) -> Result<Self::Value, ()>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
+2
-6
@@ -79,14 +79,14 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Serde types in rustdoc of other crates get linked to here.
|
||||
#![doc(html_root_url = "https://docs.rs/serde/1.0.50")]
|
||||
#![doc(html_root_url = "https://docs.rs/serde/1.0.59")]
|
||||
// Support using Serde without the standard library!
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
// Unstable functionality only if the user asks for it. For tracking and
|
||||
// discussion of these features please refer to this issue:
|
||||
//
|
||||
// https://github.com/serde-rs/serde/issues/812
|
||||
#![cfg_attr(feature = "unstable", feature(nonzero, specialization))]
|
||||
#![cfg_attr(feature = "unstable", feature(specialization))]
|
||||
#![cfg_attr(feature = "alloc", feature(alloc))]
|
||||
#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
|
||||
// Whitelisted clippy lints
|
||||
@@ -213,10 +213,6 @@ mod lib {
|
||||
#[cfg(feature = "std")]
|
||||
pub use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
#[allow(deprecated)]
|
||||
pub use core::nonzero::{NonZero, Zeroable};
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
pub use core::num::{NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize};
|
||||
}
|
||||
|
||||
+76
-26
@@ -276,6 +276,8 @@ mod content {
|
||||
match *self {
|
||||
Content::Str(x) => Some(x),
|
||||
Content::String(ref x) => Some(x),
|
||||
Content::Bytes(x) => str::from_utf8(x).ok(),
|
||||
Content::ByteBuf(ref x) => str::from_utf8(x).ok(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -1423,6 +1425,8 @@ mod content {
|
||||
match self.content {
|
||||
Content::String(v) => visitor.visit_string(v),
|
||||
Content::Str(v) => visitor.visit_borrowed_str(v),
|
||||
Content::ByteBuf(v) => visitor.visit_byte_buf(v),
|
||||
Content::Bytes(v) => visitor.visit_borrowed_bytes(v),
|
||||
_ => Err(self.invalid_type(&visitor)),
|
||||
}
|
||||
}
|
||||
@@ -2123,6 +2127,8 @@ mod content {
|
||||
match *self.content {
|
||||
Content::String(ref v) => visitor.visit_str(v),
|
||||
Content::Str(v) => visitor.visit_borrowed_str(v),
|
||||
Content::ByteBuf(ref v) => visitor.visit_bytes(v),
|
||||
Content::Bytes(v) => visitor.visit_borrowed_bytes(v),
|
||||
_ => Err(self.invalid_type(&visitor)),
|
||||
}
|
||||
}
|
||||
@@ -2317,7 +2323,8 @@ mod content {
|
||||
T: de::DeserializeSeed<'de>,
|
||||
{
|
||||
match self.iter.next() {
|
||||
Some(value) => seed.deserialize(ContentRefDeserializer::new(value))
|
||||
Some(value) => seed
|
||||
.deserialize(ContentRefDeserializer::new(value))
|
||||
.map(Some),
|
||||
None => Ok(None),
|
||||
}
|
||||
@@ -2640,6 +2647,30 @@ pub struct FlatMapDeserializer<'a, 'de: 'a, E>(
|
||||
pub PhantomData<E>,
|
||||
);
|
||||
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
impl<'a, 'de, E> FlatMapDeserializer<'a, 'de, E>
|
||||
where
|
||||
E: Error,
|
||||
{
|
||||
fn deserialize_other<V>() -> Result<V, E> {
|
||||
Err(Error::custom("can only flatten structs and maps"))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
macro_rules! forward_to_deserialize_other {
|
||||
($($func:ident ( $($arg:ty),* ))*) => {
|
||||
$(
|
||||
fn $func<V>(self, $(_: $arg,)* _visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
Self::deserialize_other()
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "std", feature = "alloc"))]
|
||||
impl<'a, 'de, E> Deserializer<'de> for FlatMapDeserializer<'a, 'de, E>
|
||||
where
|
||||
@@ -2647,11 +2678,15 @@ where
|
||||
{
|
||||
type Error = E;
|
||||
|
||||
fn deserialize_any<V>(self, _: V) -> Result<V::Value, Self::Error>
|
||||
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
Err(Error::custom("can only flatten structs and maps"))
|
||||
visitor.visit_map(FlatInternallyTaggedAccess {
|
||||
iter: self.0.iter_mut(),
|
||||
pending: None,
|
||||
_marker: PhantomData,
|
||||
})
|
||||
}
|
||||
|
||||
fn deserialize_enum<V>(
|
||||
@@ -2710,24 +2745,40 @@ where
|
||||
visitor.visit_newtype_struct(self)
|
||||
}
|
||||
|
||||
forward_to_deserialize_any! {
|
||||
bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes
|
||||
byte_buf option unit unit_struct seq tuple tuple_struct identifier
|
||||
ignored_any
|
||||
}
|
||||
|
||||
fn private_deserialize_internally_tagged_enum<V>(
|
||||
self,
|
||||
visitor: V,
|
||||
) -> Result<V::Value, Self::Error>
|
||||
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
|
||||
where
|
||||
V: Visitor<'de>,
|
||||
{
|
||||
visitor.visit_map(FlatInternallyTaggedAccess {
|
||||
iter: self.0.iter_mut(),
|
||||
pending: None,
|
||||
_marker: PhantomData,
|
||||
})
|
||||
match visitor.__private_visit_untagged_option(self) {
|
||||
Ok(value) => Ok(value),
|
||||
Err(()) => Self::deserialize_other(),
|
||||
}
|
||||
}
|
||||
|
||||
forward_to_deserialize_other! {
|
||||
deserialize_bool()
|
||||
deserialize_i8()
|
||||
deserialize_i16()
|
||||
deserialize_i32()
|
||||
deserialize_i64()
|
||||
deserialize_u8()
|
||||
deserialize_u16()
|
||||
deserialize_u32()
|
||||
deserialize_u64()
|
||||
deserialize_f32()
|
||||
deserialize_f64()
|
||||
deserialize_char()
|
||||
deserialize_str()
|
||||
deserialize_string()
|
||||
deserialize_bytes()
|
||||
deserialize_byte_buf()
|
||||
deserialize_unit()
|
||||
deserialize_unit_struct(&'static str)
|
||||
deserialize_seq()
|
||||
deserialize_tuple(usize)
|
||||
deserialize_tuple_struct(&'static str, usize)
|
||||
deserialize_identifier()
|
||||
deserialize_ignored_any()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2771,14 +2822,13 @@ where
|
||||
// about. In case we do not know which fields we want, we take them all.
|
||||
let use_item = match *item {
|
||||
None => false,
|
||||
Some((ref c, _)) => {
|
||||
c.as_str()
|
||||
.map_or(self.fields.is_none(), |key| match self.fields {
|
||||
None => true,
|
||||
Some(fields) if fields.contains(&key) => true,
|
||||
_ => false,
|
||||
})
|
||||
}
|
||||
Some((ref c, _)) => c.as_str().map_or(self.fields.is_none(), |key| {
|
||||
match self.fields {
|
||||
None => true,
|
||||
Some(fields) if fields.contains(&key) => true,
|
||||
_ => false,
|
||||
}
|
||||
}),
|
||||
};
|
||||
|
||||
if use_item {
|
||||
|
||||
@@ -947,7 +947,8 @@ mod content {
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
let key = self.key
|
||||
let key = self
|
||||
.key
|
||||
.take()
|
||||
.expect("serialize_value called before serialize_key");
|
||||
let value = try!(value.serialize(ContentSerializer::<E>::new()));
|
||||
@@ -1122,14 +1123,14 @@ where
|
||||
}
|
||||
|
||||
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
|
||||
Err(self.bad_type(Unsupported::Optional))
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn serialize_some<T: ?Sized>(self, _: &T) -> Result<Self::Ok, Self::Error>
|
||||
fn serialize_some<T: ?Sized>(self, value: &T) -> Result<Self::Ok, Self::Error>
|
||||
where
|
||||
T: Serialize,
|
||||
{
|
||||
Err(self.bad_type(Unsupported::Optional))
|
||||
value.serialize(self)
|
||||
}
|
||||
|
||||
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
|
||||
|
||||
+2
-15
@@ -408,20 +408,6 @@ where
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#[cfg(feature = "unstable")]
|
||||
#[allow(deprecated)]
|
||||
impl<T> Serialize for NonZero<T>
|
||||
where
|
||||
T: Serialize + Zeroable + Clone,
|
||||
{
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
self.clone().get().serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! nonzero_integers {
|
||||
( $( $T: ident, )+ ) => {
|
||||
$(
|
||||
@@ -549,7 +535,8 @@ impl Serialize for SystemTime {
|
||||
S: Serializer,
|
||||
{
|
||||
use super::SerializeStruct;
|
||||
let duration_since_epoch = self.duration_since(UNIX_EPOCH)
|
||||
let duration_since_epoch = self
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.expect("SystemTime must be later than UNIX_EPOCH");
|
||||
let mut state = try!(serializer.serialize_struct("SystemTime", 2));
|
||||
try!(state.serialize_field("secs_since_epoch", &duration_since_epoch.as_secs()));
|
||||
|
||||
@@ -81,7 +81,6 @@
|
||||
//! - LinkedList\<T\>
|
||||
//! - VecDeque\<T\>
|
||||
//! - Vec\<T\>
|
||||
//! - EnumSet\<T\> (unstable)
|
||||
//! - **FFI types**:
|
||||
//! - CStr
|
||||
//! - CString
|
||||
@@ -93,7 +92,6 @@
|
||||
//! - Path
|
||||
//! - PathBuf
|
||||
//! - Range\<T\>
|
||||
//! - NonZero\<T\> (unstable, deprecated)
|
||||
//! - num::NonZero* (unstable)
|
||||
//! - **Net types**:
|
||||
//! - IpAddr
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "serde_derive"
|
||||
version = "1.0.50" # remember to update html_root_url
|
||||
version = "1.0.59" # remember to update html_root_url
|
||||
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
|
||||
license = "MIT/Apache-2.0"
|
||||
description = "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]"
|
||||
@@ -23,9 +23,9 @@ name = "serde_derive"
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "0.3"
|
||||
quote = "0.5.2"
|
||||
syn = { version = "0.13", features = ["visit"] }
|
||||
proc-macro2 = "0.4"
|
||||
quote = "0.6"
|
||||
syn = { version = "0.14", features = ["visit"] }
|
||||
|
||||
[dev-dependencies]
|
||||
serde = { version = "1.0", path = "../serde" }
|
||||
|
||||
+37
-13
@@ -55,7 +55,8 @@ pub fn with_where_predicates_from_fields(
|
||||
generics: &syn::Generics,
|
||||
from_field: fn(&attr::Field) -> Option<&[syn::WherePredicate]>,
|
||||
) -> syn::Generics {
|
||||
let predicates = cont.data
|
||||
let predicates = cont
|
||||
.data
|
||||
.all_fields()
|
||||
.flat_map(|field| from_field(&field.attrs))
|
||||
.flat_map(|predicates| predicates.to_vec());
|
||||
@@ -65,6 +66,28 @@ pub fn with_where_predicates_from_fields(
|
||||
generics
|
||||
}
|
||||
|
||||
pub fn with_where_predicates_from_variants(
|
||||
cont: &Container,
|
||||
generics: &syn::Generics,
|
||||
from_variant: fn(&attr::Variant) -> Option<&[syn::WherePredicate]>,
|
||||
) -> syn::Generics {
|
||||
let variants = match cont.data {
|
||||
Data::Enum(ref variants) => variants,
|
||||
Data::Struct(_, _) => {
|
||||
return generics.clone();
|
||||
}
|
||||
};
|
||||
|
||||
let predicates = variants
|
||||
.iter()
|
||||
.flat_map(|variant| from_variant(&variant.attrs))
|
||||
.flat_map(|predicates| predicates.to_vec());
|
||||
|
||||
let mut generics = generics.clone();
|
||||
generics.make_where_clause().predicates.extend(predicates);
|
||||
generics
|
||||
}
|
||||
|
||||
// Puts the given bound on any generic type parameters that are used in fields
|
||||
// for which filter returns true.
|
||||
//
|
||||
@@ -117,9 +140,9 @@ pub fn with_bound(
|
||||
}
|
||||
}
|
||||
if path.leading_colon.is_none() && path.segments.len() == 1 {
|
||||
let id = path.segments[0].ident;
|
||||
if self.all_type_params.contains(&id) {
|
||||
self.relevant_type_params.insert(id);
|
||||
let id = &path.segments[0].ident;
|
||||
if self.all_type_params.contains(id) {
|
||||
self.relevant_type_params.insert(id.clone());
|
||||
}
|
||||
}
|
||||
visit::visit_path(self, path);
|
||||
@@ -134,7 +157,7 @@ pub fn with_bound(
|
||||
fn visit_macro(&mut self, _mac: &'ast syn::Macro) {}
|
||||
}
|
||||
|
||||
let all_type_params = generics.type_params().map(|param| param.ident).collect();
|
||||
let all_type_params = generics.type_params().map(|param| param.ident.clone()).collect();
|
||||
|
||||
let mut visitor = FindTyParams {
|
||||
all_type_params: all_type_params,
|
||||
@@ -162,7 +185,7 @@ pub fn with_bound(
|
||||
let associated_type_usage = visitor.associated_type_usage;
|
||||
let new_predicates = generics
|
||||
.type_params()
|
||||
.map(|param| param.ident)
|
||||
.map(|param| param.ident.clone())
|
||||
.filter(|id| relevant_type_params.contains(id))
|
||||
.map(|id| syn::TypePath {
|
||||
qself: None,
|
||||
@@ -224,7 +247,7 @@ pub fn with_lifetime_bound(generics: &syn::Generics, lifetime: &str) -> syn::Gen
|
||||
let bound = syn::Lifetime::new(lifetime, Span::call_site());
|
||||
let def = syn::LifetimeDef {
|
||||
attrs: Vec::new(),
|
||||
lifetime: bound,
|
||||
lifetime: bound.clone(),
|
||||
colon_token: None,
|
||||
bounds: Punctuated::new(),
|
||||
};
|
||||
@@ -234,10 +257,10 @@ pub fn with_lifetime_bound(generics: &syn::Generics, lifetime: &str) -> syn::Gen
|
||||
.chain(generics.params.iter().cloned().map(|mut param| {
|
||||
match param {
|
||||
syn::GenericParam::Lifetime(ref mut param) => {
|
||||
param.bounds.push(bound);
|
||||
param.bounds.push(bound.clone());
|
||||
}
|
||||
syn::GenericParam::Type(ref mut param) => {
|
||||
param.bounds.push(syn::TypeParamBound::Lifetime(bound));
|
||||
param.bounds.push(syn::TypeParamBound::Lifetime(bound.clone()));
|
||||
}
|
||||
syn::GenericParam::Const(_) => {}
|
||||
}
|
||||
@@ -257,23 +280,24 @@ fn type_of_item(cont: &Container) -> syn::Type {
|
||||
path: syn::Path {
|
||||
leading_colon: None,
|
||||
segments: vec![syn::PathSegment {
|
||||
ident: cont.ident,
|
||||
ident: cont.ident.clone(),
|
||||
arguments: syn::PathArguments::AngleBracketed(
|
||||
syn::AngleBracketedGenericArguments {
|
||||
colon2_token: None,
|
||||
lt_token: Default::default(),
|
||||
args: cont.generics
|
||||
args: cont
|
||||
.generics
|
||||
.params
|
||||
.iter()
|
||||
.map(|param| match *param {
|
||||
syn::GenericParam::Type(ref param) => {
|
||||
syn::GenericArgument::Type(syn::Type::Path(syn::TypePath {
|
||||
qself: None,
|
||||
path: param.ident.into(),
|
||||
path: param.ident.clone().into(),
|
||||
}))
|
||||
}
|
||||
syn::GenericParam::Lifetime(ref param) => {
|
||||
syn::GenericArgument::Lifetime(param.lifetime)
|
||||
syn::GenericArgument::Lifetime(param.lifetime.clone())
|
||||
}
|
||||
syn::GenericParam::Const(_) => {
|
||||
panic!("Serde does not support const generics yet");
|
||||
|
||||
+210
-148
@@ -6,8 +6,8 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use proc_macro2::{Literal, Span};
|
||||
use quote::{ToTokens, Tokens};
|
||||
use proc_macro2::{Literal, Span, TokenStream};
|
||||
use quote::ToTokens;
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{self, Ident, Index, Member};
|
||||
@@ -15,19 +15,19 @@ use syn::{self, Ident, Index, Member};
|
||||
use bound;
|
||||
use fragment::{Expr, Fragment, Match, Stmts};
|
||||
use internals::ast::{Container, Data, Field, Style, Variant};
|
||||
use internals::{attr, Ctxt};
|
||||
use internals::{attr, Ctxt, Derive};
|
||||
use pretend;
|
||||
use try;
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
pub fn expand_derive_deserialize(input: &syn::DeriveInput) -> Result<Tokens, String> {
|
||||
pub fn expand_derive_deserialize(input: &syn::DeriveInput) -> Result<TokenStream, String> {
|
||||
let ctxt = Ctxt::new();
|
||||
let cont = Container::from_ast(&ctxt, input);
|
||||
let cont = Container::from_ast(&ctxt, input, Derive::Deserialize);
|
||||
precondition(&ctxt, &cont);
|
||||
try!(ctxt.check());
|
||||
|
||||
let ident = cont.ident;
|
||||
let ident = &cont.ident;
|
||||
let params = Parameters::new(&cont);
|
||||
let (de_impl_generics, _, ty_generics, where_clause) = split_with_de_lifetime(¶ms);
|
||||
let dummy_const = Ident::new(
|
||||
@@ -82,6 +82,11 @@ pub fn expand_derive_deserialize(input: &syn::DeriveInput) -> Result<Tokens, Str
|
||||
}
|
||||
|
||||
fn precondition(cx: &Ctxt, cont: &Container) {
|
||||
precondition_sized(cx, cont);
|
||||
precondition_no_de_lifetime(cx, cont);
|
||||
}
|
||||
|
||||
fn precondition_sized(cx: &Ctxt, cont: &Container) {
|
||||
if let Data::Struct(_, ref fields) = cont.data {
|
||||
if let Some(last) = fields.last() {
|
||||
if let syn::Type::Slice(_) = *last.ty {
|
||||
@@ -91,6 +96,17 @@ fn precondition(cx: &Ctxt, cont: &Container) {
|
||||
}
|
||||
}
|
||||
|
||||
fn precondition_no_de_lifetime(cx: &Ctxt, cont: &Container) {
|
||||
if let BorrowedLifetimes::Borrowed(_) = borrowed_lifetimes(cont) {
|
||||
for param in cont.generics.lifetimes() {
|
||||
if param.lifetime.to_string() == "'de" {
|
||||
cx.error("cannot deserialize when there is a lifetime parameter called 'de");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Parameters {
|
||||
/// Name of the type the `derive` is on.
|
||||
local: syn::Ident,
|
||||
@@ -114,10 +130,10 @@ struct Parameters {
|
||||
|
||||
impl Parameters {
|
||||
fn new(cont: &Container) -> Self {
|
||||
let local = cont.ident;
|
||||
let local = cont.ident.clone();
|
||||
let this = match cont.attrs.remote() {
|
||||
Some(remote) => remote.clone(),
|
||||
None => cont.ident.into(),
|
||||
None => cont.ident.clone().into(),
|
||||
};
|
||||
let borrowed = borrowed_lifetimes(cont);
|
||||
let generics = build_generics(cont, &borrowed);
|
||||
@@ -134,8 +150,8 @@ impl Parameters {
|
||||
|
||||
/// Type name to use in error messages and `&'static str` arguments to
|
||||
/// various Deserializer methods.
|
||||
fn type_name(&self) -> &str {
|
||||
self.this.segments.last().unwrap().value().ident.as_ref()
|
||||
fn type_name(&self) -> String {
|
||||
self.this.segments.last().unwrap().value().ident.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,6 +163,9 @@ fn build_generics(cont: &Container, borrowed: &BorrowedLifetimes) -> syn::Generi
|
||||
|
||||
let generics = bound::with_where_predicates_from_fields(cont, &generics, attr::Field::de_bound);
|
||||
|
||||
let generics =
|
||||
bound::with_where_predicates_from_variants(cont, &generics, attr::Variant::de_bound);
|
||||
|
||||
match cont.attrs.de_bound() {
|
||||
Some(predicates) => bound::with_where_predicates(&generics, predicates),
|
||||
None => {
|
||||
@@ -175,13 +194,19 @@ fn build_generics(cont: &Container, borrowed: &BorrowedLifetimes) -> syn::Generi
|
||||
}
|
||||
}
|
||||
|
||||
// Fields with a `skip_deserializing` or `deserialize_with` attribute are not
|
||||
// deserialized by us so we do not generate a bound. Fields with a `bound`
|
||||
// attribute specify their own bound so we do not generate one. All other fields
|
||||
// may need a `T: Deserialize` bound where T is the type of the field.
|
||||
// Fields with a `skip_deserializing` or `deserialize_with` attribute, or which
|
||||
// belong to a variant with a `skip_deserializing` or `deserialize_with`
|
||||
// attribute, are not deserialized by us so we do not generate a bound. Fields
|
||||
// with a `bound` attribute specify their own bound so we do not generate one.
|
||||
// All other fields may need a `T: Deserialize` bound where T is the type of the
|
||||
// field.
|
||||
fn needs_deserialize_bound(field: &attr::Field, variant: Option<&attr::Variant>) -> bool {
|
||||
!field.skip_deserializing() && field.deserialize_with().is_none() && field.de_bound().is_none()
|
||||
&& variant.map_or(true, |variant| variant.deserialize_with().is_none())
|
||||
&& variant.map_or(true, |variant| {
|
||||
!variant.skip_deserializing()
|
||||
&& variant.deserialize_with().is_none()
|
||||
&& variant.de_bound().is_none()
|
||||
})
|
||||
}
|
||||
|
||||
// Fields with a `default` attribute (not `default=...`), and fields with a
|
||||
@@ -244,21 +269,17 @@ fn borrowed_lifetimes(cont: &Container) -> BorrowedLifetimes {
|
||||
}
|
||||
|
||||
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)
|
||||
} else if let attr::Identifier::No = cont.attrs.identifier() {
|
||||
match cont.data {
|
||||
Data::Enum(ref variants) => deserialize_enum(params, variants, &cont.attrs),
|
||||
Data::Struct(Style::Struct, ref fields) => {
|
||||
if fields.iter().any(|field| field.ident.is_none()) {
|
||||
panic!("struct has unnamed fields");
|
||||
}
|
||||
deserialize_struct(None, params, fields, &cont.attrs, None, &Untagged::No)
|
||||
}
|
||||
Data::Struct(Style::Tuple, ref fields) | Data::Struct(Style::Newtype, ref fields) => {
|
||||
if fields.iter().any(|field| field.ident.is_some()) {
|
||||
panic!("tuple struct has named fields");
|
||||
}
|
||||
deserialize_tuple(None, params, fields, &cont.attrs, None)
|
||||
}
|
||||
Data::Struct(Style::Unit, _) => deserialize_unit_struct(params, &cont.attrs),
|
||||
@@ -279,8 +300,11 @@ fn deserialize_in_place_body(cont: &Container, params: &Parameters) -> Option<St
|
||||
// deserialize_in_place for remote derives.
|
||||
assert!(!params.has_getter);
|
||||
|
||||
if cont.attrs.type_from().is_some() || cont.attrs.identifier().is_some()
|
||||
|| cont.data
|
||||
if cont.attrs.transparent()
|
||||
|| cont.attrs.type_from().is_some()
|
||||
|| cont.attrs.identifier().is_some()
|
||||
|| cont
|
||||
.data
|
||||
.all_fields()
|
||||
.all(|f| f.attrs.deserialize_with().is_some())
|
||||
{
|
||||
@@ -324,6 +348,41 @@ fn deserialize_in_place_body(_cont: &Container, _params: &Parameters) -> Option<
|
||||
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 {
|
||||
quote_block! {
|
||||
_serde::export::Result::map(
|
||||
@@ -362,11 +421,11 @@ fn deserialize_unit_struct(params: &Parameters, cattrs: &attr::Container) -> Fra
|
||||
}
|
||||
|
||||
fn deserialize_tuple(
|
||||
variant_ident: Option<syn::Ident>,
|
||||
variant_ident: Option<&syn::Ident>,
|
||||
params: &Parameters,
|
||||
fields: &[Field],
|
||||
cattrs: &attr::Container,
|
||||
deserializer: Option<Tokens>,
|
||||
deserializer: Option<TokenStream>,
|
||||
) -> Fragment {
|
||||
let this = ¶ms.this;
|
||||
let (de_impl_generics, de_ty_generics, ty_generics, where_clause) =
|
||||
@@ -379,7 +438,7 @@ fn deserialize_tuple(
|
||||
// and use an `Into` conversion to get the remote type. If there are no
|
||||
// getters then construct the target type directly.
|
||||
let construct = if params.has_getter {
|
||||
let local = params.local;
|
||||
let local = ¶ms.local;
|
||||
quote!(#local)
|
||||
} else {
|
||||
quote!(#this)
|
||||
@@ -387,7 +446,7 @@ fn deserialize_tuple(
|
||||
|
||||
let is_enum = variant_ident.is_some();
|
||||
let type_path = match variant_ident {
|
||||
Some(variant_ident) => quote!(#construct::#variant_ident),
|
||||
Some(ref variant_ident) => quote!(#construct::#variant_ident),
|
||||
None => construct,
|
||||
};
|
||||
let expecting = match variant_ident {
|
||||
@@ -403,7 +462,9 @@ fn deserialize_tuple(
|
||||
None
|
||||
};
|
||||
|
||||
let visit_seq = Stmts(deserialize_seq(&type_path, params, fields, false, cattrs));
|
||||
let visit_seq = Stmts(deserialize_seq(
|
||||
&type_path, params, fields, false, cattrs, &expecting,
|
||||
));
|
||||
|
||||
let visitor_expr = quote! {
|
||||
__Visitor {
|
||||
@@ -464,7 +525,7 @@ fn deserialize_tuple_in_place(
|
||||
params: &Parameters,
|
||||
fields: &[Field],
|
||||
cattrs: &attr::Container,
|
||||
deserializer: Option<Tokens>,
|
||||
deserializer: Option<TokenStream>,
|
||||
) -> Fragment {
|
||||
let this = ¶ms.this;
|
||||
let (de_impl_generics, de_ty_generics, ty_generics, where_clause) =
|
||||
@@ -487,7 +548,7 @@ fn deserialize_tuple_in_place(
|
||||
None
|
||||
};
|
||||
|
||||
let visit_seq = Stmts(deserialize_seq_in_place(params, fields, cattrs));
|
||||
let visit_seq = Stmts(deserialize_seq_in_place(params, fields, cattrs, &expecting));
|
||||
|
||||
let visitor_expr = quote! {
|
||||
__Visitor {
|
||||
@@ -548,21 +609,24 @@ fn deserialize_tuple_in_place(
|
||||
}
|
||||
|
||||
fn deserialize_seq(
|
||||
type_path: &Tokens,
|
||||
type_path: &TokenStream,
|
||||
params: &Parameters,
|
||||
fields: &[Field],
|
||||
is_struct: bool,
|
||||
cattrs: &attr::Container,
|
||||
expecting: &str,
|
||||
) -> Fragment {
|
||||
let vars = (0..fields.len()).map(field_i as fn(_) -> _);
|
||||
|
||||
// XXX: do we need an error for flattening here?
|
||||
|
||||
let deserialized_count = fields
|
||||
.iter()
|
||||
.filter(|field| !field.attrs.skip_deserializing())
|
||||
.count();
|
||||
let expecting = format!("tuple of {} elements", deserialized_count);
|
||||
let expecting = if deserialized_count == 1 {
|
||||
format!("{} with 1 element", expecting)
|
||||
} else {
|
||||
format!("{} with {} elements", expecting, deserialized_count)
|
||||
};
|
||||
|
||||
let mut index_in_seq = 0_usize;
|
||||
let let_values = vars.clone().zip(fields).map(|(var, field)| {
|
||||
@@ -604,7 +668,7 @@ fn deserialize_seq(
|
||||
});
|
||||
|
||||
let mut result = if is_struct {
|
||||
let names = fields.iter().map(|f| &f.ident);
|
||||
let names = fields.iter().map(|f| &f.member);
|
||||
quote! {
|
||||
#type_path { #( #names: #vars ),* }
|
||||
}
|
||||
@@ -647,69 +711,60 @@ fn deserialize_seq_in_place(
|
||||
params: &Parameters,
|
||||
fields: &[Field],
|
||||
cattrs: &attr::Container,
|
||||
expecting: &str,
|
||||
) -> Fragment {
|
||||
let vars = (0..fields.len()).map(field_i as fn(_) -> _);
|
||||
|
||||
// XXX: do we need an error for flattening here?
|
||||
|
||||
let deserialized_count = fields
|
||||
.iter()
|
||||
.filter(|field| !field.attrs.skip_deserializing())
|
||||
.count();
|
||||
let expecting = format!("tuple of {} elements", deserialized_count);
|
||||
let expecting = if deserialized_count == 1 {
|
||||
format!("{} with 1 element", expecting)
|
||||
} else {
|
||||
format!("{} with {} elements", expecting, deserialized_count)
|
||||
};
|
||||
|
||||
let mut index_in_seq = 0usize;
|
||||
let write_values = vars.clone()
|
||||
.zip(fields)
|
||||
.enumerate()
|
||||
.map(|(field_index, (_, field))| {
|
||||
// If there's no field name, assume we're a tuple-struct and use a numeric index
|
||||
let field_name = field.ident.map(Member::Named).unwrap_or_else(|| {
|
||||
Member::Unnamed(Index {
|
||||
index: field_index as u32,
|
||||
span: Span::call_site(),
|
||||
})
|
||||
});
|
||||
let write_values = fields.iter().map(|field| {
|
||||
let member = &field.member;
|
||||
|
||||
if field.attrs.skip_deserializing() {
|
||||
let default = Expr(expr_is_missing(field, cattrs));
|
||||
quote! {
|
||||
self.place.#field_name = #default;
|
||||
}
|
||||
} else {
|
||||
let return_invalid_length = quote! {
|
||||
return _serde::export::Err(_serde::de::Error::invalid_length(#index_in_seq, &#expecting));
|
||||
};
|
||||
let write = match field.attrs.deserialize_with() {
|
||||
None => {
|
||||
quote! {
|
||||
if let _serde::export::None = try!(_serde::de::SeqAccess::next_element_seed(&mut __seq,
|
||||
_serde::private::de::InPlaceSeed(&mut self.place.#field_name)))
|
||||
{
|
||||
#return_invalid_length
|
||||
}
|
||||
if field.attrs.skip_deserializing() {
|
||||
let default = Expr(expr_is_missing(field, cattrs));
|
||||
quote! {
|
||||
self.place.#member = #default;
|
||||
}
|
||||
} else {
|
||||
let return_invalid_length = quote! {
|
||||
return _serde::export::Err(_serde::de::Error::invalid_length(#index_in_seq, &#expecting));
|
||||
};
|
||||
let write = match field.attrs.deserialize_with() {
|
||||
None => {
|
||||
quote! {
|
||||
if let _serde::export::None = try!(_serde::de::SeqAccess::next_element_seed(&mut __seq,
|
||||
_serde::private::de::InPlaceSeed(&mut self.place.#member)))
|
||||
{
|
||||
#return_invalid_length
|
||||
}
|
||||
}
|
||||
Some(path) => {
|
||||
let (wrapper, wrapper_ty) =
|
||||
wrap_deserialize_field_with(params, field.ty, path);
|
||||
quote!({
|
||||
}
|
||||
Some(path) => {
|
||||
let (wrapper, wrapper_ty) = wrap_deserialize_field_with(params, field.ty, path);
|
||||
quote!({
|
||||
#wrapper
|
||||
match try!(_serde::de::SeqAccess::next_element::<#wrapper_ty>(&mut __seq)) {
|
||||
_serde::export::Some(__wrap) => {
|
||||
self.place.#field_name = __wrap.value;
|
||||
self.place.#member = __wrap.value;
|
||||
}
|
||||
_serde::export::None => {
|
||||
#return_invalid_length
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
};
|
||||
index_in_seq += 1;
|
||||
write
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
index_in_seq += 1;
|
||||
write
|
||||
}
|
||||
});
|
||||
|
||||
let this = ¶ms.this;
|
||||
let (_, ty_generics, _) = params.generics.split_for_impl();
|
||||
@@ -734,7 +789,7 @@ fn deserialize_seq_in_place(
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_newtype_struct(type_path: &Tokens, params: &Parameters, field: &Field) -> Tokens {
|
||||
fn deserialize_newtype_struct(type_path: &TokenStream, params: &Parameters, field: &Field) -> TokenStream {
|
||||
let delife = params.borrowed.de_lifetime();
|
||||
let field_ty = field.ty;
|
||||
|
||||
@@ -772,7 +827,7 @@ fn deserialize_newtype_struct(type_path: &Tokens, params: &Parameters, field: &F
|
||||
}
|
||||
|
||||
#[cfg(feature = "deserialize_in_place")]
|
||||
fn deserialize_newtype_struct_in_place(params: &Parameters, field: &Field) -> Tokens {
|
||||
fn deserialize_newtype_struct_in_place(params: &Parameters, field: &Field) -> TokenStream {
|
||||
// We do not generate deserialize_in_place if every field has a deserialize_with.
|
||||
assert!(field.attrs.deserialize_with().is_none());
|
||||
|
||||
@@ -795,11 +850,11 @@ enum Untagged {
|
||||
}
|
||||
|
||||
fn deserialize_struct(
|
||||
variant_ident: Option<syn::Ident>,
|
||||
variant_ident: Option<&syn::Ident>,
|
||||
params: &Parameters,
|
||||
fields: &[Field],
|
||||
cattrs: &attr::Container,
|
||||
deserializer: Option<Tokens>,
|
||||
deserializer: Option<TokenStream>,
|
||||
untagged: &Untagged,
|
||||
) -> Fragment {
|
||||
let is_enum = variant_ident.is_some();
|
||||
@@ -813,14 +868,14 @@ fn deserialize_struct(
|
||||
// and use an `Into` conversion to get the remote type. If there are no
|
||||
// getters then construct the target type directly.
|
||||
let construct = if params.has_getter {
|
||||
let local = params.local;
|
||||
let local = ¶ms.local;
|
||||
quote!(#local)
|
||||
} else {
|
||||
quote!(#this)
|
||||
};
|
||||
|
||||
let type_path = match variant_ident {
|
||||
Some(variant_ident) => quote!(#construct::#variant_ident),
|
||||
Some(ref variant_ident) => quote!(#construct::#variant_ident),
|
||||
None => construct,
|
||||
};
|
||||
let expecting = match variant_ident {
|
||||
@@ -828,7 +883,9 @@ fn deserialize_struct(
|
||||
None => format!("struct {}", params.type_name()),
|
||||
};
|
||||
|
||||
let visit_seq = Stmts(deserialize_seq(&type_path, params, fields, true, cattrs));
|
||||
let visit_seq = Stmts(deserialize_seq(
|
||||
&type_path, params, fields, true, cattrs, &expecting,
|
||||
));
|
||||
|
||||
let (field_visitor, fields_stmt, visit_map) = if cattrs.has_flatten() {
|
||||
deserialize_struct_as_map_visitor(&type_path, params, fields, cattrs)
|
||||
@@ -947,7 +1004,7 @@ fn deserialize_struct_in_place(
|
||||
params: &Parameters,
|
||||
fields: &[Field],
|
||||
cattrs: &attr::Container,
|
||||
deserializer: Option<Tokens>,
|
||||
deserializer: Option<TokenStream>,
|
||||
) -> Option<Fragment> {
|
||||
let is_enum = variant_ident.is_some();
|
||||
|
||||
@@ -967,7 +1024,7 @@ fn deserialize_struct_in_place(
|
||||
None => format!("struct {}", params.type_name()),
|
||||
};
|
||||
|
||||
let visit_seq = Stmts(deserialize_seq_in_place(params, fields, cattrs));
|
||||
let visit_seq = Stmts(deserialize_seq_in_place(params, fields, cattrs, &expecting));
|
||||
|
||||
let (field_visitor, fields_stmt, visit_map) =
|
||||
deserialize_struct_as_struct_in_place_visitor(params, fields, cattrs);
|
||||
@@ -1227,7 +1284,7 @@ fn deserialize_internally_tagged_enum(
|
||||
|
||||
#variants_stmt
|
||||
|
||||
let __tagged = try!(_serde::Deserializer::private_deserialize_internally_tagged_enum(
|
||||
let __tagged = try!(_serde::Deserializer::deserialize_any(
|
||||
__deserializer,
|
||||
_serde::private::de::TaggedContentVisitor::<__Field>::new(#tag)));
|
||||
|
||||
@@ -1332,7 +1389,7 @@ fn deserialize_adjacently_tagged_enum(
|
||||
.filter(|&(_, variant)| !variant.attrs.skip_deserializing() && is_unit(variant))
|
||||
.map(|(i, variant)| {
|
||||
let variant_index = field_i(i);
|
||||
let variant_ident = variant.ident;
|
||||
let variant_ident = &variant.ident;
|
||||
quote! {
|
||||
__Field::#variant_index => _serde::export::Ok(#this::#variant_ident),
|
||||
}
|
||||
@@ -1393,6 +1450,21 @@ fn deserialize_adjacently_tagged_enum(
|
||||
}
|
||||
};
|
||||
|
||||
let finish_content_then_tag = if variant_arms.is_empty() {
|
||||
quote! {
|
||||
match try!(_serde::de::MapAccess::next_value::<__Field>(&mut __map)) {}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
let __ret = try!(match try!(_serde::de::MapAccess::next_value(&mut __map)) {
|
||||
// Deserialize the buffered content now that we know the variant.
|
||||
#(#variant_arms)*
|
||||
});
|
||||
// Visit remaining keys, looking for duplicates.
|
||||
#visit_remaining_keys
|
||||
}
|
||||
};
|
||||
|
||||
quote_block! {
|
||||
#variant_visitor
|
||||
|
||||
@@ -1469,13 +1541,7 @@ fn deserialize_adjacently_tagged_enum(
|
||||
// Second key is the tag.
|
||||
_serde::export::Some(_serde::private::de::TagOrContentField::Tag) => {
|
||||
let __deserializer = _serde::private::de::ContentDeserializer::<__A::Error>::new(__content);
|
||||
// Parse the tag.
|
||||
let __ret = try!(match try!(_serde::de::MapAccess::next_value(&mut __map)) {
|
||||
// Deserialize the buffered content now that we know the variant.
|
||||
#(#variant_arms)*
|
||||
});
|
||||
// Visit remaining keys, looking for duplicates.
|
||||
#visit_remaining_keys
|
||||
#finish_content_then_tag
|
||||
}
|
||||
// Second key is a duplicate of the content.
|
||||
_serde::export::Some(_serde::private::de::TagOrContentField::Content) => {
|
||||
@@ -1587,7 +1653,7 @@ fn deserialize_externally_tagged_variant(
|
||||
};
|
||||
}
|
||||
|
||||
let variant_ident = variant.ident;
|
||||
let variant_ident = &variant.ident;
|
||||
|
||||
match variant.style {
|
||||
Style::Unit => {
|
||||
@@ -1618,19 +1684,19 @@ fn deserialize_internally_tagged_variant(
|
||||
params: &Parameters,
|
||||
variant: &Variant,
|
||||
cattrs: &attr::Container,
|
||||
deserializer: Tokens,
|
||||
deserializer: TokenStream,
|
||||
) -> Fragment {
|
||||
if variant.attrs.deserialize_with().is_some() {
|
||||
return deserialize_untagged_variant(params, variant, cattrs, deserializer);
|
||||
}
|
||||
|
||||
let variant_ident = variant.ident;
|
||||
let variant_ident = &variant.ident;
|
||||
|
||||
match variant.style {
|
||||
Style::Unit => {
|
||||
let this = ¶ms.this;
|
||||
let type_name = params.type_name();
|
||||
let variant_name = variant.ident.as_ref();
|
||||
let variant_name = variant.ident.to_string();
|
||||
quote_block! {
|
||||
try!(_serde::Deserializer::deserialize_any(#deserializer, _serde::private::de::InternallyTaggedUnitVisitor::new(#type_name, #variant_name)));
|
||||
_serde::export::Ok(#this::#variant_ident)
|
||||
@@ -1658,7 +1724,7 @@ fn deserialize_untagged_variant(
|
||||
params: &Parameters,
|
||||
variant: &Variant,
|
||||
cattrs: &attr::Container,
|
||||
deserializer: Tokens,
|
||||
deserializer: TokenStream,
|
||||
) -> Fragment {
|
||||
if let Some(path) = variant.attrs.deserialize_with() {
|
||||
let (wrapper, wrapper_ty, unwrap_fn) = wrap_deserialize_variant_with(params, variant, path);
|
||||
@@ -1669,13 +1735,13 @@ fn deserialize_untagged_variant(
|
||||
};
|
||||
}
|
||||
|
||||
let variant_ident = variant.ident;
|
||||
let variant_ident = &variant.ident;
|
||||
|
||||
match variant.style {
|
||||
Style::Unit => {
|
||||
let this = ¶ms.this;
|
||||
let type_name = params.type_name();
|
||||
let variant_name = variant.ident.as_ref();
|
||||
let variant_name = variant.ident.to_string();
|
||||
quote_expr! {
|
||||
match _serde::Deserializer::deserialize_any(
|
||||
#deserializer,
|
||||
@@ -1711,7 +1777,7 @@ fn deserialize_untagged_variant(
|
||||
}
|
||||
|
||||
fn deserialize_externally_tagged_newtype_variant(
|
||||
variant_ident: syn::Ident,
|
||||
variant_ident: &syn::Ident,
|
||||
params: &Parameters,
|
||||
field: &Field,
|
||||
) -> Fragment {
|
||||
@@ -1738,10 +1804,10 @@ fn deserialize_externally_tagged_newtype_variant(
|
||||
}
|
||||
|
||||
fn deserialize_untagged_newtype_variant(
|
||||
variant_ident: syn::Ident,
|
||||
variant_ident: &syn::Ident,
|
||||
params: &Parameters,
|
||||
field: &Field,
|
||||
deserializer: &Tokens,
|
||||
deserializer: &TokenStream,
|
||||
) -> Fragment {
|
||||
let this = ¶ms.this;
|
||||
let field_ty = field.ty;
|
||||
@@ -1755,10 +1821,8 @@ fn deserialize_untagged_newtype_variant(
|
||||
}
|
||||
Some(path) => {
|
||||
quote_block! {
|
||||
let __value: #field_ty = _serde::export::Result::map(
|
||||
#path(#deserializer),
|
||||
#this::#variant_ident);
|
||||
__value
|
||||
let __value: _serde::export::Result<#field_ty, _> = #path(#deserializer);
|
||||
_serde::export::Result::map(__value, #this::#variant_ident)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1840,7 +1904,7 @@ fn deserialize_custom_identifier(
|
||||
let this = quote!(#this);
|
||||
|
||||
let (ordinary, fallthrough) = if let Some(last) = variants.last() {
|
||||
let last_ident = last.ident;
|
||||
let last_ident = &last.ident;
|
||||
if last.attrs.other() {
|
||||
let ordinary = &variants[..variants.len() - 1];
|
||||
let fallthrough = quote!(_serde::export::Ok(#this::#last_ident));
|
||||
@@ -1863,7 +1927,7 @@ fn deserialize_custom_identifier(
|
||||
|
||||
let names_idents: Vec<_> = ordinary
|
||||
.iter()
|
||||
.map(|variant| (variant.attrs.name().deserialize_name(), variant.ident))
|
||||
.map(|variant| (variant.attrs.name().deserialize_name(), variant.ident.clone()))
|
||||
.collect();
|
||||
|
||||
let names = names_idents.iter().map(|&(ref name, _)| name);
|
||||
@@ -1916,10 +1980,10 @@ fn deserialize_custom_identifier(
|
||||
}
|
||||
|
||||
fn deserialize_identifier(
|
||||
this: &Tokens,
|
||||
this: &TokenStream,
|
||||
fields: &[(String, Ident)],
|
||||
is_variant: bool,
|
||||
fallthrough: Option<Tokens>,
|
||||
fallthrough: Option<TokenStream>,
|
||||
collect_other_fields: bool,
|
||||
) -> Fragment {
|
||||
let field_strs = fields.iter().map(|&(ref name, _)| name);
|
||||
@@ -2173,7 +2237,7 @@ fn deserialize_identifier(
|
||||
}
|
||||
|
||||
fn deserialize_struct_as_struct_visitor(
|
||||
struct_path: &Tokens,
|
||||
struct_path: &TokenStream,
|
||||
params: &Parameters,
|
||||
fields: &[Field],
|
||||
cattrs: &attr::Container,
|
||||
@@ -2202,7 +2266,7 @@ fn deserialize_struct_as_struct_visitor(
|
||||
}
|
||||
|
||||
fn deserialize_struct_as_map_visitor(
|
||||
struct_path: &Tokens,
|
||||
struct_path: &TokenStream,
|
||||
params: &Parameters,
|
||||
fields: &[Field],
|
||||
cattrs: &attr::Container,
|
||||
@@ -2222,7 +2286,7 @@ fn deserialize_struct_as_map_visitor(
|
||||
}
|
||||
|
||||
fn deserialize_map(
|
||||
struct_path: &Tokens,
|
||||
struct_path: &TokenStream,
|
||||
params: &Parameters,
|
||||
fields: &[Field],
|
||||
cattrs: &attr::Container,
|
||||
@@ -2377,12 +2441,12 @@ fn deserialize_map(
|
||||
};
|
||||
|
||||
let result = fields_names.iter().map(|&(field, ref name)| {
|
||||
let ident = field.ident.expect("struct contains unnamed fields");
|
||||
let member = &field.member;
|
||||
if field.attrs.skip_deserializing() {
|
||||
let value = Expr(expr_is_missing(field, cattrs));
|
||||
quote!(#ident: #value)
|
||||
quote!(#member: #value)
|
||||
} else {
|
||||
quote!(#ident: #name)
|
||||
quote!(#member: #name)
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2487,19 +2551,19 @@ fn deserialize_map_in_place(
|
||||
.filter(|&&(field, _)| !field.attrs.skip_deserializing())
|
||||
.map(|&(field, ref name)| {
|
||||
let deser_name = field.attrs.name().deserialize_name();
|
||||
let field_name = field.ident;
|
||||
let member = &field.member;
|
||||
|
||||
let visit = match field.attrs.deserialize_with() {
|
||||
None => {
|
||||
quote! {
|
||||
try!(_serde::de::MapAccess::next_value_seed(&mut __map, _serde::private::de::InPlaceSeed(&mut self.place.#field_name)))
|
||||
try!(_serde::de::MapAccess::next_value_seed(&mut __map, _serde::private::de::InPlaceSeed(&mut self.place.#member)))
|
||||
}
|
||||
}
|
||||
Some(path) => {
|
||||
let (wrapper, wrapper_ty) = wrap_deserialize_field_with(params, field.ty, path);
|
||||
quote!({
|
||||
#wrapper
|
||||
self.place.#field_name = try!(_serde::de::MapAccess::next_value::<#wrapper_ty>(&mut __map)).value
|
||||
self.place.#member = try!(_serde::de::MapAccess::next_value::<#wrapper_ty>(&mut __map)).value
|
||||
})
|
||||
}
|
||||
};
|
||||
@@ -2551,7 +2615,8 @@ fn deserialize_map_in_place(
|
||||
let missing_expr = expr_is_missing(field, cattrs);
|
||||
// If missing_expr unconditionally returns an error, don't try
|
||||
// to assign its value to self.place.
|
||||
if field.attrs.default().is_none() && cattrs.default().is_none()
|
||||
if field.attrs.default().is_none()
|
||||
&& cattrs.default().is_none()
|
||||
&& field.attrs.deserialize_with().is_some()
|
||||
{
|
||||
let missing_expr = Stmts(missing_expr);
|
||||
@@ -2561,11 +2626,11 @@ fn deserialize_map_in_place(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let field_name = field.ident;
|
||||
let member = &field.member;
|
||||
let missing_expr = Expr(missing_expr);
|
||||
quote! {
|
||||
if !#name {
|
||||
self.place.#field_name = #missing_expr;
|
||||
self.place.#member = #missing_expr;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -2609,9 +2674,9 @@ fn field_i(i: usize) -> Ident {
|
||||
/// in a trait to prevent it from accessing the internal `Deserialize` state.
|
||||
fn wrap_deserialize_with(
|
||||
params: &Parameters,
|
||||
value_ty: &Tokens,
|
||||
value_ty: &TokenStream,
|
||||
deserialize_with: &syn::ExprPath,
|
||||
) -> (Tokens, Tokens) {
|
||||
) -> (TokenStream, TokenStream) {
|
||||
let this = ¶ms.this;
|
||||
let (de_impl_generics, de_ty_generics, ty_generics, where_clause) =
|
||||
split_with_de_lifetime(params);
|
||||
@@ -2647,7 +2712,7 @@ fn wrap_deserialize_field_with(
|
||||
params: &Parameters,
|
||||
field_ty: &syn::Type,
|
||||
deserialize_with: &syn::ExprPath,
|
||||
) -> (Tokens, Tokens) {
|
||||
) -> (TokenStream, TokenStream) {
|
||||
wrap_deserialize_with(params, "e!(#field_ty), deserialize_with)
|
||||
}
|
||||
|
||||
@@ -2655,9 +2720,9 @@ fn wrap_deserialize_variant_with(
|
||||
params: &Parameters,
|
||||
variant: &Variant,
|
||||
deserialize_with: &syn::ExprPath,
|
||||
) -> (Tokens, Tokens, Tokens) {
|
||||
) -> (TokenStream, TokenStream, TokenStream) {
|
||||
let this = ¶ms.this;
|
||||
let variant_ident = variant.ident;
|
||||
let variant_ident = &variant.ident;
|
||||
|
||||
let field_tys = variant.fields.iter().map(|field| field.ty);
|
||||
let (wrapper, wrapper_ty) =
|
||||
@@ -2671,18 +2736,15 @@ fn wrap_deserialize_variant_with(
|
||||
});
|
||||
let unwrap_fn = match variant.style {
|
||||
Style::Struct if variant.fields.len() == 1 => {
|
||||
let field_ident = variant.fields[0].ident.unwrap();
|
||||
let member = &variant.fields[0].member;
|
||||
quote! {
|
||||
|__wrap| #this::#variant_ident { #field_ident: __wrap.value }
|
||||
|__wrap| #this::#variant_ident { #member: __wrap.value }
|
||||
}
|
||||
}
|
||||
Style::Struct => {
|
||||
let field_idents = variant
|
||||
.fields
|
||||
.iter()
|
||||
.map(|field| field.ident.as_ref().unwrap());
|
||||
let members = variant.fields.iter().map(|field| &field.member);
|
||||
quote! {
|
||||
|__wrap| #this::#variant_ident { #(#field_idents: __wrap.value.#field_access),* }
|
||||
|__wrap| #this::#variant_ident { #(#members: __wrap.value.#field_access),* }
|
||||
}
|
||||
}
|
||||
Style::Tuple => quote! {
|
||||
@@ -2712,8 +2774,8 @@ fn expr_is_missing(field: &Field, cattrs: &attr::Container) -> Fragment {
|
||||
|
||||
match *cattrs.default() {
|
||||
attr::Default::Default | attr::Default::Path(_) => {
|
||||
let ident = field.ident;
|
||||
return quote_expr!(__default.#ident);
|
||||
let member = &field.member;
|
||||
return quote_expr!(__default.#member);
|
||||
}
|
||||
attr::Default::None => { /* below */ }
|
||||
}
|
||||
@@ -2740,7 +2802,7 @@ struct DeImplGenerics<'a>(&'a Parameters);
|
||||
struct InPlaceImplGenerics<'a>(&'a Parameters);
|
||||
|
||||
impl<'a> ToTokens for DeImplGenerics<'a> {
|
||||
fn to_tokens(&self, tokens: &mut Tokens) {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let mut generics = self.0.generics.clone();
|
||||
if let Some(de_lifetime) = self.0.borrowed.de_lifetime_def() {
|
||||
generics.params = Some(syn::GenericParam::Lifetime(de_lifetime))
|
||||
@@ -2755,7 +2817,7 @@ impl<'a> ToTokens for DeImplGenerics<'a> {
|
||||
|
||||
#[cfg(feature = "deserialize_in_place")]
|
||||
impl<'a> ToTokens for InPlaceImplGenerics<'a> {
|
||||
fn to_tokens(&self, tokens: &mut Tokens) {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let place_lifetime = place_lifetime();
|
||||
let mut generics = self.0.generics.clone();
|
||||
|
||||
@@ -2763,12 +2825,12 @@ impl<'a> ToTokens for InPlaceImplGenerics<'a> {
|
||||
for param in &mut generics.params {
|
||||
match *param {
|
||||
syn::GenericParam::Lifetime(ref mut param) => {
|
||||
param.bounds.push(place_lifetime.lifetime);
|
||||
param.bounds.push(place_lifetime.lifetime.clone());
|
||||
}
|
||||
syn::GenericParam::Type(ref mut param) => {
|
||||
param
|
||||
.bounds
|
||||
.push(syn::TypeParamBound::Lifetime(place_lifetime.lifetime));
|
||||
.push(syn::TypeParamBound::Lifetime(place_lifetime.lifetime.clone()));
|
||||
}
|
||||
syn::GenericParam::Const(_) => {}
|
||||
}
|
||||
@@ -2800,7 +2862,7 @@ struct DeTypeGenerics<'a>(&'a Parameters);
|
||||
struct InPlaceTypeGenerics<'a>(&'a Parameters);
|
||||
|
||||
impl<'a> ToTokens for DeTypeGenerics<'a> {
|
||||
fn to_tokens(&self, tokens: &mut Tokens) {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let mut generics = self.0.generics.clone();
|
||||
if self.0.borrowed.de_lifetime_def().is_some() {
|
||||
let def = syn::LifetimeDef {
|
||||
@@ -2821,7 +2883,7 @@ impl<'a> ToTokens for DeTypeGenerics<'a> {
|
||||
|
||||
#[cfg(feature = "deserialize_in_place")]
|
||||
impl<'a> ToTokens for InPlaceTypeGenerics<'a> {
|
||||
fn to_tokens(&self, tokens: &mut Tokens) {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let mut generics = self.0.generics.clone();
|
||||
generics.params = Some(syn::GenericParam::Lifetime(place_lifetime()))
|
||||
.into_iter()
|
||||
|
||||
@@ -6,15 +6,16 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use quote::{ToTokens, Tokens};
|
||||
use quote::ToTokens;
|
||||
use syn::token;
|
||||
use proc_macro2::TokenStream;
|
||||
|
||||
pub enum Fragment {
|
||||
/// Tokens that can be used as an expression.
|
||||
Expr(Tokens),
|
||||
Expr(TokenStream),
|
||||
/// Tokens that can be used inside a block. The surrounding curly braces are
|
||||
/// not part of these tokens.
|
||||
Block(Tokens),
|
||||
Block(TokenStream),
|
||||
}
|
||||
|
||||
macro_rules! quote_expr {
|
||||
@@ -33,7 +34,7 @@ macro_rules! quote_block {
|
||||
/// Block fragments in curly braces.
|
||||
pub struct Expr(pub Fragment);
|
||||
impl ToTokens for Expr {
|
||||
fn to_tokens(&self, out: &mut Tokens) {
|
||||
fn to_tokens(&self, out: &mut TokenStream) {
|
||||
match self.0 {
|
||||
Fragment::Expr(ref expr) => expr.to_tokens(out),
|
||||
Fragment::Block(ref block) => {
|
||||
@@ -46,7 +47,7 @@ impl ToTokens for Expr {
|
||||
/// Interpolate a fragment as the statements of a block.
|
||||
pub struct Stmts(pub Fragment);
|
||||
impl ToTokens for Stmts {
|
||||
fn to_tokens(&self, out: &mut Tokens) {
|
||||
fn to_tokens(&self, out: &mut TokenStream) {
|
||||
match self.0 {
|
||||
Fragment::Expr(ref expr) => expr.to_tokens(out),
|
||||
Fragment::Block(ref block) => block.to_tokens(out),
|
||||
@@ -58,7 +59,7 @@ impl ToTokens for Stmts {
|
||||
/// involves putting a comma after expressions and curly braces around blocks.
|
||||
pub struct Match(pub Fragment);
|
||||
impl ToTokens for Match {
|
||||
fn to_tokens(&self, out: &mut Tokens) {
|
||||
fn to_tokens(&self, out: &mut TokenStream) {
|
||||
match self.0 {
|
||||
Fragment::Expr(ref expr) => {
|
||||
expr.to_tokens(out);
|
||||
@@ -71,8 +72,8 @@ impl ToTokens for Match {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Tokens> for Fragment {
|
||||
fn as_ref(&self) -> &Tokens {
|
||||
impl AsRef<TokenStream> for Fragment {
|
||||
fn as_ref(&self) -> &TokenStream {
|
||||
match *self {
|
||||
Fragment::Expr(ref expr) => expr,
|
||||
Fragment::Block(ref block) => block,
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
use internals::attr;
|
||||
use internals::check;
|
||||
use internals::Ctxt;
|
||||
use internals::{Ctxt, Derive};
|
||||
use syn;
|
||||
use syn::punctuated::Punctuated;
|
||||
|
||||
@@ -32,7 +32,7 @@ pub struct Variant<'a> {
|
||||
}
|
||||
|
||||
pub struct Field<'a> {
|
||||
pub ident: Option<syn::Ident>,
|
||||
pub member: syn::Member,
|
||||
pub attrs: attr::Field,
|
||||
pub ty: &'a syn::Type,
|
||||
pub original: &'a syn::Field,
|
||||
@@ -47,7 +47,7 @@ pub enum Style {
|
||||
}
|
||||
|
||||
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 data = match item.data {
|
||||
@@ -86,13 +86,13 @@ impl<'a> Container<'a> {
|
||||
attrs.mark_has_flatten();
|
||||
}
|
||||
|
||||
let item = Container {
|
||||
ident: item.ident,
|
||||
let mut item = Container {
|
||||
ident: item.ident.clone(),
|
||||
attrs: attrs,
|
||||
data: data,
|
||||
generics: &item.generics,
|
||||
};
|
||||
check::check(cx, &item);
|
||||
check::check(cx, &mut item, derive);
|
||||
item
|
||||
}
|
||||
}
|
||||
@@ -124,7 +124,7 @@ fn enum_from_ast<'a>(
|
||||
let (style, fields) =
|
||||
struct_from_ast(cx, &variant.fields, Some(&attrs), container_default);
|
||||
Variant {
|
||||
ident: variant.ident,
|
||||
ident: variant.ident.clone(),
|
||||
attrs: attrs,
|
||||
style: style,
|
||||
fields: fields,
|
||||
@@ -166,7 +166,10 @@ fn fields_from_ast<'a>(
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, field)| Field {
|
||||
ident: field.ident,
|
||||
member: match field.ident {
|
||||
Some(ref ident) => syn::Member::Named(ident.clone()),
|
||||
None => syn::Member::Unnamed(i.into()),
|
||||
},
|
||||
attrs: attr::Field::from_ast(cx, i, field, attrs, container_default),
|
||||
ty: &field.ty,
|
||||
original: field,
|
||||
|
||||
@@ -105,6 +105,7 @@ impl Name {
|
||||
/// Represents container (e.g. struct) attribute information
|
||||
pub struct Container {
|
||||
name: Name,
|
||||
transparent: bool,
|
||||
deny_unknown_fields: bool,
|
||||
default: Default,
|
||||
rename_all: RenameRule,
|
||||
@@ -181,6 +182,7 @@ impl Container {
|
||||
pub fn from_ast(cx: &Ctxt, item: &syn::DeriveInput) -> Self {
|
||||
let mut ser_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 default = Attr::none(cx, "default");
|
||||
let mut rename_all = Attr::none(cx, "rename_all");
|
||||
@@ -200,7 +202,7 @@ impl Container {
|
||||
match meta_item {
|
||||
// Parse `#[serde(rename = "foo")]`
|
||||
Meta(NameValue(ref m)) if m.ident == "rename" => {
|
||||
if let Ok(s) = get_lit_str(cx, m.ident.as_ref(), m.ident.as_ref(), &m.lit) {
|
||||
if let Ok(s) = get_lit_str(cx, &m.ident, &m.ident, &m.lit) {
|
||||
ser_name.set(s.value());
|
||||
de_name.set(s.value());
|
||||
}
|
||||
@@ -216,7 +218,7 @@ impl Container {
|
||||
|
||||
// Parse `#[serde(rename_all = "foo")]`
|
||||
Meta(NameValue(ref m)) if m.ident == "rename_all" => {
|
||||
if let Ok(s) = get_lit_str(cx, m.ident.as_ref(), m.ident.as_ref(), &m.lit) {
|
||||
if let Ok(s) = get_lit_str(cx, &m.ident, &m.ident, &m.lit) {
|
||||
match RenameRule::from_str(&s.value()) {
|
||||
Ok(rename_rule) => rename_all.set(rename_rule),
|
||||
Err(()) => cx.error(format!(
|
||||
@@ -228,13 +230,18 @@ impl Container {
|
||||
}
|
||||
}
|
||||
|
||||
// Parse `#[serde(transparent)]`
|
||||
Meta(Word(ref word)) if word == "transparent" => {
|
||||
transparent.set_true();
|
||||
}
|
||||
|
||||
// Parse `#[serde(deny_unknown_fields)]`
|
||||
Meta(Word(word)) if word == "deny_unknown_fields" => {
|
||||
Meta(Word(ref word)) if word == "deny_unknown_fields" => {
|
||||
deny_unknown_fields.set_true();
|
||||
}
|
||||
|
||||
// Parse `#[serde(default)]`
|
||||
Meta(Word(word)) if word == "default" => match item.data {
|
||||
Meta(Word(ref word)) if word == "default" => match item.data {
|
||||
syn::Data::Struct(syn::DataStruct {
|
||||
fields: syn::Fields::Named(_),
|
||||
..
|
||||
@@ -249,7 +256,7 @@ impl Container {
|
||||
|
||||
// Parse `#[serde(default = "...")]`
|
||||
Meta(NameValue(ref m)) if m.ident == "default" => {
|
||||
if let Ok(path) = parse_lit_into_expr_path(cx, m.ident.as_ref(), &m.lit) {
|
||||
if let Ok(path) = parse_lit_into_expr_path(cx, &m.ident, &m.lit) {
|
||||
match item.data {
|
||||
syn::Data::Struct(syn::DataStruct {
|
||||
fields: syn::Fields::Named(_),
|
||||
@@ -268,7 +275,7 @@ impl Container {
|
||||
// Parse `#[serde(bound = "D: Serialize")]`
|
||||
Meta(NameValue(ref m)) if m.ident == "bound" => {
|
||||
if let Ok(where_predicates) =
|
||||
parse_lit_into_where(cx, m.ident.as_ref(), m.ident.as_ref(), &m.lit)
|
||||
parse_lit_into_where(cx, &m.ident, &m.ident, &m.lit)
|
||||
{
|
||||
ser_bound.set(where_predicates.clone());
|
||||
de_bound.set(where_predicates);
|
||||
@@ -284,7 +291,7 @@ impl Container {
|
||||
}
|
||||
|
||||
// Parse `#[serde(untagged)]`
|
||||
Meta(Word(word)) if word == "untagged" => match item.data {
|
||||
Meta(Word(ref word)) if word == "untagged" => match item.data {
|
||||
syn::Data::Enum(_) => {
|
||||
untagged.set_true();
|
||||
}
|
||||
@@ -295,7 +302,7 @@ impl Container {
|
||||
|
||||
// Parse `#[serde(tag = "type")]`
|
||||
Meta(NameValue(ref m)) if m.ident == "tag" => {
|
||||
if let Ok(s) = get_lit_str(cx, m.ident.as_ref(), m.ident.as_ref(), &m.lit) {
|
||||
if let Ok(s) = get_lit_str(cx, &m.ident, &m.ident, &m.lit) {
|
||||
match item.data {
|
||||
syn::Data::Enum(_) => {
|
||||
internal_tag.set(s.value());
|
||||
@@ -309,7 +316,7 @@ impl Container {
|
||||
|
||||
// Parse `#[serde(content = "c")]`
|
||||
Meta(NameValue(ref m)) if m.ident == "content" => {
|
||||
if let Ok(s) = get_lit_str(cx, m.ident.as_ref(), m.ident.as_ref(), &m.lit) {
|
||||
if let Ok(s) = get_lit_str(cx, &m.ident, &m.ident, &m.lit) {
|
||||
match item.data {
|
||||
syn::Data::Enum(_) => {
|
||||
content.set(s.value());
|
||||
@@ -324,23 +331,23 @@ impl Container {
|
||||
|
||||
// Parse `#[serde(from = "Type")]
|
||||
Meta(NameValue(ref m)) if m.ident == "from" => {
|
||||
if let Ok(from_ty) = parse_lit_into_ty(cx, m.ident.as_ref(), &m.lit) {
|
||||
if let Ok(from_ty) = parse_lit_into_ty(cx, &m.ident, &m.lit) {
|
||||
type_from.set_opt(Some(from_ty));
|
||||
}
|
||||
}
|
||||
|
||||
// Parse `#[serde(into = "Type")]
|
||||
Meta(NameValue(ref m)) if m.ident == "into" => {
|
||||
if let Ok(into_ty) = parse_lit_into_ty(cx, m.ident.as_ref(), &m.lit) {
|
||||
if let Ok(into_ty) = parse_lit_into_ty(cx, &m.ident, &m.lit) {
|
||||
type_into.set_opt(Some(into_ty));
|
||||
}
|
||||
}
|
||||
|
||||
// Parse `#[serde(remote = "...")]`
|
||||
Meta(NameValue(ref m)) if m.ident == "remote" => {
|
||||
if let Ok(path) = parse_lit_into_path(cx, m.ident.as_ref(), &m.lit) {
|
||||
if let Ok(path) = parse_lit_into_path(cx, &m.ident, &m.lit) {
|
||||
if is_primitive_path(&path, "Self") {
|
||||
remote.set(item.ident.into());
|
||||
remote.set(item.ident.clone().into());
|
||||
} else {
|
||||
remote.set(path);
|
||||
}
|
||||
@@ -348,12 +355,12 @@ impl Container {
|
||||
}
|
||||
|
||||
// Parse `#[serde(field_identifier)]`
|
||||
Meta(Word(word)) if word == "field_identifier" => {
|
||||
Meta(Word(ref word)) if word == "field_identifier" => {
|
||||
field_identifier.set_true();
|
||||
}
|
||||
|
||||
// Parse `#[serde(variant_identifier)]`
|
||||
Meta(Word(word)) if word == "variant_identifier" => {
|
||||
Meta(Word(ref word)) if word == "variant_identifier" => {
|
||||
variant_identifier.set_true();
|
||||
}
|
||||
|
||||
@@ -376,6 +383,7 @@ impl Container {
|
||||
serialize: ser_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(),
|
||||
default: default.get().unwrap_or(Default::None),
|
||||
rename_all: rename_all.get().unwrap_or(RenameRule::None),
|
||||
@@ -398,6 +406,10 @@ impl Container {
|
||||
&self.rename_all
|
||||
}
|
||||
|
||||
pub fn transparent(&self) -> bool {
|
||||
self.transparent
|
||||
}
|
||||
|
||||
pub fn deny_unknown_fields(&self) -> bool {
|
||||
self.deny_unknown_fields
|
||||
}
|
||||
@@ -527,6 +539,8 @@ pub struct Variant {
|
||||
ser_renamed: bool,
|
||||
de_renamed: bool,
|
||||
rename_all: RenameRule,
|
||||
ser_bound: Option<Vec<syn::WherePredicate>>,
|
||||
de_bound: Option<Vec<syn::WherePredicate>>,
|
||||
skip_deserializing: bool,
|
||||
skip_serializing: bool,
|
||||
other: bool,
|
||||
@@ -542,6 +556,8 @@ impl Variant {
|
||||
let mut skip_deserializing = BoolAttr::none(cx, "skip_deserializing");
|
||||
let mut skip_serializing = BoolAttr::none(cx, "skip_serializing");
|
||||
let mut rename_all = Attr::none(cx, "rename_all");
|
||||
let mut ser_bound = Attr::none(cx, "bound");
|
||||
let mut de_bound = Attr::none(cx, "bound");
|
||||
let mut other = BoolAttr::none(cx, "other");
|
||||
let mut serialize_with = Attr::none(cx, "serialize_with");
|
||||
let mut deserialize_with = Attr::none(cx, "deserialize_with");
|
||||
@@ -552,7 +568,7 @@ impl Variant {
|
||||
match meta_item {
|
||||
// Parse `#[serde(rename = "foo")]`
|
||||
Meta(NameValue(ref m)) if m.ident == "rename" => {
|
||||
if let Ok(s) = get_lit_str(cx, m.ident.as_ref(), m.ident.as_ref(), &m.lit) {
|
||||
if let Ok(s) = get_lit_str(cx, &m.ident, &m.ident, &m.lit) {
|
||||
ser_name.set(s.value());
|
||||
de_name.set(s.value());
|
||||
}
|
||||
@@ -568,7 +584,7 @@ impl Variant {
|
||||
|
||||
// Parse `#[serde(rename_all = "foo")]`
|
||||
Meta(NameValue(ref m)) if m.ident == "rename_all" => {
|
||||
if let Ok(s) = get_lit_str(cx, m.ident.as_ref(), m.ident.as_ref(), &m.lit) {
|
||||
if let Ok(s) = get_lit_str(cx, &m.ident, &m.ident, &m.lit) {
|
||||
match RenameRule::from_str(&s.value()) {
|
||||
Ok(rename_rule) => rename_all.set(rename_rule),
|
||||
Err(()) => cx.error(format!(
|
||||
@@ -580,24 +596,48 @@ impl Variant {
|
||||
}
|
||||
}
|
||||
|
||||
// Parse `#[serde(skip)]`
|
||||
Meta(Word(ref word)) if word == "skip" => {
|
||||
skip_serializing.set_true();
|
||||
skip_deserializing.set_true();
|
||||
}
|
||||
|
||||
// Parse `#[serde(skip_deserializing)]`
|
||||
Meta(Word(word)) if word == "skip_deserializing" => {
|
||||
Meta(Word(ref word)) if word == "skip_deserializing" => {
|
||||
skip_deserializing.set_true();
|
||||
}
|
||||
|
||||
// Parse `#[serde(skip_serializing)]`
|
||||
Meta(Word(word)) if word == "skip_serializing" => {
|
||||
Meta(Word(ref word)) if word == "skip_serializing" => {
|
||||
skip_serializing.set_true();
|
||||
}
|
||||
|
||||
// Parse `#[serde(other)]`
|
||||
Meta(Word(word)) if word == "other" => {
|
||||
Meta(Word(ref word)) if word == "other" => {
|
||||
other.set_true();
|
||||
}
|
||||
|
||||
// Parse `#[serde(bound = "D: Serialize")]`
|
||||
Meta(NameValue(ref m)) if m.ident == "bound" => {
|
||||
if let Ok(where_predicates) =
|
||||
parse_lit_into_where(cx, &m.ident, &m.ident, &m.lit)
|
||||
{
|
||||
ser_bound.set(where_predicates.clone());
|
||||
de_bound.set(where_predicates);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse `#[serde(bound(serialize = "D: Serialize", deserialize = "D: Deserialize"))]`
|
||||
Meta(List(ref m)) if m.ident == "bound" => {
|
||||
if let Ok((ser, de)) = get_where_predicates(cx, &m.nested) {
|
||||
ser_bound.set_opt(ser);
|
||||
de_bound.set_opt(de);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse `#[serde(with = "...")]`
|
||||
Meta(NameValue(ref m)) if m.ident == "with" => {
|
||||
if let Ok(path) = parse_lit_into_expr_path(cx, m.ident.as_ref(), &m.lit) {
|
||||
if let Ok(path) = parse_lit_into_expr_path(cx, &m.ident, &m.lit) {
|
||||
let mut ser_path = path.clone();
|
||||
ser_path
|
||||
.path
|
||||
@@ -615,14 +655,14 @@ impl Variant {
|
||||
|
||||
// Parse `#[serde(serialize_with = "...")]`
|
||||
Meta(NameValue(ref m)) if m.ident == "serialize_with" => {
|
||||
if let Ok(path) = parse_lit_into_expr_path(cx, m.ident.as_ref(), &m.lit) {
|
||||
if let Ok(path) = parse_lit_into_expr_path(cx, &m.ident, &m.lit) {
|
||||
serialize_with.set(path);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse `#[serde(deserialize_with = "...")]`
|
||||
Meta(NameValue(ref m)) if m.ident == "deserialize_with" => {
|
||||
if let Ok(path) = parse_lit_into_expr_path(cx, m.ident.as_ref(), &m.lit) {
|
||||
if let Ok(path) = parse_lit_into_expr_path(cx, &m.ident, &m.lit) {
|
||||
deserialize_with.set(path);
|
||||
}
|
||||
}
|
||||
@@ -663,6 +703,8 @@ impl Variant {
|
||||
ser_renamed: ser_renamed,
|
||||
de_renamed: de_renamed,
|
||||
rename_all: rename_all.get().unwrap_or(RenameRule::None),
|
||||
ser_bound: ser_bound.get(),
|
||||
de_bound: de_bound.get(),
|
||||
skip_deserializing: skip_deserializing.get(),
|
||||
skip_serializing: skip_serializing.get(),
|
||||
other: other.get(),
|
||||
@@ -689,6 +731,14 @@ impl Variant {
|
||||
&self.rename_all
|
||||
}
|
||||
|
||||
pub fn ser_bound(&self) -> Option<&[syn::WherePredicate]> {
|
||||
self.ser_bound.as_ref().map(|vec| &vec[..])
|
||||
}
|
||||
|
||||
pub fn de_bound(&self) -> Option<&[syn::WherePredicate]> {
|
||||
self.de_bound.as_ref().map(|vec| &vec[..])
|
||||
}
|
||||
|
||||
pub fn skip_deserializing(&self) -> bool {
|
||||
self.skip_deserializing
|
||||
}
|
||||
@@ -726,6 +776,7 @@ pub struct Field {
|
||||
borrowed_lifetimes: BTreeSet<syn::Lifetime>,
|
||||
getter: Option<syn::ExprPath>,
|
||||
flatten: bool,
|
||||
transparent: bool,
|
||||
}
|
||||
|
||||
/// Represents the default to use for a field when deserializing.
|
||||
@@ -739,7 +790,6 @@ pub enum Default {
|
||||
}
|
||||
|
||||
impl Default {
|
||||
#[cfg(feature = "deserialize_in_place")]
|
||||
pub fn is_none(&self) -> bool {
|
||||
match *self {
|
||||
Default::None => true,
|
||||
@@ -790,7 +840,7 @@ impl Field {
|
||||
match meta_item {
|
||||
// Parse `#[serde(rename = "foo")]`
|
||||
Meta(NameValue(ref m)) if m.ident == "rename" => {
|
||||
if let Ok(s) = get_lit_str(cx, m.ident.as_ref(), m.ident.as_ref(), &m.lit) {
|
||||
if let Ok(s) = get_lit_str(cx, &m.ident, &m.ident, &m.lit) {
|
||||
ser_name.set(s.value());
|
||||
de_name.set(s.value());
|
||||
}
|
||||
@@ -805,57 +855,57 @@ impl Field {
|
||||
}
|
||||
|
||||
// Parse `#[serde(default)]`
|
||||
Meta(Word(word)) if word == "default" => {
|
||||
Meta(Word(ref word)) if word == "default" => {
|
||||
default.set(Default::Default);
|
||||
}
|
||||
|
||||
// Parse `#[serde(default = "...")]`
|
||||
Meta(NameValue(ref m)) if m.ident == "default" => {
|
||||
if let Ok(path) = parse_lit_into_expr_path(cx, m.ident.as_ref(), &m.lit) {
|
||||
if let Ok(path) = parse_lit_into_expr_path(cx, &m.ident, &m.lit) {
|
||||
default.set(Default::Path(path));
|
||||
}
|
||||
}
|
||||
|
||||
// Parse `#[serde(skip_serializing)]`
|
||||
Meta(Word(word)) if word == "skip_serializing" => {
|
||||
Meta(Word(ref word)) if word == "skip_serializing" => {
|
||||
skip_serializing.set_true();
|
||||
}
|
||||
|
||||
// Parse `#[serde(skip_deserializing)]`
|
||||
Meta(Word(word)) if word == "skip_deserializing" => {
|
||||
Meta(Word(ref word)) if word == "skip_deserializing" => {
|
||||
skip_deserializing.set_true();
|
||||
}
|
||||
|
||||
// Parse `#[serde(skip)]`
|
||||
Meta(Word(word)) if word == "skip" => {
|
||||
Meta(Word(ref word)) if word == "skip" => {
|
||||
skip_serializing.set_true();
|
||||
skip_deserializing.set_true();
|
||||
}
|
||||
|
||||
// Parse `#[serde(skip_serializing_if = "...")]`
|
||||
Meta(NameValue(ref m)) if m.ident == "skip_serializing_if" => {
|
||||
if let Ok(path) = parse_lit_into_expr_path(cx, m.ident.as_ref(), &m.lit) {
|
||||
if let Ok(path) = parse_lit_into_expr_path(cx, &m.ident, &m.lit) {
|
||||
skip_serializing_if.set(path);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse `#[serde(serialize_with = "...")]`
|
||||
Meta(NameValue(ref m)) if m.ident == "serialize_with" => {
|
||||
if let Ok(path) = parse_lit_into_expr_path(cx, m.ident.as_ref(), &m.lit) {
|
||||
if let Ok(path) = parse_lit_into_expr_path(cx, &m.ident, &m.lit) {
|
||||
serialize_with.set(path);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse `#[serde(deserialize_with = "...")]`
|
||||
Meta(NameValue(ref m)) if m.ident == "deserialize_with" => {
|
||||
if let Ok(path) = parse_lit_into_expr_path(cx, m.ident.as_ref(), &m.lit) {
|
||||
if let Ok(path) = parse_lit_into_expr_path(cx, &m.ident, &m.lit) {
|
||||
deserialize_with.set(path);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse `#[serde(with = "...")]`
|
||||
Meta(NameValue(ref m)) if m.ident == "with" => {
|
||||
if let Ok(path) = parse_lit_into_expr_path(cx, m.ident.as_ref(), &m.lit) {
|
||||
if let Ok(path) = parse_lit_into_expr_path(cx, &m.ident, &m.lit) {
|
||||
let mut ser_path = path.clone();
|
||||
ser_path
|
||||
.path
|
||||
@@ -874,7 +924,7 @@ impl Field {
|
||||
// Parse `#[serde(bound = "D: Serialize")]`
|
||||
Meta(NameValue(ref m)) if m.ident == "bound" => {
|
||||
if let Ok(where_predicates) =
|
||||
parse_lit_into_where(cx, m.ident.as_ref(), m.ident.as_ref(), &m.lit)
|
||||
parse_lit_into_where(cx, &m.ident, &m.ident, &m.lit)
|
||||
{
|
||||
ser_bound.set(where_predicates.clone());
|
||||
de_bound.set(where_predicates);
|
||||
@@ -890,7 +940,7 @@ impl Field {
|
||||
}
|
||||
|
||||
// Parse `#[serde(borrow)]`
|
||||
Meta(Word(word)) if word == "borrow" => {
|
||||
Meta(Word(ref word)) if word == "borrow" => {
|
||||
if let Ok(borrowable) = borrowable_lifetimes(cx, &ident, &field.ty) {
|
||||
borrowed_lifetimes.set(borrowable);
|
||||
}
|
||||
@@ -899,7 +949,7 @@ impl Field {
|
||||
// Parse `#[serde(borrow = "'a + 'b")]`
|
||||
Meta(NameValue(ref m)) if m.ident == "borrow" => {
|
||||
if let Ok(lifetimes) =
|
||||
parse_lit_into_lifetimes(cx, m.ident.as_ref(), &m.lit)
|
||||
parse_lit_into_lifetimes(cx, &m.ident, &m.lit)
|
||||
{
|
||||
if let Ok(borrowable) = borrowable_lifetimes(cx, &ident, &field.ty) {
|
||||
for lifetime in &lifetimes {
|
||||
@@ -917,13 +967,13 @@ impl Field {
|
||||
|
||||
// Parse `#[serde(getter = "...")]`
|
||||
Meta(NameValue(ref m)) if m.ident == "getter" => {
|
||||
if let Ok(path) = parse_lit_into_expr_path(cx, m.ident.as_ref(), &m.lit) {
|
||||
if let Ok(path) = parse_lit_into_expr_path(cx, &m.ident, &m.lit) {
|
||||
getter.set(path);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse `#[serde(flatten)]`
|
||||
Meta(Word(word)) if word == "flatten" => {
|
||||
Meta(Word(ref word)) if word == "flatten" => {
|
||||
flatten.set_true();
|
||||
}
|
||||
|
||||
@@ -1000,7 +1050,7 @@ impl Field {
|
||||
};
|
||||
deserialize_with.set_if_none(expr);
|
||||
}
|
||||
} else if is_rptr(&field.ty, is_str) || is_rptr(&field.ty, is_slice_u8) {
|
||||
} else if is_implicitly_borrowed(&field.ty) {
|
||||
// Types &str and &[u8] are always implicitly borrowed. No need for
|
||||
// a #[serde(borrow)].
|
||||
collect_lifetimes(&field.ty, &mut borrowed_lifetimes);
|
||||
@@ -1028,6 +1078,7 @@ impl Field {
|
||||
borrowed_lifetimes: borrowed_lifetimes,
|
||||
getter: getter.get(),
|
||||
flatten: flatten.get(),
|
||||
transparent: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1087,6 +1138,14 @@ impl Field {
|
||||
pub fn flatten(&self) -> bool {
|
||||
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>);
|
||||
@@ -1099,21 +1158,22 @@ fn get_ser_and_de<'a, T, F>(
|
||||
) -> Result<SerAndDe<T>, ()>
|
||||
where
|
||||
T: 'a,
|
||||
F: Fn(&Ctxt, &str, &str, &'a syn::Lit) -> Result<T, ()>,
|
||||
F: Fn(&Ctxt, &Ident, &Ident, &'a syn::Lit) -> Result<T, ()>,
|
||||
{
|
||||
let mut ser_meta = Attr::none(cx, attr_name);
|
||||
let mut de_meta = Attr::none(cx, attr_name);
|
||||
let attr_name = Ident::new(attr_name, Span::call_site());
|
||||
|
||||
for meta in metas {
|
||||
match *meta {
|
||||
Meta(NameValue(ref meta)) if meta.ident == "serialize" => {
|
||||
if let Ok(v) = f(cx, attr_name, meta.ident.as_ref(), &meta.lit) {
|
||||
if let Ok(v) = f(cx, &attr_name, &meta.ident, &meta.lit) {
|
||||
ser_meta.set(v);
|
||||
}
|
||||
}
|
||||
|
||||
Meta(NameValue(ref meta)) if meta.ident == "deserialize" => {
|
||||
if let Ok(v) = f(cx, attr_name, meta.ident.as_ref(), &meta.lit) {
|
||||
if let Ok(v) = f(cx, &attr_name, &meta.ident, &meta.lit) {
|
||||
de_meta.set(v);
|
||||
}
|
||||
}
|
||||
@@ -1162,8 +1222,8 @@ pub fn get_serde_meta_items(attr: &syn::Attribute) -> Option<Vec<syn::NestedMeta
|
||||
|
||||
fn get_lit_str<'a>(
|
||||
cx: &Ctxt,
|
||||
attr_name: &str,
|
||||
meta_item_name: &str,
|
||||
attr_name: &Ident,
|
||||
meta_item_name: &Ident,
|
||||
lit: &'a syn::Lit,
|
||||
) -> Result<&'a syn::LitStr, ()> {
|
||||
if let syn::Lit::Str(ref lit) = *lit {
|
||||
@@ -1177,7 +1237,7 @@ fn get_lit_str<'a>(
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_lit_into_path(cx: &Ctxt, attr_name: &str, lit: &syn::Lit) -> Result<syn::Path, ()> {
|
||||
fn parse_lit_into_path(cx: &Ctxt, attr_name: &Ident, lit: &syn::Lit) -> Result<syn::Path, ()> {
|
||||
let string = try!(get_lit_str(cx, attr_name, attr_name, lit));
|
||||
parse_lit_str(string)
|
||||
.map_err(|_| cx.error(format!("failed to parse path: {:?}", string.value())))
|
||||
@@ -1185,7 +1245,7 @@ fn parse_lit_into_path(cx: &Ctxt, attr_name: &str, lit: &syn::Lit) -> Result<syn
|
||||
|
||||
fn parse_lit_into_expr_path(
|
||||
cx: &Ctxt,
|
||||
attr_name: &str,
|
||||
attr_name: &Ident,
|
||||
lit: &syn::Lit,
|
||||
) -> Result<syn::ExprPath, ()> {
|
||||
let string = try!(get_lit_str(cx, attr_name, attr_name, lit));
|
||||
@@ -1195,8 +1255,8 @@ fn parse_lit_into_expr_path(
|
||||
|
||||
fn parse_lit_into_where(
|
||||
cx: &Ctxt,
|
||||
attr_name: &str,
|
||||
meta_item_name: &str,
|
||||
attr_name: &Ident,
|
||||
meta_item_name: &Ident,
|
||||
lit: &syn::Lit,
|
||||
) -> Result<Vec<syn::WherePredicate>, ()> {
|
||||
let string = try!(get_lit_str(cx, attr_name, meta_item_name, lit));
|
||||
@@ -1211,7 +1271,7 @@ fn parse_lit_into_where(
|
||||
.map_err(|err| cx.error(err))
|
||||
}
|
||||
|
||||
fn parse_lit_into_ty(cx: &Ctxt, attr_name: &str, lit: &syn::Lit) -> Result<syn::Type, ()> {
|
||||
fn parse_lit_into_ty(cx: &Ctxt, attr_name: &Ident, lit: &syn::Lit) -> Result<syn::Type, ()> {
|
||||
let string = try!(get_lit_str(cx, attr_name, attr_name, lit));
|
||||
|
||||
parse_lit_str(string).map_err(|_| {
|
||||
@@ -1227,7 +1287,7 @@ fn parse_lit_into_ty(cx: &Ctxt, attr_name: &str, lit: &syn::Lit) -> Result<syn::
|
||||
// lifetimes separated by `+`.
|
||||
fn parse_lit_into_lifetimes(
|
||||
cx: &Ctxt,
|
||||
attr_name: &str,
|
||||
attr_name: &Ident,
|
||||
lit: &syn::Lit,
|
||||
) -> Result<BTreeSet<syn::Lifetime>, ()> {
|
||||
let string = try!(get_lit_str(cx, attr_name, attr_name, lit));
|
||||
@@ -1248,7 +1308,7 @@ fn parse_lit_into_lifetimes(
|
||||
if let Ok(BorrowedLifetimes(lifetimes)) = parse_lit_str(string) {
|
||||
let mut set = BTreeSet::new();
|
||||
for lifetime in lifetimes {
|
||||
if !set.insert(lifetime) {
|
||||
if !set.insert(lifetime.clone()) {
|
||||
cx.error(format!("duplicate borrowed lifetime `{}`", lifetime));
|
||||
}
|
||||
}
|
||||
@@ -1262,6 +1322,14 @@ fn parse_lit_into_lifetimes(
|
||||
Err(())
|
||||
}
|
||||
|
||||
fn is_implicitly_borrowed(ty: &syn::Type) -> bool {
|
||||
is_implicitly_borrowed_reference(ty) || is_option(ty, is_implicitly_borrowed_reference)
|
||||
}
|
||||
|
||||
fn is_implicitly_borrowed_reference(ty: &syn::Type) -> bool {
|
||||
is_reference(ty, is_str) || is_reference(ty, is_slice_u8)
|
||||
}
|
||||
|
||||
// Whether the type looks like it might be `std::borrow::Cow<T>` where elem="T".
|
||||
// This can have false negatives and false positives.
|
||||
//
|
||||
@@ -1309,6 +1377,31 @@ fn is_cow(ty: &syn::Type, elem: fn(&syn::Type) -> bool) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
fn is_option(ty: &syn::Type, elem: fn(&syn::Type) -> bool) -> bool {
|
||||
let path = match *ty {
|
||||
syn::Type::Path(ref ty) => &ty.path,
|
||||
_ => {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
let seg = match path.segments.last() {
|
||||
Some(seg) => seg.into_value(),
|
||||
None => {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
let args = match seg.arguments {
|
||||
syn::PathArguments::AngleBracketed(ref bracketed) => &bracketed.args,
|
||||
_ => {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
seg.ident == "Option" && args.len() == 1 && match args[0] {
|
||||
syn::GenericArgument::Type(ref arg) => elem(arg),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
// Whether the type looks like it might be `&T` where elem="T". This can have
|
||||
// false negatives and false positives.
|
||||
//
|
||||
@@ -1329,7 +1422,7 @@ fn is_cow(ty: &syn::Type, elem: fn(&syn::Type) -> bool) -> bool {
|
||||
// struct S<'a> {
|
||||
// r: &'a str,
|
||||
// }
|
||||
fn is_rptr(ty: &syn::Type, elem: fn(&syn::Type) -> bool) -> bool {
|
||||
fn is_reference(ty: &syn::Type, elem: fn(&syn::Type) -> bool) -> bool {
|
||||
match *ty {
|
||||
syn::Type::Reference(ref ty) => ty.mutability.is_none() && elem(&ty.elem),
|
||||
_ => false,
|
||||
@@ -1355,7 +1448,9 @@ fn is_primitive_type(ty: &syn::Type, primitive: &str) -> bool {
|
||||
}
|
||||
|
||||
fn is_primitive_path(path: &syn::Path, primitive: &str) -> bool {
|
||||
path.leading_colon.is_none() && path.segments.len() == 1 && path.segments[0].ident == primitive
|
||||
path.leading_colon.is_none()
|
||||
&& path.segments.len() == 1
|
||||
&& path.segments[0].ident == primitive
|
||||
&& path.segments[0].arguments.is_empty()
|
||||
}
|
||||
|
||||
|
||||
@@ -8,17 +8,19 @@
|
||||
|
||||
use internals::ast::{Container, Data, Field, Style};
|
||||
use internals::attr::{EnumTag, Identifier};
|
||||
use internals::Ctxt;
|
||||
use internals::{Ctxt, Derive};
|
||||
use syn::{Member, Type};
|
||||
|
||||
/// Cross-cutting checks that require looking at more than a single 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_flatten(cx, cont);
|
||||
check_identifier(cx, cont);
|
||||
check_variant_skip_attrs(cx, cont);
|
||||
check_internal_tag_field_name_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`
|
||||
@@ -171,17 +173,14 @@ fn check_variant_skip_attrs(cx: &Ctxt, cont: &Container) {
|
||||
));
|
||||
}
|
||||
|
||||
for (i, field) in variant.fields.iter().enumerate() {
|
||||
let ident = field
|
||||
.ident
|
||||
.as_ref()
|
||||
.map_or_else(|| format!("{}", i), |ident| format!("`{}`", ident));
|
||||
for field in &variant.fields {
|
||||
let member = member_message(&field.member);
|
||||
|
||||
if field.attrs.skip_serializing() {
|
||||
cx.error(format!(
|
||||
"variant `{}` cannot have both #[serde(serialize_with)] and \
|
||||
a field {} marked with #[serde(skip_serializing)]",
|
||||
variant.ident, ident
|
||||
variant.ident, member
|
||||
));
|
||||
}
|
||||
|
||||
@@ -189,7 +188,7 @@ fn check_variant_skip_attrs(cx: &Ctxt, cont: &Container) {
|
||||
cx.error(format!(
|
||||
"variant `{}` cannot have both #[serde(serialize_with)] and \
|
||||
a field {} marked with #[serde(skip_serializing_if)]",
|
||||
variant.ident, ident
|
||||
variant.ident, member
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -204,17 +203,14 @@ fn check_variant_skip_attrs(cx: &Ctxt, cont: &Container) {
|
||||
));
|
||||
}
|
||||
|
||||
for (i, field) in variant.fields.iter().enumerate() {
|
||||
for field in &variant.fields {
|
||||
if field.attrs.skip_deserializing() {
|
||||
let ident = field
|
||||
.ident
|
||||
.as_ref()
|
||||
.map_or_else(|| format!("{}", i), |ident| format!("`{}`", ident));
|
||||
let member = member_message(&field.member);
|
||||
|
||||
cx.error(format!(
|
||||
"variant `{}` cannot have both #[serde(deserialize_with)] \
|
||||
and a field {} marked with #[serde(skip_deserializing)]",
|
||||
variant.ident, ident
|
||||
variant.ident, member
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -282,3 +278,76 @@ fn check_adjacent_tag_conflict(cx: &Ctxt, cont: &Container) {
|
||||
cx.error(message);
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
match *member {
|
||||
Member::Named(ref ident) => format!("`{}`", ident),
|
||||
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 check;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum Derive {
|
||||
Serialize,
|
||||
Deserialize,
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
//!
|
||||
//! [https://serde.rs/derive.html]: https://serde.rs/derive.html
|
||||
|
||||
#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.50")]
|
||||
#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.59")]
|
||||
#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
|
||||
// Whitelisted clippy lints
|
||||
#![cfg_attr(
|
||||
|
||||
+13
-14
@@ -1,5 +1,4 @@
|
||||
use proc_macro2::Span;
|
||||
use quote::Tokens;
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use syn::Ident;
|
||||
|
||||
use internals::ast::{Container, Data, Field, Style};
|
||||
@@ -21,7 +20,7 @@ use internals::ast::{Container, Data, Field, Style};
|
||||
// 8 | enum EnumDef { V }
|
||||
// | ^
|
||||
//
|
||||
pub fn pretend_used(cont: &Container) -> Tokens {
|
||||
pub fn pretend_used(cont: &Container) -> TokenStream {
|
||||
let pretend_fields = pretend_fields_used(cont);
|
||||
let pretend_variants = pretend_variants_used(cont);
|
||||
|
||||
@@ -49,8 +48,8 @@ pub fn pretend_used(cont: &Container) -> Tokens {
|
||||
// The `ref` is important in case the user has written a Drop impl on their
|
||||
// type. Rust does not allow destructuring a struct or enum that has a Drop
|
||||
// impl.
|
||||
fn pretend_fields_used(cont: &Container) -> Tokens {
|
||||
let type_ident = cont.ident;
|
||||
fn pretend_fields_used(cont: &Container) -> TokenStream {
|
||||
let type_ident = &cont.ident;
|
||||
let (_, ty_generics, _) = cont.generics.split_for_impl();
|
||||
|
||||
let patterns = match cont.data {
|
||||
@@ -58,7 +57,7 @@ fn pretend_fields_used(cont: &Container) -> Tokens {
|
||||
.iter()
|
||||
.filter_map(|variant| match variant.style {
|
||||
Style::Struct => {
|
||||
let variant_ident = variant.ident;
|
||||
let variant_ident = &variant.ident;
|
||||
let pat = struct_pattern(&variant.fields);
|
||||
Some(quote!(#type_ident::#variant_ident #pat))
|
||||
}
|
||||
@@ -93,7 +92,7 @@ fn pretend_fields_used(cont: &Container) -> Tokens {
|
||||
// _ => {}
|
||||
// }
|
||||
//
|
||||
fn pretend_variants_used(cont: &Container) -> Tokens {
|
||||
fn pretend_variants_used(cont: &Container) -> TokenStream {
|
||||
let variants = match cont.data {
|
||||
Data::Enum(ref variants) => variants,
|
||||
Data::Struct(_, _) => {
|
||||
@@ -101,20 +100,20 @@ fn pretend_variants_used(cont: &Container) -> Tokens {
|
||||
}
|
||||
};
|
||||
|
||||
let type_ident = cont.ident;
|
||||
let type_ident = &cont.ident;
|
||||
let (_, ty_generics, _) = cont.generics.split_for_impl();
|
||||
let turbofish = ty_generics.as_turbofish();
|
||||
|
||||
let cases = variants.iter().map(|variant| {
|
||||
let variant_ident = variant.ident;
|
||||
let variant_ident = &variant.ident;
|
||||
let placeholders = &(0..variant.fields.len())
|
||||
.map(|i| Ident::new(&format!("__v{}", i), Span::call_site()))
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let pat = match variant.style {
|
||||
Style::Struct => {
|
||||
let names = variant.fields.iter().map(|field| field.ident);
|
||||
quote!({ #(#names: #placeholders),* })
|
||||
let members = variant.fields.iter().map(|field| &field.member);
|
||||
quote!({ #(#members: #placeholders),* })
|
||||
}
|
||||
Style::Tuple | Style::Newtype => quote!(( #(#placeholders),* )),
|
||||
Style::Unit => quote!(),
|
||||
@@ -133,9 +132,9 @@ fn pretend_variants_used(cont: &Container) -> Tokens {
|
||||
quote!(#(#cases)*)
|
||||
}
|
||||
|
||||
fn struct_pattern(fields: &[Field]) -> Tokens {
|
||||
let names = fields.iter().map(|field| field.ident);
|
||||
fn struct_pattern(fields: &[Field]) -> TokenStream {
|
||||
let members = fields.iter().map(|field| &field.member);
|
||||
let placeholders =
|
||||
(0..fields.len()).map(|i| Ident::new(&format!("__v{}", i), Span::call_site()));
|
||||
quote!({ #(#names: ref #placeholders),* })
|
||||
quote!({ #(#members: ref #placeholders),* })
|
||||
}
|
||||
|
||||
+123
-72
@@ -6,25 +6,24 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use proc_macro2::Span;
|
||||
use quote::Tokens;
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{self, Ident, Index, Member};
|
||||
|
||||
use bound;
|
||||
use fragment::{Fragment, Match, Stmts};
|
||||
use internals::ast::{Container, Data, Field, Style, Variant};
|
||||
use internals::{attr, Ctxt};
|
||||
use internals::{attr, Ctxt, Derive};
|
||||
use pretend;
|
||||
use try;
|
||||
|
||||
pub fn expand_derive_serialize(input: &syn::DeriveInput) -> Result<Tokens, String> {
|
||||
pub fn expand_derive_serialize(input: &syn::DeriveInput) -> Result<TokenStream, String> {
|
||||
let ctxt = Ctxt::new();
|
||||
let cont = Container::from_ast(&ctxt, input);
|
||||
let cont = Container::from_ast(&ctxt, input, Derive::Serialize);
|
||||
precondition(&ctxt, &cont);
|
||||
try!(ctxt.check());
|
||||
|
||||
let ident = cont.ident;
|
||||
let ident = &cont.ident;
|
||||
let params = Parameters::new(&cont);
|
||||
let (impl_generics, ty_generics, where_clause) = params.generics.split_for_impl();
|
||||
let dummy_const = Ident::new(&format!("_IMPL_SERIALIZE_FOR_{}", ident), Span::call_site());
|
||||
@@ -110,7 +109,7 @@ impl Parameters {
|
||||
|
||||
let this = match cont.attrs.remote() {
|
||||
Some(remote) => remote.clone(),
|
||||
None => cont.ident.into(),
|
||||
None => cont.ident.clone().into(),
|
||||
};
|
||||
|
||||
let generics = build_generics(cont);
|
||||
@@ -125,8 +124,8 @@ impl Parameters {
|
||||
|
||||
/// Type name to use in error messages and `&'static str` arguments to
|
||||
/// various Serializer methods.
|
||||
fn type_name(&self) -> &str {
|
||||
self.this.segments.last().unwrap().value().ident.as_ref()
|
||||
fn type_name(&self) -> String {
|
||||
self.this.segments.last().unwrap().value().ident.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,6 +137,9 @@ fn build_generics(cont: &Container) -> syn::Generics {
|
||||
let generics =
|
||||
bound::with_where_predicates_from_fields(cont, &generics, attr::Field::ser_bound);
|
||||
|
||||
let generics =
|
||||
bound::with_where_predicates_from_variants(cont, &generics, attr::Variant::ser_bound);
|
||||
|
||||
match cont.attrs.ser_bound() {
|
||||
Some(predicates) => bound::with_where_predicates(&generics, predicates),
|
||||
None => bound::with_bound(
|
||||
@@ -150,31 +152,31 @@ fn build_generics(cont: &Container) -> syn::Generics {
|
||||
}
|
||||
|
||||
// Fields with a `skip_serializing` or `serialize_with` attribute, or which
|
||||
// belong to a variant with a `serialize_with` attribute, are not serialized by
|
||||
// us so we do not generate a bound. Fields with a `bound` attribute specify
|
||||
// their own bound so we do not generate one. All other fields may need a `T:
|
||||
// Serialize` bound where T is the type of the field.
|
||||
// belong to a variant with a 'skip_serializing` or `serialize_with` attribute,
|
||||
// are not serialized by us so we do not generate a bound. Fields with a `bound`
|
||||
// attribute specify their own bound so we do not generate one. All other fields
|
||||
// may need a `T: Serialize` bound where T is the type of the field.
|
||||
fn needs_serialize_bound(field: &attr::Field, variant: Option<&attr::Variant>) -> bool {
|
||||
!field.skip_serializing() && field.serialize_with().is_none() && field.ser_bound().is_none()
|
||||
&& variant.map_or(true, |variant| variant.serialize_with().is_none())
|
||||
&& variant.map_or(true, |variant| {
|
||||
!variant.skip_serializing()
|
||||
&& variant.serialize_with().is_none()
|
||||
&& variant.ser_bound().is_none()
|
||||
})
|
||||
}
|
||||
|
||||
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)
|
||||
} else {
|
||||
match cont.data {
|
||||
Data::Enum(ref variants) => serialize_enum(params, variants, &cont.attrs),
|
||||
Data::Struct(Style::Struct, ref fields) => {
|
||||
if fields.iter().any(|field| field.ident.is_none()) {
|
||||
panic!("struct has unnamed fields");
|
||||
}
|
||||
serialize_struct(params, fields, &cont.attrs)
|
||||
}
|
||||
Data::Struct(Style::Tuple, ref fields) => {
|
||||
if fields.iter().any(|field| field.ident.is_some()) {
|
||||
panic!("tuple struct has named fields");
|
||||
}
|
||||
serialize_tuple_struct(params, fields, &cont.attrs)
|
||||
}
|
||||
Data::Struct(Style::Newtype, ref fields) => {
|
||||
@@ -185,8 +187,28 @@ 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 {
|
||||
let self_var = params.self_var;
|
||||
let self_var = ¶ms.self_var;
|
||||
quote_block! {
|
||||
_serde::Serialize::serialize(
|
||||
&_serde::export::Into::<#type_into>::into(_serde::export::Clone::clone(#self_var)),
|
||||
@@ -237,8 +259,28 @@ fn serialize_tuple_struct(
|
||||
serialize_tuple_struct_visitor(fields, params, false, &TupleTrait::SerializeTupleStruct);
|
||||
|
||||
let type_name = cattrs.name().serialize_name();
|
||||
let len = serialize_stmts.len();
|
||||
let let_mut = mut_if(len > 0);
|
||||
|
||||
let mut serialized_fields = fields
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|&(_, ref field)| !field.attrs.skip_serializing())
|
||||
.peekable();
|
||||
|
||||
let let_mut = mut_if(serialized_fields.peek().is_some());
|
||||
|
||||
let len = serialized_fields
|
||||
.map(|(i, field)| match field.attrs.skip_serializing_if() {
|
||||
None => quote!(1),
|
||||
Some(path) => {
|
||||
let index = syn::Index {
|
||||
index: i as u32,
|
||||
span: Span::call_site(),
|
||||
};
|
||||
let field_expr = get_member(params, field, &Member::Unnamed(index));
|
||||
quote!(if #path(#field_expr) { 0 } else { 1 })
|
||||
}
|
||||
})
|
||||
.fold(quote!(0), |sum, expr| quote!(#sum + #expr));
|
||||
|
||||
quote_block! {
|
||||
let #let_mut __serde_state = try!(_serde::Serializer::serialize_tuple_struct(__serializer, #type_name, #len));
|
||||
@@ -278,8 +320,7 @@ fn serialize_struct_as_struct(
|
||||
.map(|field| match field.attrs.skip_serializing_if() {
|
||||
None => quote!(1),
|
||||
Some(path) => {
|
||||
let ident = field.ident.expect("struct has unnamed fields");
|
||||
let field_expr = get_member(params, field, &Member::Named(ident));
|
||||
let field_expr = get_member(params, field, &field.member);
|
||||
quote!(if #path(#field_expr) { 0 } else { 1 })
|
||||
}
|
||||
})
|
||||
@@ -314,8 +355,7 @@ fn serialize_struct_as_map(
|
||||
.map(|field| match field.attrs.skip_serializing_if() {
|
||||
None => quote!(1),
|
||||
Some(path) => {
|
||||
let ident = field.ident.expect("struct has unnamed fields");
|
||||
let field_expr = get_member(params, field, &Member::Named(ident));
|
||||
let field_expr = get_member(params, field, &field.member);
|
||||
quote!(if #path(#field_expr) { 0 } else { 1 })
|
||||
}
|
||||
})
|
||||
@@ -333,7 +373,7 @@ fn serialize_struct_as_map(
|
||||
fn serialize_enum(params: &Parameters, variants: &[Variant], cattrs: &attr::Container) -> Fragment {
|
||||
assert!(variants.len() as u64 <= u64::from(u32::max_value()));
|
||||
|
||||
let self_var = params.self_var;
|
||||
let self_var = ¶ms.self_var;
|
||||
|
||||
let arms: Vec<_> = variants
|
||||
.iter()
|
||||
@@ -355,9 +395,9 @@ fn serialize_variant(
|
||||
variant: &Variant,
|
||||
variant_index: u32,
|
||||
cattrs: &attr::Container,
|
||||
) -> Tokens {
|
||||
) -> TokenStream {
|
||||
let this = ¶ms.this;
|
||||
let variant_ident = variant.ident;
|
||||
let variant_ident = &variant.ident;
|
||||
|
||||
if variant.attrs.skip_serializing() {
|
||||
let skipped_msg = format!(
|
||||
@@ -397,12 +437,9 @@ fn serialize_variant(
|
||||
}
|
||||
}
|
||||
Style::Struct => {
|
||||
let fields = variant
|
||||
.fields
|
||||
.iter()
|
||||
.map(|f| f.ident.expect("struct variant has unnamed fields"));
|
||||
let members = variant.fields.iter().map(|f| &f.member);
|
||||
quote! {
|
||||
#this::#variant_ident { #(ref #fields),* }
|
||||
#this::#variant_ident { #(ref #members),* }
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -508,7 +545,7 @@ fn serialize_internally_tagged_variant(
|
||||
let variant_name = variant.attrs.name().serialize_name();
|
||||
|
||||
let enum_ident_str = params.type_name();
|
||||
let variant_ident_str = variant.ident.as_ref();
|
||||
let variant_ident_str = variant.ident.to_string();
|
||||
|
||||
if let Some(path) = variant.attrs.serialize_with() {
|
||||
let ser = wrap_serialize_variant_with(params, path, variant);
|
||||
@@ -630,15 +667,11 @@ fn serialize_adjacently_tagged_variant(
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
Style::Newtype => vec![Ident::new("__field0", Span::call_site())],
|
||||
Style::Newtype => vec![Member::Named(Ident::new("__field0", Span::call_site()))],
|
||||
Style::Tuple => (0..variant.fields.len())
|
||||
.map(|i| Ident::new(&format!("__field{}", i), Span::call_site()))
|
||||
.collect(),
|
||||
Style::Struct => variant
|
||||
.fields
|
||||
.iter()
|
||||
.map(|f| f.ident.expect("struct variant has unnamed fields"))
|
||||
.map(|i| Member::Named(Ident::new(&format!("__field{}", i), Span::call_site())))
|
||||
.collect(),
|
||||
Style::Struct => variant.fields.iter().map(|f| f.member.clone()).collect(),
|
||||
};
|
||||
|
||||
let (_, ty_generics, where_clause) = params.generics.split_for_impl();
|
||||
@@ -737,8 +770,23 @@ fn serialize_tuple_variant(
|
||||
|
||||
let serialize_stmts = serialize_tuple_struct_visitor(fields, params, true, &tuple_trait);
|
||||
|
||||
let len = serialize_stmts.len();
|
||||
let let_mut = mut_if(len > 0);
|
||||
let mut serialized_fields = fields
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|&(_, ref field)| !field.attrs.skip_serializing())
|
||||
.peekable();
|
||||
|
||||
let let_mut = mut_if(serialized_fields.peek().is_some());
|
||||
|
||||
let len = serialized_fields
|
||||
.map(|(i, field)| match field.attrs.skip_serializing_if() {
|
||||
None => quote!(1),
|
||||
Some(path) => {
|
||||
let field_expr = Ident::new(&format!("__field{}", i), Span::call_site());
|
||||
quote!(if #path(#field_expr) { 0 } else { 1 })
|
||||
}
|
||||
})
|
||||
.fold(quote!(0), |sum, expr| quote!(#sum + #expr));
|
||||
|
||||
match context {
|
||||
TupleVariant::ExternallyTagged {
|
||||
@@ -809,10 +857,10 @@ fn serialize_struct_variant<'a>(
|
||||
|
||||
let len = serialized_fields
|
||||
.map(|field| {
|
||||
let ident = field.ident.expect("struct has unnamed fields");
|
||||
let member = &field.member;
|
||||
|
||||
match field.attrs.skip_serializing_if() {
|
||||
Some(path) => quote!(if #path(#ident) { 0 } else { 1 }),
|
||||
Some(path) => quote!(if #path(#member) { 0 } else { 1 }),
|
||||
None => quote!(1),
|
||||
}
|
||||
})
|
||||
@@ -888,7 +936,7 @@ fn serialize_struct_variant_with_flatten<'a>(
|
||||
} => {
|
||||
let this = ¶ms.this;
|
||||
let fields_ty = fields.iter().map(|f| &f.ty);
|
||||
let fields_ident = &fields.iter().map(|f| f.ident).collect::<Vec<_>>();
|
||||
let members = &fields.iter().map(|f| &f.member).collect::<Vec<_>>();
|
||||
|
||||
let (_, ty_generics, where_clause) = params.generics.split_for_impl();
|
||||
let wrapper_generics = bound::with_lifetime_bound(¶ms.generics, "'__a");
|
||||
@@ -905,7 +953,7 @@ fn serialize_struct_variant_with_flatten<'a>(
|
||||
where
|
||||
__S: _serde::Serializer,
|
||||
{
|
||||
let (#(#fields_ident,)*) = self.data;
|
||||
let (#(#members,)*) = self.data;
|
||||
let #let_mut __serde_state = try!(_serde::Serializer::serialize_map(
|
||||
__serializer,
|
||||
_serde::export::None));
|
||||
@@ -920,7 +968,7 @@ fn serialize_struct_variant_with_flatten<'a>(
|
||||
#variant_index,
|
||||
#variant_name,
|
||||
&__EnumFlatten {
|
||||
data: (#(#fields_ident,)*),
|
||||
data: (#(#members,)*),
|
||||
phantom: _serde::export::PhantomData::<#this #ty_generics>,
|
||||
})
|
||||
}
|
||||
@@ -956,10 +1004,11 @@ fn serialize_tuple_struct_visitor(
|
||||
params: &Parameters,
|
||||
is_enum: bool,
|
||||
tuple_trait: &TupleTrait,
|
||||
) -> Vec<Tokens> {
|
||||
) -> Vec<TokenStream> {
|
||||
fields
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|&(_, ref field)| !field.attrs.skip_serializing())
|
||||
.map(|(i, field)| {
|
||||
let mut field_expr = if is_enum {
|
||||
let id = Ident::new(&format!("__field{}", i), Span::call_site());
|
||||
@@ -1003,17 +1052,17 @@ fn serialize_struct_visitor(
|
||||
params: &Parameters,
|
||||
is_enum: bool,
|
||||
struct_trait: &StructTrait,
|
||||
) -> Vec<Tokens> {
|
||||
) -> Vec<TokenStream> {
|
||||
fields
|
||||
.iter()
|
||||
.filter(|&field| !field.attrs.skip_serializing())
|
||||
.map(|field| {
|
||||
let field_ident = field.ident.expect("struct has unnamed field");
|
||||
let member = &field.member;
|
||||
|
||||
let mut field_expr = if is_enum {
|
||||
quote!(#field_ident)
|
||||
quote!(#member)
|
||||
} else {
|
||||
get_member(params, field, &Member::Named(field_ident))
|
||||
get_member(params, field, &member)
|
||||
};
|
||||
|
||||
let key_expr = field.attrs.name().serialize_name();
|
||||
@@ -1067,8 +1116,8 @@ fn wrap_serialize_field_with(
|
||||
params: &Parameters,
|
||||
field_ty: &syn::Type,
|
||||
serialize_with: &syn::ExprPath,
|
||||
field_expr: &Tokens,
|
||||
) -> Tokens {
|
||||
field_expr: &TokenStream,
|
||||
) -> TokenStream {
|
||||
wrap_serialize_with(params, serialize_with, &[field_ty], &[quote!(#field_expr)])
|
||||
}
|
||||
|
||||
@@ -1076,16 +1125,18 @@ fn wrap_serialize_variant_with(
|
||||
params: &Parameters,
|
||||
serialize_with: &syn::ExprPath,
|
||||
variant: &Variant,
|
||||
) -> Tokens {
|
||||
) -> TokenStream {
|
||||
let field_tys: Vec<_> = variant.fields.iter().map(|field| field.ty).collect();
|
||||
let field_exprs: Vec<_> = variant
|
||||
.fields
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, field)| {
|
||||
let id = field
|
||||
.ident
|
||||
.unwrap_or_else(|| Ident::new(&format!("__field{}", i), Span::call_site()));
|
||||
.map(|field| {
|
||||
let id = match field.member {
|
||||
Member::Named(ref ident) => ident.clone(),
|
||||
Member::Unnamed(ref member) => {
|
||||
Ident::new(&format!("__field{}", member.index), Span::call_site())
|
||||
}
|
||||
};
|
||||
quote!(#id)
|
||||
})
|
||||
.collect();
|
||||
@@ -1101,8 +1152,8 @@ fn wrap_serialize_with(
|
||||
params: &Parameters,
|
||||
serialize_with: &syn::ExprPath,
|
||||
field_tys: &[&syn::Type],
|
||||
field_exprs: &[Tokens],
|
||||
) -> Tokens {
|
||||
field_exprs: &[TokenStream],
|
||||
) -> TokenStream {
|
||||
let this = ¶ms.this;
|
||||
let (_, ty_generics, where_clause) = params.generics.split_for_impl();
|
||||
|
||||
@@ -1148,7 +1199,7 @@ fn wrap_serialize_with(
|
||||
// _serde::ser::SerializeStruct::end(__serde_state)
|
||||
//
|
||||
// where we want to omit the `mut` to avoid a warning.
|
||||
fn mut_if(is_mut: bool) -> Option<Tokens> {
|
||||
fn mut_if(is_mut: bool) -> Option<TokenStream> {
|
||||
if is_mut {
|
||||
Some(quote!(mut))
|
||||
} else {
|
||||
@@ -1156,8 +1207,8 @@ fn mut_if(is_mut: bool) -> Option<Tokens> {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_member(params: &Parameters, field: &Field, member: &Member) -> Tokens {
|
||||
let self_var = params.self_var;
|
||||
fn get_member(params: &Parameters, field: &Field, member: &Member) -> TokenStream {
|
||||
let self_var = ¶ms.self_var;
|
||||
match (params.is_remote, field.attrs.getter()) {
|
||||
(false, None) => quote!(&#self_var.#member),
|
||||
(true, None) => {
|
||||
@@ -1182,7 +1233,7 @@ enum StructTrait {
|
||||
}
|
||||
|
||||
impl StructTrait {
|
||||
fn serialize_field(&self, span: Span) -> Tokens {
|
||||
fn serialize_field(&self, span: Span) -> TokenStream {
|
||||
match *self {
|
||||
StructTrait::SerializeMap => {
|
||||
quote_spanned!(span=> _serde::ser::SerializeMap::serialize_entry)
|
||||
@@ -1196,7 +1247,7 @@ impl StructTrait {
|
||||
}
|
||||
}
|
||||
|
||||
fn skip_field(&self, span: Span) -> Option<Tokens> {
|
||||
fn skip_field(&self, span: Span) -> Option<TokenStream> {
|
||||
match *self {
|
||||
StructTrait::SerializeMap => None,
|
||||
StructTrait::SerializeStruct => {
|
||||
@@ -1216,7 +1267,7 @@ enum TupleTrait {
|
||||
}
|
||||
|
||||
impl TupleTrait {
|
||||
fn serialize_element(&self, span: Span) -> Tokens {
|
||||
fn serialize_element(&self, span: Span) -> TokenStream {
|
||||
match *self {
|
||||
TupleTrait::SerializeTuple => {
|
||||
quote_spanned!(span=> _serde::ser::SerializeTuple::serialize_element)
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
use proc_macro2::{Op, Spacing};
|
||||
use quote::Tokens;
|
||||
use proc_macro2::{Punct, Spacing, TokenStream};
|
||||
|
||||
// None of our generated code requires the `From::from` error conversion
|
||||
// performed by the standard library's `try!` macro. With this simplified macro
|
||||
// we see a significant improvement in type checking and borrow checking time of
|
||||
// the generated code and a slight improvement in binary size.
|
||||
pub fn replacement() -> Tokens {
|
||||
pub fn replacement() -> TokenStream {
|
||||
// Cannot pass `$expr` to `quote!` prior to Rust 1.17.0 so interpolate it.
|
||||
let dollar = Op::new('$', Spacing::Alone);
|
||||
let dollar = Punct::new('$', Spacing::Alone);
|
||||
|
||||
quote! {
|
||||
#[allow(unused_macros)]
|
||||
|
||||
@@ -15,8 +15,8 @@ include = ["Cargo.toml", "lib.rs", "src/**/*.rs", "README.md", "LICENSE-APACHE",
|
||||
path = "lib.rs"
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "0.3"
|
||||
syn = { version = "0.13", default-features = false, features = ["derive", "parsing", "clone-impls"] }
|
||||
proc-macro2 = "0.4"
|
||||
syn = { version = "0.14", default-features = false, features = ["derive", "parsing", "clone-impls"] }
|
||||
|
||||
[badges]
|
||||
travis-ci = { repository = "serde-rs/serde" }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "serde_test"
|
||||
version = "1.0.50" # remember to update html_root_url
|
||||
version = "1.0.59" # remember to update html_root_url
|
||||
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
|
||||
license = "MIT/Apache-2.0"
|
||||
description = "Token De/Serializer for testing De/Serialize implementations"
|
||||
|
||||
@@ -599,7 +599,8 @@ impl<'de, 'a> MapAccess<'de> for EnumMapVisitor<'a, 'de> {
|
||||
{
|
||||
match self.variant.take() {
|
||||
Some(Token::Str(variant)) => seed.deserialize(variant.into_deserializer()).map(Some),
|
||||
Some(Token::Bytes(variant)) => seed.deserialize(BytesDeserializer { value: variant })
|
||||
Some(Token::Bytes(variant)) => seed
|
||||
.deserialize(BytesDeserializer { value: variant })
|
||||
.map(Some),
|
||||
Some(Token::U32(variant)) => seed.deserialize(variant.into_deserializer()).map(Some),
|
||||
Some(other) => unexpected!(other),
|
||||
|
||||
@@ -161,7 +161,7 @@
|
||||
//! # }
|
||||
//! ```
|
||||
|
||||
#![doc(html_root_url = "https://docs.rs/serde_test/1.0.50")]
|
||||
#![doc(html_root_url = "https://docs.rs/serde_test/1.0.59")]
|
||||
#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
|
||||
// Whitelisted clippy lints
|
||||
#![cfg_attr(feature = "cargo-clippy", allow(float_cmp))]
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
// 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
|
||||
struct S<'de> {
|
||||
s: &'de str, //~^^ HELP: cannot deserialize when there is a lifetime parameter called 'de
|
||||
}
|
||||
@@ -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() {}
|
||||
@@ -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::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::collections::HashMap;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
extern crate serde_test;
|
||||
use self::serde_test::{
|
||||
@@ -657,6 +658,44 @@ fn test_skip_serializing_struct() {
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize)]
|
||||
struct SkipSerializingTupleStruct<'a, B, C>(
|
||||
&'a i8,
|
||||
#[serde(skip_serializing)] B,
|
||||
#[serde(skip_serializing_if = "ShouldSkip::should_skip")] C,
|
||||
)
|
||||
where
|
||||
C: ShouldSkip;
|
||||
|
||||
#[test]
|
||||
fn test_skip_serializing_tuple_struct() {
|
||||
let a = 1;
|
||||
assert_ser_tokens(
|
||||
&SkipSerializingTupleStruct(&a, 2, 3),
|
||||
&[
|
||||
Token::TupleStruct {
|
||||
name: "SkipSerializingTupleStruct",
|
||||
len: 2,
|
||||
},
|
||||
Token::I8(1),
|
||||
Token::I32(3),
|
||||
Token::TupleStructEnd,
|
||||
],
|
||||
);
|
||||
|
||||
assert_ser_tokens(
|
||||
&SkipSerializingTupleStruct(&a, 2, 123),
|
||||
&[
|
||||
Token::TupleStruct {
|
||||
name: "SkipSerializingTupleStruct",
|
||||
len: 1,
|
||||
},
|
||||
Token::I8(1),
|
||||
Token::TupleStructEnd,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize, Deserialize)]
|
||||
struct SkipStruct<B> {
|
||||
a: i8,
|
||||
@@ -705,6 +744,11 @@ where
|
||||
#[serde(skip_serializing_if = "ShouldSkip::should_skip")]
|
||||
c: C,
|
||||
},
|
||||
Tuple(
|
||||
&'a i8,
|
||||
#[serde(skip_serializing)] B,
|
||||
#[serde(skip_serializing_if = "ShouldSkip::should_skip")] C,
|
||||
),
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -743,6 +787,33 @@ fn test_skip_serializing_enum() {
|
||||
Token::StructVariantEnd,
|
||||
],
|
||||
);
|
||||
|
||||
assert_ser_tokens(
|
||||
&SkipSerializingEnum::Tuple(&a, 2, 3),
|
||||
&[
|
||||
Token::TupleVariant {
|
||||
name: "SkipSerializingEnum",
|
||||
variant: "Tuple",
|
||||
len: 2,
|
||||
},
|
||||
Token::I8(1),
|
||||
Token::I32(3),
|
||||
Token::TupleVariantEnd,
|
||||
],
|
||||
);
|
||||
|
||||
assert_ser_tokens(
|
||||
&SkipSerializingEnum::Tuple(&a, 2, 123),
|
||||
&[
|
||||
Token::TupleVariant {
|
||||
name: "SkipSerializingEnum",
|
||||
variant: "Tuple",
|
||||
len: 1,
|
||||
},
|
||||
Token::I8(1),
|
||||
Token::TupleVariantEnd,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
@@ -1230,7 +1301,7 @@ fn test_invalid_length_enum() {
|
||||
Token::I32(1),
|
||||
Token::TupleVariantEnd,
|
||||
],
|
||||
"invalid length 1, expected tuple of 3 elements",
|
||||
"invalid length 1, expected tuple variant InvalidLengthEnum::A with 3 elements",
|
||||
);
|
||||
assert_de_tokens_error::<InvalidLengthEnum>(
|
||||
&[
|
||||
@@ -1242,7 +1313,7 @@ fn test_invalid_length_enum() {
|
||||
Token::I32(1),
|
||||
Token::TupleVariantEnd,
|
||||
],
|
||||
"invalid length 1, expected tuple of 2 elements",
|
||||
"invalid length 1, expected tuple variant InvalidLengthEnum::B with 2 elements",
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1992,3 +2063,140 @@ fn test_untagged_enum_containing_flatten() {
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_flatten_untagged_enum() {
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
struct Outer {
|
||||
#[serde(flatten)]
|
||||
inner: Inner,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
#[serde(untagged)]
|
||||
enum Inner {
|
||||
Variant { a: i32 },
|
||||
}
|
||||
|
||||
let data = Outer {
|
||||
inner: Inner::Variant { a: 0 },
|
||||
};
|
||||
|
||||
assert_tokens(
|
||||
&data,
|
||||
&[
|
||||
Token::Map { len: None },
|
||||
Token::Str("a"),
|
||||
Token::I32(0),
|
||||
Token::MapEnd,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_flatten_option() {
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
struct Outer {
|
||||
#[serde(flatten)]
|
||||
inner1: Option<Inner1>,
|
||||
#[serde(flatten)]
|
||||
inner2: Option<Inner2>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
struct Inner1 {
|
||||
inner1: i32,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
struct Inner2 {
|
||||
inner2: i32,
|
||||
}
|
||||
|
||||
assert_tokens(
|
||||
&Outer {
|
||||
inner1: Some(Inner1 { inner1: 1 }),
|
||||
inner2: Some(Inner2 { inner2: 2 }),
|
||||
},
|
||||
&[
|
||||
Token::Map { len: None },
|
||||
Token::Str("inner1"),
|
||||
Token::I32(1),
|
||||
Token::Str("inner2"),
|
||||
Token::I32(2),
|
||||
Token::MapEnd,
|
||||
],
|
||||
);
|
||||
|
||||
assert_tokens(
|
||||
&Outer {
|
||||
inner1: Some(Inner1 { inner1: 1 }),
|
||||
inner2: None,
|
||||
},
|
||||
&[
|
||||
Token::Map { len: None },
|
||||
Token::Str("inner1"),
|
||||
Token::I32(1),
|
||||
Token::MapEnd,
|
||||
],
|
||||
);
|
||||
|
||||
assert_tokens(
|
||||
&Outer {
|
||||
inner1: None,
|
||||
inner2: Some(Inner2 { inner2: 2 }),
|
||||
},
|
||||
&[
|
||||
Token::Map { len: None },
|
||||
Token::Str("inner2"),
|
||||
Token::I32(2),
|
||||
Token::MapEnd,
|
||||
],
|
||||
);
|
||||
|
||||
assert_tokens(
|
||||
&Outer {
|
||||
inner1: None,
|
||||
inner2: None,
|
||||
},
|
||||
&[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)]);
|
||||
}
|
||||
|
||||
@@ -216,6 +216,42 @@ fn test_gen() {
|
||||
}
|
||||
assert::<WithTraits2<X, X>>();
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(bound = "D: SerializeWith + DeserializeWith")]
|
||||
enum VariantWithTraits1<D, E> {
|
||||
#[serde(
|
||||
serialize_with = "SerializeWith::serialize_with",
|
||||
deserialize_with = "DeserializeWith::deserialize_with"
|
||||
)]
|
||||
D(D),
|
||||
#[serde(
|
||||
serialize_with = "SerializeWith::serialize_with",
|
||||
deserialize_with = "DeserializeWith::deserialize_with",
|
||||
bound = "E: SerializeWith + DeserializeWith"
|
||||
)]
|
||||
E(E),
|
||||
}
|
||||
assert::<VariantWithTraits1<X, X>>();
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(bound(serialize = "D: SerializeWith", deserialize = "D: DeserializeWith"))]
|
||||
enum VariantWithTraits2<D, E> {
|
||||
#[serde(
|
||||
serialize_with = "SerializeWith::serialize_with",
|
||||
deserialize_with = "DeserializeWith::deserialize_with"
|
||||
)]
|
||||
D(D),
|
||||
#[serde(
|
||||
serialize_with = "SerializeWith::serialize_with", bound(serialize = "E: SerializeWith")
|
||||
)]
|
||||
#[serde(
|
||||
deserialize_with = "DeserializeWith::deserialize_with",
|
||||
bound(deserialize = "E: DeserializeWith")
|
||||
)]
|
||||
E(E),
|
||||
}
|
||||
assert::<VariantWithTraits2<X, X>>();
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct CowStr<'a>(Cow<'a, str>);
|
||||
assert::<CowStr>();
|
||||
@@ -588,6 +624,43 @@ fn test_gen() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(tag = "t", content = "c")]
|
||||
enum AdjacentlyTaggedVoid {}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
enum SkippedVariant<T> {
|
||||
#[serde(skip)]
|
||||
#[allow(dead_code)]
|
||||
T(T),
|
||||
Unit,
|
||||
}
|
||||
|
||||
assert::<SkippedVariant<X>>();
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct ImpliciltyBorrowedOption<'a> {
|
||||
option: std::option::Option<&'a str>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum UntaggedNewtypeVariantWith {
|
||||
Newtype(
|
||||
#[serde(serialize_with = "ser_x")]
|
||||
#[serde(deserialize_with = "de_x")]
|
||||
X,
|
||||
),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
struct TransparentWith {
|
||||
#[serde(serialize_with = "ser_x")]
|
||||
#[serde(deserialize_with = "de_x")]
|
||||
x: X,
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -55,7 +55,8 @@ else
|
||||
channel build
|
||||
cd "$DIR/test_suite"
|
||||
channel test --features unstable
|
||||
channel build --tests --features proc-macro2/nightly
|
||||
# Broken while syn and quote update to the new proc-macro API
|
||||
#channel build --tests --features proc-macro2/nightly
|
||||
if [ -z "${APPVEYOR}" ]; then
|
||||
cd "$DIR/test_suite/no_std"
|
||||
channel build
|
||||
|
||||
Reference in New Issue
Block a user