mirror of
https://github.com/pezkuwichain/serde.git
synced 2026-04-25 10:27:56 +00:00
Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8a09f05644 | |||
| 5923a0cd2f | |||
| 1576b5a8a0 | |||
| 2c4dbf5a84 | |||
| 021f4f2d70 | |||
| decc571988 | |||
| 322d7a90db | |||
| dd3f653103 | |||
| 46a1860601 | |||
| 84a573c926 | |||
| 7dfa8f43f4 | |||
| 7375b4e847 | |||
| 48da62ed07 | |||
| 9834af7ed9 | |||
| 6b404d8529 | |||
| 3119cc8857 | |||
| bb059b97c0 | |||
| b7188f7022 | |||
| a64fe99d1b | |||
| c716c4e261 | |||
| 3d2e3beafe | |||
| 1917e54a6e | |||
| 898b346d48 | |||
| e90adb20ef | |||
| a52e7f5554 | |||
| 7afb8b52ae | |||
| cb4694387e | |||
| 58fa302007 | |||
| bf33daf124 | |||
| 4b472be56e | |||
| bdffaf3ea1 | |||
| f197c3ce96 | |||
| 01dfad6705 | |||
| 2e06786262 | |||
| 578f34ecaf | |||
| 2c8767cb46 | |||
| 45c51d3198 | |||
| bd40830905 | |||
| 4e6cd2d63f | |||
| 2256a04926 | |||
| 660ea7bd7b | |||
| 7052833512 | |||
| 5c2cf5778f | |||
| b5c0406afe | |||
| 96cd910c92 |
+5
-2
@@ -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)
|
||||||
|
|||||||
@@ -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
|
|
||||||
@@ -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
|
||||||
========================
|
========================
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
paths = [
|
||||||
|
"../serde",
|
||||||
|
"../serde_codegen",
|
||||||
|
"../serde_macros",
|
||||||
|
]
|
||||||
@@ -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 }
|
||||||
|
|||||||
@@ -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
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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`.
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
@@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 }
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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 => {
|
||||||
|
|||||||
@@ -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
@@ -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 {
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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 }
|
||||||
|
|||||||
@@ -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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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![
|
||||||
|
|||||||
@@ -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!() }
|
||||||
|
|||||||
@@ -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")],
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user