Compare commits

...

45 Commits

Author SHA1 Message Date
David Tolnay 8a09f05644 Release 0.7.10 2016-06-11 13:08:33 -07:00
David Tolnay 5923a0cd2f Merge pull request #371 from dtolnay/hasher
De/serialize for HashMap<K, V, S> and HashSet<T, S>
2016-06-11 11:51:57 -07:00
David Tolnay 1576b5a8a0 Serde_macros tests depend on fnv 2016-06-11 11:15:10 -07:00
Homu 2c4dbf5a84 Auto merge of #370 - dtolnay:expand, r=erickt
Use serde_codegen::expand in serde_tests/build.rs
2016-06-12 02:51:08 +09:00
David Tolnay 021f4f2d70 Use serde_codegen::expand in serde_tests/build.rs 2016-06-11 10:02:10 -07:00
David Tolnay decc571988 De/serialize for HashSet<T, S> 2016-06-11 10:00:33 -07:00
David Tolnay 322d7a90db Add ser tests for normal HashMap 2016-06-11 10:00:33 -07:00
David Tolnay dd3f653103 Move bounds to where-clause to increase legibility 2016-06-11 10:00:33 -07:00
David Tolnay 46a1860601 De/serialize for HashMap<K, V, S> 2016-06-11 10:00:33 -07:00
Homu 84a573c926 Auto merge of #372 - dtolnay:old, r=erickt
Stop building on 1.5.0

Syntex no longer supports this version of Rust.
2016-06-12 01:09:49 +09:00
Erick Tryzelaar 7dfa8f43f4 Merge pull request #373 from erickt/master
Updating to rustc 1.11.0-nightly (7d2f75a95 2016-06-09)
2016-06-11 09:09:22 -07:00
David Tolnay 7375b4e847 Add travis builds for 1.6.0 and 1.7.0 2016-06-11 08:59:02 -07:00
Erick Tryzelaar 48da62ed07 Updating to rustc 1.11.0-nightly (7d2f75a95 2016-06-09) 2016-06-11 08:19:51 -07:00
David Tolnay 9834af7ed9 Stop building on 1.5.0
Syntex no longer supports this version of Rust.
2016-06-11 01:31:17 -07:00
Homu 6b404d8529 Auto merge of #367 - dtolnay:default, r=oli-obk
Simplify implementation of #[serde(default=...)]
2016-06-10 17:57:39 +09:00
David Tolnay 3119cc8857 Simplify implementation of #[serde(default=...)] 2016-06-09 23:21:42 -07:00
David Tolnay bb059b97c0 Release 0.7.9 2016-06-09 20:16:49 -07:00
Homu b7188f7022 Auto merge of #362 - dtolnay:expand, r=oli-obk
Add serde_codegen::expand to avoid public Syntex dependency

Required for #358. We can remove `serde_codegen::register` in the next breaking release.

This allows Syntex users to avoid being broken by Serde bumping its Syntex dependency.
2016-06-10 07:46:33 +09:00
David Tolnay a64fe99d1b Add cargo override for building examples 2016-06-09 11:23:43 -07:00
David Tolnay c716c4e261 Use AsRef to accept paths in serde_codegen::expand 2016-06-09 11:23:43 -07:00
David Tolnay 3d2e3beafe Add serde_codegen::expand to avoid public Syntex dependency 2016-06-09 11:23:43 -07:00
Homu 1917e54a6e Auto merge of #363 - dtolnay:example, r=oli-obk
Fix nightly check in serde-syntex-example

Fixes #361.
2016-06-10 01:58:51 +09:00
David Tolnay 898b346d48 1.5 does not have a stable libcore 2016-06-09 09:17:21 -07:00
David Tolnay e90adb20ef Run serde-syntex-example in Travis 2016-06-09 01:42:00 -07:00
Homu a52e7f5554 Auto merge of #364 - dtolnay:rustup, r=oli-obk
Use rustup in serde-syntex-example instead of multirust

Multirust is deprecated.
2016-06-09 17:36:01 +09:00
David Tolnay 7afb8b52ae Use rustup in serde-syntex-example instead of multirust 2016-06-09 01:16:31 -07:00
David Tolnay cb4694387e Fix nightly check in serde-syntex-example 2016-06-09 01:08:12 -07:00
David Tolnay 58fa302007 Release 0.7.8 2016-06-06 10:13:52 -07:00
Homu bf33daf124 Auto merge of #354 - dtolnay:attr, r=oli-obk
Fix attributes canceling each other

Fixes #353.
2016-06-06 18:08:20 +09:00
Homu 4b472be56e Auto merge of #352 - dtolnay:where, r=oli-obk
Attribute for handwritten where clauses

Addresses (2) and (3) in https://github.com/serde-rs/serde/issues/336#issuecomment-220378916.

- If there is a `#[serde(bound="...")]` attribute on the type, use the union of that and the actual type's `where` clause as the `where` clause for the impl and do not attempt to generate any additional `where` clauses whatsoever.
- If there is a `#[serde(bound="...")]` attribute on a field, use that and do not attempt to generate any additional `where` clauses for the field.

The `bound` attribute behaves similar to `rename` in that you can specify a single attribute that applies to both ser and de, or individual ones.

```
#[serde(bound="D: Serialize + Deserialize")]

#[serde(bound(serialize="D: Serialize", deserialize="D: Deserialize"))]
```

EDIT: now addresses (4) from https://github.com/serde-rs/serde/issues/336#issuecomment-220378916 as well.

- If a field contains direct recursion, do not generate any bounds based on that field except from `bound` attributes.
2016-06-06 17:47:45 +09:00
David Tolnay bdffaf3ea1 Re-enable clippy lint "useless_let_if_seq"
This reverts commit 4e6cd2d63f.
2016-06-05 13:01:22 -07:00
David Tolnay f197c3ce96 Readme for "bound" attribute 2016-06-05 11:54:36 -07:00
David Tolnay 01dfad6705 Fix attributes canceling each other 2016-06-05 11:40:30 -07:00
David Tolnay 2e06786262 Remove unnecessary clones 2016-06-05 11:23:01 -07:00
David Tolnay 578f34ecaf Use "bound" attribute instead of "where" 2016-06-05 11:17:43 -07:00
David Tolnay 2c8767cb46 Remove changelog in favor of github release notes 2016-06-05 10:05:56 -07:00
David Tolnay 45c51d3198 Fix build on 1.5.0 which does not have Vec::as_slice 2016-06-04 16:53:45 -07:00
David Tolnay bd40830905 Do not generate bounds from recursive types 2016-06-04 16:12:01 -07:00
David Tolnay 4e6cd2d63f Disable clippy lint "useless_let_if_seq" 2016-06-04 15:48:44 -07:00
David Tolnay 2256a04926 Address clippy lint "ptr_arg" 2016-06-04 15:48:44 -07:00
David Tolnay 660ea7bd7b Attribute for handwritten where clauses 2016-06-04 15:48:42 -07:00
Homu 7052833512 Auto merge of #351 - oli-obk:publish_nits, r=oli-obk
also publish the `.in` file used by the build script
2016-06-02 01:47:39 +09:00
Oliver Schneider 5c2cf5778f also publish the .in file used by the build script 2016-06-01 13:09:43 +02:00
Homu b5c0406afe Auto merge of #349 - oli-obk:undo, r=oli-obk
undo the breaking change introduced in 0.7.6

I should probably yank 0.7.6, too

cc @alexcrichton this should unbreak your setup, sorry about that.
2016-06-01 20:01:53 +09:00
Oliver Schneider 96cd910c92 undo the breaking change introduced in 0.7.6 2016-06-01 11:08:59 +02:00
29 changed files with 669 additions and 322 deletions
+5 -2
View File
@@ -3,7 +3,8 @@ rust:
- stable - stable
- beta - beta
- nightly - nightly
- 1.5.0 - 1.7.0
- 1.8.0
addons: addons:
apt: apt:
packages: packages:
@@ -18,7 +19,7 @@ script:
- (cd serde && travis-cargo build) - (cd serde && travis-cargo build)
- (cd serde && travis-cargo test) - (cd serde && travis-cargo test)
- (cd serde && travis-cargo --only nightly test -- --features nightly-testing) - (cd serde && travis-cargo --only nightly test -- --features nightly-testing)
- (cd serde && travis-cargo --skip 1.5.0 build -- --no-default-features) - (cd serde && travis-cargo build -- --no-default-features)
- (cd serde && travis-cargo --only nightly build -- --no-default-features) - (cd serde && travis-cargo --only nightly build -- --no-default-features)
- (cd serde && travis-cargo --only nightly build -- --no-default-features --features alloc) - (cd serde && travis-cargo --only nightly build -- --no-default-features --features alloc)
- (cd serde && travis-cargo --only nightly build -- --no-default-features --features collections) - (cd serde && travis-cargo --only nightly build -- --no-default-features --features collections)
@@ -26,6 +27,8 @@ script:
- (cd serde_tests && travis-cargo --only nightly test -- --features nightly-testing) - (cd serde_tests && travis-cargo --only nightly test -- --features nightly-testing)
- (cd serde_macros && travis-cargo --only nightly test -- --features nightly-testing) - (cd serde_macros && travis-cargo --only nightly test -- --features nightly-testing)
- (cd serde_macros && travis-cargo --only nightly bench -- --features nightly-testing) - (cd serde_macros && travis-cargo --only nightly bench -- --features nightly-testing)
- (cd examples/serde-syntex-example && travis-cargo run)
- (cd examples/serde-syntex-example && travis-cargo --only nightly run -- --features nightly --no-default-features)
- (cd serde && travis-cargo --only stable doc) - (cd serde && travis-cargo --only stable doc)
- (cd serde_codegen && travis-cargo --only stable doc) - (cd serde_codegen && travis-cargo --only stable doc)
- (cd serde_macros && travis-cargo --only nightly doc) - (cd serde_macros && travis-cargo --only nightly doc)
-10
View File
@@ -1,10 +0,0 @@
## 0.7.6
NOTES:
* Syncs `serde_codegen` and `serde_macros` with rustc 1.10.0-nightly (7bddce693 2016-05-27).
FEATURES:
* `#[serde(serialize_with=..., deserialize_with=...)]` now supports tuples. #335
* Serde now can be used in `#[no_std]` environments. #316
+26 -26
View File
@@ -142,10 +142,7 @@ pub fn main() {
let src = Path::new("src/main.rs.in"); let src = Path::new("src/main.rs.in");
let dst = Path::new(&out_dir).join("main.rs"); let dst = Path::new(&out_dir).join("main.rs");
let mut registry = syntex::Registry::new(); serde_codegen::expand(&src, &dst).unwrap();
serde_codegen::register(&mut registry);
registry.expand("", &src, &dst).unwrap();
} }
``` ```
@@ -203,10 +200,7 @@ mod inner {
let src = Path::new("src/main.rs.in"); let src = Path::new("src/main.rs.in");
let dst = Path::new(&out_dir).join("main.rs"); let dst = Path::new(&out_dir).join("main.rs");
let mut registry = syntex::Registry::new(); serde_codegen::expand(&src, &dst).unwrap();
serde_codegen::register(&mut registry);
registry.expand("", &src, &dst).unwrap();
} }
} }
@@ -688,12 +682,15 @@ how types are serialized. Here are the supported annotations:
Container Annotations: Container Annotations:
| Annotation | Function | | Annotation | Function |
| ---------- | -------- | | ---------- | -------- |
| `#[serde(rename="name")]` | Serialize and deserialize this container with the given name | | `#[serde(rename="name")]` | Serialize and deserialize this container with the given name |
| `#[serde(rename(serialize="name1"))]` | Serialize this container with the given name | | `#[serde(rename(serialize="name1"))]` | Serialize this container with the given name |
| `#[serde(rename(deserialize="name1"))]` | Deserialize this container with the given name | | `#[serde(rename(deserialize="name1"))]` | Deserialize this container with the given name |
| `#[serde(deny_unknown_fields)]` | Always error during serialization when encountering unknown fields. When absent, unknown fields are ignored for self-describing formats like JSON. | | `#[serde(deny_unknown_fields)]` | Always error during serialization when encountering unknown fields. When absent, unknown fields are ignored for self-describing formats like JSON. |
| `#[serde(bound="T: MyTrait")]` | Where-clause for the Serialize and Deserialize impls. This replaces any bounds inferred by Serde. |
| `#[serde(bound(serialize="T: MyTrait"))]` | Where-clause for the Serialize impl. |
| `#[serde(bound(deserialize="T: MyTrait"))]` | Where-clause for the Deserialize impl. |
Variant Annotations: Variant Annotations:
@@ -705,18 +702,21 @@ Variant Annotations:
Field Annotations: Field Annotations:
| Annotation | Function | | Annotation | Function |
| ---------- | -------- | | ---------- | -------- |
| `#[serde(rename="name")]` | Serialize and deserialize this field with the given name | | `#[serde(rename="name")]` | Serialize and deserialize this field with the given name |
| `#[serde(rename(serialize="name1"))]` | Serialize this field with the given name | | `#[serde(rename(serialize="name1"))]` | Serialize this field with the given name |
| `#[serde(rename(deserialize="name1"))]` | Deserialize this field with the given name | | `#[serde(rename(deserialize="name1"))]` | Deserialize this field with the given name |
| `#[serde(default)]` | If the value is not specified, use the `Default::default()` | | `#[serde(default)]` | If the value is not specified, use the `Default::default()` |
| `#[serde(default="$path")]` | Call the path to a function `fn() -> T` to build the value | | `#[serde(default="$path")]` | Call the path to a function `fn() -> T` to build the value |
| `#[serde(skip_serializing)]` | Do not serialize this value | | `#[serde(skip_serializing)]` | Do not serialize this value |
| `#[serde(skip_deserializing)]` | Always use `Default::default()` or `#[serde(default="$path")]` instead of deserializing this value | | `#[serde(skip_deserializing)]` | Always use `Default::default()` or `#[serde(default="$path")]` instead of deserializing this value |
| `#[serde(skip_serializing_if="$path")]` | Do not serialize this value if this function `fn(&T) -> bool` returns `true` | | `#[serde(skip_serializing_if="$path")]` | Do not serialize this value if this function `fn(&T) -> bool` returns `true` |
| `#[serde(serialize_with="$path")]` | Call a function `fn<S>(&T, &mut S) -> Result<(), S::Error> where S: Serializer` to serialize this value of type `T` | | `#[serde(serialize_with="$path")]` | Call a function `fn<S>(&T, &mut S) -> Result<(), S::Error> where S: Serializer` to serialize this value of type `T` |
| `#[serde(deserialize_with="$path")]` | Call a function `fn<D>(&mut D) -> Result<T, D::Error> where D: Deserializer` to deserialize this value of type `T` | | `#[serde(deserialize_with="$path")]` | Call a function `fn<D>(&mut D) -> Result<T, D::Error> where D: Deserializer` to deserialize this value of type `T` |
| `#[serde(bound="T: MyTrait")]` | Where-clause for the Serialize and Deserialize impls. This replaces any bounds inferred by Serde for the current field. |
| `#[serde(bound(serialize="T: MyTrait"))]` | Where-clause for the Serialize impl. |
| `#[serde(bound(deserialize="T: MyTrait"))]` | Where-clause for the Deserialize impl. |
Using in `no_std` crates Using in `no_std` crates
======================== ========================
+5
View File
@@ -0,0 +1,5 @@
paths = [
"../serde",
"../serde_codegen",
"../serde_macros",
]
+4 -4
View File
@@ -9,10 +9,10 @@ default = ["serde_codegen"]
nightly = ["serde_macros"] nightly = ["serde_macros"]
[build-dependencies] [build-dependencies]
serde_codegen = { version = "^0.7.5", optional = true } serde_codegen = { version = "^0.7.10", optional = true }
syntex = "^0.32.0" syntex = "^0.33.0"
[dependencies] [dependencies]
serde = "^0.7.5" serde = "^0.7.10"
serde_json = "^0.7.0" serde_json = "^0.7.0"
serde_macros = { version = "^0.7.5", optional = true } serde_macros = { version = "^0.7.10", optional = true }
+3 -3
View File
@@ -2,12 +2,12 @@ This example demonstrates how to use Serde with Syntex. On stable or nightly
with Syntex, it can be built with: with Syntex, it can be built with:
``` ```
% multirust run stable cargo run % rustup run stable cargo run
Running `target/debug/serde-syntex-example` Running `target/debug/serde-syntex-example`
{"x":1,"y":2} {"x":1,"y":2}
Point { x: 1, y: 2 } Point { x: 1, y: 2 }
% multirust run nightly cargo run % rustup run nightly cargo run
Running `target/debug/serde-syntex-example` Running `target/debug/serde-syntex-example`
{"x":1,"y":2} {"x":1,"y":2}
Point { x: 1, y: 2 } Point { x: 1, y: 2 }
@@ -16,5 +16,5 @@ Point { x: 1, y: 2 }
On nightly, it can use a plugin with: On nightly, it can use a plugin with:
``` ```
% multirust run nightly cargo run --features nightly --no-default-features % rustup run nightly cargo run --features nightly --no-default-features
``` ```
+1 -4
View File
@@ -12,10 +12,7 @@ mod inner {
let src = Path::new("src/main.rs.in"); let src = Path::new("src/main.rs.in");
let dst = Path::new(&out_dir).join("main.rs"); let dst = Path::new(&out_dir).join("main.rs");
let mut registry = syntex::Registry::new(); serde_codegen::expand(&src, &dst).unwrap();
serde_codegen::register(&mut registry);
registry.expand("", &src, &dst).unwrap();
} }
} }
+2 -2
View File
@@ -1,5 +1,5 @@
#![cfg_attr(nightly, feature(custom_derive, plugin))] #![cfg_attr(feature = "serde_macros", feature(custom_derive, plugin))]
#![cfg_attr(nightly, plugin(serde_macros))] #![cfg_attr(feature = "serde_macros", plugin(serde_macros))]
extern crate serde; extern crate serde;
extern crate serde_json; extern crate serde_json;
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "serde" name = "serde"
version = "0.7.6" version = "0.7.10"
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"] authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"]
license = "MIT/Apache-2.0" license = "MIT/Apache-2.0"
description = "A generic serialization/deserialization framework" description = "A generic serialization/deserialization framework"
+46 -54
View File
@@ -32,7 +32,7 @@ use collections::enum_set::{CLike, EnumSet};
#[cfg(all(feature = "nightly", feature = "collections"))] #[cfg(all(feature = "nightly", feature = "collections"))]
use collections::borrow::ToOwned; use collections::borrow::ToOwned;
use core::hash::Hash; use core::hash::{Hash, BuildHasher};
use core::marker::PhantomData; use core::marker::PhantomData;
#[cfg(feature = "std")] #[cfg(feature = "std")]
use std::net; use std::net;
@@ -381,29 +381,30 @@ impl<T> Deserialize for PhantomData<T> where T: Deserialize {
macro_rules! seq_impl { macro_rules! seq_impl {
( (
$ty:ty, $ty:ty,
< $($constraints:ident),* >, $visitor_ty:ident < $($typaram:ident : $bound1:ident $(+ $bound2:ident)*),* >,
$visitor_name:ident,
$visitor:ident, $visitor:ident,
$ctor:expr, $ctor:expr,
$with_capacity:expr, $with_capacity:expr,
$insert:expr $insert:expr
) => { ) => {
/// A visitor that produces a sequence. /// A visitor that produces a sequence.
pub struct $visitor_name<T> { pub struct $visitor_ty<$($typaram),*> {
marker: PhantomData<T>, marker: PhantomData<$ty>,
} }
impl<T> $visitor_name<T> { impl<$($typaram),*> $visitor_ty<$($typaram),*>
where $($typaram: $bound1 $(+ $bound2)*),*
{
/// Construct a new sequence visitor. /// Construct a new sequence visitor.
pub fn new() -> Self { pub fn new() -> Self {
$visitor_name { $visitor_ty {
marker: PhantomData, marker: PhantomData,
} }
} }
} }
impl<T> Visitor for $visitor_name<T> impl<$($typaram),*> Visitor for $visitor_ty<$($typaram),*>
where T: $($constraints +)*, where $($typaram: $bound1 $(+ $bound2)*),*
{ {
type Value = $ty; type Value = $ty;
@@ -430,13 +431,13 @@ macro_rules! seq_impl {
} }
} }
impl<T> Deserialize for $ty impl<$($typaram),*> Deserialize for $ty
where T: $($constraints +)*, where $($typaram: $bound1 $(+ $bound2)*),*
{ {
fn deserialize<D>(deserializer: &mut D) -> Result<$ty, D::Error> fn deserialize<D>(deserializer: &mut D) -> Result<$ty, D::Error>
where D: Deserializer, where D: Deserializer,
{ {
deserializer.deserialize_seq($visitor_name::new()) deserializer.deserialize_seq($visitor_ty::new())
} }
} }
} }
@@ -445,8 +446,7 @@ macro_rules! seq_impl {
#[cfg(any(feature = "std", feature = "collections"))] #[cfg(any(feature = "std", feature = "collections"))]
seq_impl!( seq_impl!(
BinaryHeap<T>, BinaryHeap<T>,
<Deserialize, Ord>, BinaryHeapVisitor<T: Deserialize + Ord>,
BinaryHeapVisitor,
visitor, visitor,
BinaryHeap::new(), BinaryHeap::new(),
BinaryHeap::with_capacity(visitor.size_hint().0), BinaryHeap::with_capacity(visitor.size_hint().0),
@@ -455,8 +455,7 @@ seq_impl!(
#[cfg(any(feature = "std", feature = "collections"))] #[cfg(any(feature = "std", feature = "collections"))]
seq_impl!( seq_impl!(
BTreeSet<T>, BTreeSet<T>,
<Deserialize, Eq, Ord>, BTreeSetVisitor<T: Deserialize + Eq + Ord>,
BTreeSetVisitor,
visitor, visitor,
BTreeSet::new(), BTreeSet::new(),
BTreeSet::new(), BTreeSet::new(),
@@ -465,8 +464,7 @@ seq_impl!(
#[cfg(all(feature = "nightly", feature = "collections"))] #[cfg(all(feature = "nightly", feature = "collections"))]
seq_impl!( seq_impl!(
EnumSet<T>, EnumSet<T>,
<Deserialize, CLike>, EnumSetVisitor<T: Deserialize + CLike>,
EnumSetVisitor,
visitor, visitor,
EnumSet::new(), EnumSet::new(),
EnumSet::new(), EnumSet::new(),
@@ -475,8 +473,7 @@ seq_impl!(
#[cfg(any(feature = "std", feature = "collections"))] #[cfg(any(feature = "std", feature = "collections"))]
seq_impl!( seq_impl!(
LinkedList<T>, LinkedList<T>,
<Deserialize>, LinkedListVisitor<T: Deserialize>,
LinkedListVisitor,
visitor, visitor,
LinkedList::new(), LinkedList::new(),
LinkedList::new(), LinkedList::new(),
@@ -484,19 +481,18 @@ seq_impl!(
#[cfg(feature = "std")] #[cfg(feature = "std")]
seq_impl!( seq_impl!(
HashSet<T>, HashSet<T, S>,
<Deserialize, Eq, Hash>, HashSetVisitor<T: Deserialize + Eq + Hash,
HashSetVisitor, S: BuildHasher + Default>,
visitor, visitor,
HashSet::new(), HashSet::with_hasher(S::default()),
HashSet::with_capacity(visitor.size_hint().0), HashSet::with_capacity_and_hasher(visitor.size_hint().0, S::default()),
HashSet::insert); HashSet::insert);
#[cfg(any(feature = "std", feature = "collections"))] #[cfg(any(feature = "std", feature = "collections"))]
seq_impl!( seq_impl!(
Vec<T>, Vec<T>,
<Deserialize>, VecVisitor<T: Deserialize>,
VecVisitor,
visitor, visitor,
Vec::new(), Vec::new(),
Vec::with_capacity(visitor.size_hint().0), Vec::with_capacity(visitor.size_hint().0),
@@ -505,8 +501,7 @@ seq_impl!(
#[cfg(any(feature = "std", feature = "collections"))] #[cfg(any(feature = "std", feature = "collections"))]
seq_impl!( seq_impl!(
VecDeque<T>, VecDeque<T>,
<Deserialize>, VecDequeVisitor<T: Deserialize>,
VecDequeVisitor,
visitor, visitor,
VecDeque::new(), VecDeque::new(),
VecDeque::with_capacity(visitor.size_hint().0), VecDeque::with_capacity(visitor.size_hint().0),
@@ -726,30 +721,29 @@ tuple_impls! {
macro_rules! map_impl { macro_rules! map_impl {
( (
$ty:ty, $ty:ty,
< $($constraints:ident),* >, $visitor_ty:ident < $($typaram:ident : $bound1:ident $(+ $bound2:ident)*),* >,
$visitor_name:ident,
$visitor:ident, $visitor:ident,
$ctor:expr, $ctor:expr,
$with_capacity:expr, $with_capacity:expr
$insert:expr
) => { ) => {
/// A visitor that produces a map. /// A visitor that produces a map.
pub struct $visitor_name<K, V> { pub struct $visitor_ty<$($typaram),*> {
marker: PhantomData<$ty>, marker: PhantomData<$ty>,
} }
impl<K, V> $visitor_name<K, V> { impl<$($typaram),*> $visitor_ty<$($typaram),*>
where $($typaram: $bound1 $(+ $bound2)*),*
{
/// Construct a `MapVisitor*<T>`. /// Construct a `MapVisitor*<T>`.
pub fn new() -> Self { pub fn new() -> Self {
$visitor_name { $visitor_ty {
marker: PhantomData, marker: PhantomData,
} }
} }
} }
impl<K, V> Visitor for $visitor_name<K, V> impl<$($typaram),*> Visitor for $visitor_ty<$($typaram),*>
where K: $($constraints +)*, where $($typaram: $bound1 $(+ $bound2)*),*
V: Deserialize,
{ {
type Value = $ty; type Value = $ty;
@@ -767,7 +761,7 @@ macro_rules! map_impl {
let mut values = $with_capacity; let mut values = $with_capacity;
while let Some((key, value)) = try!($visitor.visit()) { while let Some((key, value)) = try!($visitor.visit()) {
$insert(&mut values, key, value); values.insert(key, value);
} }
try!($visitor.end()); try!($visitor.end());
@@ -776,14 +770,13 @@ macro_rules! map_impl {
} }
} }
impl<K, V> Deserialize for $ty impl<$($typaram),*> Deserialize for $ty
where K: $($constraints +)*, where $($typaram: $bound1 $(+ $bound2)*),*
V: Deserialize,
{ {
fn deserialize<D>(deserializer: &mut D) -> Result<$ty, D::Error> fn deserialize<D>(deserializer: &mut D) -> Result<$ty, D::Error>
where D: Deserializer, where D: Deserializer,
{ {
deserializer.deserialize_map($visitor_name::new()) deserializer.deserialize_map($visitor_ty::new())
} }
} }
} }
@@ -792,22 +785,21 @@ macro_rules! map_impl {
#[cfg(any(feature = "std", feature = "collections"))] #[cfg(any(feature = "std", feature = "collections"))]
map_impl!( map_impl!(
BTreeMap<K, V>, BTreeMap<K, V>,
<Deserialize, Eq, Ord>, BTreeMapVisitor<K: Deserialize + Eq + Ord,
BTreeMapVisitor, V: Deserialize>,
visitor, visitor,
BTreeMap::new(), BTreeMap::new(),
BTreeMap::new(), BTreeMap::new());
BTreeMap::insert);
#[cfg(feature = "std")] #[cfg(feature = "std")]
map_impl!( map_impl!(
HashMap<K, V>, HashMap<K, V, S>,
<Deserialize, Eq, Hash>, HashMapVisitor<K: Deserialize + Eq + Hash,
HashMapVisitor, V: Deserialize,
S: BuildHasher + Default>,
visitor, visitor,
HashMap::new(), HashMap::with_hasher(S::default()),
HashMap::with_capacity(visitor.size_hint().0), HashMap::with_capacity_and_hasher(visitor.size_hint().0, S::default()));
HashMap::insert);
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
+1 -1
View File
@@ -578,7 +578,7 @@ pub trait Visitor {
fn visit_char<E>(&mut self, v: char) -> Result<Self::Value, E> fn visit_char<E>(&mut self, v: char) -> Result<Self::Value, E>
where E: Error, where E: Error,
{ {
self.visit_str(::core::str::from_utf8(::utils::encode_utf8(v).as_slice()).unwrap()) self.visit_str(::utils::encode_utf8(v).as_str())
} }
/// `visit_str` deserializes a `&str` into a `Value`. /// `visit_str` deserializes a `&str` into a `Value`.
+5 -3
View File
@@ -31,7 +31,7 @@ use collections::enum_set::{CLike, EnumSet};
#[cfg(all(feature = "nightly", feature = "collections"))] #[cfg(all(feature = "nightly", feature = "collections"))]
use collections::borrow::ToOwned; use collections::borrow::ToOwned;
use core::hash::Hash; use core::hash::{Hash, BuildHasher};
#[cfg(feature = "nightly")] #[cfg(feature = "nightly")]
use core::iter; use core::iter;
#[cfg(feature = "std")] #[cfg(feature = "std")]
@@ -330,8 +330,9 @@ impl<T> Serialize for EnumSet<T>
} }
#[cfg(feature = "std")] #[cfg(feature = "std")]
impl<T> Serialize for HashSet<T> impl<T, H> Serialize for HashSet<T, H>
where T: Serialize + Eq + Hash, where T: Serialize + Eq + Hash,
H: BuildHasher,
{ {
#[inline] #[inline]
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
@@ -651,9 +652,10 @@ impl<K, V> Serialize for BTreeMap<K, V>
} }
#[cfg(feature = "std")] #[cfg(feature = "std")]
impl<K, V> Serialize for HashMap<K, V> impl<K, V, H> Serialize for HashMap<K, V, H>
where K: Serialize + Eq + Hash, where K: Serialize + Eq + Hash,
V: Serialize, V: Serialize,
H: BuildHasher,
{ {
#[inline] #[inline]
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
+3 -3
View File
@@ -122,11 +122,11 @@ pub trait Serializer {
/// Serializes a `f64` value. /// Serializes a `f64` value.
fn serialize_f64(&mut self, v: f64) -> Result<(), Self::Error>; fn serialize_f64(&mut self, v: f64) -> Result<(), Self::Error>;
/// Serializes a character. By default it serializes as bytes containing the UTF-8 encoding /// Serializes a character. By default it serializes it as a `&str` containing a
/// of the character. /// single character.
#[inline] #[inline]
fn serialize_char(&mut self, v: char) -> Result<(), Self::Error> { fn serialize_char(&mut self, v: char) -> Result<(), Self::Error> {
self.serialize_bytes(::utils::encode_utf8(v).as_slice()) self.serialize_str(::utils::encode_utf8(v).as_str())
} }
/// Serializes a `&str`. /// Serializes a `&str`.
+3 -3
View File
@@ -40,9 +40,9 @@ pub struct EncodeUtf8 {
} }
impl EncodeUtf8 { impl EncodeUtf8 {
/// Returns the remaining bytes of this iterator as a slice. // FIXME: use this from_utf8_unchecked, since we know it can never fail
pub fn as_slice(&self) -> &[u8] { pub fn as_str(&self) -> &str {
&self.buf[self.pos..] ::core::str::from_utf8(&self.buf[self.pos..]).unwrap()
} }
} }
+8 -8
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "serde_codegen" name = "serde_codegen"
version = "0.7.6" version = "0.7.10"
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"] authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"]
license = "MIT/Apache-2.0" license = "MIT/Apache-2.0"
description = "Macros to auto-generate implementations for the serde framework" description = "Macros to auto-generate implementations for the serde framework"
@@ -8,7 +8,7 @@ repository = "https://github.com/serde-rs/serde"
documentation = "https://github.com/serde-rs/serde" documentation = "https://github.com/serde-rs/serde"
keywords = ["serde", "serialization"] keywords = ["serde", "serialization"]
build = "build.rs" build = "build.rs"
include = ["Cargo.toml", "build.rs", "src/**/*.rs"] include = ["Cargo.toml", "build.rs", "src/**/*.rs", "src/lib.rs.in"]
[features] [features]
default = ["with-syntex"] default = ["with-syntex"]
@@ -17,13 +17,13 @@ nightly-testing = ["clippy"]
with-syntex = ["quasi/with-syntex", "quasi_codegen", "quasi_codegen/with-syntex", "syntex", "syntex_syntax"] with-syntex = ["quasi/with-syntex", "quasi_codegen", "quasi_codegen/with-syntex", "syntex", "syntex_syntax"]
[build-dependencies] [build-dependencies]
quasi_codegen = { version = "^0.11.0", optional = true } quasi_codegen = { version = "^0.12.0", optional = true }
syntex = { version = "^0.33.0", optional = true } syntex = { version = "^0.33.0", optional = true }
[dependencies] [dependencies]
aster = { version = "^0.17.0", default-features = false } aster = { version = "^0.18.0", default-features = false }
clippy = { version = "^0.*", optional = true } clippy = { version = "^0.*", optional = true }
quasi = { version = "^0.11.0", default-features = false } quasi = { version = "^0.12.0", default-features = false }
quasi_macros = { version = "^0.11.0", optional = true } quasi_macros = { version = "^0.12.0", optional = true }
syntex = { version = "^0.33.0", optional = true } syntex = { version = "^0.35.0", optional = true }
syntex_syntax = { version = "^0.33.0", optional = true } syntex_syntax = { version = "^0.35.0", optional = true }
+1 -3
View File
@@ -8,13 +8,11 @@ mod inner {
pub fn main() { pub fn main() {
let out_dir = env::var_os("OUT_DIR").unwrap(); let out_dir = env::var_os("OUT_DIR").unwrap();
let mut registry = syntex::Registry::new();
quasi_codegen::register(&mut registry);
let src = Path::new("src/lib.rs.in"); let src = Path::new("src/lib.rs.in");
let dst = Path::new(&out_dir).join("lib.rs"); let dst = Path::new(&out_dir).join("lib.rs");
registry.expand("", &src, &dst).unwrap(); quasi_codegen::expand(&src, &dst).unwrap();
} }
} }
+165 -58
View File
@@ -4,7 +4,7 @@ use syntax::attr;
use syntax::codemap::Span; use syntax::codemap::Span;
use syntax::ext::base::ExtCtxt; use syntax::ext::base::ExtCtxt;
use syntax::fold::Folder; use syntax::fold::Folder;
use syntax::parse::parser::PathStyle; use syntax::parse::parser::{Parser, PathStyle};
use syntax::parse::token::{self, InternedString}; use syntax::parse::token::{self, InternedString};
use syntax::parse; use syntax::parse;
use syntax::print::pprust::{lit_to_string, meta_item_to_string}; use syntax::print::pprust::{lit_to_string, meta_item_to_string};
@@ -62,6 +62,8 @@ impl Name {
pub struct ContainerAttrs { pub struct ContainerAttrs {
name: Name, name: Name,
deny_unknown_fields: bool, deny_unknown_fields: bool,
ser_bound: Option<Vec<ast::WherePredicate>>,
de_bound: Option<Vec<ast::WherePredicate>>,
} }
impl ContainerAttrs { impl ContainerAttrs {
@@ -70,6 +72,8 @@ impl ContainerAttrs {
let mut container_attrs = ContainerAttrs { let mut container_attrs = ContainerAttrs {
name: Name::new(item.ident), name: Name::new(item.ident),
deny_unknown_fields: false, deny_unknown_fields: false,
ser_bound: None,
de_bound: None,
}; };
for meta_items in item.attrs().iter().filter_map(get_serde_meta_items) { for meta_items in item.attrs().iter().filter_map(get_serde_meta_items) {
@@ -78,7 +82,6 @@ impl ContainerAttrs {
// Parse `#[serde(rename="foo")]` // Parse `#[serde(rename="foo")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"rename" => { ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"rename" => {
let s = try!(get_str_from_lit(cx, name, lit)); let s = try!(get_str_from_lit(cx, name, lit));
container_attrs.name.serialize_name = Some(s.clone()); container_attrs.name.serialize_name = Some(s.clone());
container_attrs.name.deserialize_name = Some(s); container_attrs.name.deserialize_name = Some(s);
} }
@@ -86,9 +89,12 @@ impl ContainerAttrs {
// Parse `#[serde(rename(serialize="foo", deserialize="bar"))]` // Parse `#[serde(rename(serialize="foo", deserialize="bar"))]`
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"rename" => { ast::MetaItemKind::List(ref name, ref meta_items) if name == &"rename" => {
let (ser_name, de_name) = try!(get_renames(cx, meta_items)); let (ser_name, de_name) = try!(get_renames(cx, meta_items));
if ser_name.is_some() {
container_attrs.name.serialize_name = ser_name; container_attrs.name.serialize_name = ser_name;
container_attrs.name.deserialize_name = de_name; }
if de_name.is_some() {
container_attrs.name.deserialize_name = de_name;
}
} }
// Parse `#[serde(deny_unknown_fields)]` // Parse `#[serde(deny_unknown_fields)]`
@@ -96,6 +102,24 @@ impl ContainerAttrs {
container_attrs.deny_unknown_fields = true; container_attrs.deny_unknown_fields = true;
} }
// Parse `#[serde(bound="D: Serialize")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"bound" => {
let where_predicates = try!(parse_lit_into_where(cx, name, lit));
container_attrs.ser_bound = Some(where_predicates.clone());
container_attrs.de_bound = Some(where_predicates);
}
// Parse `#[serde(bound(serialize="D: Serialize", deserialize="D: Deserialize"))]`
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"bound" => {
let (ser_bound, de_bound) = try!(get_where_predicates(cx, meta_items));
if ser_bound.is_some() {
container_attrs.ser_bound = ser_bound;
}
if de_bound.is_some() {
container_attrs.de_bound = de_bound;
}
}
_ => { _ => {
cx.span_err( cx.span_err(
meta_item.span, meta_item.span,
@@ -118,6 +142,14 @@ impl ContainerAttrs {
pub fn deny_unknown_fields(&self) -> bool { pub fn deny_unknown_fields(&self) -> bool {
self.deny_unknown_fields self.deny_unknown_fields
} }
pub fn ser_bound(&self) -> Option<&[ast::WherePredicate]> {
self.ser_bound.as_ref().map(|vec| &vec[..])
}
pub fn de_bound(&self) -> Option<&[ast::WherePredicate]> {
self.de_bound.as_ref().map(|vec| &vec[..])
}
} }
/// Represents variant attribute information /// Represents variant attribute information
@@ -138,7 +170,6 @@ impl VariantAttrs {
// Parse `#[serde(rename="foo")]` // Parse `#[serde(rename="foo")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"rename" => { ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"rename" => {
let s = try!(get_str_from_lit(cx, name, lit)); let s = try!(get_str_from_lit(cx, name, lit));
variant_attrs.name.serialize_name = Some(s.clone()); variant_attrs.name.serialize_name = Some(s.clone());
variant_attrs.name.deserialize_name = Some(s); variant_attrs.name.deserialize_name = Some(s);
} }
@@ -146,9 +177,12 @@ impl VariantAttrs {
// Parse `#[serde(rename(serialize="foo", deserialize="bar"))]` // Parse `#[serde(rename(serialize="foo", deserialize="bar"))]`
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"rename" => { ast::MetaItemKind::List(ref name, ref meta_items) if name == &"rename" => {
let (ser_name, de_name) = try!(get_renames(cx, meta_items)); let (ser_name, de_name) = try!(get_renames(cx, meta_items));
if ser_name.is_some() {
variant_attrs.name.serialize_name = ser_name; variant_attrs.name.serialize_name = ser_name;
variant_attrs.name.deserialize_name = de_name; }
if de_name.is_some() {
variant_attrs.name.deserialize_name = de_name;
}
} }
_ => { _ => {
@@ -178,9 +212,22 @@ pub struct FieldAttrs {
skip_serializing_field: bool, skip_serializing_field: bool,
skip_deserializing_field: bool, skip_deserializing_field: bool,
skip_serializing_if: Option<ast::Path>, skip_serializing_if: Option<ast::Path>,
default_expr_if_missing: Option<P<ast::Expr>>, default: FieldDefault,
serialize_with: Option<ast::Path>, serialize_with: Option<ast::Path>,
deserialize_with: Option<ast::Path>, deserialize_with: Option<ast::Path>,
ser_bound: Option<Vec<ast::WherePredicate>>,
de_bound: Option<Vec<ast::WherePredicate>>,
}
/// Represents the default to use for a field when deserializing.
#[derive(Debug, PartialEq)]
pub enum FieldDefault {
/// Field must always be specified because it does not have a default.
None,
/// The default is given by `std::default::Default::default()`.
Default,
/// The default is given by this function.
Path(ast::Path),
} }
impl FieldAttrs { impl FieldAttrs {
@@ -200,9 +247,11 @@ impl FieldAttrs {
skip_serializing_field: false, skip_serializing_field: false,
skip_deserializing_field: false, skip_deserializing_field: false,
skip_serializing_if: None, skip_serializing_if: None,
default_expr_if_missing: None, default: FieldDefault::None,
serialize_with: None, serialize_with: None,
deserialize_with: None, deserialize_with: None,
ser_bound: None,
de_bound: None,
}; };
for meta_items in field.attrs.iter().filter_map(get_serde_meta_items) { for meta_items in field.attrs.iter().filter_map(get_serde_meta_items) {
@@ -211,7 +260,6 @@ impl FieldAttrs {
// Parse `#[serde(rename="foo")]` // Parse `#[serde(rename="foo")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"rename" => { ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"rename" => {
let s = try!(get_str_from_lit(cx, name, lit)); let s = try!(get_str_from_lit(cx, name, lit));
field_attrs.name.serialize_name = Some(s.clone()); field_attrs.name.serialize_name = Some(s.clone());
field_attrs.name.deserialize_name = Some(s); field_attrs.name.deserialize_name = Some(s);
} }
@@ -219,24 +267,23 @@ impl FieldAttrs {
// Parse `#[serde(rename(serialize="foo", deserialize="bar"))]` // Parse `#[serde(rename(serialize="foo", deserialize="bar"))]`
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"rename" => { ast::MetaItemKind::List(ref name, ref meta_items) if name == &"rename" => {
let (ser_name, de_name) = try!(get_renames(cx, meta_items)); let (ser_name, de_name) = try!(get_renames(cx, meta_items));
if ser_name.is_some() {
field_attrs.name.serialize_name = ser_name; field_attrs.name.serialize_name = ser_name;
field_attrs.name.deserialize_name = de_name; }
if de_name.is_some() {
field_attrs.name.deserialize_name = de_name;
}
} }
// Parse `#[serde(default)]` // Parse `#[serde(default)]`
ast::MetaItemKind::Word(ref name) if name == &"default" => { ast::MetaItemKind::Word(ref name) if name == &"default" => {
let default_expr = builder.expr().default(); field_attrs.default = FieldDefault::Default;
field_attrs.default_expr_if_missing = Some(default_expr);
} }
// Parse `#[serde(default="...")]` // Parse `#[serde(default="...")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"default" => { ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"default" => {
let wrapped_expr = wrap_default( let path = try!(parse_lit_into_path(cx, name, lit));
try!(parse_lit_into_path(cx, name, lit)), field_attrs.default = FieldDefault::Path(path);
);
field_attrs.default_expr_if_missing = Some(wrapped_expr);
} }
// Parse `#[serde(skip_serializing)]` // Parse `#[serde(skip_serializing)]`
@@ -250,9 +297,8 @@ impl FieldAttrs {
// Initialize field to Default::default() unless a different // Initialize field to Default::default() unless a different
// default is specified by `#[serde(default="...")]` // default is specified by `#[serde(default="...")]`
if field_attrs.default_expr_if_missing.is_none() { if field_attrs.default == FieldDefault::None {
let default_expr = builder.expr().default(); field_attrs.default = FieldDefault::Default;
field_attrs.default_expr_if_missing = Some(default_expr);
} }
} }
@@ -274,6 +320,24 @@ impl FieldAttrs {
field_attrs.deserialize_with = Some(path); field_attrs.deserialize_with = Some(path);
} }
// Parse `#[serde(bound="D: Serialize")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"bound" => {
let where_predicates = try!(parse_lit_into_where(cx, name, lit));
field_attrs.ser_bound = Some(where_predicates.clone());
field_attrs.de_bound = Some(where_predicates);
}
// Parse `#[serde(bound(serialize="D: Serialize", deserialize="D: Deserialize"))]`
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"bound" => {
let (ser_bound, de_bound) = try!(get_where_predicates(cx, meta_items));
if ser_bound.is_some() {
field_attrs.ser_bound = ser_bound;
}
if de_bound.is_some() {
field_attrs.de_bound = de_bound;
}
}
_ => { _ => {
cx.span_err( cx.span_err(
meta_item.span, meta_item.span,
@@ -305,8 +369,8 @@ impl FieldAttrs {
self.skip_serializing_if.as_ref() self.skip_serializing_if.as_ref()
} }
pub fn default_expr_if_missing(&self) -> Option<&P<ast::Expr>> { pub fn default(&self) -> &FieldDefault {
self.default_expr_if_missing.as_ref() &self.default
} }
pub fn serialize_with(&self) -> Option<&ast::Path> { pub fn serialize_with(&self) -> Option<&ast::Path> {
@@ -316,45 +380,59 @@ impl FieldAttrs {
pub fn deserialize_with(&self) -> Option<&ast::Path> { pub fn deserialize_with(&self) -> Option<&ast::Path> {
self.deserialize_with.as_ref() self.deserialize_with.as_ref()
} }
pub fn ser_bound(&self) -> Option<&[ast::WherePredicate]> {
self.ser_bound.as_ref().map(|vec| &vec[..])
}
pub fn de_bound(&self) -> Option<&[ast::WherePredicate]> {
self.de_bound.as_ref().map(|vec| &vec[..])
}
} }
/// Zip together fields and `#[serde(...)]` attributes on those fields. /// Zip together fields and `#[serde(...)]` attributes on those fields.
pub fn fields_with_attrs<'a>( pub fn fields_with_attrs(
cx: &ExtCtxt, cx: &ExtCtxt,
fields: &'a [ast::StructField], fields: &[ast::StructField],
) -> Result<Vec<(&'a ast::StructField, FieldAttrs)>, Error> { ) -> Result<Vec<(ast::StructField, FieldAttrs)>, Error> {
fields.iter() fields.iter()
.enumerate() .enumerate()
.map(|(i, field)| { .map(|(i, field)| {
let attrs = try!(FieldAttrs::from_field(cx, i, field)); let attrs = try!(FieldAttrs::from_field(cx, i, field));
Ok((field, attrs)) Ok((field.clone(), attrs))
}) })
.collect() .collect()
} }
fn get_renames(cx: &ExtCtxt, fn get_ser_and_de<T, F>(
items: &[P<ast::MetaItem>], cx: &ExtCtxt,
)-> Result<(Option<InternedString>, Option<InternedString>), Error> { attribute: &str,
let mut ser_name = None; items: &[P<ast::MetaItem>],
let mut de_name = None; f: F
) -> Result<(Option<T>, Option<T>), Error>
where F: Fn(&ExtCtxt, &str, &ast::Lit) -> Result<T, Error>,
{
let mut ser_item = None;
let mut de_item = None;
for item in items { for item in items {
match item.node { match item.node {
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"serialize" => { ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"serialize" => {
let s = try!(get_str_from_lit(cx, name, lit)); let s = try!(f(cx, name, lit));
ser_name = Some(s); ser_item = Some(s);
} }
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"deserialize" => { ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"deserialize" => {
let s = try!(get_str_from_lit(cx, name, lit)); let s = try!(f(cx, name, lit));
de_name = Some(s); de_item = Some(s);
} }
_ => { _ => {
cx.span_err( cx.span_err(
item.span, item.span,
&format!("unknown rename attribute `{}`", &format!("unknown {} attribute `{}`",
attribute,
meta_item_to_string(item))); meta_item_to_string(item)));
return Err(Error); return Err(Error);
@@ -362,7 +440,21 @@ fn get_renames(cx: &ExtCtxt,
} }
} }
Ok((ser_name, de_name)) Ok((ser_item, de_item))
}
fn get_renames(
cx: &ExtCtxt,
items: &[P<ast::MetaItem>],
) -> Result<(Option<InternedString>, Option<InternedString>), Error> {
get_ser_and_de(cx, "rename", items, get_str_from_lit)
}
fn get_where_predicates(
cx: &ExtCtxt,
items: &[P<ast::MetaItem>],
) -> Result<(Option<Vec<ast::WherePredicate>>, Option<Vec<ast::WherePredicate>>), Error> {
get_ser_and_de(cx, "bound", items, parse_lit_into_where)
} }
pub fn get_serde_meta_items(attr: &ast::Attribute) -> Option<&[P<ast::MetaItem>]> { pub fn get_serde_meta_items(attr: &ast::Attribute) -> Option<&[P<ast::MetaItem>]> {
@@ -437,17 +529,18 @@ fn get_str_from_lit(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result<Interned
} }
} }
fn parse_lit_into_path(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result<ast::Path, Error> { // If we just parse a string literal from an attibute, any syntax errors in the
let source = try!(get_str_from_lit(cx, name, lit)); // source will only have spans that point inside the string and not back to the
// attribute. So to have better error reporting, we'll first parse the string
// If we just parse the string into an expression, any syntax errors in the source will only // into a token tree. Then we'll update those spans to say they're coming from a
// have spans that point inside the string, and not back to the attribute. So to have better // macro context that originally came from the attribnute, and then finally
// error reporting, we'll first parse the string into a token tree. Then we'll update those // parse them into an expression or where-clause.
// spans to say they're coming from a macro context that originally came from the attribute, fn parse_string_via_tts<T, F>(cx: &ExtCtxt, name: &str, string: String, action: F) -> Result<T, Error>
// and then finally parse them into an expression. where F: for<'a> Fn(&'a mut Parser) -> parse::PResult<'a, T>,
{
let tts = panictry!(parse::parse_tts_from_source_str( let tts = panictry!(parse::parse_tts_from_source_str(
format!("<serde {} expansion>", name), format!("<serde {} expansion>", name),
(*source).to_owned(), string,
cx.cfg(), cx.cfg(),
cx.parse_sess())); cx.parse_sess()));
@@ -456,7 +549,7 @@ fn parse_lit_into_path(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result<ast::
let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(), tts); let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(), tts);
let path = match parser.parse_path(PathStyle::Type) { let path = match action(&mut parser) {
Ok(path) => path, Ok(path) => path,
Err(mut e) => { Err(mut e) => {
e.emit(); e.emit();
@@ -476,10 +569,24 @@ fn parse_lit_into_path(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result<ast::
Ok(path) Ok(path)
} }
/// This function wraps the expression in `#[serde(default="...")]` in a function to prevent it fn parse_lit_into_path(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result<ast::Path, Error> {
/// from accessing the internal `Deserialize` state. let string = try!(get_str_from_lit(cx, name, lit)).to_string();
fn wrap_default(path: ast::Path) -> P<ast::Expr> {
AstBuilder::new().expr().call() parse_string_via_tts(cx, name, string, |parser| {
.build_path(path) parser.parse_path(PathStyle::Type)
.build() })
}
fn parse_lit_into_where(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result<Vec<ast::WherePredicate>, Error> {
let string = try!(get_str_from_lit(cx, name, lit));
if string.is_empty() {
return Ok(Vec::new());
}
let where_string = format!("where {}", string);
parse_string_via_tts(cx, name, where_string, |parser| {
let where_clause = try!(parser.parse_where_clause());
Ok(where_clause.predicates)
})
} }
+101 -10
View File
@@ -7,6 +7,9 @@ use syntax::ext::base::ExtCtxt;
use syntax::ptr::P; use syntax::ptr::P;
use syntax::visit; use syntax::visit;
use attr;
use error::Error;
// Remove the default from every type parameter because in the generated impls // Remove the default from every type parameter because in the generated impls
// they look like associated types: "error: associated type bindings are not // they look like associated types: "error: associated type bindings are not
// allowed here". // allowed here".
@@ -21,22 +24,53 @@ pub fn without_defaults(generics: &ast::Generics) -> ast::Generics {
} }
} }
pub fn with_bound( pub fn with_where_predicates(
builder: &AstBuilder,
generics: &ast::Generics,
predicates: &[ast::WherePredicate],
) -> ast::Generics {
builder.from_generics(generics.clone())
.with_predicates(predicates.to_vec())
.build()
}
pub fn with_where_predicates_from_fields<F>(
cx: &ExtCtxt, cx: &ExtCtxt,
builder: &AstBuilder, builder: &AstBuilder,
item: &ast::Item, item: &ast::Item,
generics: &ast::Generics, generics: &ast::Generics,
filter: &Fn(&ast::StructField) -> bool, from_field: F,
bound: &ast::Path, ) -> Result<ast::Generics, Error>
) -> ast::Generics { where F: Fn(&attr::FieldAttrs) -> Option<&[ast::WherePredicate]>,
builder.from_generics(generics.clone()) {
Ok(builder.from_generics(generics.clone())
.with_predicates( .with_predicates(
all_variants(cx, item).iter() try!(all_fields_with_attrs(cx, item))
.flat_map(|variant_data| all_struct_fields(variant_data)) .iter()
.filter(|field| filter(field)) .flat_map(|&(_, ref attrs)| from_field(attrs))
.map(|field| &field.ty) .flat_map(|predicates| predicates.to_vec()))
.build())
}
pub fn with_bound<F>(
cx: &ExtCtxt,
builder: &AstBuilder,
item: &ast::Item,
generics: &ast::Generics,
filter: F,
bound: &ast::Path,
) -> Result<ast::Generics, Error>
where F: Fn(&attr::FieldAttrs) -> bool,
{
Ok(builder.from_generics(generics.clone())
.with_predicates(
try!(all_fields_with_attrs(cx, item))
.iter()
.filter(|&&(_, ref attrs)| filter(attrs))
.map(|&(ref field, _)| &field.ty)
// TODO this filter can be removed later, see comment on function // TODO this filter can be removed later, see comment on function
.filter(|ty| contains_generic(ty, generics)) .filter(|ty| contains_generic(ty, generics))
.filter(|ty| !contains_recursion(ty, item.ident))
.map(|ty| strip_reference(ty)) .map(|ty| strip_reference(ty))
.map(|ty| builder.where_predicate() .map(|ty| builder.where_predicate()
// the type that is being bounded e.g. T // the type that is being bounded e.g. T
@@ -44,7 +78,20 @@ pub fn with_bound(
// the bound e.g. Serialize // the bound e.g. Serialize
.bound().trait_(bound.clone()).build() .bound().trait_(bound.clone()).build()
.build())) .build()))
.build() .build())
}
fn all_fields_with_attrs(
cx: &ExtCtxt,
item: &ast::Item,
) -> Result<Vec<(ast::StructField, attr::FieldAttrs)>, Error> {
let fields: Vec<ast::StructField> =
all_variants(cx, item).iter()
.flat_map(|variant_data| all_struct_fields(variant_data))
.cloned()
.collect();
attr::fields_with_attrs(cx, &fields)
} }
fn all_variants<'a>(cx: &ExtCtxt, item: &'a ast::Item) -> Vec<&'a ast::VariantData> { fn all_variants<'a>(cx: &ExtCtxt, item: &'a ast::Item) -> Vec<&'a ast::VariantData> {
@@ -113,6 +160,50 @@ fn contains_generic(ty: &ast::Ty, generics: &ast::Generics) -> bool {
visitor.found_generic visitor.found_generic
} }
// We do not attempt to generate any bounds based on field types that are
// directly recursive, as in:
//
// struct Test<D> {
// next: Box<Test<D>>,
// }
//
// This does not catch field types that are mutually recursive with some other
// type. For those, we require bounds to be specified by a `bound` attribute if
// the inferred ones are not correct.
//
// struct Test<D> {
// #[serde(bound="D: Serialize + Deserialize")]
// next: Box<Other<D>>,
// }
// struct Other<D> {
// #[serde(bound="D: Serialize + Deserialize")]
// next: Box<Test<D>>,
// }
fn contains_recursion(ty: &ast::Ty, ident: ast::Ident) -> bool {
struct FindRecursion {
ident: ast::Ident,
found_recursion: bool,
}
impl<'v> visit::Visitor<'v> for FindRecursion {
fn visit_path(&mut self, path: &'v ast::Path, _id: ast::NodeId) {
if !path.global
&& path.segments.len() == 1
&& path.segments[0].identifier == self.ident {
self.found_recursion = true;
} else {
visit::walk_path(self, path);
}
}
}
let mut visitor = FindRecursion {
ident: ident,
found_recursion: false,
};
visit::walk_ty(&mut visitor, ty);
visitor.found_recursion
}
// This is required to handle types that use both a reference and a value of // This is required to handle types that use both a reference and a value of
// the same type, as in: // the same type, as in:
// //
+90 -82
View File
@@ -35,36 +35,53 @@ pub fn expand_derive_deserialize(
let builder = aster::AstBuilder::new().span(span); let builder = aster::AstBuilder::new().span(span);
let generics = match item.node { let impl_item = match deserialize_item(cx, &builder, &item) {
ast::ItemKind::Struct(_, ref generics) => generics, Ok(item) => item,
ast::ItemKind::Enum(_, ref generics) => generics,
_ => {
cx.span_err(
meta_item.span,
"`#[derive(Deserialize)]` may only be applied to structs and enums");
return;
}
};
let impl_generics = build_impl_generics(cx, &builder, item, generics);
let ty = builder.ty().path()
.segment(item.ident).with_generics(impl_generics.clone()).build()
.build();
let body = match deserialize_body(cx, &builder, &item, &impl_generics, ty.clone()) {
Ok(body) => body,
Err(Error) => { Err(Error) => {
// An error occured, but it should have been reported already. // An error occured, but it should have been reported already.
return; return;
} }
}; };
push(Annotatable::Item(impl_item))
}
fn deserialize_item(
cx: &ExtCtxt,
builder: &aster::AstBuilder,
item: &Item,
) -> Result<P<ast::Item>, Error> {
let generics = match item.node {
ast::ItemKind::Struct(_, ref generics) => generics,
ast::ItemKind::Enum(_, ref generics) => generics,
_ => {
cx.span_err(
item.span,
"`#[derive(Deserialize)]` may only be applied to structs and enums");
return Err(Error);
}
};
let container_attrs = try!(attr::ContainerAttrs::from_item(cx, item));
let impl_generics = try!(build_impl_generics(cx, &builder, item, generics, &container_attrs));
let ty = builder.ty().path()
.segment(item.ident).with_generics(impl_generics.clone()).build()
.build();
let body = try!(deserialize_body(cx,
&builder,
&item,
&impl_generics,
ty.clone(),
&container_attrs));
let where_clause = &impl_generics.where_clause; let where_clause = &impl_generics.where_clause;
let dummy_const = builder.id(format!("_IMPL_DESERIALIZE_FOR_{}", item.ident)); let dummy_const = builder.id(format!("_IMPL_DESERIALIZE_FOR_{}", item.ident));
let impl_item = quote_item!(cx, Ok(quote_item!(cx,
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
const $dummy_const: () = { const $dummy_const: () = {
extern crate serde as _serde; extern crate serde as _serde;
@@ -77,9 +94,7 @@ pub fn expand_derive_deserialize(
} }
} }
}; };
).unwrap(); ).unwrap())
push(Annotatable::Item(impl_item))
} }
// All the generics in the input, plus a bound `T: Deserialize` for each generic // All the generics in the input, plus a bound `T: Deserialize` for each generic
@@ -90,58 +105,45 @@ fn build_impl_generics(
builder: &aster::AstBuilder, builder: &aster::AstBuilder,
item: &Item, item: &Item,
generics: &ast::Generics, generics: &ast::Generics,
) -> ast::Generics { container_attrs: &attr::ContainerAttrs,
) -> Result<ast::Generics, Error> {
let generics = bound::without_defaults(generics); let generics = bound::without_defaults(generics);
let generics = bound::with_bound(cx, builder, item, &generics,
&deserialized_by_us, let generics = try!(bound::with_where_predicates_from_fields(
&builder.path().ids(&["_serde", "de", "Deserialize"]).build()); cx, builder, item, &generics,
let generics = bound::with_bound(cx, builder, item, &generics, |attrs| attrs.de_bound()));
&requires_default,
&builder.path().global().ids(&["std", "default", "Default"]).build()); match container_attrs.de_bound() {
generics Some(predicates) => {
let generics = bound::with_where_predicates(builder, &generics, predicates);
Ok(generics)
}
None => {
let generics = try!(bound::with_bound(cx, builder, item, &generics,
needs_deserialize_bound,
&builder.path().ids(&["_serde", "de", "Deserialize"]).build()));
let generics = try!(bound::with_bound(cx, builder, item, &generics,
requires_default,
&builder.path().global().ids(&["std", "default", "Default"]).build()));
Ok(generics)
}
}
} }
// Fields with a `skip_deserializing` or `deserialize_with` attribute are not // Fields with a `skip_deserializing` or `deserialize_with` attribute are not
// deserialized by us. All other fields may need a `T: Deserialize` bound where // deserialized by us so we do not generate a bound. Fields with a `bound`
// T is the type of the field. // attribute specify their own bound so we do not generate one. All other fields
fn deserialized_by_us(field: &ast::StructField) -> bool { // may need a `T: Deserialize` bound where T is the type of the field.
for meta_items in field.attrs.iter().filter_map(attr::get_serde_meta_items) { fn needs_deserialize_bound(attrs: &attr::FieldAttrs) -> bool {
for meta_item in meta_items { !attrs.skip_deserializing_field()
match meta_item.node { && attrs.deserialize_with().is_none()
ast::MetaItemKind::Word(ref name) if name == &"skip_deserializing" => { && attrs.de_bound().is_none()
return false
}
ast::MetaItemKind::NameValue(ref name, _) if name == &"deserialize_with" => {
return false
}
_ => {}
}
}
}
true
} }
// Fields with a `default` attribute (not `default=...`), and fields with a // Fields with a `default` attribute (not `default=...`), and fields with a
// `skip_deserializing` attribute that do not also have `default=...`. // `skip_deserializing` attribute that do not also have `default=...`.
fn requires_default(field: &ast::StructField) -> bool { fn requires_default(attrs: &attr::FieldAttrs) -> bool {
let mut has_skip_deserializing = false; attrs.default() == &attr::FieldDefault::Default
for meta_items in field.attrs.iter().filter_map(attr::get_serde_meta_items) {
for meta_item in meta_items {
match meta_item.node {
ast::MetaItemKind::Word(ref name) if name == &"default" => {
return true
}
ast::MetaItemKind::NameValue(ref name, _) if name == &"default" => {
return false
}
ast::MetaItemKind::Word(ref name) if name == &"skip_deserializing" => {
has_skip_deserializing = true
}
_ => {}
}
}
}
has_skip_deserializing
} }
fn deserialize_body( fn deserialize_body(
@@ -150,9 +152,8 @@ fn deserialize_body(
item: &Item, item: &Item,
impl_generics: &ast::Generics, impl_generics: &ast::Generics,
ty: P<ast::Ty>, ty: P<ast::Ty>,
container_attrs: &attr::ContainerAttrs,
) -> Result<P<ast::Expr>, Error> { ) -> Result<P<ast::Expr>, Error> {
let container_attrs = try!(attr::ContainerAttrs::from_item(cx, item));
match item.node { match item.node {
ast::ItemKind::Struct(ref variant_data, _) => { ast::ItemKind::Struct(ref variant_data, _) => {
deserialize_item_struct( deserialize_item_struct(
@@ -163,7 +164,7 @@ fn deserialize_body(
ty, ty,
item.span, item.span,
variant_data, variant_data,
&container_attrs, container_attrs,
) )
} }
ast::ItemKind::Enum(ref enum_def, _) => { ast::ItemKind::Enum(ref enum_def, _) => {
@@ -174,7 +175,7 @@ fn deserialize_body(
impl_generics, impl_generics,
ty, ty,
enum_def, enum_def,
&container_attrs, container_attrs,
) )
} }
_ => { _ => {
@@ -441,12 +442,12 @@ fn deserialize_seq(
type_ident: Ident, type_ident: Ident,
type_path: ast::Path, type_path: ast::Path,
impl_generics: &ast::Generics, impl_generics: &ast::Generics,
fields: &[(&ast::StructField, attr::FieldAttrs)], fields: &[(ast::StructField, attr::FieldAttrs)],
is_struct: bool, is_struct: bool,
) -> Result<P<ast::Expr>, Error> { ) -> Result<P<ast::Expr>, Error> {
let let_values: Vec<_> = fields.iter() let let_values: Vec<_> = fields.iter()
.enumerate() .enumerate()
.map(|(i, &(field, ref attrs))| { .map(|(i, &(ref field, ref attrs))| {
let name = builder.id(format!("__field{}", i)); let name = builder.id(format!("__field{}", i));
if attrs.skip_deserializing_field() { if attrs.skip_deserializing_field() {
let default = expr_is_missing(cx, attrs); let default = expr_is_missing(cx, attrs);
@@ -486,7 +487,7 @@ fn deserialize_seq(
.with_id_exprs( .with_id_exprs(
fields.iter() fields.iter()
.enumerate() .enumerate()
.map(|(i, &(field, _))| { .map(|(i, &(ref field, _))| {
( (
match field.ident { match field.ident {
Some(name) => name.clone(), Some(name) => name.clone(),
@@ -521,9 +522,9 @@ fn deserialize_newtype_struct(
type_ident: Ident, type_ident: Ident,
type_path: &ast::Path, type_path: &ast::Path,
impl_generics: &ast::Generics, impl_generics: &ast::Generics,
field: &(&ast::StructField, attr::FieldAttrs), field: &(ast::StructField, attr::FieldAttrs),
) -> Result<Vec<ast::TokenTree>, Error> { ) -> Result<Vec<ast::TokenTree>, Error> {
let &(field, ref attrs) = field; let &(ref field, ref attrs) = field;
let value = match attrs.deserialize_with() { let value = match attrs.deserialize_with() {
None => { None => {
let field_ty = &field.ty; let field_ty = &field.ty;
@@ -982,7 +983,7 @@ fn deserialize_struct_visitor(
type_ident: Ident, type_ident: Ident,
struct_path: ast::Path, struct_path: ast::Path,
impl_generics: &ast::Generics, impl_generics: &ast::Generics,
fields: &[(&ast::StructField, attr::FieldAttrs)], fields: &[(ast::StructField, attr::FieldAttrs)],
container_attrs: &attr::ContainerAttrs, container_attrs: &attr::ContainerAttrs,
) -> Result<(Vec<P<ast::Item>>, ast::Stmt, P<ast::Expr>), Error> { ) -> Result<(Vec<P<ast::Item>>, ast::Stmt, P<ast::Expr>), Error> {
let field_exprs = fields.iter() let field_exprs = fields.iter()
@@ -1010,7 +1011,7 @@ fn deserialize_struct_visitor(
let fields_expr = builder.expr().ref_().slice() let fields_expr = builder.expr().ref_().slice()
.with_exprs( .with_exprs(
fields.iter() fields.iter()
.map(|&(field, _)| { .map(|&(ref field, _)| {
match field.ident { match field.ident {
Some(name) => builder.expr().str(name), Some(name) => builder.expr().str(name),
None => { None => {
@@ -1034,7 +1035,7 @@ fn deserialize_map(
type_ident: Ident, type_ident: Ident,
struct_path: ast::Path, struct_path: ast::Path,
impl_generics: &ast::Generics, impl_generics: &ast::Generics,
fields: &[(&ast::StructField, attr::FieldAttrs)], fields: &[(ast::StructField, attr::FieldAttrs)],
container_attrs: &attr::ContainerAttrs, container_attrs: &attr::ContainerAttrs,
) -> Result<P<ast::Expr>, Error> { ) -> Result<P<ast::Expr>, Error> {
// Create the field names for the fields. // Create the field names for the fields.
@@ -1056,7 +1057,7 @@ fn deserialize_map(
// Match arms to extract a value for a field. // Match arms to extract a value for a field.
let value_arms = fields_attrs_names.iter() let value_arms = fields_attrs_names.iter()
.filter(|&&(_, ref attrs, _)| !attrs.skip_deserializing_field()) .filter(|&&(_, ref attrs, _)| !attrs.skip_deserializing_field())
.map(|&(field, ref attrs, name)| { .map(|&(ref field, ref attrs, name)| {
let deser_name = attrs.name().deserialize_name(); let deser_name = attrs.name().deserialize_name();
let name_str = builder.expr().lit().str(deser_name); let name_str = builder.expr().lit().str(deser_name);
@@ -1220,9 +1221,16 @@ fn expr_is_missing(
cx: &ExtCtxt, cx: &ExtCtxt,
attrs: &attr::FieldAttrs, attrs: &attr::FieldAttrs,
) -> P<ast::Expr> { ) -> P<ast::Expr> {
if let Some(expr) = attrs.default_expr_if_missing() { match *attrs.default() {
return expr.clone(); attr::FieldDefault::Default => {
return quote_expr!(cx, ::std::default::Default::default());
}
attr::FieldDefault::Path(ref path) => {
return quote_expr!(cx, $path());
}
attr::FieldDefault::None => { /* below */ }
} }
let name = attrs.name().deserialize_name_expr(); let name = attrs.name().deserialize_name_expr();
match attrs.deserialize_with() { match attrs.deserialize_with() {
None => { None => {
+15
View File
@@ -22,6 +22,11 @@ extern crate syntax;
#[cfg(not(feature = "with-syntex"))] #[cfg(not(feature = "with-syntex"))]
extern crate rustc_plugin; extern crate rustc_plugin;
#[cfg(feature = "with-syntex")]
use std::io;
#[cfg(feature = "with-syntex")]
use std::path::Path;
#[cfg(not(feature = "with-syntex"))] #[cfg(not(feature = "with-syntex"))]
use syntax::feature_gate::AttributeType; use syntax::feature_gate::AttributeType;
@@ -31,6 +36,16 @@ include!(concat!(env!("OUT_DIR"), "/lib.rs"));
#[cfg(not(feature = "with-syntex"))] #[cfg(not(feature = "with-syntex"))]
include!("lib.rs.in"); include!("lib.rs.in");
#[cfg(feature = "with-syntex")]
pub fn expand<S, D>(src: S, dst: D) -> io::Result<()>
where S: AsRef<Path>,
D: AsRef<Path>,
{
let mut registry = syntex::Registry::new();
register(&mut registry);
registry.expand("", src.as_ref(), dst.as_ref())
}
#[cfg(feature = "with-syntex")] #[cfg(feature = "with-syntex")]
pub fn register(reg: &mut syntex::Registry) { pub fn register(reg: &mut syntex::Registry) {
use syntax::{ast, fold}; use syntax::{ast, fold};
+37 -31
View File
@@ -60,7 +60,9 @@ fn serialize_item(
} }
}; };
let impl_generics = build_impl_generics(cx, builder, item, generics); let container_attrs = try!(attr::ContainerAttrs::from_item(cx, item));
let impl_generics = try!(build_impl_generics(cx, builder, item, generics, &container_attrs));
let ty = builder.ty().path() let ty = builder.ty().path()
.segment(item.ident).with_generics(impl_generics.clone()).build() .segment(item.ident).with_generics(impl_generics.clone()).build()
@@ -70,7 +72,8 @@ fn serialize_item(
&builder, &builder,
&item, &item,
&impl_generics, &impl_generics,
ty.clone())); ty.clone(),
&container_attrs));
let where_clause = &impl_generics.where_clause; let where_clause = &impl_generics.where_clause;
@@ -99,32 +102,36 @@ fn build_impl_generics(
builder: &aster::AstBuilder, builder: &aster::AstBuilder,
item: &Item, item: &Item,
generics: &ast::Generics, generics: &ast::Generics,
) -> ast::Generics { container_attrs: &attr::ContainerAttrs,
) -> Result<ast::Generics, Error> {
let generics = bound::without_defaults(generics); let generics = bound::without_defaults(generics);
let generics = bound::with_bound(cx, builder, item, &generics,
&serialized_by_us, let generics = try!(bound::with_where_predicates_from_fields(
&builder.path().ids(&["_serde", "ser", "Serialize"]).build()); cx, builder, item, &generics,
generics |attrs| attrs.ser_bound()));
match container_attrs.ser_bound() {
Some(predicates) => {
let generics = bound::with_where_predicates(builder, &generics, predicates);
Ok(generics)
}
None => {
let generics = try!(bound::with_bound(cx, builder, item, &generics,
needs_serialize_bound,
&builder.path().ids(&["_serde", "ser", "Serialize"]).build()));
Ok(generics)
}
}
} }
// Fields with a `skip_serializing` or `serialize_with` attribute are not // Fields with a `skip_serializing` or `serialize_with` attribute are not
// serialized by us. All other fields may need a `T: Serialize` bound where T is // serialized by us so we do not generate a bound. Fields with a `bound`
// the type of the field. // attribute specify their own bound so we do not generate one. All other fields
fn serialized_by_us(field: &ast::StructField) -> bool { // may need a `T: Serialize` bound where T is the type of the field.
for meta_items in field.attrs.iter().filter_map(attr::get_serde_meta_items) { fn needs_serialize_bound(attrs: &attr::FieldAttrs) -> bool {
for meta_item in meta_items { !attrs.skip_serializing_field()
match meta_item.node { && attrs.serialize_with().is_none()
ast::MetaItemKind::Word(ref name) if name == &"skip_serializing" => { && attrs.ser_bound().is_none()
return false
}
ast::MetaItemKind::NameValue(ref name, _) if name == &"serialize_with" => {
return false
}
_ => {}
}
}
}
true
} }
fn serialize_body( fn serialize_body(
@@ -133,9 +140,8 @@ fn serialize_body(
item: &Item, item: &Item,
impl_generics: &ast::Generics, impl_generics: &ast::Generics,
ty: P<ast::Ty>, ty: P<ast::Ty>,
container_attrs: &attr::ContainerAttrs,
) -> Result<P<ast::Expr>, Error> { ) -> Result<P<ast::Expr>, Error> {
let container_attrs = try!(attr::ContainerAttrs::from_item(cx, item));
match item.node { match item.node {
ast::ItemKind::Struct(ref variant_data, _) => { ast::ItemKind::Struct(ref variant_data, _) => {
serialize_item_struct( serialize_item_struct(
@@ -145,7 +151,7 @@ fn serialize_body(
ty, ty,
item.span, item.span,
variant_data, variant_data,
&container_attrs, container_attrs,
) )
} }
ast::ItemKind::Enum(ref enum_def, _) => { ast::ItemKind::Enum(ref enum_def, _) => {
@@ -156,7 +162,7 @@ fn serialize_body(
impl_generics, impl_generics,
ty, ty,
enum_def, enum_def,
&container_attrs, container_attrs,
) )
} }
_ => { _ => {
@@ -661,7 +667,7 @@ fn serialize_tuple_struct_visitor(
let arms: Vec<_> = fields_with_attrs.iter() let arms: Vec<_> = fields_with_attrs.iter()
.enumerate() .enumerate()
.map(|(i, &(field, ref attrs))| { .map(|(i, &(ref field, ref attrs))| {
let mut field_expr = builder.expr().tup_field(i).field("value").self_(); let mut field_expr = builder.expr().tup_field(i).field("value").self_();
if !is_enum { if !is_enum {
field_expr = quote_expr!(cx, &$field_expr); field_expr = quote_expr!(cx, &$field_expr);
@@ -745,7 +751,7 @@ fn serialize_struct_visitor(
let arms: Vec<ast::Arm> = fields_with_attrs.iter() let arms: Vec<ast::Arm> = fields_with_attrs.iter()
.filter(|&&(_, ref attrs)| !attrs.skip_serializing_field()) .filter(|&&(_, ref attrs)| !attrs.skip_serializing_field())
.enumerate() .enumerate()
.map(|(i, &(field, ref attrs))| { .map(|(i, &(ref field, ref attrs))| {
let ident = field.ident.expect("struct has unnamed field"); let ident = field.ident.expect("struct has unnamed field");
let mut field_expr = quote_expr!(cx, self.value.$ident); let mut field_expr = quote_expr!(cx, self.value.$ident);
if !is_enum { if !is_enum {
@@ -789,7 +795,7 @@ fn serialize_struct_visitor(
let len = fields_with_attrs.iter() let len = fields_with_attrs.iter()
.filter(|&&(_, ref attrs)| !attrs.skip_serializing_field()) .filter(|&&(_, ref attrs)| !attrs.skip_serializing_field())
.map(|&(field, ref attrs)| { .map(|&(ref field, ref attrs)| {
let ident = field.ident.expect("struct has unnamed fields"); let ident = field.ident.expect("struct has unnamed fields");
let mut field_expr = quote_expr!(cx, self.value.$ident); let mut field_expr = quote_expr!(cx, self.value.$ident);
if !is_enum { if !is_enum {
+4 -3
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "serde_macros" name = "serde_macros"
version = "0.7.6" version = "0.7.10"
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"] authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"]
license = "MIT/Apache-2.0" license = "MIT/Apache-2.0"
description = "Macros to auto-generate implementations for the serde framework" description = "Macros to auto-generate implementations for the serde framework"
@@ -18,12 +18,13 @@ nightly-testing = ["clippy", "serde/nightly-testing", "serde_codegen/nightly-tes
[dependencies] [dependencies]
clippy = { version = "^0.*", optional = true } clippy = { version = "^0.*", optional = true }
serde_codegen = { version = "^0.7.6", path = "../serde_codegen", default-features = false, features = ["nightly"] } serde_codegen = { version = "^0.7.10", path = "../serde_codegen", default-features = false, features = ["nightly"] }
[dev-dependencies] [dev-dependencies]
compiletest_rs = "^0.1.1" compiletest_rs = "^0.1.1"
fnv = "1.0"
rustc-serialize = "^0.3.16" rustc-serialize = "^0.3.16"
serde = { version = "^0.7.6", path = "../serde" } serde = { version = "^0.7.10", path = "../serde" }
[[test]] [[test]]
name = "test" name = "test"
+5 -4
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "serde_tests" name = "serde_tests"
version = "0.7.6" version = "0.7.10"
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"] authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"]
license = "MIT/Apache-2.0" license = "MIT/Apache-2.0"
description = "A generic serialization/deserialization framework" description = "A generic serialization/deserialization framework"
@@ -14,14 +14,15 @@ build = "build.rs"
nightly-testing = ["clippy", "serde/nightly-testing", "serde_codegen/nightly-testing"] nightly-testing = ["clippy", "serde/nightly-testing", "serde_codegen/nightly-testing"]
[build-dependencies] [build-dependencies]
syntex = { version = "^0.33.0" } syntex = { version = "^0.35.0" }
syntex_syntax = { version = "^0.33.0" } syntex_syntax = { version = "^0.35.0" }
serde_codegen = { version = "*", path = "../serde_codegen", features = ["with-syntex"] } serde_codegen = { version = "*", path = "../serde_codegen", features = ["with-syntex"] }
[dev-dependencies] [dev-dependencies]
fnv = "1.0"
rustc-serialize = "^0.3.16" rustc-serialize = "^0.3.16"
serde = { version = "*", path = "../serde" } serde = { version = "*", path = "../serde" }
syntex = "^0.33.0" syntex = "^0.35.0"
[dependencies] [dependencies]
clippy = { version = "^0.*", optional = true } clippy = { version = "^0.*", optional = true }
+1 -5
View File
@@ -13,10 +13,6 @@ fn main() {
] { ] {
let src = Path::new(src); let src = Path::new(src);
let dst = Path::new(&out_dir).join(dst); let dst = Path::new(&out_dir).join(dst);
serde_codegen::expand(&src, &dst).unwrap();
let mut registry = syntex::Registry::new();
serde_codegen::register(&mut registry);
registry.expand("", &src, &dst).unwrap();
} }
} }
+16
View File
@@ -62,6 +62,14 @@ macro_rules! hashset {
$(set.insert($value);)+ $(set.insert($value);)+
set set
} }
};
($hasher:ident @ $($value:expr),+) => {
{
use std::hash::BuildHasherDefault;
let mut set = HashSet::with_hasher(BuildHasherDefault::<$hasher>::default());
$(set.insert($value);)+
set
}
} }
} }
@@ -75,5 +83,13 @@ macro_rules! hashmap {
$(map.insert($key, $value);)+ $(map.insert($key, $value);)+
map map
} }
};
($hasher:ident @ $($key:expr => $value:expr),+) => {
{
use std::hash::BuildHasherDefault;
let mut map = HashMap::with_hasher(BuildHasherDefault::<$hasher>::default());
$(map.insert($key, $value);)+
map
}
} }
} }
+2 -1
View File
@@ -441,7 +441,8 @@ enum RenameEnumSerializeDeserialize<A> {
#[serde(rename(serialize="dick_grayson", deserialize="jason_todd"))] #[serde(rename(serialize="dick_grayson", deserialize="jason_todd"))]
Robin { Robin {
a: i8, a: i8,
#[serde(rename(serialize="c", deserialize="d"))] #[serde(rename(serialize="c"))]
#[serde(rename(deserialize="d"))]
b: A, b: A,
}, },
} }
+26
View File
@@ -2,6 +2,9 @@ use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::net; use std::net;
use std::path::PathBuf; use std::path::PathBuf;
extern crate fnv;
use self::fnv::FnvHasher;
use token::{ use token::{
Error, Error,
Token, Token,
@@ -284,6 +287,18 @@ declare_tests! {
Token::TupleStructStart("Anything", Some(0)), Token::TupleStructStart("Anything", Some(0)),
Token::SeqEnd, Token::SeqEnd,
], ],
hashset![FnvHasher @ 1, 2, 3] => vec![
Token::SeqStart(Some(3)),
Token::SeqSep,
Token::I32(1),
Token::SeqSep,
Token::I32(2),
Token::SeqSep,
Token::I32(3),
Token::SeqEnd,
],
} }
test_vec { test_vec {
Vec::<isize>::new() => vec![ Vec::<isize>::new() => vec![
@@ -532,6 +547,17 @@ declare_tests! {
Token::StructStart("Anything", Some(0)), Token::StructStart("Anything", Some(0)),
Token::MapEnd, Token::MapEnd,
], ],
hashmap![FnvHasher @ 1 => 2, 3 => 4] => vec![
Token::MapStart(Some(2)),
Token::MapSep,
Token::I32(1),
Token::I32(2),
Token::MapSep,
Token::I32(3),
Token::I32(4),
Token::MapEnd,
],
} }
test_struct { test_struct {
Struct { a: 1, b: 2, c: 0 } => vec![ Struct { a: 1, b: 2, c: 0 } => vec![
+51
View File
@@ -74,8 +74,59 @@ struct Tuple<T>(
X, X,
); );
#[derive(Serialize, Deserialize)]
enum TreeNode<D> {
Split {
left: Box<TreeNode<D>>,
right: Box<TreeNode<D>>,
},
Leaf {
data: D,
},
}
#[derive(Serialize, Deserialize)]
struct ListNode<D> {
data: D,
next: Box<ListNode<D>>,
}
#[derive(Serialize, Deserialize)]
#[serde(bound="D: SerializeWith + DeserializeWith")]
struct WithTraits1<D, E> {
#[serde(serialize_with="SerializeWith::serialize_with",
deserialize_with="DeserializeWith::deserialize_with")]
d: D,
#[serde(serialize_with="SerializeWith::serialize_with",
deserialize_with="DeserializeWith::deserialize_with",
bound="E: SerializeWith + DeserializeWith")]
e: E,
}
#[derive(Serialize, Deserialize)]
#[serde(bound(serialize="D: SerializeWith",
deserialize="D: DeserializeWith"))]
struct WithTraits2<D, E> {
#[serde(serialize_with="SerializeWith::serialize_with",
deserialize_with="DeserializeWith::deserialize_with")]
d: D,
#[serde(serialize_with="SerializeWith::serialize_with",
bound(serialize="E: SerializeWith"))]
#[serde(deserialize_with="DeserializeWith::deserialize_with",
bound(deserialize="E: DeserializeWith"))]
e: E,
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
trait SerializeWith {
fn serialize_with<S: Serializer>(_: &Self, _: &mut S) -> Result<(), S::Error>;
}
trait DeserializeWith: Sized {
fn deserialize_with<D: Deserializer>(_: &mut D) -> Result<Self, D::Error>;
}
// Implements neither Serialize nor Deserialize // Implements neither Serialize nor Deserialize
struct X; struct X;
fn ser_x<S: Serializer>(_: &X, _: &mut S) -> Result<(), S::Error> { panic!() } fn ser_x<S: Serializer>(_: &X, _: &mut S) -> Result<(), S::Error> { panic!() }
+42 -1
View File
@@ -1,10 +1,13 @@
use std::collections::BTreeMap; use std::collections::{BTreeMap, HashMap, HashSet};
use std::net; use std::net;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use std::str; use std::str;
use token::{self, Token}; use token::{self, Token};
extern crate fnv;
use self::fnv::FnvHasher;
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
#[derive(Serialize)] #[derive(Serialize)]
@@ -144,6 +147,24 @@ declare_ser_tests! {
Token::SeqEnd, Token::SeqEnd,
], ],
} }
test_hashset {
HashSet::<isize>::new() => &[
Token::SeqStart(Some(0)),
Token::SeqEnd,
],
hashset![1] => &[
Token::SeqStart(Some(1)),
Token::SeqSep,
Token::I32(1),
Token::SeqEnd,
],
hashset![FnvHasher @ 1] => &[
Token::SeqStart(Some(1)),
Token::SeqSep,
Token::I32(1),
Token::SeqEnd,
],
}
test_tuple { test_tuple {
(1,) => &[ (1,) => &[
Token::TupleStart(1), Token::TupleStart(1),
@@ -204,6 +225,26 @@ declare_ser_tests! {
Token::MapEnd, Token::MapEnd,
], ],
} }
test_hashmap {
HashMap::<isize, isize>::new() => &[
Token::MapStart(Some(0)),
Token::MapEnd,
],
hashmap![1 => 2] => &[
Token::MapStart(Some(1)),
Token::MapSep,
Token::I32(1),
Token::I32(2),
Token::MapEnd,
],
hashmap![FnvHasher @ 1 => 2] => &[
Token::MapStart(Some(1)),
Token::MapSep,
Token::I32(1),
Token::I32(2),
Token::MapEnd,
],
}
test_unit_struct { test_unit_struct {
UnitStruct => &[Token::UnitStruct("UnitStruct")], UnitStruct => &[Token::UnitStruct("UnitStruct")],
} }