Compare commits

...

63 Commits

Author SHA1 Message Date
David Tolnay 422191fcb0 Release 1.0.16 2017-10-22 11:29:44 -07:00
David Tolnay 4ba748c902 Clean up trailing whitespace 2017-10-22 11:29:35 -07:00
David Tolnay 14ed6f2dab Simplify some IntoDeserializer deserializer types 2017-10-20 21:38:18 -07:00
David Tolnay 30606a43aa IntoDeserializer for HashSet and HashMap with non-default hasher 2017-10-20 21:36:40 -07:00
David Tolnay 9be3d32016 Remove redundant readable/compact tests
This functionality is covered well enough by the std::net types.
2017-10-17 23:16:21 -07:00
David Tolnay 5daf1b89a1 Consolidate readable/compact deserialization tests 2017-10-17 23:15:35 -07:00
David Tolnay f8f5d0ca2f No need for readable setting for the ignore test 2017-10-17 23:09:50 -07:00
David Tolnay 57873cce28 Remove unused deserialization macros on no_std 2017-10-17 10:04:26 -07:00
David Tolnay 4ed0362c8e Panic by default in serde_test is_human_readable
The serde_test Serializer and Deserializer panic in is_human_readable unless the
readableness has been set explicitly through one of the hidden functions. This
is to force types that have distinct readable/compact representations to be
tested explicitly in one or the other, rather than with a plain assert_tokens
which arbitrarily picks one.

We need to follow up by designing a better API in serde_test to expose this
publicly. For now serde_test cannot be used to test types that rely on
is_human_readable. (The hidden functions are meant for our test suite only.)
2017-10-17 09:49:42 -07:00
David Tolnay 4cecaf8d02 Test the maximum std::net string lengths 2017-10-15 20:32:30 -07:00
David Tolnay 50c696aabe Write is_human_readable examples 2017-10-15 20:27:03 -07:00
David Tolnay 2f58a20bc6 Inline is_human_readable 2017-10-15 16:54:48 -07:00
David Tolnay 030459a040 Merge pull request #1044 from Marwes/human_readable
Serialize to binary if the serde format is not human readable
2017-10-15 16:39:58 -07:00
Markus Westerlind e9b530a000 Hide is_human_readable constructors in serde_test
Until a good API can be found
2017-10-13 17:37:47 +02:00
David Tolnay ea1a729088 Release 1.0.15 2017-09-17 13:58:35 -07:00
David Tolnay 857dcea774 Merge pull request #1058 from serde-rs/internally-tagged-seq
Support deserializing internally tagged enum from seq
2017-09-17 13:57:33 -07:00
David Tolnay b98a9a8f9b Support deserializing internally tagged enum from seq
During serialization, internally tagged enums invoke the Serializer's
serialize_struct. In JSON this turns into a map which uses visit_map
when deserialized. But some formats employ visit_seq when
deserializing a struct. One example is rmp-serde. Such formats were
previously unable to deserialize an internally tagged enum. This
change fixes it by adding visit_seq for internally tagged enums.
2017-09-17 13:45:12 -07:00
Markus Westerlind 3b135431fd Try to fix compilation on 1.13 2017-09-14 17:12:23 +02:00
Markus Westerlind 945d12c0b4 Use the variant_identifier macro for OsString 2017-09-14 17:08:17 +02:00
Markus Westerlind e36915300f Properly deserialize non-readable IpAddr and SocketAddr 2017-09-14 17:08:17 +02:00
Markus Westerlind 85c05d301a Fix the non-readble IpAddr serialize implementations 2017-09-11 17:40:02 +02:00
Markus Westerlind c2474bf6ee Document that is_human_readable == false is a breaking change 2017-09-11 17:18:35 +02:00
Markus Westerlind a52f436788 Fix rustc 1.13 and clippy errors on travis 2017-09-11 16:03:00 +02:00
Markus Westerlind ad3335e5d6 Serialize non-human-readble ip addresses as tuples
Since we know exactly how many bytes we should serialize as we can hint
to the serializer that it is not required which further reduces the
serialized size when compared to just serializing as bytes.
2017-09-11 15:54:53 +02:00
David Tolnay 4b00de0e22 Release 1.0.14 2017-09-09 12:50:57 -07:00
David Tolnay 8403fa018e Merge pull request #1052 from serde-rs/static
Special case for 'static fields
2017-09-09 12:50:11 -07:00
David Tolnay 0e9f1b42de Merge pull request #1053 from serde-rs/cast
Fix trivial numeric cast in visit_u64
2017-09-09 12:43:46 -07:00
David Tolnay 0085d05e55 Special case for 'static fields 2017-09-09 12:39:14 -07:00
David Tolnay 2eed855bff Fix trivial numeric cast in visit_u64 2017-09-09 12:37:00 -07:00
David Tolnay c3eced410f Release 1.0.13 2017-09-09 11:40:31 -07:00
David Tolnay 8a630fea7c Suppress cast_lossless lint in test suite 2017-09-09 11:08:19 -07:00
David Tolnay 2e597ed3f0 Remove unused functions in with-variant tests
Macro expansion fails before it would generate code to call any of these.
2017-09-09 10:58:32 -07:00
David Tolnay 0963121beb Support consolidated with attribute for variants 2017-09-09 10:50:40 -07:00
David Tolnay 15b2714058 Merge pull request #1015 from spinda/with-variant
implement (de)serialize_with for variants
2017-09-09 10:49:24 -07:00
David Tolnay 9ce107de25 Merge pull request 963 from sfackler/u64-identifier
Conflicts:
    serde_derive/src/de.rs
2017-09-08 21:35:41 -07:00
David Tolnay e47284c0e0 Merge pull request #1043 from greyblake/screaming-kebab-case
SCREAMING-KEBAB-CASE support
2017-09-08 21:30:01 -07:00
David Tolnay 800620e2aa Merge pull request #1022 from sfackler/skip-field
Inform serializers about skipped fields.
2017-09-08 09:47:43 -07:00
Markus Westerlind 40c670e625 Add non-human readable serializations for ip addresses 2017-09-08 10:37:33 +02:00
David Tolnay ba260b0e5f Merge pull request #1045 from xfix/patch-1
Fix a type name typo in visit_i64 documentation
2017-09-07 12:07:03 -07:00
Konrad Borowski 8452e313cc Fix a type name typo in visit_i64 documentation 2017-09-07 19:53:07 +02:00
Markus Westerlind 0dccbb1f11 Serialize to binary if the serde format is not human readable
This implements the KISS suggested in https://github.com/serde-rs/serde/issues/790.
It is possible that one of the other approaches may be better but this
seemed like the simplest one to reignite som discussion.

Personally I find the original suggestion of adding two traits perhaps slightly
cleaner in theory but I think it ends up more complicated in the end
since the added traits also need to be duplicated to to the `Seed`
traits.

Closes #790
2017-09-07 16:20:57 +02:00
Steven Fackler deca49315a Inline skip_field 2017-09-05 22:36:42 -07:00
Steven Fackler 95407a4ca5 Support field ident deserialization from u32 2017-09-05 21:55:33 -07:00
Steven Fackler 2fe9a860cd Inform serializers about skipped fields.
Closes #960.
2017-09-05 21:55:33 -07:00
Sergey Potapov e67d941b78 Support for SCREAMING-KEBAB-CASE 2017-09-05 22:07:08 +02:00
David Tolnay d4042872f5 Release 1.0.12 2017-09-04 11:11:44 -07:00
David Tolnay 64af86b830 Suppress cast_lossless lint 2017-09-04 11:10:43 -07:00
David Tolnay 370c8a91cb Merge pull request #1039 from serde-rs/rcde
Deserialize unsized Arc and Rc
2017-09-04 11:07:19 -07:00
David Tolnay 972da59ebc Deserialize unsized Arc and Rc 2017-09-04 10:56:42 -07:00
David Tolnay a42008f695 Merge pull request #1038 from serde-rs/rcser
Serialize unsized Arc and Rc
2017-09-04 10:54:20 -07:00
David Tolnay e4ea2a56e9 Serialize unsized Arc and Rc 2017-09-04 10:31:03 -07:00
David Tolnay 7650a48fdd Opt in to clippy_pedantic lints 2017-08-24 00:41:42 -07:00
David Tolnay d665a2f2b2 Merge pull request #1023 from hcpl/fix-doc-typo
Fix `SeqAcccess` typo in docs
2017-08-20 15:03:10 -07:00
hcpl 44e23254c9 Fix SeqAcccess typo in docs 2017-08-20 22:02:28 +03:00
Michael Smith 552971196d Fix Clippy errors in variant serialize_with tests 2017-08-16 12:04:39 -07:00
David Tolnay 0681cd5003 Replace deprecated compiletest::default_config() 2017-08-15 22:10:18 -07:00
David Tolnay d965367238 No longer need feature(into_boxed_c_str) 2017-08-15 22:08:23 -07:00
David Tolnay a6df35b3d2 Disable no_std test on appveyor 2017-08-15 21:58:52 -07:00
Michael Smith 9fc180e62f Implement deserialize_with for variants
Complements variant serialize_with and closes #1013.
2017-08-14 14:41:05 -07:00
Michael Smith 5b815b7001 Implement serialize_with for variants
As discussed in #1013, serialize_with functions attached to variants receive an
argument for each inner value contained within the variant. Internally such a
function is wired up to the serializer as if the variant were a newtype variant.
2017-08-14 11:17:08 -07:00
David Tolnay 4831482695 Doc comment on statement is not used by rustdoc
Fixes #1014.
2017-08-05 23:35:14 -07:00
David Tolnay d3e5dd9cd7 Disagree that 0x10000 is unreadable 2017-08-05 23:26:15 -07:00
Steven Fackler 8e8694261b Fix identifier deserialization from non-u32
Closes #962
2017-06-19 20:23:14 -07:00
46 changed files with 2099 additions and 408 deletions
+1 -1
View File
@@ -22,7 +22,7 @@ efforts from contributors on the same issue.
master you may be asked to rebase your changes.
- Commits should be as small as possible, while ensuring that each commit is
correct independently (i.e., each commit should compile and pass tests).
correct independently (i.e., each commit should compile and pass tests).
- If your patch is not getting reviewed or you need a specific person to review
it, you can @-reply a reviewer asking for a review in the pull request or a
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "serde"
version = "1.0.11" # remember to update html_root_url
version = "1.0.16" # 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"
+210 -76
View File
@@ -869,37 +869,206 @@ map_impl!(
////////////////////////////////////////////////////////////////////////////////
#[cfg(feature = "std")]
macro_rules! parse_impl {
($ty:ty) => {
macro_rules! parse_ip_impl {
($ty:ty; $size: expr) => {
impl<'de> Deserialize<'de> for $ty {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = try!(String::deserialize(deserializer));
s.parse().map_err(Error::custom)
if deserializer.is_human_readable() {
let s = try!(String::deserialize(deserializer));
s.parse().map_err(Error::custom)
} else {
<[u8; $size]>::deserialize(deserializer).map(<$ty>::from)
}
}
}
}
}
#[cfg(feature = "std")]
parse_impl!(net::IpAddr);
macro_rules! variant_identifier {
(
$name_kind: ident ( $($variant: ident; $bytes: expr; $index: expr),* )
$expecting_message: expr,
$variants_name: ident
) => {
enum $name_kind {
$( $variant ),*
}
static $variants_name: &'static [&'static str] = &[ $( stringify!($variant) ),*];
impl<'de> Deserialize<'de> for $name_kind {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct KindVisitor;
impl<'de> Visitor<'de> for KindVisitor {
type Value = $name_kind;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str($expecting_message)
}
fn visit_u32<E>(self, value: u32) -> Result<Self::Value, E>
where
E: Error,
{
match value {
$(
$index => Ok($name_kind :: $variant),
)*
_ => Err(Error::invalid_value(Unexpected::Unsigned(value as u64), &self),),
}
}
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: Error,
{
match value {
$(
stringify!($variant) => Ok($name_kind :: $variant),
)*
_ => Err(Error::unknown_variant(value, $variants_name)),
}
}
fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
where
E: Error,
{
match value {
$(
$bytes => Ok($name_kind :: $variant),
)*
_ => {
match str::from_utf8(value) {
Ok(value) => Err(Error::unknown_variant(value, $variants_name)),
Err(_) => Err(Error::invalid_value(Unexpected::Bytes(value), &self)),
}
}
}
}
}
deserializer.deserialize_identifier(KindVisitor)
}
}
}
}
#[cfg(feature = "std")]
parse_impl!(net::Ipv4Addr);
macro_rules! deserialize_enum {
(
$name: ident $name_kind: ident ( $($variant: ident; $bytes: expr; $index: expr),* )
$expecting_message: expr,
$deserializer: expr
) => {
variant_identifier!{
$name_kind ( $($variant; $bytes; $index),* )
$expecting_message,
VARIANTS
}
struct EnumVisitor;
impl<'de> Visitor<'de> for EnumVisitor {
type Value = $name;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str(concat!("a ", stringify!($name)))
}
fn visit_enum<A>(self, data: A) -> Result<Self::Value, A::Error>
where
A: EnumAccess<'de>,
{
match try!(data.variant()) {
$(
($name_kind :: $variant, v) => v.newtype_variant().map($name :: $variant),
)*
}
}
}
$deserializer.deserialize_enum(stringify!($name), VARIANTS, EnumVisitor)
}
}
#[cfg(feature = "std")]
parse_impl!(net::Ipv6Addr);
impl<'de> Deserialize<'de> for net::IpAddr {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
if deserializer.is_human_readable() {
let s = try!(String::deserialize(deserializer));
s.parse().map_err(Error::custom)
} else {
use lib::net::IpAddr;
deserialize_enum!{
IpAddr IpAddrKind (V4; b"V4"; 0, V6; b"V6"; 1)
"`V4` or `V6`",
deserializer
}
}
}
}
#[cfg(feature = "std")]
parse_impl!(net::SocketAddr);
parse_ip_impl!(net::Ipv4Addr; 4);
#[cfg(feature = "std")]
parse_impl!(net::SocketAddrV4);
parse_ip_impl!(net::Ipv6Addr; 16);
#[cfg(feature = "std")]
parse_impl!(net::SocketAddrV6);
macro_rules! parse_socket_impl {
($ty:ty, $new: expr) => {
impl<'de> Deserialize<'de> for $ty {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
if deserializer.is_human_readable() {
let s = try!(String::deserialize(deserializer));
s.parse().map_err(Error::custom)
} else {
<(_, u16)>::deserialize(deserializer).map(|(ip, port)| $new(ip, port))
}
}
}
}
}
#[cfg(feature = "std")]
impl<'de> Deserialize<'de> for net::SocketAddr {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
if deserializer.is_human_readable() {
let s = try!(String::deserialize(deserializer));
s.parse().map_err(Error::custom)
} else {
use lib::net::SocketAddr;
deserialize_enum!{
SocketAddr SocketAddrKind (V4; b"V4"; 0, V6; b"V6"; 1)
"`V4` or `V6`",
deserializer
}
}
}
}
#[cfg(feature = "std")]
parse_socket_impl!(net::SocketAddrV4, net::SocketAddrV4::new);
#[cfg(feature = "std")]
parse_socket_impl!(net::SocketAddrV6, |ip, port| net::SocketAddrV6::new(ip, port, 0, 0));
////////////////////////////////////////////////////////////////////////////////
@@ -984,70 +1153,10 @@ impl<'de> Deserialize<'de> for PathBuf {
// #[derive(Deserialize)]
// #[serde(variant_identifier)]
#[cfg(all(feature = "std", any(unix, windows)))]
enum OsStringKind {
Unix,
Windows,
}
#[cfg(all(feature = "std", any(unix, windows)))]
static OSSTR_VARIANTS: &'static [&'static str] = &["Unix", "Windows"];
#[cfg(all(feature = "std", any(unix, windows)))]
impl<'de> Deserialize<'de> for OsStringKind {
fn deserialize<D>(deserializer: D) -> Result<OsStringKind, D::Error>
where
D: Deserializer<'de>,
{
struct KindVisitor;
impl<'de> Visitor<'de> for KindVisitor {
type Value = OsStringKind;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("`Unix` or `Windows`")
}
fn visit_u32<E>(self, value: u32) -> Result<OsStringKind, E>
where
E: Error,
{
match value {
0 => Ok(OsStringKind::Unix),
1 => Ok(OsStringKind::Windows),
_ => Err(Error::invalid_value(Unexpected::Unsigned(value as u64), &self),),
}
}
fn visit_str<E>(self, value: &str) -> Result<OsStringKind, E>
where
E: Error,
{
match value {
"Unix" => Ok(OsStringKind::Unix),
"Windows" => Ok(OsStringKind::Windows),
_ => Err(Error::unknown_variant(value, OSSTR_VARIANTS)),
}
}
fn visit_bytes<E>(self, value: &[u8]) -> Result<OsStringKind, E>
where
E: Error,
{
match value {
b"Unix" => Ok(OsStringKind::Unix),
b"Windows" => Ok(OsStringKind::Windows),
_ => {
match str::from_utf8(value) {
Ok(value) => Err(Error::unknown_variant(value, OSSTR_VARIANTS)),
Err(_) => Err(Error::invalid_value(Unexpected::Bytes(value), &self)),
}
}
}
}
}
deserializer.deserialize_identifier(KindVisitor)
}
variant_identifier!{
OsStringKind (Unix; b"Unix"; 0, Windows; b"Windows"; 1)
"`Unix` or `Windows`",
OSSTR_VARIANTS
}
#[cfg(all(feature = "std", any(unix, windows)))]
@@ -1112,10 +1221,10 @@ forwarded_impl!((T), Box<[T]>, Vec::into_boxed_slice);
#[cfg(any(feature = "std", feature = "alloc"))]
forwarded_impl!((), Box<str>, String::into_boxed_str);
#[cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))]
#[cfg(all(not(feature = "unstable"), feature = "rc", any(feature = "std", feature = "alloc")))]
forwarded_impl!((T), Arc<T>, Arc::new);
#[cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))]
#[cfg(all(not(feature = "unstable"), feature = "rc", any(feature = "std", feature = "alloc")))]
forwarded_impl!((T), Rc<T>, Rc::new);
#[cfg(any(feature = "std", feature = "alloc"))]
@@ -1135,6 +1244,31 @@ where
////////////////////////////////////////////////////////////////////////////////
#[cfg(all(feature = "unstable", feature = "rc", any(feature = "std", feature = "alloc")))]
macro_rules! box_forwarded_impl {
($t:ident) => {
impl<'de, T: ?Sized> Deserialize<'de> for $t<T>
where
Box<T>: Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
Box::deserialize(deserializer).map(Into::into)
}
}
}
}
#[cfg(all(feature = "unstable", feature = "rc", any(feature = "std", feature = "alloc")))]
box_forwarded_impl!(Rc);
#[cfg(all(feature = "unstable", feature = "rc", any(feature = "std", feature = "alloc")))]
box_forwarded_impl!(Arc);
////////////////////////////////////////////////////////////////////////////////
impl<'de, T> Deserialize<'de> for Cell<T>
where
T: Deserialize<'de> + Copy,
+68 -2
View File
@@ -1011,6 +1011,72 @@ pub trait Deserializer<'de>: Sized {
fn deserialize_ignored_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: Visitor<'de>;
/// Determine whether `Deserialize` implementations should expect to
/// deserialize their human-readable form.
///
/// Some types have a human-readable form that may be somewhat expensive to
/// construct, as well as a binary form that is compact and efficient.
/// Generally text-based formats like JSON and YAML will prefer to use the
/// human-readable one and binary formats like Bincode will prefer the
/// compact one.
///
/// ```
/// # use std::ops::Add;
/// # use std::str::FromStr;
/// #
/// # struct Timestamp;
/// #
/// # impl FromStr for Timestamp {
/// # type Err = String;
/// # fn from_str(_: &str) -> Result<Self, Self::Err> {
/// # unimplemented!()
/// # }
/// # }
/// #
/// # struct Duration;
/// #
/// # impl Duration {
/// # fn seconds(_: u64) -> Self { unimplemented!() }
/// # }
/// #
/// # impl Add<Duration> for () {
/// # type Output = Timestamp;
/// # fn add(self, _: Duration) -> Self::Output {
/// # unimplemented!()
/// # }
/// # }
/// #
/// use serde::de::{self, Deserialize, Deserializer};
///
/// impl<'de> Deserialize<'de> for Timestamp {
/// fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
/// where D: Deserializer<'de>
/// {
/// if deserializer.is_human_readable() {
/// // Deserialize from a human-readable string like "2015-05-15T17:01:00Z".
/// let s = String::deserialize(deserializer)?;
/// Timestamp::from_str(&s).map_err(de::Error::custom)
/// } else {
/// # // Make it look like an associated constant but compile on older rustc.
/// # mod Timestamp { pub const EPOCH: () = (); }
/// // Deserialize from a compact binary representation, seconds since
/// // the Unix epoch.
/// let n = u64::deserialize(deserializer)?;
/// Ok(Timestamp::EPOCH + Duration::seconds(n))
/// }
/// }
/// }
/// ```
///
/// The default implementation of this method returns `true`. Data formats
/// may override this to `false` to request a compact form for types that
/// support one. Note that modifying this method to change a format from
/// human-readable to compact or vice versa should be regarded as a breaking
/// change, as a value serialized in human-readable mode is not required to
/// deserialize from the same data in compact mode.
#[inline]
fn is_human_readable(&self) -> bool { true }
}
////////////////////////////////////////////////////////////////////////////////
@@ -1120,7 +1186,7 @@ pub trait Visitor<'de>: Sized {
self.visit_i64(v as i64)
}
/// The input contains an `i32`.
/// The input contains an `i64`.
///
/// The default implementation fails with a type error.
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
@@ -1424,7 +1490,7 @@ pub trait SeqAccess<'de> {
/// `Ok(None)` if there are no more remaining items.
///
/// `Deserialize` implementations should typically use
/// `SeqAcccess::next_element` instead.
/// `SeqAccess::next_element` instead.
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
where
T: DeserializeSeed<'de>;
+9 -7
View File
@@ -733,7 +733,7 @@ where
T: IntoDeserializer<'de, E>,
E: de::Error,
{
type Deserializer = SeqDeserializer<<Vec<T> as IntoIterator>::IntoIter, E>;
type Deserializer = SeqDeserializer<<Self as IntoIterator>::IntoIter, E>;
fn into_deserializer(self) -> Self::Deserializer {
SeqDeserializer::new(self.into_iter())
@@ -746,7 +746,7 @@ where
T: IntoDeserializer<'de, E> + Eq + Ord,
E: de::Error,
{
type Deserializer = SeqDeserializer<<BTreeSet<T> as IntoIterator>::IntoIter, E>;
type Deserializer = SeqDeserializer<<Self as IntoIterator>::IntoIter, E>;
fn into_deserializer(self) -> Self::Deserializer {
SeqDeserializer::new(self.into_iter())
@@ -754,12 +754,13 @@ where
}
#[cfg(feature = "std")]
impl<'de, T, E> IntoDeserializer<'de, E> for HashSet<T>
impl<'de, T, S, E> IntoDeserializer<'de, E> for HashSet<T, S>
where
T: IntoDeserializer<'de, E> + Eq + Hash,
S: BuildHasher,
E: de::Error,
{
type Deserializer = SeqDeserializer<<HashSet<T> as IntoIterator>::IntoIter, E>;
type Deserializer = SeqDeserializer<<Self as IntoIterator>::IntoIter, E>;
fn into_deserializer(self) -> Self::Deserializer {
SeqDeserializer::new(self.into_iter())
@@ -1152,7 +1153,7 @@ where
V: IntoDeserializer<'de, E>,
E: de::Error,
{
type Deserializer = MapDeserializer<'de, <BTreeMap<K, V> as IntoIterator>::IntoIter, E>;
type Deserializer = MapDeserializer<'de, <Self as IntoIterator>::IntoIter, E>;
fn into_deserializer(self) -> Self::Deserializer {
MapDeserializer::new(self.into_iter())
@@ -1160,13 +1161,14 @@ where
}
#[cfg(feature = "std")]
impl<'de, K, V, E> IntoDeserializer<'de, E> for HashMap<K, V>
impl<'de, K, V, S, E> IntoDeserializer<'de, E> for HashMap<K, V, S>
where
K: IntoDeserializer<'de, E> + Eq + Hash,
V: IntoDeserializer<'de, E>,
S: BuildHasher,
E: de::Error,
{
type Deserializer = MapDeserializer<'de, <HashMap<K, V> as IntoIterator>::IntoIter, E>;
type Deserializer = MapDeserializer<'de, <Self as IntoIterator>::IntoIter, E>;
fn into_deserializer(self) -> Self::Deserializer {
MapDeserializer::new(self.into_iter())
+33 -7
View File
@@ -79,7 +79,7 @@
////////////////////////////////////////////////////////////////////////////////
// Serde types in rustdoc of other crates get linked to here.
#![doc(html_root_url = "https://docs.rs/serde/1.0.11")]
#![doc(html_root_url = "https://docs.rs/serde/1.0.16")]
// Support using Serde without the standard library!
#![cfg_attr(not(feature = "std"), no_std)]
@@ -89,14 +89,40 @@
//
// https://github.com/serde-rs/serde/issues/812
#![cfg_attr(feature = "unstable", feature(nonzero, specialization))]
#![cfg_attr(all(feature = "std", feature = "unstable"), feature(into_boxed_c_str))]
#![cfg_attr(feature = "alloc", feature(alloc))]
// Whitelisted clippy lints.
#![cfg_attr(feature = "cargo-clippy", allow(doc_markdown))]
#![cfg_attr(feature = "cargo-clippy", allow(linkedlist))]
#![cfg_attr(feature = "cargo-clippy", allow(type_complexity))]
#![cfg_attr(feature = "cargo-clippy", allow(zero_prefixed_literal))]
#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
// Whitelisted clippy lints
#![cfg_attr(feature = "cargo-clippy", allow(
cast_lossless,
doc_markdown,
linkedlist,
type_complexity,
unreadable_literal,
zero_prefixed_literal,
))]
// Whitelisted clippy_pedantic lints
#![cfg_attr(feature = "cargo-clippy", allow(
// integer and float ser/de requires these sorts of casts
cast_possible_truncation,
cast_possible_wrap,
cast_precision_loss,
cast_sign_loss,
// simplifies some macros
invalid_upcast_comparisons,
// things are often more readable this way
option_unwrap_used,
result_unwrap_used,
shadow_reuse,
single_match_else,
stutter,
use_self,
// not practical
missing_docs_in_private_items,
// alternative is not stable
empty_enum,
use_debug,
))]
// Blacklisted Rust lints.
#![deny(missing_docs, unused_imports)]
+33 -9
View File
@@ -834,26 +834,43 @@ mod content {
type Value = TaggedContent<'de, T>;
fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.write_str("any value")
fmt.write_str("internally tagged enum")
}
fn visit_map<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
fn visit_seq<S>(self, mut seq: S) -> Result<Self::Value, S::Error>
where
V: MapAccess<'de>,
S: SeqAccess<'de>,
{
let tag = match try!(seq.next_element()) {
Some(tag) => tag,
None => {
return Err(de::Error::missing_field(self.tag_name));
}
};
let rest = de::value::SeqAccessDeserializer::new(seq);
Ok(TaggedContent {
tag: tag,
content: try!(Content::deserialize(rest)),
})
}
fn visit_map<M>(self, mut map: M) -> Result<Self::Value, M::Error>
where
M: MapAccess<'de>,
{
let mut tag = None;
let mut vec = Vec::with_capacity(size_hint::cautious(visitor.size_hint()));
let mut vec = Vec::with_capacity(size_hint::cautious(map.size_hint()));
while let Some(k) =
try!(visitor.next_key_seed(TagOrContentVisitor::new(self.tag_name))) {
try!(map.next_key_seed(TagOrContentVisitor::new(self.tag_name))) {
match k {
TagOrContent::Tag => {
if tag.is_some() {
return Err(de::Error::duplicate_field(self.tag_name));
}
tag = Some(try!(visitor.next_value()));
tag = Some(try!(map.next_value()));
}
TagOrContent::Content(k) => {
let v = try!(visitor.next_value());
let v = try!(map.next_value());
vec.push((k, v));
}
}
@@ -1802,9 +1819,16 @@ mod content {
write!(formatter, "unit variant {}::{}", self.type_name, self.variant_name)
}
fn visit_map<V>(self, _: V) -> Result<(), V::Error>
fn visit_seq<S>(self, _: S) -> Result<(), S::Error>
where
V: MapAccess<'de>,
S: SeqAccess<'de>,
{
Ok(())
}
fn visit_map<M>(self, _: M) -> Result<(), M::Error>
where
M: MapAccess<'de>,
{
Ok(())
}
+54 -20
View File
@@ -340,10 +340,10 @@ deref_impl!(<'a, T: ?Sized> Serialize for &'a mut T where T: Serialize);
deref_impl!(<T: ?Sized> Serialize for Box<T> where T: Serialize);
#[cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))]
deref_impl!(<T> Serialize for Rc<T> where T: Serialize);
deref_impl!(<T: ?Sized> Serialize for Rc<T> where T: Serialize);
#[cfg(all(feature = "rc", any(feature = "std", feature = "alloc")))]
deref_impl!(<T> Serialize for Arc<T> where T: Serialize);
deref_impl!(<T: ?Sized> Serialize for Arc<T> where T: Serialize);
#[cfg(any(feature = "std", feature = "alloc"))]
deref_impl!(<'a, T: ?Sized> Serialize for Cow<'a, T> where T: Serialize + ToOwned);
@@ -506,9 +506,18 @@ impl Serialize for net::IpAddr {
where
S: Serializer,
{
match *self {
net::IpAddr::V4(ref a) => a.serialize(serializer),
net::IpAddr::V6(ref a) => a.serialize(serializer),
if serializer.is_human_readable() {
match *self {
net::IpAddr::V4(ref a) => a.serialize(serializer),
net::IpAddr::V6(ref a) => a.serialize(serializer),
}
} else {
match *self {
net::IpAddr::V4(ref a) =>
serializer.serialize_newtype_variant("IpAddr", 0, "V4", a),
net::IpAddr::V6(ref a) =>
serializer.serialize_newtype_variant("IpAddr", 1, "V6", a),
}
}
}
}
@@ -519,9 +528,13 @@ impl Serialize for net::Ipv4Addr {
where
S: Serializer,
{
/// "101.102.103.104".len()
const MAX_LEN: usize = 15;
serialize_display_bounded_length!(self, MAX_LEN, serializer)
if serializer.is_human_readable() {
const MAX_LEN: usize = 15;
debug_assert_eq!(MAX_LEN, "101.102.103.104".len());
serialize_display_bounded_length!(self, MAX_LEN, serializer)
} else {
self.octets().serialize(serializer)
}
}
}
@@ -531,9 +544,13 @@ impl Serialize for net::Ipv6Addr {
where
S: Serializer,
{
/// "1000:1002:1003:1004:1005:1006:1007:1008".len()
const MAX_LEN: usize = 39;
serialize_display_bounded_length!(self, MAX_LEN, serializer)
if serializer.is_human_readable() {
const MAX_LEN: usize = 39;
debug_assert_eq!(MAX_LEN, "1001:1002:1003:1004:1005:1006:1007:1008".len());
serialize_display_bounded_length!(self, MAX_LEN, serializer)
} else {
self.octets().serialize(serializer)
}
}
}
@@ -543,9 +560,18 @@ impl Serialize for net::SocketAddr {
where
S: Serializer,
{
match *self {
net::SocketAddr::V4(ref addr) => addr.serialize(serializer),
net::SocketAddr::V6(ref addr) => addr.serialize(serializer),
if serializer.is_human_readable() {
match *self {
net::SocketAddr::V4(ref addr) => addr.serialize(serializer),
net::SocketAddr::V6(ref addr) => addr.serialize(serializer),
}
} else {
match *self {
net::SocketAddr::V4(ref addr) =>
serializer.serialize_newtype_variant("SocketAddr", 0, "V4", addr),
net::SocketAddr::V6(ref addr) =>
serializer.serialize_newtype_variant("SocketAddr", 1, "V6", addr),
}
}
}
}
@@ -556,9 +582,13 @@ impl Serialize for net::SocketAddrV4 {
where
S: Serializer,
{
/// "101.102.103.104:65000".len()
const MAX_LEN: usize = 21;
serialize_display_bounded_length!(self, MAX_LEN, serializer)
if serializer.is_human_readable() {
const MAX_LEN: usize = 21;
debug_assert_eq!(MAX_LEN, "101.102.103.104:65000".len());
serialize_display_bounded_length!(self, MAX_LEN, serializer)
} else {
(self.ip(), self.port()).serialize(serializer)
}
}
}
@@ -568,9 +598,13 @@ impl Serialize for net::SocketAddrV6 {
where
S: Serializer,
{
/// "[1000:1002:1003:1004:1005:1006:1007:1008]:65000".len()
const MAX_LEN: usize = 47;
serialize_display_bounded_length!(self, MAX_LEN, serializer)
if serializer.is_human_readable() {
const MAX_LEN: usize = 47;
debug_assert_eq!(MAX_LEN, "[1001:1002:1003:1004:1005:1006:1007:1008]:65000".len());
serialize_display_bounded_length!(self, MAX_LEN, serializer)
} else {
(self.ip(), self.port()).serialize(serializer)
}
}
}
+64
View File
@@ -1363,6 +1363,56 @@ pub trait Serializer: Sized {
fn collect_str<T: ?Sized>(self, value: &T) -> Result<Self::Ok, Self::Error>
where
T: Display;
/// Determine whether `Serialize` implementations should serialize in
/// human-readable form.
///
/// Some types have a human-readable form that may be somewhat expensive to
/// construct, as well as a binary form that is compact and efficient.
/// Generally text-based formats like JSON and YAML will prefer to use the
/// human-readable one and binary formats like Bincode will prefer the
/// compact one.
///
/// ```
/// # use std::fmt::{self, Display};
/// #
/// # struct Timestamp;
/// #
/// # impl Timestamp {
/// # fn seconds_since_epoch(&self) -> u64 { unimplemented!() }
/// # }
/// #
/// # impl Display for Timestamp {
/// # fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
/// # unimplemented!()
/// # }
/// # }
/// #
/// use serde::{Serialize, Serializer};
///
/// impl Serialize for Timestamp {
/// fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
/// where S: Serializer
/// {
/// if serializer.is_human_readable() {
/// // Serialize to a human-readable string "2015-05-15T17:01:00Z".
/// self.to_string().serialize(serializer)
/// } else {
/// // Serialize to a compact binary representation.
/// self.seconds_since_epoch().serialize(serializer)
/// }
/// }
/// }
/// ```
///
/// The default implementation of this method returns `true`. Data formats
/// may override this to `false` to request a compact form for types that
/// support one. Note that modifying this method to change a format from
/// human-readable to compact or vice versa should be regarded as a breaking
/// change, as a value serialized in human-readable mode is not required to
/// deserialize from the same data in compact mode.
#[inline]
fn is_human_readable(&self) -> bool { true }
}
/// Returned from `Serializer::serialize_seq`.
@@ -1727,6 +1777,13 @@ pub trait SerializeStruct {
where
T: Serialize;
/// Indicate that a struct field has been skipped.
#[inline]
fn skip_field(&mut self, key: &'static str) -> Result<(), Self::Error> {
let _ = key;
Ok(())
}
/// Finish serializing a struct.
fn end(self) -> Result<Self::Ok, Self::Error>;
}
@@ -1772,6 +1829,13 @@ pub trait SerializeStructVariant {
where
T: Serialize;
/// Indicate that a struct variant field has been skipped.
#[inline]
fn skip_field(&mut self, key: &'static str) -> Result<(), Self::Error> {
let _ = key;
Ok(())
}
/// Finish serializing a struct variant.
fn end(self) -> Result<Self::Ok, Self::Error>;
}
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "serde_derive"
version = "1.0.11" # remember to update html_root_url
version = "1.0.16" # 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)]"
@@ -20,5 +20,5 @@ proc-macro = true
[dependencies]
quote = "0.3.8"
serde_derive_internals = { version = "=0.15.1", default-features = false, path = "../serde_derive_internals" }
serde_derive_internals = { version = "=0.16.0", default-features = false, path = "../serde_derive_internals" }
syn = { version = "0.11", features = ["visit"] }
+20 -10
View File
@@ -10,12 +10,12 @@ use std::collections::HashSet;
use syn::{self, visit};
use internals::ast::Container;
use internals::ast::{Body, Container};
use internals::attr;
macro_rules! path {
($($path:tt)+) => {
syn::parse_path(stringify!($($path)+)).unwrap()
syn::parse_path(quote!($($path)+).as_str()).unwrap()
};
}
@@ -88,7 +88,7 @@ pub fn with_bound<F>(
bound: &syn::Path,
) -> syn::Generics
where
F: Fn(&attr::Field) -> bool,
F: Fn(&attr::Field, Option<&attr::Variant>) -> bool,
{
struct FindTyParams {
// Set of all generic type parameters on the current struct (A, B, C in
@@ -124,17 +124,27 @@ where
.map(|ty_param| ty_param.ident.clone())
.collect();
let relevant_tys = cont.body
.all_fields()
.filter(|&field| filter(&field.attrs))
.map(|field| &field.ty);
let mut visitor = FindTyParams {
all_ty_params: all_ty_params,
relevant_ty_params: HashSet::new(),
};
for ty in relevant_tys {
visit::walk_ty(&mut visitor, ty);
match cont.body {
Body::Enum(ref variants) => {
for variant in variants.iter() {
let relevant_fields = variant
.fields
.iter()
.filter(|field| filter(&field.attrs, Some(&variant.attrs)));
for field in relevant_fields {
visit::walk_ty(&mut visitor, field.ty);
}
}
}
Body::Struct(_, ref fields) => {
for field in fields.iter().filter(|field| filter(&field.attrs, None)) {
visit::walk_ty(&mut visitor, field.ty);
}
}
}
let new_predicates = generics
+253 -102
View File
@@ -26,13 +26,14 @@ pub fn expand_derive_deserialize(input: &syn::DeriveInput) -> Result<Tokens, Str
let (de_impl_generics, _, ty_generics, where_clause) = split_with_de_lifetime(&params);
let dummy_const = Ident::new(format!("_IMPL_DESERIALIZE_FOR_{}", ident));
let body = Stmts(deserialize_body(&cont, &params));
let delife = params.borrowed.de_lifetime();
let impl_block = if let Some(remote) = cont.attrs.remote() {
let vis = &input.vis;
quote! {
impl #de_impl_generics #ident #ty_generics #where_clause {
#vis fn deserialize<__D>(__deserializer: __D) -> _serde::export::Result<#remote #ty_generics, __D::Error>
where __D: _serde::Deserializer<'de>
where __D: _serde::Deserializer<#delife>
{
#body
}
@@ -41,9 +42,9 @@ pub fn expand_derive_deserialize(input: &syn::DeriveInput) -> Result<Tokens, Str
} else {
quote! {
#[automatically_derived]
impl #de_impl_generics _serde::Deserialize<'de> for #ident #ty_generics #where_clause {
impl #de_impl_generics _serde::Deserialize<#delife> for #ident #ty_generics #where_clause {
fn deserialize<__D>(__deserializer: __D) -> _serde::export::Result<Self, __D::Error>
where __D: _serde::Deserializer<'de>
where __D: _serde::Deserializer<#delife>
{
#body
}
@@ -75,7 +76,7 @@ struct Parameters {
/// Lifetimes borrowed from the deserializer. These will become bounds on
/// the `'de` lifetime of the deserializer.
borrowed: BTreeSet<syn::Lifetime>,
borrowed: BorrowedLifetimes,
/// At least one field has a serde(getter) attribute, implying that the
/// remote type has a private field.
@@ -89,8 +90,8 @@ impl Parameters {
Some(remote) => remote.clone(),
None => cont.ident.clone().into(),
};
let generics = build_generics(cont);
let borrowed = borrowed_lifetimes(cont);
let generics = build_generics(cont, &borrowed);
let has_getter = cont.body.has_getter();
Parameters {
@@ -107,20 +108,12 @@ impl Parameters {
fn type_name(&self) -> &str {
self.this.segments.last().unwrap().ident.as_ref()
}
fn de_lifetime_def(&self) -> syn::LifetimeDef {
syn::LifetimeDef {
attrs: Vec::new(),
lifetime: syn::Lifetime::new("'de"),
bounds: self.borrowed.iter().cloned().collect(),
}
}
}
// All the generics in the input, plus a bound `T: Deserialize` for each generic
// field type that will be deserialized by us, plus a bound `T: Default` for
// each generic field type that will be set to a default value.
fn build_generics(cont: &Container) -> syn::Generics {
fn build_generics(cont: &Container, borrowed: &BorrowedLifetimes) -> syn::Generics {
let generics = bound::without_defaults(cont.generics);
let generics = bound::with_where_predicates_from_fields(cont, &generics, attr::Field::de_bound);
@@ -136,11 +129,12 @@ fn build_generics(cont: &Container) -> syn::Generics {
attr::Default::Path(_) => generics,
};
let delife = borrowed.de_lifetime();
let generics = bound::with_bound(
cont,
&generics,
needs_deserialize_bound,
&path!(_serde::Deserialize<'de>),
&path!(_serde::Deserialize<#delife>),
);
bound::with_bound(
@@ -157,14 +151,44 @@ fn build_generics(cont: &Container) -> syn::Generics {
// 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(attrs: &attr::Field) -> bool {
!attrs.skip_deserializing() && attrs.deserialize_with().is_none() && attrs.de_bound().is_none()
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())
}
// Fields with a `default` attribute (not `default=...`), and fields with a
// `skip_deserializing` attribute that do not also have `default=...`.
fn requires_default(attrs: &attr::Field) -> bool {
attrs.default() == &attr::Default::Default
fn requires_default(field: &attr::Field, _variant: Option<&attr::Variant>) -> bool {
field.default() == &attr::Default::Default
}
enum BorrowedLifetimes {
Borrowed(BTreeSet<syn::Lifetime>),
Static,
}
impl BorrowedLifetimes {
fn de_lifetime(&self) -> syn::Lifetime {
match *self {
BorrowedLifetimes::Borrowed(_) => syn::Lifetime::new("'de"),
BorrowedLifetimes::Static => syn::Lifetime::new("'static"),
}
}
fn de_lifetime_def(&self) -> Option<syn::LifetimeDef> {
match *self {
BorrowedLifetimes::Borrowed(ref bounds) => {
Some(syn::LifetimeDef {
attrs: Vec::new(),
lifetime: syn::Lifetime::new("'de"),
bounds: bounds.iter().cloned().collect(),
})
}
BorrowedLifetimes::Static => None,
}
}
}
// The union of lifetimes borrowed by each field of the container.
@@ -173,12 +197,19 @@ fn requires_default(attrs: &attr::Field) -> bool {
// lifetimes `'a` and `'b` are borrowed but `'c` is not, the impl is:
//
// impl<'de: 'a + 'b, 'a, 'b, 'c> Deserialize<'de> for S<'a, 'b, 'c>
fn borrowed_lifetimes(cont: &Container) -> BTreeSet<syn::Lifetime> {
//
// If any borrowed lifetime is `'static`, then `'de: 'static` would be redundant
// and we use plain `'static` instead of `'de`.
fn borrowed_lifetimes(cont: &Container) -> BorrowedLifetimes {
let mut lifetimes = BTreeSet::new();
for field in cont.body.all_fields() {
lifetimes.extend(field.attrs.borrowed_lifetimes().iter().cloned());
}
lifetimes
if lifetimes.iter().any(|b| b.ident == "'static") {
BorrowedLifetimes::Static
} else {
BorrowedLifetimes::Borrowed(lifetimes)
}
}
fn deserialize_body(cont: &Container, params: &Parameters) -> Fragment {
@@ -191,7 +222,7 @@ fn deserialize_body(cont: &Container, params: &Parameters) -> Fragment {
if fields.iter().any(|field| field.ident.is_none()) {
panic!("struct has unnamed fields");
}
deserialize_struct(None, params, fields, &cont.attrs, None)
deserialize_struct(None, params, fields, &cont.attrs, None, Untagged::No)
}
Body::Struct(Style::Tuple, ref fields) |
Body::Struct(Style::Newtype, ref fields) => {
@@ -257,6 +288,7 @@ fn deserialize_tuple(
) -> Fragment {
let this = &params.this;
let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = split_with_de_lifetime(params,);
let delife = params.borrowed.de_lifetime();
// If there are getters (implying private fields), construct the local type
// and use an `Into` conversion to get the remote type. If there are no
@@ -318,10 +350,10 @@ fn deserialize_tuple(
quote_block! {
struct __Visitor #de_impl_generics #where_clause {
marker: _serde::export::PhantomData<#this #ty_generics>,
lifetime: _serde::export::PhantomData<&'de ()>,
lifetime: _serde::export::PhantomData<&#delife ()>,
}
impl #de_impl_generics _serde::de::Visitor<'de> for __Visitor #de_ty_generics #where_clause {
impl #de_impl_generics _serde::de::Visitor<#delife> for __Visitor #de_ty_generics #where_clause {
type Value = #this #ty_generics;
fn expecting(&self, formatter: &mut _serde::export::Formatter) -> _serde::export::fmt::Result {
@@ -332,7 +364,7 @@ fn deserialize_tuple(
#[inline]
fn visit_seq<__A>(self, #visitor_var: __A) -> _serde::export::Result<Self::Value, __A::Error>
where __A: _serde::de::SeqAccess<'de>
where __A: _serde::de::SeqAccess<#delife>
{
#visit_seq
}
@@ -372,7 +404,7 @@ fn deserialize_seq(
quote!(try!(_serde::de::SeqAccess::next_element::<#field_ty>(&mut __seq)))
}
Some(path) => {
let (wrapper, wrapper_ty) = wrap_deserialize_with(
let (wrapper, wrapper_ty) = wrap_deserialize_field_with(
params, field.ty, path);
quote!({
#wrapper
@@ -420,6 +452,8 @@ fn deserialize_seq(
}
fn deserialize_newtype_struct(type_path: &Tokens, params: &Parameters, field: &Field) -> Tokens {
let delife = params.borrowed.de_lifetime();
let value = match field.attrs.deserialize_with() {
None => {
let field_ty = &field.ty;
@@ -428,7 +462,7 @@ fn deserialize_newtype_struct(type_path: &Tokens, params: &Parameters, field: &F
}
}
Some(path) => {
let (wrapper, wrapper_ty) = wrap_deserialize_with(params, field.ty, path);
let (wrapper, wrapper_ty) = wrap_deserialize_field_with(params, field.ty, path);
quote!({
#wrapper
try!(<#wrapper_ty as _serde::Deserialize>::deserialize(__e)).value
@@ -447,25 +481,31 @@ fn deserialize_newtype_struct(type_path: &Tokens, params: &Parameters, field: &F
quote! {
#[inline]
fn visit_newtype_struct<__E>(self, __e: __E) -> _serde::export::Result<Self::Value, __E::Error>
where __E: _serde::Deserializer<'de>
where __E: _serde::Deserializer<#delife>
{
_serde::export::Ok(#result)
}
}
}
enum Untagged {
Yes,
No,
}
fn deserialize_struct(
variant_ident: Option<&syn::Ident>,
params: &Parameters,
fields: &[Field],
cattrs: &attr::Container,
deserializer: Option<Tokens>,
untagged: Untagged,
) -> Fragment {
let is_enum = variant_ident.is_some();
let is_untagged = deserializer.is_some();
let this = &params.this;
let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = split_with_de_lifetime(params,);
let delife = params.borrowed.de_lifetime();
// If there are getters (implying private fields), construct the local type
// and use an `Into` conversion to get the remote type. If there are no
@@ -524,18 +564,19 @@ fn deserialize_struct(
quote!(mut __seq)
};
let visit_seq = if is_untagged {
// untagged struct variants do not get a visit_seq method
None
} else {
Some(quote! {
#[inline]
fn visit_seq<__A>(self, #visitor_var: __A) -> _serde::export::Result<Self::Value, __A::Error>
where __A: _serde::de::SeqAccess<'de>
{
#visit_seq
}
})
// untagged struct variants do not get a visit_seq method
let visit_seq = match untagged {
Untagged::Yes => None,
Untagged::No => {
Some(quote! {
#[inline]
fn visit_seq<__A>(self, #visitor_var: __A) -> _serde::export::Result<Self::Value, __A::Error>
where __A: _serde::de::SeqAccess<#delife>
{
#visit_seq
}
})
}
};
quote_block! {
@@ -543,10 +584,10 @@ fn deserialize_struct(
struct __Visitor #de_impl_generics #where_clause {
marker: _serde::export::PhantomData<#this #ty_generics>,
lifetime: _serde::export::PhantomData<&'de ()>,
lifetime: _serde::export::PhantomData<&#delife ()>,
}
impl #de_impl_generics _serde::de::Visitor<'de> for __Visitor #de_ty_generics #where_clause {
impl #de_impl_generics _serde::de::Visitor<#delife> for __Visitor #de_ty_generics #where_clause {
type Value = #this #ty_generics;
fn expecting(&self, formatter: &mut _serde::export::Formatter) -> _serde::export::fmt::Result {
@@ -557,7 +598,7 @@ fn deserialize_struct(
#[inline]
fn visit_map<__A>(self, mut __map: __A) -> _serde::export::Result<Self::Value, __A::Error>
where __A: _serde::de::MapAccess<'de>
where __A: _serde::de::MapAccess<#delife>
{
#visit_map
}
@@ -594,6 +635,7 @@ fn deserialize_externally_tagged_enum(
) -> Fragment {
let this = &params.this;
let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = split_with_de_lifetime(params,);
let delife = params.borrowed.de_lifetime();
let type_name = cattrs.name().deserialize_name();
@@ -659,10 +701,10 @@ fn deserialize_externally_tagged_enum(
struct __Visitor #de_impl_generics #where_clause {
marker: _serde::export::PhantomData<#this #ty_generics>,
lifetime: _serde::export::PhantomData<&'de ()>,
lifetime: _serde::export::PhantomData<&#delife ()>,
}
impl #de_impl_generics _serde::de::Visitor<'de> for __Visitor #de_ty_generics #where_clause {
impl #de_impl_generics _serde::de::Visitor<#delife> for __Visitor #de_ty_generics #where_clause {
type Value = #this #ty_generics;
fn expecting(&self, formatter: &mut _serde::export::Formatter) -> _serde::export::fmt::Result {
@@ -670,7 +712,7 @@ fn deserialize_externally_tagged_enum(
}
fn visit_enum<__A>(self, __data: __A) -> _serde::export::Result<Self::Value, __A::Error>
where __A: _serde::de::EnumAccess<'de>
where __A: _serde::de::EnumAccess<#delife>
{
#match_variant
}
@@ -751,6 +793,7 @@ fn deserialize_adjacently_tagged_enum(
) -> Fragment {
let this = &params.this;
let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = split_with_de_lifetime(params,);
let delife = params.borrowed.de_lifetime();
let variant_names_idents: Vec<_> = variants
.iter()
@@ -796,9 +839,8 @@ fn deserialize_adjacently_tagged_enum(
let type_name = cattrs.name().deserialize_name();
let deny_unknown_fields = cattrs.deny_unknown_fields();
/// If unknown fields are allowed, we pick the visitor that can
/// step over those. Otherwise we pick the visitor that fails on
/// unknown keys.
// If unknown fields are allowed, we pick the visitor that can step over
// those. Otherwise we pick the visitor that fails on unknown keys.
let field_visitor_ty = if deny_unknown_fields {
quote! { _serde::private::de::TagOrContentFieldVisitor }
} else {
@@ -853,13 +895,13 @@ fn deserialize_adjacently_tagged_enum(
};
}
/// Advance the map by one key, returning early in case of error.
// Advance the map by one key, returning early in case of error.
let next_key = quote! {
try!(_serde::de::MapAccess::next_key_seed(&mut __map, #tag_or_content))
};
/// When allowing unknown fields, we want to transparently step through keys we don't care
/// about until we find `tag`, `content`, or run out of keys.
// When allowing unknown fields, we want to transparently step through keys
// we don't care about until we find `tag`, `content`, or run out of keys.
let next_relevant_key = if deny_unknown_fields {
next_key
} else {
@@ -888,9 +930,9 @@ fn deserialize_adjacently_tagged_enum(
}
};
/// Step through remaining keys, looking for duplicates of previously-seen keys.
/// When unknown fields are denied, any key that isn't a duplicate will at this
/// point immediately produce an error.
// Step through remaining keys, looking for duplicates of previously-seen
// keys. When unknown fields are denied, any key that isn't a duplicate will
// at this point immediately produce an error.
let visit_remaining_keys = quote! {
match #next_relevant_key {
_serde::export::Some(_serde::private::de::TagOrContentField::Tag) => {
@@ -911,14 +953,14 @@ fn deserialize_adjacently_tagged_enum(
struct __Seed #de_impl_generics #where_clause {
field: __Field,
marker: _serde::export::PhantomData<#this #ty_generics>,
lifetime: _serde::export::PhantomData<&'de ()>,
lifetime: _serde::export::PhantomData<&#delife ()>,
}
impl #de_impl_generics _serde::de::DeserializeSeed<'de> for __Seed #de_ty_generics #where_clause {
impl #de_impl_generics _serde::de::DeserializeSeed<#delife> for __Seed #de_ty_generics #where_clause {
type Value = #this #ty_generics;
fn deserialize<__D>(self, __deserializer: __D) -> _serde::export::Result<Self::Value, __D::Error>
where __D: _serde::Deserializer<'de>
where __D: _serde::Deserializer<#delife>
{
match self.field {
#(#variant_arms)*
@@ -928,10 +970,10 @@ fn deserialize_adjacently_tagged_enum(
struct __Visitor #de_impl_generics #where_clause {
marker: _serde::export::PhantomData<#this #ty_generics>,
lifetime: _serde::export::PhantomData<&'de ()>,
lifetime: _serde::export::PhantomData<&#delife ()>,
}
impl #de_impl_generics _serde::de::Visitor<'de> for __Visitor #de_ty_generics #where_clause {
impl #de_impl_generics _serde::de::Visitor<#delife> for __Visitor #de_ty_generics #where_clause {
type Value = #this #ty_generics;
fn expecting(&self, formatter: &mut _serde::export::Formatter) -> _serde::export::fmt::Result {
@@ -939,7 +981,7 @@ fn deserialize_adjacently_tagged_enum(
}
fn visit_map<__A>(self, mut __map: __A) -> _serde::export::Result<Self::Value, __A::Error>
where __A: _serde::de::MapAccess<'de>
where __A: _serde::de::MapAccess<#delife>
{
// Visit the first relevant key.
match #next_relevant_key {
@@ -1003,7 +1045,7 @@ fn deserialize_adjacently_tagged_enum(
}
fn visit_seq<__A>(self, mut __seq: __A) -> _serde::export::Result<Self::Value, __A::Error>
where __A: _serde::de::SeqAccess<'de>
where __A: _serde::de::SeqAccess<#delife>
{
// Visit the first element - the tag.
match try!(_serde::de::SeqAccess::next_element(&mut __seq)) {
@@ -1085,6 +1127,16 @@ fn deserialize_externally_tagged_variant(
variant: &Variant,
cattrs: &attr::Container,
) -> Fragment {
if let Some(path) = variant.attrs.deserialize_with() {
let (wrapper, wrapper_ty, unwrap_fn) =
wrap_deserialize_variant_with(params, &variant, path);
return quote_block! {
#wrapper
_serde::export::Result::map(
_serde::de::VariantAccess::newtype_variant::<#wrapper_ty>(__variant), #unwrap_fn)
};
}
let variant_ident = &variant.ident;
match variant.style {
@@ -1102,7 +1154,7 @@ fn deserialize_externally_tagged_variant(
deserialize_tuple(Some(variant_ident), params, &variant.fields, cattrs, None)
}
Style::Struct => {
deserialize_struct(Some(variant_ident), params, &variant.fields, cattrs, None)
deserialize_struct(Some(variant_ident), params, &variant.fields, cattrs, None, Untagged::No)
}
}
}
@@ -1113,6 +1165,10 @@ fn deserialize_internally_tagged_variant(
cattrs: &attr::Container,
deserializer: Tokens,
) -> Fragment {
if variant.attrs.deserialize_with().is_some() {
return deserialize_untagged_variant(params, variant, cattrs, deserializer);
}
let variant_ident = &variant.ident;
match variant.style {
@@ -1125,8 +1181,23 @@ fn deserialize_internally_tagged_variant(
_serde::export::Ok(#this::#variant_ident)
}
}
Style::Newtype | Style::Struct => {
deserialize_untagged_variant(params, variant, cattrs, deserializer)
Style::Newtype => {
deserialize_untagged_newtype_variant(
variant_ident,
params,
&variant.fields[0],
deserializer,
)
}
Style::Struct => {
deserialize_struct(
Some(variant_ident),
params,
&variant.fields,
cattrs,
Some(deserializer),
Untagged::No,
)
}
Style::Tuple => unreachable!("checked in serde_derive_internals"),
}
@@ -1138,6 +1209,16 @@ fn deserialize_untagged_variant(
cattrs: &attr::Container,
deserializer: Tokens,
) -> Fragment {
if let Some(path) = variant.attrs.deserialize_with() {
let (wrapper, wrapper_ty, unwrap_fn) =
wrap_deserialize_variant_with(params, &variant, path);
return quote_block! {
#wrapper
_serde::export::Result::map(
<#wrapper_ty as _serde::Deserialize>::deserialize(#deserializer), #unwrap_fn)
};
}
let variant_ident = &variant.ident;
match variant.style {
@@ -1178,6 +1259,7 @@ fn deserialize_untagged_variant(
&variant.fields,
cattrs,
Some(deserializer),
Untagged::Yes,
)
}
}
@@ -1199,7 +1281,7 @@ fn deserialize_externally_tagged_newtype_variant(
}
}
Some(path) => {
let (wrapper, wrapper_ty) = wrap_deserialize_with(params, field.ty, path);
let (wrapper, wrapper_ty) = wrap_deserialize_field_with(params, field.ty, path);
quote_block! {
#wrapper
_serde::export::Result::map(
@@ -1227,7 +1309,7 @@ fn deserialize_untagged_newtype_variant(
}
}
Some(path) => {
let (wrapper, wrapper_ty) = wrap_deserialize_with(params, field.ty, path);
let (wrapper, wrapper_ty) = wrap_deserialize_field_with(params, field.ty, path);
quote_block! {
#wrapper
_serde::export::Result::map(
@@ -1340,6 +1422,7 @@ fn deserialize_custom_identifier(
};
let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = split_with_de_lifetime(params,);
let delife = params.borrowed.de_lifetime();
let visitor_impl =
Stmts(deserialize_identifier(this.clone(), &names_idents, is_variant, fallthrough),);
@@ -1348,10 +1431,10 @@ fn deserialize_custom_identifier(
struct __FieldVisitor #de_impl_generics #where_clause {
marker: _serde::export::PhantomData<#this #ty_generics>,
lifetime: _serde::export::PhantomData<&'de ()>,
lifetime: _serde::export::PhantomData<&#delife ()>,
}
impl #de_impl_generics _serde::de::Visitor<'de> for __FieldVisitor #de_ty_generics #where_clause {
impl #de_impl_generics _serde::de::Visitor<#delife> for __FieldVisitor #de_ty_generics #where_clause {
type Value = #this #ty_generics;
#visitor_impl
@@ -1385,26 +1468,27 @@ fn deserialize_identifier(
"field identifier"
};
let visit_index = if is_variant {
let variant_indices = 0u32..;
let fallthrough_msg = format!("variant index 0 <= i < {}", fields.len());
let visit_index = quote! {
fn visit_u32<__E>(self, __value: u32) -> _serde::export::Result<Self::Value, __E>
where __E: _serde::de::Error
{
match __value {
#(
#variant_indices => _serde::export::Ok(#constructors),
)*
_ => _serde::export::Err(_serde::de::Error::invalid_value(
_serde::de::Unexpected::Unsigned(__value as u64),
&#fallthrough_msg))
}
}
};
Some(visit_index)
let index_expecting = if is_variant {
"variant"
} else {
None
"field"
};
let variant_indices = 0u64..;
let fallthrough_msg = format!("{} index 0 <= i < {}", index_expecting, fields.len());
let visit_index = quote! {
fn visit_u64<__E>(self, __value: u64) -> _serde::export::Result<Self::Value, __E>
where __E: _serde::de::Error
{
match __value {
#(
#variant_indices => _serde::export::Ok(#constructors),
)*
_ => _serde::export::Err(_serde::de::Error::invalid_value(
_serde::de::Unexpected::Unsigned(__value),
&#fallthrough_msg))
}
}
};
let bytes_to_str = if fallthrough.is_some() {
@@ -1529,7 +1613,7 @@ fn deserialize_map(
}
}
Some(path) => {
let (wrapper, wrapper_ty) = wrap_deserialize_with(
let (wrapper, wrapper_ty) = wrap_deserialize_field_with(
params, field.ty, path);
quote!({
#wrapper
@@ -1662,22 +1746,23 @@ fn field_i(i: usize) -> Ident {
/// in a trait to prevent it from accessing the internal `Deserialize` state.
fn wrap_deserialize_with(
params: &Parameters,
field_ty: &syn::Ty,
value_ty: Tokens,
deserialize_with: &syn::Path,
) -> (Tokens, Tokens) {
let this = &params.this;
let (de_impl_generics, de_ty_generics, ty_generics, where_clause) = split_with_de_lifetime(params,);
let delife = params.borrowed.de_lifetime();
let wrapper = quote! {
struct __DeserializeWith #de_impl_generics #where_clause {
value: #field_ty,
value: #value_ty,
phantom: _serde::export::PhantomData<#this #ty_generics>,
lifetime: _serde::export::PhantomData<&'de ()>,
lifetime: _serde::export::PhantomData<&#delife ()>,
}
impl #de_impl_generics _serde::Deserialize<'de> for __DeserializeWith #de_ty_generics #where_clause {
impl #de_impl_generics _serde::Deserialize<#delife> for __DeserializeWith #de_ty_generics #where_clause {
fn deserialize<__D>(__deserializer: __D) -> _serde::export::Result<Self, __D::Error>
where __D: _serde::Deserializer<'de>
where __D: _serde::Deserializer<#delife>
{
_serde::export::Ok(__DeserializeWith {
value: try!(#deserialize_with(__deserializer)),
@@ -1693,6 +1778,68 @@ fn wrap_deserialize_with(
(wrapper, wrapper_ty)
}
fn wrap_deserialize_field_with(
params: &Parameters,
field_ty: &syn::Ty,
deserialize_with: &syn::Path,
) -> (Tokens, Tokens) {
wrap_deserialize_with(params, quote!(#field_ty), deserialize_with)
}
fn wrap_deserialize_variant_with(
params: &Parameters,
variant: &Variant,
deserialize_with: &syn::Path,
) -> (Tokens, Tokens, Tokens) {
let this = &params.this;
let variant_ident = &variant.ident;
let field_tys = variant.fields.iter().map(|field| field.ty);
let (wrapper, wrapper_ty) =
wrap_deserialize_with(params, quote!((#(#field_tys),*)), deserialize_with);
let field_access = (0..variant.fields.len()).map(|n| Ident::new(format!("{}", n)));
let unwrap_fn = match variant.style {
Style::Struct => {
let field_idents = variant.fields.iter().map(|field| field.ident.as_ref().unwrap());
quote! {
{
|__wrap| {
#this::#variant_ident { #(#field_idents: __wrap.value.#field_access),* }
}
}
}
}
Style::Tuple => {
quote! {
{
|__wrap| {
#this::#variant_ident(#(__wrap.value.#field_access),*)
}
}
}
}
Style::Newtype => {
quote! {
{
|__wrap| {
#this::#variant_ident(__wrap.value)
}
}
}
}
Style::Unit => {
quote! {
{
|__wrap| { #this::#variant_ident }
}
}
}
};
(wrapper, wrapper_ty, unwrap_fn)
}
fn expr_is_missing(field: &Field, cattrs: &attr::Container) -> Fragment {
match *field.attrs.default() {
attr::Default::Default => {
@@ -1733,20 +1880,24 @@ struct DeImplGenerics<'a>(&'a Parameters);
impl<'a> ToTokens for DeImplGenerics<'a> {
fn to_tokens(&self, tokens: &mut Tokens) {
let mut generics = self.0.generics.clone();
generics.lifetimes.insert(0, self.0.de_lifetime_def());
if let Some(de_lifetime) = self.0.borrowed.de_lifetime_def() {
generics.lifetimes.insert(0, de_lifetime);
}
let (impl_generics, _, _) = generics.split_for_impl();
impl_generics.to_tokens(tokens);
}
}
struct DeTyGenerics<'a>(&'a syn::Generics);
struct DeTyGenerics<'a>(&'a Parameters);
impl<'a> ToTokens for DeTyGenerics<'a> {
fn to_tokens(&self, tokens: &mut Tokens) {
let mut generics = self.0.clone();
generics
.lifetimes
.insert(0, syn::LifetimeDef::new("'de"));
let mut generics = self.0.generics.clone();
if self.0.borrowed.de_lifetime_def().is_some() {
generics
.lifetimes
.insert(0, syn::LifetimeDef::new("'de"));
}
let (_, ty_generics, _) = generics.split_for_impl();
ty_generics.to_tokens(tokens);
}
@@ -1755,7 +1906,7 @@ impl<'a> ToTokens for DeTyGenerics<'a> {
fn split_with_de_lifetime(params: &Parameters,)
-> (DeImplGenerics, DeTyGenerics, syn::TyGenerics, &syn::WhereClause) {
let de_impl_generics = DeImplGenerics(&params);
let de_ty_generics = DeTyGenerics(&params.generics);
let de_ty_generics = DeTyGenerics(&params);
let (_, ty_generics, where_clause) = params.generics.split_for_impl();
(de_impl_generics, de_ty_generics, ty_generics, where_clause)
}
+1 -1
View File
@@ -16,7 +16,7 @@
//!
//! [https://serde.rs/derive.html]: https://serde.rs/derive.html
#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.11")]
#![doc(html_root_url = "https://docs.rs/serde_derive/1.0.16")]
#![cfg_attr(feature = "cargo-clippy", allow(too_many_arguments))]
#![cfg_attr(feature = "cargo-clippy", allow(used_underscore_binding))]
+160 -52
View File
@@ -143,12 +143,16 @@ fn build_generics(cont: &Container) -> syn::Generics {
}
}
// Fields 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(attrs: &attr::Field) -> bool {
!attrs.skip_serializing() && attrs.serialize_with().is_none() && attrs.ser_bound().is_none()
// 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.
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())
}
fn serialize_body(cont: &Container, params: &Parameters) -> Fragment {
@@ -203,7 +207,7 @@ fn serialize_newtype_struct(
let mut field_expr = get_field(params, field, 0);
if let Some(path) = field.attrs.serialize_with() {
field_expr = wrap_serialize_with(params, field.ty, path, field_expr);
field_expr = wrap_serialize_field_with(params, field.ty, path, field_expr);
}
quote_expr! {
@@ -242,6 +246,7 @@ fn serialize_struct(params: &Parameters, fields: &[Field], cattrs: &attr::Contai
params,
false,
quote!(_serde::ser::SerializeStruct::serialize_field),
quote!(_serde::ser::SerializeStruct::skip_field),
);
let type_name = cattrs.name().serialize_name();
@@ -395,6 +400,19 @@ fn serialize_externally_tagged_variant(
let type_name = cattrs.name().serialize_name();
let variant_name = variant.attrs.name().serialize_name();
if let Some(path) = variant.attrs.serialize_with() {
let ser = wrap_serialize_variant_with(params, path, &variant);
return quote_expr! {
_serde::Serializer::serialize_newtype_variant(
__serializer,
#type_name,
#variant_index,
#variant_name,
#ser,
)
};
}
match variant.style {
Style::Unit => {
quote_expr! {
@@ -410,7 +428,7 @@ fn serialize_externally_tagged_variant(
let field = &variant.fields[0];
let mut field_expr = quote!(__field0);
if let Some(path) = field.attrs.serialize_with() {
field_expr = wrap_serialize_with(params, field.ty, path, field_expr);
field_expr = wrap_serialize_field_with(params, field.ty, path, field_expr);
}
quote_expr! {
@@ -460,6 +478,20 @@ fn serialize_internally_tagged_variant(
let enum_ident_str = params.type_name();
let variant_ident_str = variant.ident.as_ref();
if let Some(path) = variant.attrs.serialize_with() {
let ser = wrap_serialize_variant_with(params, path, &variant);
return quote_expr! {
_serde::private::ser::serialize_tagged_newtype(
__serializer,
#enum_ident_str,
#variant_ident_str,
#tag,
#variant_name,
#ser,
)
};
}
match variant.style {
Style::Unit => {
quote_block! {
@@ -474,7 +506,7 @@ fn serialize_internally_tagged_variant(
let field = &variant.fields[0];
let mut field_expr = quote!(__field0);
if let Some(path) = field.attrs.serialize_with() {
field_expr = wrap_serialize_with(params, field.ty, path, field_expr);
field_expr = wrap_serialize_field_with(params, field.ty, path, field_expr);
}
quote_expr! {
@@ -515,44 +547,57 @@ fn serialize_adjacently_tagged_variant(
let variant_name = variant.attrs.name().serialize_name();
let inner = Stmts(
match variant.style {
Style::Unit => {
return quote_block! {
let mut __struct = try!(_serde::Serializer::serialize_struct(
__serializer, #type_name, 1));
try!(_serde::ser::SerializeStruct::serialize_field(
&mut __struct, #tag, #variant_name));
_serde::ser::SerializeStruct::end(__struct)
};
if let Some(path) = variant.attrs.serialize_with() {
let ser = wrap_serialize_variant_with(params, path, &variant);
quote_expr! {
_serde::Serialize::serialize(#ser, __serializer)
}
Style::Newtype => {
let field = &variant.fields[0];
let mut field_expr = quote!(__field0);
if let Some(path) = field.attrs.serialize_with() {
field_expr = wrap_serialize_with(params, field.ty, path, field_expr);
} else {
match variant.style {
Style::Unit => {
return quote_block! {
let mut __struct = try!(_serde::Serializer::serialize_struct(
__serializer, #type_name, 1));
try!(_serde::ser::SerializeStruct::serialize_field(
&mut __struct, #tag, #variant_name));
_serde::ser::SerializeStruct::end(__struct)
};
}
Style::Newtype => {
let field = &variant.fields[0];
let mut field_expr = quote!(__field0);
if let Some(path) = field.attrs.serialize_with() {
field_expr = wrap_serialize_field_with(params, field.ty, path, field_expr);
}
quote_expr! {
_serde::Serialize::serialize(#field_expr, __serializer)
quote_expr! {
_serde::Serialize::serialize(#field_expr, __serializer)
}
}
Style::Tuple => {
serialize_tuple_variant(TupleVariant::Untagged, params, &variant.fields)
}
Style::Struct => {
serialize_struct_variant(
StructVariant::Untagged,
params,
&variant.fields,
&variant_name,
)
}
}
Style::Tuple => {
serialize_tuple_variant(TupleVariant::Untagged, params, &variant.fields)
}
Style::Struct => {
serialize_struct_variant(
StructVariant::Untagged,
params,
&variant.fields,
&variant_name,
)
}
},
);
let fields_ty = variant.fields.iter().map(|f| &f.ty);
let ref fields_ident: Vec<_> = match variant.style {
Style::Unit => unreachable!(),
Style::Unit => {
if variant.attrs.serialize_with().is_some() {
vec![]
} else {
unreachable!()
}
}
Style::Newtype => vec![Ident::new("__field0")],
Style::Tuple => {
(0..variant.fields.len())
@@ -576,7 +621,11 @@ fn serialize_adjacently_tagged_variant(
let (_, ty_generics, where_clause) = params.generics.split_for_impl();
let wrapper_generics = bound::with_lifetime_bound(&params.generics, "'__a");
let wrapper_generics = if let Style::Unit = variant.style {
params.generics.clone()
} else {
bound::with_lifetime_bound(&params.generics, "'__a")
};
let (wrapper_impl_generics, wrapper_ty_generics, _) = wrapper_generics.split_for_impl();
quote_block! {
@@ -612,6 +661,13 @@ fn serialize_untagged_variant(
variant: &Variant,
cattrs: &attr::Container,
) -> Fragment {
if let Some(path) = variant.attrs.serialize_with() {
let ser = wrap_serialize_variant_with(params, path, &variant);
return quote_expr! {
_serde::Serialize::serialize(#ser, __serializer)
};
}
match variant.style {
Style::Unit => {
quote_expr! {
@@ -622,7 +678,7 @@ fn serialize_untagged_variant(
let field = &variant.fields[0];
let mut field_expr = quote!(__field0);
if let Some(path) = field.attrs.serialize_with() {
field_expr = wrap_serialize_with(params, field.ty, path, field_expr);
field_expr = wrap_serialize_field_with(params, field.ty, path, field_expr);
}
quote_expr! {
@@ -707,15 +763,23 @@ fn serialize_struct_variant<'a>(
fields: &[Field],
name: &str,
) -> Fragment {
let method = match context {
let (method, skip_method) = match context {
StructVariant::ExternallyTagged { .. } => {
quote!(_serde::ser::SerializeStructVariant::serialize_field)
(
quote!(_serde::ser::SerializeStructVariant::serialize_field),
quote!(_serde::ser::SerializeStructVariant::skip_field),
)
}
StructVariant::InternallyTagged { .. } |
StructVariant::Untagged => quote!(_serde::ser::SerializeStruct::serialize_field),
StructVariant::Untagged => {
(
quote!(_serde::ser::SerializeStruct::serialize_field),
quote!(_serde::ser::SerializeStruct::skip_field),
)
}
};
let serialize_fields = serialize_struct_visitor(fields, params, true, method);
let serialize_fields = serialize_struct_visitor(fields, params, true, method, skip_method);
let mut serialized_fields = fields
.iter()
@@ -808,7 +872,7 @@ fn serialize_tuple_struct_visitor(
.map(|path| quote!(#path(#field_expr)));
if let Some(path) = field.attrs.serialize_with() {
field_expr = wrap_serialize_with(params, field.ty, path, field_expr);
field_expr = wrap_serialize_field_with(params, field.ty, path, field_expr);
}
let ser = quote! {
@@ -829,6 +893,7 @@ fn serialize_struct_visitor(
params: &Parameters,
is_enum: bool,
func: Tokens,
skip_func: Tokens,
) -> Vec<Tokens> {
fields
.iter()
@@ -850,7 +915,7 @@ fn serialize_struct_visitor(
.map(|path| quote!(#path(#field_expr)));
if let Some(path) = field.attrs.serialize_with() {
field_expr = wrap_serialize_with(params, field.ty, path, field_expr)
field_expr = wrap_serialize_field_with(params, field.ty, path, field_expr);
}
let ser = quote! {
@@ -859,28 +924,71 @@ fn serialize_struct_visitor(
match skip {
None => ser,
Some(skip) => quote!(if !#skip { #ser }),
Some(skip) => {
quote! {
if !#skip {
#ser
} else {
try!(#skip_func(&mut __serde_state, #key_expr));
}
}
}
}
},
)
.collect()
}
fn wrap_serialize_with(
fn wrap_serialize_field_with(
params: &Parameters,
field_ty: &syn::Ty,
serialize_with: &syn::Path,
value: Tokens,
field_expr: Tokens,
) -> Tokens {
wrap_serialize_with(params,
serialize_with,
&[field_ty],
&[quote!(#field_expr)])
}
fn wrap_serialize_variant_with(
params: &Parameters,
serialize_with: &syn::Path,
variant: &Variant,
) -> Tokens {
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.as_ref().map_or_else(|| Ident::new(format!("__field{}", i)),
|id| id.clone());
quote!(#id)
})
.collect();
wrap_serialize_with(params, serialize_with, field_tys.as_slice(), field_exprs.as_slice())
}
fn wrap_serialize_with(
params: &Parameters,
serialize_with: &syn::Path,
field_tys: &[&syn::Ty],
field_exprs: &[Tokens],
) -> Tokens {
let this = &params.this;
let (_, ty_generics, where_clause) = params.generics.split_for_impl();
let wrapper_generics = bound::with_lifetime_bound(&params.generics, "'__a");
let wrapper_generics = if field_exprs.len() == 0 {
params.generics.clone()
} else {
bound::with_lifetime_bound(&params.generics, "'__a")
};
let (wrapper_impl_generics, wrapper_ty_generics, _) = wrapper_generics.split_for_impl();
let field_access = (0..field_exprs.len()).map(|n| Ident::new(format!("{}", n)));
quote!({
struct __SerializeWith #wrapper_impl_generics #where_clause {
value: &'__a #field_ty,
values: (#(&'__a #field_tys, )*),
phantom: _serde::export::PhantomData<#this #ty_generics>,
}
@@ -888,12 +996,12 @@ fn wrap_serialize_with(
fn serialize<__S>(&self, __s: __S) -> _serde::export::Result<__S::Ok, __S::Error>
where __S: _serde::Serializer
{
#serialize_with(self.value, __s)
#serialize_with(#(self.values.#field_access, )* __s)
}
}
&__SerializeWith {
value: #value,
values: (#(#field_exprs, )*),
phantom: _serde::export::PhantomData::<#this #ty_generics>,
}
})
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "serde_derive_internals"
version = "0.15.1" # remember to update html_root_url
version = "0.16.0" # remember to update html_root_url
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>", "David Tolnay <dtolnay@gmail.com>"]
license = "MIT/Apache-2.0"
description = "AST representation used by Serde derive macros. Unstable."
+40
View File
@@ -510,6 +510,8 @@ pub struct Variant {
skip_deserializing: bool,
skip_serializing: bool,
other: bool,
serialize_with: Option<syn::Path>,
deserialize_with: Option<syn::Path>,
}
impl Variant {
@@ -520,6 +522,8 @@ impl Variant {
let mut skip_serializing = BoolAttr::none(cx, "skip_serializing");
let mut rename_all = Attr::none(cx, "rename_all");
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");
for meta_items in variant.attrs.iter().filter_map(get_serde_meta_items) {
for meta_item in meta_items {
@@ -569,6 +573,32 @@ impl Variant {
other.set_true();
}
// Parse `#[serde(with = "...")]`
MetaItem(NameValue(ref name, ref lit)) if name == "with" => {
if let Ok(path) = parse_lit_into_path(cx, name.as_ref(), lit) {
let mut ser_path = path.clone();
ser_path.segments.push("serialize".into());
serialize_with.set(ser_path);
let mut de_path = path;
de_path.segments.push("deserialize".into());
deserialize_with.set(de_path);
}
}
// Parse `#[serde(serialize_with = "...")]`
MetaItem(NameValue(ref name, ref lit)) if name == "serialize_with" => {
if let Ok(path) = parse_lit_into_path(cx, name.as_ref(), lit) {
serialize_with.set(path);
}
}
// Parse `#[serde(deserialize_with = "...")]`
MetaItem(NameValue(ref name, ref lit)) if name == "deserialize_with" => {
if let Ok(path) = parse_lit_into_path(cx, name.as_ref(), lit) {
deserialize_with.set(path);
}
}
MetaItem(ref meta_item) => {
cx.error(format!("unknown serde variant attribute `{}`", meta_item.name()));
}
@@ -595,6 +625,8 @@ impl Variant {
skip_deserializing: skip_deserializing.get(),
skip_serializing: skip_serializing.get(),
other: other.get(),
serialize_with: serialize_with.get(),
deserialize_with: deserialize_with.get(),
}
}
@@ -626,6 +658,14 @@ impl Variant {
pub fn other(&self) -> bool {
self.other
}
pub fn serialize_with(&self) -> Option<&syn::Path> {
self.serialize_with.as_ref()
}
pub fn deserialize_with(&self) -> Option<&syn::Path> {
self.deserialize_with.as_ref()
}
}
/// Represents field attribute information
+17 -10
View File
@@ -27,6 +27,8 @@ pub enum RenameRule {
ScreamingSnakeCase,
/// Rename direct children to "kebab-case" style.
KebabCase,
/// Rename direct children to "SCREAMING-KEBAB-CASE" style.
ScreamingKebabCase
}
impl RenameRule {
@@ -47,6 +49,7 @@ impl RenameRule {
}
ScreamingSnakeCase => SnakeCase.apply_to_variant(variant).to_ascii_uppercase(),
KebabCase => SnakeCase.apply_to_variant(variant).replace('_', "-"),
ScreamingKebabCase => ScreamingSnakeCase.apply_to_variant(variant).replace('_', "-")
}
}
@@ -74,6 +77,7 @@ impl RenameRule {
}
ScreamingSnakeCase => field.to_ascii_uppercase(),
KebabCase => field.replace('_', "-"),
ScreamingKebabCase => ScreamingSnakeCase.apply_to_field(field).replace('_', "-")
}
}
}
@@ -89,6 +93,7 @@ impl FromStr for RenameRule {
"snake_case" => Ok(SnakeCase),
"SCREAMING_SNAKE_CASE" => Ok(ScreamingSnakeCase),
"kebab-case" => Ok(KebabCase),
"SCREAMING-KEBAB-CASE" => Ok(ScreamingKebabCase),
_ => Err(()),
}
}
@@ -96,12 +101,12 @@ impl FromStr for RenameRule {
#[test]
fn rename_variants() {
for &(original, lower, camel, snake, screaming, kebab) in
for &(original, lower, camel, snake, screaming, kebab, screaming_kebab) in
&[
("Outcome", "outcome", "outcome", "outcome", "OUTCOME", "outcome"),
("VeryTasty", "verytasty", "veryTasty", "very_tasty", "VERY_TASTY", "very-tasty"),
("A", "a", "a", "a", "A", "a"),
("Z42", "z42", "z42", "z42", "Z42", "z42"),
("Outcome", "outcome", "outcome", "outcome", "OUTCOME", "outcome", "OUTCOME"),
("VeryTasty", "verytasty", "veryTasty", "very_tasty", "VERY_TASTY", "very-tasty", "VERY-TASTY"),
("A", "a", "a", "a", "A", "a", "A"),
("Z42", "z42", "z42", "z42", "Z42", "z42", "Z42"),
] {
assert_eq!(None.apply_to_variant(original), original);
assert_eq!(LowerCase.apply_to_variant(original), lower);
@@ -110,17 +115,18 @@ fn rename_variants() {
assert_eq!(SnakeCase.apply_to_variant(original), snake);
assert_eq!(ScreamingSnakeCase.apply_to_variant(original), screaming);
assert_eq!(KebabCase.apply_to_variant(original), kebab);
assert_eq!(ScreamingKebabCase.apply_to_variant(original), screaming_kebab);
}
}
#[test]
fn rename_fields() {
for &(original, pascal, camel, screaming, kebab) in
for &(original, pascal, camel, screaming, kebab, screaming_kebab) in
&[
("outcome", "Outcome", "outcome", "OUTCOME", "outcome"),
("very_tasty", "VeryTasty", "veryTasty", "VERY_TASTY", "very-tasty"),
("a", "A", "a", "A", "a"),
("z42", "Z42", "z42", "Z42", "z42"),
("outcome", "Outcome", "outcome", "OUTCOME", "outcome", "OUTCOME"),
("very_tasty", "VeryTasty", "veryTasty", "VERY_TASTY", "very-tasty", "VERY-TASTY"),
("a", "A", "a", "A", "a", "A"),
("z42", "Z42", "z42", "Z42", "z42", "Z42"),
] {
assert_eq!(None.apply_to_field(original), original);
assert_eq!(PascalCase.apply_to_field(original), pascal);
@@ -128,5 +134,6 @@ fn rename_fields() {
assert_eq!(SnakeCase.apply_to_field(original), original);
assert_eq!(ScreamingSnakeCase.apply_to_field(original), screaming);
assert_eq!(KebabCase.apply_to_field(original), kebab);
assert_eq!(ScreamingKebabCase.apply_to_field(original), screaming_kebab);
}
}
+56
View File
@@ -15,6 +15,7 @@ use Ctxt;
pub fn check(cx: &Ctxt, cont: &Container) {
check_getter(cx, cont);
check_identifier(cx, cont);
check_variant_skip_attrs(cx, cont);
}
/// Getters are only allowed inside structs (not enums) with the `remote`
@@ -94,3 +95,58 @@ fn check_identifier(cx: &Ctxt, cont: &Container) {
}
}
}
/// Skip-(de)serializing attributes are not allowed on variants marked
/// (de)serialize_with.
fn check_variant_skip_attrs(cx: &Ctxt, cont: &Container) {
let variants = match cont.body {
Body::Enum(ref variants) => variants,
Body::Struct(_, _) => {
return;
}
};
for variant in variants.iter() {
if variant.attrs.serialize_with().is_some() {
if variant.attrs.skip_serializing() {
cx.error(format!("variant `{}` cannot have both #[serde(serialize_with)] and \
#[serde(skip_serializing)]", variant.ident));
}
for (i, field) in variant.fields.iter().enumerate() {
let ident = field.ident.as_ref().map_or_else(|| format!("{}", i),
|ident| format!("`{}`", ident));
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));
}
if field.attrs.skip_serializing_if().is_some() {
cx.error(format!("variant `{}` cannot have both #[serde(serialize_with)] and \
a field {} marked with #[serde(skip_serializing_if)]",
variant.ident, ident));
}
}
}
if variant.attrs.deserialize_with().is_some() {
if variant.attrs.skip_deserializing() {
cx.error(format!("variant `{}` cannot have both #[serde(deserialize_with)] and \
#[serde(skip_deserializing)]", variant.ident));
}
for (i, field) in variant.fields.iter().enumerate() {
if field.attrs.skip_deserializing() {
let ident = field.ident.as_ref().map_or_else(|| format!("{}", i),
|ident| format!("`{}`", ident));
cx.error(format!("variant `{}` cannot have both #[serde(deserialize_with)] \
and a field {} marked with #[serde(skip_deserializing)]",
variant.ident, ident));
}
}
}
}
}
+1 -1
View File
@@ -6,7 +6,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![doc(html_root_url = "https://docs.rs/serde_derive_internals/0.15.1")]
#![doc(html_root_url = "https://docs.rs/serde_derive_internals/0.16.0")]
extern crate syn;
#[macro_use]
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "serde_test"
version = "1.0.11" # remember to update html_root_url
version = "1.0.16" # 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"
+37 -4
View File
@@ -47,8 +47,20 @@ pub fn assert_tokens<'de, T>(value: &T, tokens: &'de [Token])
where
T: Serialize + Deserialize<'de> + PartialEq + Debug,
{
assert_ser_tokens(value, tokens);
assert_de_tokens(value, tokens);
assert_tokens_readable(value, tokens, None);
}
// Not public API
#[doc(hidden)]
/// Runs both `assert_ser_tokens` and `assert_de_tokens`.
///
/// See: `assert_tokens`
pub fn assert_tokens_readable<'de, T>(value: &T, tokens: &'de [Token], human_readable: Option<bool>)
where
T: Serialize + Deserialize<'de> + PartialEq + Debug,
{
assert_ser_tokens_readable(value, tokens, human_readable);
assert_de_tokens_readable(value, tokens, human_readable);
}
/// Asserts that `value` serializes to the given `tokens`.
@@ -84,7 +96,19 @@ pub fn assert_ser_tokens<T>(value: &T, tokens: &[Token])
where
T: Serialize,
{
let mut ser = Serializer::new(tokens);
assert_ser_tokens_readable(value, tokens, None)
}
// Not public API
#[doc(hidden)]
/// Asserts that `value` serializes to the given `tokens`.
///
/// See: `assert_ser_tokens`
pub fn assert_ser_tokens_readable<T>(value: &T, tokens: &[Token], human_readable: Option<bool>)
where
T: Serialize,
{
let mut ser = Serializer::readable(tokens, human_readable);
match value.serialize(&mut ser) {
Ok(_) => {}
Err(err) => panic!("value failed to serialize: {}", err),
@@ -183,7 +207,16 @@ pub fn assert_de_tokens<'de, T>(value: &T, tokens: &'de [Token])
where
T: Deserialize<'de> + PartialEq + Debug,
{
let mut de = Deserializer::new(tokens);
assert_de_tokens_readable(value, tokens, None)
}
// Not public API
#[doc(hidden)]
pub fn assert_de_tokens_readable<'de, T>(value: &T, tokens: &'de [Token], human_readable: Option<bool>)
where
T: Deserialize<'de> + PartialEq + Debug,
{
let mut de = Deserializer::readable(tokens, human_readable);
match T::deserialize(&mut de) {
Ok(v) => assert_eq!(v, *value),
Err(e) => panic!("tokens failed to deserialize: {}", e),
+20 -1
View File
@@ -16,6 +16,7 @@ use token::Token;
#[derive(Debug)]
pub struct Deserializer<'de> {
tokens: &'de [Token],
is_human_readable: Option<bool>,
}
macro_rules! assert_next_token {
@@ -48,7 +49,13 @@ macro_rules! end_of_tokens {
impl<'de> Deserializer<'de> {
pub fn new(tokens: &'de [Token]) -> Self {
Deserializer { tokens: tokens }
Deserializer::readable(tokens, None)
}
// Not public API
#[doc(hidden)]
pub fn readable(tokens: &'de [Token], is_human_readable: Option<bool>) -> Self {
Deserializer { tokens: tokens, is_human_readable: is_human_readable }
}
fn peek_token_opt(&self) -> Option<Token> {
@@ -364,6 +371,18 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
_ => self.deserialize_any(visitor),
}
}
fn is_human_readable(&self) -> bool {
match self.is_human_readable {
Some(is) => is,
None => {
panic!("There is no serde_test API currently for testing types \
that have different human-readable and compact \
representation. See \
https://github.com/serde-rs/serde/issues/1065.");
}
}
}
}
//////////////////////////////////////////////////////////////////////////
+7 -3
View File
@@ -155,7 +155,7 @@
//! # }
//! ```
#![doc(html_root_url = "https://docs.rs/serde_test/1.0.11")]
#![doc(html_root_url = "https://docs.rs/serde_test/1.0.16")]
#[macro_use]
extern crate serde;
@@ -168,8 +168,12 @@ mod token;
mod assert;
pub use token::Token;
pub use assert::{assert_tokens, assert_ser_tokens, assert_ser_tokens_error, assert_de_tokens,
assert_de_tokens_error};
pub use assert::{assert_tokens, assert_ser_tokens, assert_ser_tokens_error,
assert_de_tokens, assert_de_tokens_error};
// Not public API.
#[doc(hidden)]
pub use assert::{assert_tokens_readable, assert_de_tokens_readable, assert_ser_tokens_readable};
// Not public API.
#[doc(hidden)]
+20 -1
View File
@@ -15,12 +15,19 @@ use token::Token;
#[derive(Debug)]
pub struct Serializer<'a> {
tokens: &'a [Token],
is_human_readable: Option<bool>,
}
impl<'a> Serializer<'a> {
/// Creates the serializer.
pub fn new(tokens: &'a [Token]) -> Self {
Serializer { tokens: tokens }
Serializer::readable(tokens, None)
}
// Not public API
#[doc(hidden)]
pub fn readable(tokens: &'a [Token], is_human_readable: Option<bool>) -> Self {
Serializer { tokens: tokens, is_human_readable: is_human_readable }
}
/// Pulls the next token off of the serializer, ignoring it.
@@ -282,6 +289,18 @@ impl<'s, 'a> ser::Serializer for &'s mut Serializer<'a> {
Ok(Variant { ser: self, end: Token::StructVariantEnd })
}
}
fn is_human_readable(&self) -> bool {
match self.is_human_readable {
Some(is) => is,
None => {
panic!("There is no serde_test API currently for testing types \
that have different human-readable and compact \
representation. See \
https://github.com/serde-rs/serde/issues/1065.");
}
}
}
}
pub struct Variant<'s, 'a: 's> {
+1 -1
View File
@@ -10,7 +10,7 @@ unstable = ["serde/unstable", "compiletest_rs"]
[dev-dependencies]
fnv = "1.0"
rustc-serialize = "0.3.16"
serde = { path = "../serde" }
serde = { path = "../serde", features = ["rc"] }
serde_derive = { path = "../serde_derive" }
serde_test = { path = "../serde_test" }
@@ -0,0 +1,19 @@
// Copyright 2017 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
//~^ HELP: variant `Newtype` cannot have both #[serde(deserialize_with)] and a field 0 marked with #[serde(skip_deserializing)]
enum Enum {
#[serde(deserialize_with = "deserialize_some_newtype_variant")]
Newtype(#[serde(skip_deserializing)] String),
}
fn main() { }
@@ -0,0 +1,23 @@
// Copyright 2017 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
//~^ HELP: variant `Struct` cannot have both #[serde(deserialize_with)] and a field `f1` marked with #[serde(skip_deserializing)]
enum Enum {
#[serde(deserialize_with = "deserialize_some_other_variant")]
Struct {
#[serde(skip_deserializing)]
f1: String,
f2: u8,
},
}
fn main() { }
@@ -0,0 +1,19 @@
// Copyright 2017 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
//~^ HELP: variant `Tuple` cannot have both #[serde(deserialize_with)] and a field 0 marked with #[serde(skip_deserializing)]
enum Enum {
#[serde(deserialize_with = "deserialize_some_other_variant")]
Tuple(#[serde(skip_deserializing)] String, u8),
}
fn main() { }
@@ -0,0 +1,20 @@
// Copyright 2017 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
//~^ HELP: variant `Unit` cannot have both #[serde(deserialize_with)] and #[serde(skip_deserializing)]
enum Enum {
#[serde(deserialize_with = "deserialize_some_unit_variant")]
#[serde(skip_deserializing)]
Unit,
}
fn main() { }
@@ -0,0 +1,19 @@
// Copyright 2017 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
//~^ HELP: variant `Newtype` cannot have both #[serde(serialize_with)] and a field 0 marked with #[serde(skip_serializing)]
enum Enum {
#[serde(serialize_with = "serialize_some_newtype_variant")]
Newtype(#[serde(skip_serializing)] String),
}
fn main() { }
@@ -0,0 +1,19 @@
// Copyright 2017 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
//~^ HELP: variant `Newtype` cannot have both #[serde(serialize_with)] and a field 0 marked with #[serde(skip_serializing_if)]
enum Enum {
#[serde(serialize_with = "serialize_some_newtype_variant")]
Newtype(#[serde(skip_serializing_if = "always")] String),
}
fn main() { }
@@ -0,0 +1,23 @@
// Copyright 2017 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
//~^ HELP: variant `Struct` cannot have both #[serde(serialize_with)] and a field `f1` marked with #[serde(skip_serializing)]
enum Enum {
#[serde(serialize_with = "serialize_some_other_variant")]
Struct {
#[serde(skip_serializing)]
f1: String,
f2: u8,
},
}
fn main() { }
@@ -0,0 +1,23 @@
// Copyright 2017 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
//~^ HELP: variant `Struct` cannot have both #[serde(serialize_with)] and a field `f1` marked with #[serde(skip_serializing_if)]
enum Enum {
#[serde(serialize_with = "serialize_some_newtype_variant")]
Struct {
#[serde(skip_serializing_if = "always")]
f1: String,
f2: u8,
},
}
fn main() { }
@@ -0,0 +1,19 @@
// Copyright 2017 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
//~^ HELP: variant `Tuple` cannot have both #[serde(serialize_with)] and a field 0 marked with #[serde(skip_serializing)]
enum Enum {
#[serde(serialize_with = "serialize_some_other_variant")]
Tuple(#[serde(skip_serializing)] String, u8),
}
fn main() { }
@@ -0,0 +1,19 @@
// Copyright 2017 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
//~^ HELP: variant `Tuple` cannot have both #[serde(serialize_with)] and a field 0 marked with #[serde(skip_serializing_if)]
enum Enum {
#[serde(serialize_with = "serialize_some_other_variant")]
Tuple(#[serde(skip_serializing_if = "always")] String, u8),
}
fn main() { }
@@ -0,0 +1,20 @@
// Copyright 2017 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
//~^ HELP: variant `Unit` cannot have both #[serde(serialize_with)] and #[serde(skip_serializing)]
enum Enum {
#[serde(serialize_with = "serialize_some_unit_variant")]
#[serde(skip_serializing)]
Unit,
}
fn main() { }
+1 -1
View File
@@ -13,7 +13,7 @@ extern crate compiletest_rs as compiletest;
use std::env;
fn run_mode(mode: &'static str) {
let mut config = compiletest::default_config();
let mut config = compiletest::Config::default();
config.mode = mode.parse().expect("invalid mode");
config.target_rustcflags = Some("-L deps/target/debug/deps".to_owned());
+26
View File
@@ -73,3 +73,29 @@ macro_rules! hashmap {
}
}
}
macro_rules! seq_impl {
(seq $first:expr,) => {
seq_impl!(seq $first)
};
($first:expr,) => {
seq_impl!($first)
};
(seq $first:expr) => {
$first.into_iter()
};
($first:expr) => {
Some($first).into_iter()
};
(seq $first:expr , $( $elem: tt)*) => {
$first.into_iter().chain(seq!( $($elem)* ))
};
($first:expr , $($elem: tt)*) => {
Some($first).into_iter().chain(seq!( $($elem)* ))
}
}
macro_rules! seq {
($($tt: tt)*) => {
seq_impl!($($tt)*).collect::<Vec<_>>()
};
}
+142
View File
@@ -6,11 +6,14 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![cfg_attr(feature = "cargo-clippy", allow(cast_lossless))]
#[macro_use]
extern crate serde_derive;
extern crate serde;
use self::serde::{Serialize, Serializer, Deserialize, Deserializer};
use self::serde::de::{self, Unexpected};
extern crate serde_test;
use self::serde_test::{Token, assert_tokens, assert_ser_tokens, assert_de_tokens,
@@ -811,6 +814,145 @@ fn test_serialize_with_enum() {
);
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
enum WithVariant {
#[serde(serialize_with="serialize_unit_variant_as_i8")]
#[serde(deserialize_with="deserialize_i8_as_unit_variant")]
Unit,
#[serde(serialize_with="SerializeWith::serialize_with")]
#[serde(deserialize_with="DeserializeWith::deserialize_with")]
Newtype(i32),
#[serde(serialize_with="serialize_variant_as_string")]
#[serde(deserialize_with="deserialize_string_as_variant")]
Tuple(String, u8),
#[serde(serialize_with="serialize_variant_as_string")]
#[serde(deserialize_with="deserialize_string_as_variant")]
Struct {
f1: String,
f2: u8,
},
}
fn serialize_unit_variant_as_i8<S>(serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer,
{
serializer.serialize_i8(0)
}
fn deserialize_i8_as_unit_variant<'de, D>(deserializer: D) -> Result<(), D::Error>
where D: Deserializer<'de>,
{
let n = i8::deserialize(deserializer)?;
match n {
0 => Ok(()),
_ => Err(de::Error::invalid_value(Unexpected::Signed(n as i64), &"0")),
}
}
fn serialize_variant_as_string<S>(f1: &str,
f2: &u8,
serializer: S)
-> Result<S::Ok, S::Error>
where S: Serializer,
{
serializer.serialize_str(format!("{};{:?}", f1, f2).as_str())
}
fn deserialize_string_as_variant<'de, D>(deserializer: D) -> Result<(String, u8), D::Error>
where D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
let mut pieces = s.split(';');
let f1 = match pieces.next() {
Some(x) => x,
None => return Err(de::Error::invalid_length(0, &"2")),
};
let f2 = match pieces.next() {
Some(x) => x,
None => return Err(de::Error::invalid_length(1, &"2")),
};
let f2 = match f2.parse() {
Ok(n) => n,
Err(_) => {
return Err(de::Error::invalid_value(Unexpected::Str(f2), &"an 8-bit signed integer"));
}
};
Ok((f1.into(), f2))
}
#[test]
fn test_serialize_with_variant() {
assert_ser_tokens(
&WithVariant::Unit,
&[
Token::NewtypeVariant { name: "WithVariant", variant: "Unit" },
Token::I8(0),
],
);
assert_ser_tokens(
&WithVariant::Newtype(123),
&[
Token::NewtypeVariant { name: "WithVariant", variant: "Newtype" },
Token::Bool(true),
],
);
assert_ser_tokens(
&WithVariant::Tuple("hello".into(), 0),
&[
Token::NewtypeVariant { name: "WithVariant", variant: "Tuple" },
Token::Str("hello;0"),
],
);
assert_ser_tokens(
&WithVariant::Struct { f1: "world".into(), f2: 1 },
&[
Token::NewtypeVariant { name: "WithVariant", variant: "Struct" },
Token::Str("world;1"),
],
);
}
#[test]
fn test_deserialize_with_variant() {
assert_de_tokens(
&WithVariant::Unit,
&[
Token::NewtypeVariant { name: "WithVariant", variant: "Unit" },
Token::I8(0),
],
);
assert_de_tokens(
&WithVariant::Newtype(123),
&[
Token::NewtypeVariant { name: "WithVariant", variant: "Newtype" },
Token::Bool(true),
],
);
assert_de_tokens(
&WithVariant::Tuple("hello".into(), 0),
&[
Token::NewtypeVariant { name: "WithVariant", variant: "Tuple" },
Token::Str("hello;0"),
],
);
assert_de_tokens(
&WithVariant::Struct { f1: "world".into(), f2: 1 },
&[
Token::NewtypeVariant { name: "WithVariant", variant: "Struct" },
Token::Str("world;1"),
],
);
}
#[derive(Debug, PartialEq, Deserialize)]
struct DeserializeWithStruct<B>
where
+165 -39
View File
@@ -6,8 +6,6 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![cfg_attr(feature = "unstable", feature(into_boxed_c_str))]
#[macro_use]
extern crate serde_derive;
@@ -17,6 +15,8 @@ use std::path::{Path, PathBuf};
use std::time::{Duration, UNIX_EPOCH};
use std::default::Default;
use std::ffi::{CString, OsString};
use std::rc::Rc;
use std::sync::Arc;
#[cfg(feature = "unstable")]
use std::ffi::CStr;
@@ -28,7 +28,7 @@ extern crate fnv;
use self::fnv::FnvHasher;
extern crate serde_test;
use self::serde_test::{Token, assert_de_tokens, assert_de_tokens_error};
use self::serde_test::{Token, assert_de_tokens, assert_de_tokens_error, assert_de_tokens_readable};
#[macro_use]
mod macros;
@@ -109,25 +109,37 @@ enum EnumSkipAll {
//////////////////////////////////////////////////////////////////////////
macro_rules! declare_test {
($name:ident { $($value:expr => $tokens:expr,)+ }) => {
#[test]
fn $name() {
$(
// Test ser/de roundtripping
assert_de_tokens(&$value, $tokens);
// Test that the tokens are ignorable
assert_de_tokens_ignore($tokens);
)+
}
}
}
macro_rules! declare_tests {
(
readable: $readable:tt
$($name:ident { $($value:expr => $tokens:expr,)+ })+
) => {
$(
#[test]
fn $name() {
$(
// Test ser/de roundtripping
assert_de_tokens_readable(&$value, $tokens, Some($readable));
// Test that the tokens are ignorable
assert_de_tokens_ignore($tokens);
)+
}
)+
};
($($name:ident { $($value:expr => $tokens:expr,)+ })+) => {
$(
declare_test!($name { $($value => $tokens,)+ });
#[test]
fn $name() {
$(
// Test ser/de roundtripping
assert_de_tokens(&$value, $tokens);
// Test that the tokens are ignorable
assert_de_tokens_ignore($tokens);
)+
}
)+
}
}
@@ -521,6 +533,15 @@ declare_tests! {
Token::I32(2),
Token::MapEnd,
],
Struct { a: 1, b: 2, c: 0 } => &[
Token::Map { len: Some(3) },
Token::U32(0),
Token::I32(1),
Token::U32(1),
Token::I32(2),
Token::MapEnd,
],
Struct { a: 1, b: 2, c: 0 } => &[
Token::Struct { name: "Struct", len: 3 },
Token::Str("a"),
@@ -716,17 +737,6 @@ declare_tests! {
Token::SeqEnd,
],
}
test_net_ipv4addr {
"1.2.3.4".parse::<net::Ipv4Addr>().unwrap() => &[Token::Str("1.2.3.4")],
}
test_net_ipv6addr {
"::1".parse::<net::Ipv6Addr>().unwrap() => &[Token::Str("::1")],
}
test_net_socketaddr {
"1.2.3.4:1234".parse::<net::SocketAddr>().unwrap() => &[Token::Str("1.2.3.4:1234")],
"1.2.3.4:1234".parse::<net::SocketAddrV4>().unwrap() => &[Token::Str("1.2.3.4:1234")],
"[::1]:1234".parse::<net::SocketAddrV6>().unwrap() => &[Token::Str("[::1]:1234")],
}
test_path {
Path::new("/usr/local/lib") => &[
Token::BorrowedStr("/usr/local/lib"),
@@ -742,6 +752,131 @@ declare_tests! {
Token::Bytes(b"abc"),
],
}
test_rc {
Rc::new(true) => &[
Token::Bool(true),
],
}
test_arc {
Arc::new(true) => &[
Token::Bool(true),
],
}
}
declare_tests! {
readable: true
test_net_ipv4addr_readable {
"1.2.3.4".parse::<net::Ipv4Addr>().unwrap() => &[Token::Str("1.2.3.4")],
}
test_net_ipv6addr_readable {
"::1".parse::<net::Ipv6Addr>().unwrap() => &[Token::Str("::1")],
}
test_net_ipaddr_readable {
"1.2.3.4".parse::<net::IpAddr>().unwrap() => &[Token::Str("1.2.3.4")],
}
test_net_socketaddr_readable {
"1.2.3.4:1234".parse::<net::SocketAddr>().unwrap() => &[Token::Str("1.2.3.4:1234")],
"1.2.3.4:1234".parse::<net::SocketAddrV4>().unwrap() => &[Token::Str("1.2.3.4:1234")],
"[::1]:1234".parse::<net::SocketAddrV6>().unwrap() => &[Token::Str("[::1]:1234")],
}
}
declare_tests! {
readable: false
test_net_ipv4addr_compact {
net::Ipv4Addr::from(*b"1234") => &seq![
Token::Tuple { len: 4 },
seq b"1234".iter().map(|&b| Token::U8(b)),
Token::TupleEnd
],
}
test_net_ipv6addr_compact {
net::Ipv6Addr::from(*b"1234567890123456") => &seq![
Token::Tuple { len: 4 },
seq b"1234567890123456".iter().map(|&b| Token::U8(b)),
Token::TupleEnd
],
}
test_net_ipaddr_compact {
net::IpAddr::from(*b"1234") => &seq![
Token::NewtypeVariant { name: "IpAddr", variant: "V4" },
Token::Tuple { len: 4 },
seq b"1234".iter().map(|&b| Token::U8(b)),
Token::TupleEnd
],
}
test_net_socketaddr_compact {
net::SocketAddr::from((*b"1234567890123456", 1234)) => &seq![
Token::NewtypeVariant { name: "SocketAddr", variant: "V6" },
Token::Tuple { len: 2 },
Token::Tuple { len: 16 },
seq b"1234567890123456".iter().map(|&b| Token::U8(b)),
Token::TupleEnd,
Token::U16(1234),
Token::TupleEnd
],
net::SocketAddr::from((*b"1234", 1234)) => &seq![
Token::NewtypeVariant { name: "SocketAddr", variant: "V4" },
Token::Tuple { len: 2 },
Token::Tuple { len: 4 },
seq b"1234".iter().map(|&b| Token::U8(b)),
Token::TupleEnd,
Token::U16(1234),
Token::TupleEnd
],
net::SocketAddrV4::new(net::Ipv4Addr::from(*b"1234"), 1234) => &seq![
Token::Tuple { len: 2 },
Token::Tuple { len: 4 },
seq b"1234".iter().map(|&b| Token::U8(b)),
Token::TupleEnd,
Token::U16(1234),
Token::TupleEnd
],
net::SocketAddrV6::new(net::Ipv6Addr::from(*b"1234567890123456"), 1234, 0, 0) => &seq![
Token::Tuple { len: 2 },
Token::Tuple { len: 16 },
seq b"1234567890123456".iter().map(|&b| Token::U8(b)),
Token::TupleEnd,
Token::U16(1234),
Token::TupleEnd
],
}
}
#[cfg(feature = "unstable")]
declare_tests! {
test_rc_dst {
Rc::<str>::from("s") => &[
Token::Str("s"),
],
Rc::<[bool]>::from(&[true][..]) => &[
Token::Seq { len: Some(1) },
Token::Bool(true),
Token::SeqEnd,
],
}
test_arc_dst {
Arc::<str>::from("s") => &[
Token::Str("s"),
],
Arc::<[bool]>::from(&[true][..]) => &[
Token::Seq { len: Some(1) },
Token::Bool(true),
Token::SeqEnd,
],
}
}
#[cfg(unix)]
@@ -793,15 +928,6 @@ fn test_cstr() {
);
}
#[cfg(feature = "unstable")]
#[test]
fn test_net_ipaddr() {
assert_de_tokens(
&"1.2.3.4".parse::<net::IpAddr>().unwrap(),
&[Token::Str("1.2.3.4")],
);
}
#[cfg(feature = "unstable")]
#[test]
fn test_cstr_internal_null() {
+168
View File
@@ -10,6 +10,8 @@
// successfully when there are a variety of generics and non-(de)serializable
// types involved.
#![deny(warnings)]
#![cfg_attr(feature = "unstable", feature(non_ascii_idents))]
// Clippy false positive
@@ -373,6 +375,146 @@ fn test_gen() {
#[serde(with = "vis::SDef")]
s: vis::S,
}
#[derive(Serialize, Deserialize)]
enum ExternallyTaggedVariantWith {
#[allow(dead_code)]
Normal { f1: String },
#[serde(serialize_with = "ser_x")]
#[serde(deserialize_with = "de_x")]
#[allow(dead_code)]
Newtype(X),
#[serde(serialize_with = "serialize_some_other_variant")]
#[serde(deserialize_with = "deserialize_some_other_variant")]
#[allow(dead_code)]
Tuple(String, u8),
#[serde(serialize_with = "serialize_some_other_variant")]
#[serde(deserialize_with = "deserialize_some_other_variant")]
#[allow(dead_code)]
Struct {
f1: String,
f2: u8,
},
#[serde(serialize_with = "serialize_some_unit_variant")]
#[serde(deserialize_with = "deserialize_some_unit_variant")]
#[allow(dead_code)]
Unit,
}
assert_ser::<ExternallyTaggedVariantWith>();
#[derive(Serialize, Deserialize)]
#[serde(tag = "t")]
enum InternallyTaggedVariantWith {
#[allow(dead_code)]
Normal { f1: String },
#[serde(serialize_with = "ser_x")]
#[serde(deserialize_with = "de_x")]
#[allow(dead_code)]
Newtype(X),
#[serde(serialize_with = "serialize_some_other_variant")]
#[serde(deserialize_with = "deserialize_some_other_variant")]
#[allow(dead_code)]
Struct {
f1: String,
f2: u8,
},
#[serde(serialize_with = "serialize_some_unit_variant")]
#[serde(deserialize_with = "deserialize_some_unit_variant")]
#[allow(dead_code)]
Unit,
}
assert_ser::<InternallyTaggedVariantWith>();
#[derive(Serialize, Deserialize)]
#[serde(tag = "t", content = "c")]
enum AdjacentlyTaggedVariantWith {
#[allow(dead_code)]
Normal { f1: String },
#[serde(serialize_with = "ser_x")]
#[serde(deserialize_with = "de_x")]
#[allow(dead_code)]
Newtype(X),
#[serde(serialize_with = "serialize_some_other_variant")]
#[serde(deserialize_with = "deserialize_some_other_variant")]
#[allow(dead_code)]
Tuple(String, u8),
#[serde(serialize_with = "serialize_some_other_variant")]
#[serde(deserialize_with = "deserialize_some_other_variant")]
#[allow(dead_code)]
Struct {
f1: String,
f2: u8,
},
#[serde(serialize_with = "serialize_some_unit_variant")]
#[serde(deserialize_with = "deserialize_some_unit_variant")]
#[allow(dead_code)]
Unit,
}
assert_ser::<AdjacentlyTaggedVariantWith>();
#[derive(Serialize, Deserialize)]
#[serde(untagged)]
enum UntaggedVariantWith {
#[allow(dead_code)]
Normal { f1: String },
#[serde(serialize_with = "ser_x")]
#[serde(deserialize_with = "de_x")]
#[allow(dead_code)]
Newtype(X),
#[serde(serialize_with = "serialize_some_other_variant")]
#[serde(deserialize_with = "deserialize_some_other_variant")]
#[allow(dead_code)]
Tuple(String, u8),
#[serde(serialize_with = "serialize_some_other_variant")]
#[serde(deserialize_with = "deserialize_some_other_variant")]
#[allow(dead_code)]
Struct {
f1: String,
f2: u8,
},
#[serde(serialize_with = "serialize_some_unit_variant")]
#[serde(deserialize_with = "deserialize_some_unit_variant")]
#[allow(dead_code)]
Unit,
}
assert_ser::<UntaggedVariantWith>();
#[derive(Serialize, Deserialize)]
struct StaticStrStruct<'a> {
a: &'a str,
b: &'static str,
}
#[derive(Serialize, Deserialize)]
struct StaticStrTupleStruct<'a>(&'a str, &'static str);
#[derive(Serialize, Deserialize)]
struct StaticStrNewtypeStruct(&'static str);
#[derive(Serialize, Deserialize)]
enum StaticStrEnum<'a> {
Struct {
a: &'a str,
b: &'static str,
},
Tuple(&'a str, &'static str),
Newtype(&'static str),
}
}
//////////////////////////////////////////////////////////////////////////
@@ -414,3 +556,29 @@ impl DeserializeWith for X {
unimplemented!()
}
}
pub fn serialize_some_unit_variant<S>(_: S) -> StdResult<S::Ok, S::Error>
where S: Serializer,
{
unimplemented!()
}
pub fn deserialize_some_unit_variant<'de, D>(_: D) -> StdResult<(), D::Error>
where D: Deserializer<'de>,
{
unimplemented!()
}
pub fn serialize_some_other_variant<S>(_: &str, _: &u8, _: S) -> StdResult<S::Ok, S::Error>
where S: Serializer,
{
unimplemented!()
}
pub fn deserialize_some_other_variant<'de, D>(_: D) -> StdResult<(String, u8), D::Error>
where D: Deserializer<'de>,
{
unimplemented!()
}
pub fn is_zero(n: &u8) -> bool { *n == 0 }
+3
View File
@@ -23,7 +23,10 @@ fn test_variant_identifier() {
Bbb,
}
assert_de_tokens(&V::Aaa, &[Token::U8(0)]);
assert_de_tokens(&V::Aaa, &[Token::U16(0)]);
assert_de_tokens(&V::Aaa, &[Token::U32(0)]);
assert_de_tokens(&V::Aaa, &[Token::U64(0)]);
assert_de_tokens(&V::Aaa, &[Token::Str("Aaa")]);
assert_de_tokens(&V::Aaa, &[Token::Bytes(b"Aaa")]);
}
+83 -35
View File
@@ -6,6 +6,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![deny(trivial_numeric_casts)]
#[macro_use]
extern crate serde_derive;
@@ -589,11 +591,10 @@ fn test_internally_tagged_enum() {
#[serde(tag = "type")]
enum InternallyTagged {
A { a: u8 },
B { b: u8 },
C,
D(BTreeMap<String, String>),
E(Newtype),
F(Struct),
B,
C(BTreeMap<String, String>),
D(Newtype),
E(Struct),
}
assert_tokens(
@@ -611,35 +612,62 @@ fn test_internally_tagged_enum() {
],
);
assert_tokens(
&InternallyTagged::B { b: 2 },
assert_de_tokens(
&InternallyTagged::A { a: 1 },
&[
Token::Struct { name: "InternallyTagged", len: 2 },
Token::Str("type"),
Token::Str("B"),
Token::Str("b"),
Token::U8(2),
Token::StructEnd,
Token::Seq { len: Some(2) },
Token::Str("A"),
Token::U8(1),
Token::SeqEnd,
],
);
assert_tokens(
&InternallyTagged::C,
&InternallyTagged::B,
&[
Token::Struct { name: "InternallyTagged", len: 1 },
Token::Str("type"),
Token::Str("C"),
Token::Str("B"),
Token::StructEnd,
],
);
assert_de_tokens(
&InternallyTagged::B,
&[
Token::Seq { len: Some(1) },
Token::Str("B"),
Token::SeqEnd,
],
);
assert_tokens(
&InternallyTagged::D(BTreeMap::new()),
&InternallyTagged::C(BTreeMap::new()),
&[
Token::Map { len: Some(1) },
Token::Str("type"),
Token::Str("C"),
Token::MapEnd,
],
);
assert_de_tokens_error::<InternallyTagged>(
&[
Token::Seq { len: Some(2) },
Token::Str("C"),
Token::Map { len: Some(0) },
Token::MapEnd,
Token::SeqEnd,
],
"invalid type: sequence, expected a map",
);
assert_tokens(
&InternallyTagged::D(Newtype(BTreeMap::new())),
&[
Token::Map { len: Some(1) },
@@ -651,24 +679,12 @@ fn test_internally_tagged_enum() {
);
assert_tokens(
&InternallyTagged::E(Newtype(BTreeMap::new())),
&[
Token::Map { len: Some(1) },
Token::Str("type"),
Token::Str("E"),
Token::MapEnd,
],
);
assert_tokens(
&InternallyTagged::F(Struct { f: 6 }),
&InternallyTagged::E(Struct { f: 6 }),
&[
Token::Struct { name: "Struct", len: 2 },
Token::Str("type"),
Token::Str("F"),
Token::Str("E"),
Token::Str("f"),
Token::U8(6),
@@ -677,6 +693,16 @@ fn test_internally_tagged_enum() {
],
);
assert_de_tokens(
&InternallyTagged::E(Struct { f: 6 }),
&[
Token::Seq { len: Some(2) },
Token::Str("E"),
Token::U8(6),
Token::SeqEnd,
],
);
assert_de_tokens_error::<InternallyTagged>(
&[Token::Map { len: Some(0) }, Token::MapEnd],
"missing field `type`",
@@ -691,7 +717,7 @@ fn test_internally_tagged_enum() {
Token::MapEnd,
],
"unknown variant `Z`, expected one of `A`, `B`, `C`, `D`, `E`, `F`",
"unknown variant `Z`, expected one of `A`, `B`, `C`, `D`, `E`",
);
}
@@ -1149,7 +1175,7 @@ fn test_rename_all() {
SerializeMap {
serialize: bool,
serialize_seq: bool,
},
}
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
@@ -1159,6 +1185,13 @@ fn test_rename_all() {
serialize_seq: bool,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "SCREAMING-KEBAB-CASE")]
struct ScreamingKebab {
serialize: bool,
serialize_seq: bool,
}
assert_tokens(
&E::Serialize {
serialize: true,
@@ -1218,4 +1251,19 @@ fn test_rename_all() {
Token::StructEnd,
],
);
assert_tokens(
&ScreamingKebab {
serialize: true,
serialize_seq: true,
},
&[
Token::Struct { name: "ScreamingKebab", len: 2 },
Token::Str("SERIALIZE"),
Token::Bool(true),
Token::Str("SERIALIZE-SEQ"),
Token::Bool(true),
Token::StructEnd,
]
);
}
+45
View File
@@ -0,0 +1,45 @@
extern crate serde_test;
use self::serde_test::{Token, assert_tokens_readable};
use std::net;
#[macro_use]
#[allow(unused_macros)]
mod macros;
#[test]
fn ip_addr_roundtrip() {
assert_tokens_readable(
&net::IpAddr::from(*b"1234"),
&seq![
Token::NewtypeVariant { name: "IpAddr", variant: "V4" },
Token::Tuple { len: 4 },
seq b"1234".iter().map(|&b| Token::U8(b)),
Token::TupleEnd,
],
Some(false),
);
}
#[test]
fn socked_addr_roundtrip() {
assert_tokens_readable(
&net::SocketAddr::from((*b"1234567890123456", 1234)),
&seq![
Token::NewtypeVariant { name: "SocketAddr", variant: "V6" },
Token::Tuple { len: 2 },
Token::Tuple { len: 16 },
seq b"1234567890123456".iter().map(|&b| Token::U8(b)),
Token::TupleEnd,
Token::U16(1234),
Token::TupleEnd,
],
Some(false),
);
}
+129 -18
View File
@@ -14,6 +14,8 @@ use std::net;
use std::path::{Path, PathBuf};
use std::time::{Duration, UNIX_EPOCH};
use std::ffi::CString;
use std::rc::Rc;
use std::sync::Arc;
#[cfg(unix)]
use std::str;
@@ -21,7 +23,8 @@ use std::str;
extern crate serde;
extern crate serde_test;
use self::serde_test::{Token, assert_ser_tokens, assert_ser_tokens_error};
use self::serde_test::{Token, assert_ser_tokens, assert_ser_tokens_error,
assert_ser_tokens_readable};
extern crate fnv;
use self::fnv::FnvHasher;
@@ -63,6 +66,20 @@ enum Enum {
//////////////////////////////////////////////////////////////////////////
macro_rules! declare_tests {
(
readable: $readable:tt
$($name:ident { $($value:expr => $tokens:expr,)+ })+
) => {
$(
#[test]
fn $name() {
$(
assert_ser_tokens_readable(&$value, $tokens, Some($readable));
)+
}
)+
};
($($name:ident { $($value:expr => $tokens:expr,)+ })+) => {
$(
#[test]
@@ -352,17 +369,6 @@ declare_tests! {
Token::StructEnd,
],
}
test_net_ipv4addr {
"1.2.3.4".parse::<net::Ipv4Addr>().unwrap() => &[Token::Str("1.2.3.4")],
}
test_net_ipv6addr {
"::1".parse::<net::Ipv6Addr>().unwrap() => &[Token::Str("::1")],
}
test_net_socketaddr {
"1.2.3.4:1234".parse::<net::SocketAddr>().unwrap() => &[Token::Str("1.2.3.4:1234")],
"1.2.3.4:1234".parse::<net::SocketAddrV4>().unwrap() => &[Token::Str("1.2.3.4:1234")],
"[::1]:1234".parse::<net::SocketAddrV6>().unwrap() => &[Token::Str("[::1]:1234")],
}
test_path {
Path::new("/usr/local/lib") => &[
Token::Str("/usr/local/lib"),
@@ -383,15 +389,120 @@ declare_tests! {
Token::Bytes(b"abc"),
],
}
test_rc {
Rc::new(true) => &[
Token::Bool(true),
],
}
test_arc {
Arc::new(true) => &[
Token::Bool(true),
],
}
}
declare_tests! {
readable: true
test_net_ipv4addr_readable {
"1.2.3.4".parse::<net::Ipv4Addr>().unwrap() => &[Token::Str("1.2.3.4")],
}
test_net_ipv6addr_readable {
"::1".parse::<net::Ipv6Addr>().unwrap() => &[Token::Str("::1")],
}
test_net_ipaddr_readable {
"1.2.3.4".parse::<net::IpAddr>().unwrap() => &[Token::Str("1.2.3.4")],
}
test_net_socketaddr_readable {
"1.2.3.4:1234".parse::<net::SocketAddr>().unwrap() => &[Token::Str("1.2.3.4:1234")],
"1.2.3.4:1234".parse::<net::SocketAddrV4>().unwrap() => &[Token::Str("1.2.3.4:1234")],
"[::1]:1234".parse::<net::SocketAddrV6>().unwrap() => &[Token::Str("[::1]:1234")],
}
}
declare_tests! {
readable: false
test_net_ipv4addr_compact {
net::Ipv4Addr::from(*b"1234") => &seq![
Token::Tuple { len: 4 },
seq b"1234".iter().map(|&b| Token::U8(b)),
Token::TupleEnd,
],
}
test_net_ipv6addr_compact {
net::Ipv6Addr::from(*b"1234567890123456") => &seq![
Token::Tuple { len: 16 },
seq b"1234567890123456".iter().map(|&b| Token::U8(b)),
Token::TupleEnd,
],
}
test_net_ipaddr_compact {
net::IpAddr::from(*b"1234") => &seq![
Token::NewtypeVariant { name: "IpAddr", variant: "V4" },
Token::Tuple { len: 4 },
seq b"1234".iter().map(|&b| Token::U8(b)),
Token::TupleEnd,
],
}
test_net_socketaddr_compact {
net::SocketAddr::from((*b"1234567890123456", 1234)) => &seq![
Token::NewtypeVariant { name: "SocketAddr", variant: "V6" },
Token::Tuple { len: 2 },
Token::Tuple { len: 16 },
seq b"1234567890123456".iter().map(|&b| Token::U8(b)),
Token::TupleEnd,
Token::U16(1234),
Token::TupleEnd,
],
net::SocketAddrV4::new(net::Ipv4Addr::from(*b"1234"), 1234) => &seq![
Token::Tuple { len: 2 },
Token::Tuple { len: 4 },
seq b"1234".iter().map(|&b| Token::U8(b)),
Token::TupleEnd,
Token::U16(1234),
Token::TupleEnd,
],
net::SocketAddrV6::new(net::Ipv6Addr::from(*b"1234567890123456"), 1234, 0, 0) => &seq![
Token::Tuple { len: 2 },
Token::Tuple { len: 16 },
seq b"1234567890123456".iter().map(|&b| Token::U8(b)),
Token::TupleEnd,
Token::U16(1234),
Token::TupleEnd,
],
}
}
// Serde's implementation is not unstable, but the constructors are.
#[cfg(feature = "unstable")]
#[test]
fn test_net_ipaddr() {
assert_ser_tokens(
&"1.2.3.4".parse::<net::IpAddr>().unwrap(),
&[Token::Str("1.2.3.4")],
);
declare_tests! {
test_rc_dst {
Rc::<str>::from("s") => &[
Token::Str("s"),
],
Rc::<[bool]>::from(&[true][..]) => &[
Token::Seq { len: Some(1) },
Token::Bool(true),
Token::SeqEnd,
],
}
test_arc_dst {
Arc::<str>::from("s") => &[
Token::Str("s"),
],
Arc::<[bool]>::from(&[true][..]) => &[
Token::Seq { len: Some(1) },
Token::Bool(true),
Token::SeqEnd,
],
}
}
#[test]
+4 -2
View File
@@ -52,8 +52,10 @@ else
channel build
cd "$DIR/test_suite"
channel test --features unstable
cd "$DIR/test_suite/no_std"
channel build
if [ -z "${APPVEYOR}" ]; then
cd "$DIR/test_suite/no_std"
channel build
fi
CHANNEL=beta
cd "$DIR"