mirror of
https://github.com/pezkuwichain/serde.git
synced 2026-04-25 13:57:55 +00:00
Compare commits
40 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3f9cbc157a | |||
| a51f930101 | |||
| 77edd8e544 | |||
| 8087b7cec6 | |||
| 8df841f048 | |||
| bfa2b69193 | |||
| fbcf905c9f | |||
| 979a4bcd88 | |||
| 342ea25290 | |||
| 8d8f17982a | |||
| dd3233ac85 | |||
| 9b57f60c2a | |||
| 8038832a79 | |||
| 072ff149f5 | |||
| 8f08baf43a | |||
| b3b3b7d565 | |||
| 1a8a11e924 | |||
| f3f098e7f5 | |||
| 09822c99cc | |||
| 966b104d48 | |||
| 59e0d5e081 | |||
| c687ee60ff | |||
| af6fbba9b8 | |||
| a577574cfe | |||
| 7521db7b0b | |||
| 72de877ec3 | |||
| f872b3fb4b | |||
| ddc33ee747 | |||
| 612e384b03 | |||
| 1c88856fc8 | |||
| 534e6d1f4c | |||
| 7ad31a01dd | |||
| 00cd2900e7 | |||
| 05a238fac4 | |||
| 310db21d64 | |||
| 72af0896e8 | |||
| c4392ff256 | |||
| c68ab508c0 | |||
| 76cca814f0 | |||
| 22b69fc5c9 |
@@ -17,8 +17,8 @@ Documentation is available at:
|
|||||||
* [serde\_json](https://serde-rs.github.io/serde/serde_json/serde_json/index.html)
|
* [serde\_json](https://serde-rs.github.io/serde/serde_json/serde_json/index.html)
|
||||||
* [serde\_codegen](https://serde-rs.github.io/serde/serde_codegen/serde_codegen/index.html)
|
* [serde\_codegen](https://serde-rs.github.io/serde/serde_codegen/serde_codegen/index.html)
|
||||||
|
|
||||||
Using Serde
|
Using Serde with Nightly Rust and serde\_macros
|
||||||
===========
|
===============================================
|
||||||
|
|
||||||
Here is a simple example that demonstrates how to use Serde by serializing and
|
Here is a simple example that demonstrates how to use Serde by serializing and
|
||||||
deserializing to JSON. Serde comes with some powerful code generation libraries
|
deserializing to JSON. Serde comes with some powerful code generation libraries
|
||||||
@@ -76,6 +76,9 @@ When run, it produces:
|
|||||||
Point { x: 1, y: 2 }
|
Point { x: 1, y: 2 }
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Using Serde with Stable Rust, syntex, and serde\_codegen
|
||||||
|
========================================================
|
||||||
|
|
||||||
Stable Rust is a little more complicated because it does not yet support
|
Stable Rust is a little more complicated because it does not yet support
|
||||||
compiler plugins. Instead we need to use the code generation library
|
compiler plugins. Instead we need to use the code generation library
|
||||||
[syntex](https://github.com/erickt/rust-syntex) for this:
|
[syntex](https://github.com/erickt/rust-syntex) for this:
|
||||||
@@ -215,6 +218,20 @@ include!(concat!(env!("OUT_DIR"), "/main.rs"));
|
|||||||
|
|
||||||
The `src/main.rs.in` is the same as before.
|
The `src/main.rs.in` is the same as before.
|
||||||
|
|
||||||
|
Then to run with stable:
|
||||||
|
|
||||||
|
```
|
||||||
|
% cargo build
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
Or with nightly:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
% cargo build --features nightly --no-default-features
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
Serialization without Macros
|
Serialization without Macros
|
||||||
============================
|
============================
|
||||||
|
|
||||||
@@ -311,6 +328,8 @@ as a named map. Its visitor uses a simple state machine to iterate through all
|
|||||||
the fields:
|
the fields:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
extern crate serde;
|
||||||
|
|
||||||
struct Point {
|
struct Point {
|
||||||
x: i32,
|
x: i32,
|
||||||
y: i32,
|
y: i32,
|
||||||
@@ -479,6 +498,13 @@ deserializes an enum variant from a string. So for our `Point` example from
|
|||||||
before, we need to generate:
|
before, we need to generate:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
extern crate serde;
|
||||||
|
|
||||||
|
struct Point {
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
enum PointField {
|
enum PointField {
|
||||||
X,
|
X,
|
||||||
Y,
|
Y,
|
||||||
@@ -507,11 +533,7 @@ impl serde::Deserialize for PointField {
|
|||||||
deserializer.visit(PointFieldVisitor)
|
deserializer.visit(PointFieldVisitor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
|
||||||
|
|
||||||
This is then used in our actual deserializer:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
impl serde::Deserialize for Point {
|
impl serde::Deserialize for Point {
|
||||||
fn deserialize<D>(deserializer: &mut D) -> Result<Point, D::Error>
|
fn deserialize<D>(deserializer: &mut D) -> Result<Point, D::Error>
|
||||||
where D: serde::de::Deserializer
|
where D: serde::de::Deserializer
|
||||||
@@ -557,6 +579,21 @@ impl serde::de::Visitor for PointVisitor {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Annotations
|
||||||
|
===========
|
||||||
|
|
||||||
|
`serde_codegen` and `serde_macros` support annotations that help to customize
|
||||||
|
how types are serialized. Here are the supported annotations:
|
||||||
|
|
||||||
|
| Annotation | Function |
|
||||||
|
| ---------- | -------- |
|
||||||
|
| `#[serde(rename(json="name1", xml="name2"))` | Serialize this field with the given name for the given formats |
|
||||||
|
| `#[serde(default)` | If the value is not specified, use the `Default::default()` |
|
||||||
|
| `#[serde(rename="name")` | Serialize this field with the given name |
|
||||||
|
| `#[serde(skip_serializing)` | Do not serialize this value |
|
||||||
|
| `#[serde(skip_serializing_if_empty)` | Do not serialize this value if `$value.is_empty()` is `true` |
|
||||||
|
| `#[serde(skip_serializing_if_none)` | Do not serialize this value if `$value.is_none()` is `true` |
|
||||||
|
|
||||||
Serialization Formats Using Serde
|
Serialization Formats Using Serde
|
||||||
=================================
|
=================================
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
target
|
||||||
|
Cargo.lock
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
[package]
|
||||||
|
name = "serde-syntex-example"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"]
|
||||||
|
build = "build.rs"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["serde_codegen"]
|
||||||
|
nightly = ["serde_macros"]
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
serde_codegen = { version = "^0.6.4", optional = true }
|
||||||
|
syntex = "^0.22.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde = "^0.6.1"
|
||||||
|
serde_json = "^0.6.0"
|
||||||
|
serde_macros = { version = "^0.6.1", optional = true }
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
This example demonstrates how to use Serde with Syntex. On stable or nightly
|
||||||
|
with Syntex, it can be built with:
|
||||||
|
|
||||||
|
```
|
||||||
|
% multirust run stable cargo run
|
||||||
|
Running `target/debug/serde-syntex-example`
|
||||||
|
{"x":1,"y":2}
|
||||||
|
Point { x: 1, y: 2 }
|
||||||
|
|
||||||
|
% multirust run nightly cargo run
|
||||||
|
Running `target/debug/serde-syntex-example`
|
||||||
|
{"x":1,"y":2}
|
||||||
|
Point { x: 1, y: 2 }
|
||||||
|
```
|
||||||
|
|
||||||
|
On nightly, it can use a plugin with:
|
||||||
|
|
||||||
|
```
|
||||||
|
% multirust run nightly cargo run --features nightly --no-default-features
|
||||||
|
```
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
#[cfg(not(feature = "serde_macros"))]
|
||||||
|
mod inner {
|
||||||
|
extern crate syntex;
|
||||||
|
extern crate serde_codegen;
|
||||||
|
|
||||||
|
use std::env;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||||
|
|
||||||
|
let src = Path::new("src/main.rs.in");
|
||||||
|
let dst = Path::new(&out_dir).join("main.rs");
|
||||||
|
|
||||||
|
let mut registry = syntex::Registry::new();
|
||||||
|
|
||||||
|
serde_codegen::register(&mut registry);
|
||||||
|
registry.expand("", &src, &dst).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serde_macros")]
|
||||||
|
mod inner {
|
||||||
|
pub fn main() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
inner::main();
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#![cfg_attr(nightly, feature(custom_derive, plugin))]
|
||||||
|
#![cfg_attr(nightly, plugin(serde_macros))]
|
||||||
|
|
||||||
|
extern crate serde;
|
||||||
|
extern crate serde_json;
|
||||||
|
|
||||||
|
#[cfg(feature = "serde_macros")]
|
||||||
|
include!("main.rs.in");
|
||||||
|
|
||||||
|
#[cfg(not(feature = "serde_macros"))]
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/main.rs"));
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
struct Point {
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let point = Point { x: 1, y: 2 };
|
||||||
|
let serialized = serde_json::to_string(&point).unwrap();
|
||||||
|
|
||||||
|
println!("{}", serialized);
|
||||||
|
|
||||||
|
let deserialized: Point = serde_json::from_str(&serialized).unwrap();
|
||||||
|
|
||||||
|
println!("{:?}", deserialized);
|
||||||
|
}
|
||||||
+9
-5
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "0.6.0"
|
version = "0.6.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"
|
||||||
@@ -9,9 +9,13 @@ documentation = "https://serde-rs.github.io/serde/serde/serde/index.html"
|
|||||||
readme = "../README.md"
|
readme = "../README.md"
|
||||||
keywords = ["serde", "serialization"]
|
keywords = ["serde", "serialization"]
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
num = "*"
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
nightly = []
|
nightly = ["clippy"]
|
||||||
|
num-bigint = ["num/bigint"]
|
||||||
|
num-complex = ["num/complex"]
|
||||||
|
num-impls = ["num-bigint", "num-complex", "num-rational"]
|
||||||
|
num-rational = ["num/rational"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clippy = { version = "^0.0.36", optional = true }
|
||||||
|
num = { version = "^0.1.27", default-features = false }
|
||||||
|
|||||||
+90
-4
@@ -154,7 +154,9 @@ impl<
|
|||||||
fn visit_str<E>(&mut self, v: &str) -> Result<T, E>
|
fn visit_str<E>(&mut self, v: &str) -> Result<T, E>
|
||||||
where E: Error,
|
where E: Error,
|
||||||
{
|
{
|
||||||
str::FromStr::from_str(v.trim()).or(Err(Error::type_mismatch(Type::Str)))
|
str::FromStr::from_str(v.trim()).or_else(|_| {
|
||||||
|
Err(Error::type_mismatch(Type::Str))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,7 +236,7 @@ impl Visitor for StringVisitor {
|
|||||||
fn visit_str<E>(&mut self, v: &str) -> Result<String, E>
|
fn visit_str<E>(&mut self, v: &str) -> Result<String, E>
|
||||||
where E: Error,
|
where E: Error,
|
||||||
{
|
{
|
||||||
Ok(v.to_string())
|
Ok(v.to_owned())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_string<E>(&mut self, v: String) -> Result<String, E>
|
fn visit_string<E>(&mut self, v: String) -> Result<String, E>
|
||||||
@@ -247,12 +249,12 @@ impl Visitor for StringVisitor {
|
|||||||
where E: Error,
|
where E: Error,
|
||||||
{
|
{
|
||||||
match str::from_utf8(v) {
|
match str::from_utf8(v) {
|
||||||
Ok(s) => Ok(s.to_string()),
|
Ok(s) => Ok(s.to_owned()),
|
||||||
Err(_) => Err(Error::type_mismatch(Type::String)),
|
Err(_) => Err(Error::type_mismatch(Type::String)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_byte_buf<'a, E>(&mut self, v: Vec<u8>) -> Result<String, E>
|
fn visit_byte_buf<E>(&mut self, v: Vec<u8>) -> Result<String, E>
|
||||||
where E: Error,
|
where E: Error,
|
||||||
{
|
{
|
||||||
match String::from_utf8(v) {
|
match String::from_utf8(v) {
|
||||||
@@ -895,3 +897,87 @@ impl<T, E> Deserialize for Result<T, E> where T: Deserialize, E: Deserialize {
|
|||||||
deserializer.visit_enum("Result", VARIANTS, Visitor(PhantomData))
|
deserializer.visit_enum("Result", VARIANTS, Visitor(PhantomData))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#[cfg(feature = "num-bigint")]
|
||||||
|
impl Deserialize for ::num::bigint::BigInt {
|
||||||
|
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
|
||||||
|
where D: Deserializer,
|
||||||
|
{
|
||||||
|
use ::num::Num;
|
||||||
|
use ::num::bigint::BigInt;
|
||||||
|
|
||||||
|
struct BigIntVisitor;
|
||||||
|
|
||||||
|
impl Visitor for BigIntVisitor {
|
||||||
|
type Value = BigInt;
|
||||||
|
|
||||||
|
fn visit_str<E>(&mut self, s: &str) -> Result<Self::Value, E>
|
||||||
|
where E: Error,
|
||||||
|
{
|
||||||
|
match BigInt::from_str_radix(s, 10) {
|
||||||
|
Ok(v) => Ok(v),
|
||||||
|
Err(err) => Err(Error::invalid_value(&err.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.visit(BigIntVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "num-bigint")]
|
||||||
|
impl Deserialize for ::num::bigint::BigUint {
|
||||||
|
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
|
||||||
|
where D: Deserializer,
|
||||||
|
{
|
||||||
|
use ::num::Num;
|
||||||
|
use ::num::bigint::BigUint;
|
||||||
|
|
||||||
|
struct BigUintVisitor;
|
||||||
|
|
||||||
|
impl Visitor for BigUintVisitor {
|
||||||
|
type Value = ::num::bigint::BigUint;
|
||||||
|
|
||||||
|
fn visit_str<E>(&mut self, s: &str) -> Result<Self::Value, E>
|
||||||
|
where E: Error,
|
||||||
|
{
|
||||||
|
match BigUint::from_str_radix(s, 10) {
|
||||||
|
Ok(v) => Ok(v),
|
||||||
|
Err(err) => Err(Error::invalid_value(&err.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.visit(BigUintVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "num-complex")]
|
||||||
|
impl<T> Deserialize for ::num::complex::Complex<T>
|
||||||
|
where T: Deserialize + Clone + ::num::Num
|
||||||
|
{
|
||||||
|
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
|
||||||
|
where D: Deserializer,
|
||||||
|
{
|
||||||
|
let (re, im) = try!(Deserialize::deserialize(deserializer));
|
||||||
|
Ok(::num::complex::Complex::new(re, im))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "num-rational")]
|
||||||
|
impl<T> Deserialize for ::num::rational::Ratio<T>
|
||||||
|
where T: Deserialize + Clone + ::num::Integer + PartialOrd
|
||||||
|
{
|
||||||
|
fn deserialize<D>(deserializer: &mut D) -> Result<Self, D::Error>
|
||||||
|
where D: Deserializer,
|
||||||
|
{
|
||||||
|
let (numer, denom) = try!(Deserialize::deserialize(deserializer));
|
||||||
|
if denom == ::num::Zero::zero() {
|
||||||
|
Err(Error::invalid_value("denominator is zero"))
|
||||||
|
} else {
|
||||||
|
Ok(::num::rational::Ratio::new_raw(numer, denom))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -21,6 +21,11 @@ pub trait Error: Sized {
|
|||||||
Error::syntax("incorrect type")
|
Error::syntax("incorrect type")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Raised when a `Deserialize` was passed an incorrect value.
|
||||||
|
fn invalid_value(msg: &str) -> Self {
|
||||||
|
Error::syntax(msg)
|
||||||
|
}
|
||||||
|
|
||||||
/// Raised when a `Deserialize` type unexpectedly hit the end of the stream.
|
/// Raised when a `Deserialize` type unexpectedly hit the end of the stream.
|
||||||
fn end_of_stream() -> Self;
|
fn end_of_stream() -> Self;
|
||||||
|
|
||||||
|
|||||||
+7
-1
@@ -5,9 +5,15 @@
|
|||||||
//! handshake protocol between serializers and serializees can be completely optimized away,
|
//! handshake protocol between serializers and serializees can be completely optimized away,
|
||||||
//! leaving serde to perform roughly the same speed as a hand written serializer for a specific
|
//! leaving serde to perform roughly the same speed as a hand written serializer for a specific
|
||||||
//! type.
|
//! type.
|
||||||
|
//!
|
||||||
|
//! For a detailed tutorial on the different ways to use serde please check out the
|
||||||
|
//! [github repository](https://github.com/serde-rs/serde)
|
||||||
|
|
||||||
#![doc(html_root_url="https://serde-rs.github.io/serde/serde")]
|
#![doc(html_root_url="https://serde-rs.github.io/serde/serde")]
|
||||||
#![cfg_attr(feature = "nightly", feature(collections, core, enumset, nonzero, step_trait, zero_one))]
|
#![cfg_attr(feature = "nightly", feature(collections, enumset, nonzero, plugin, step_trait,
|
||||||
|
zero_one))]
|
||||||
|
#![cfg_attr(feature = "nightly", plugin(clippy))]
|
||||||
|
#![cfg_attr(feature = "nightly", allow(linkedlist))]
|
||||||
|
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
|
|||||||
+36
-2
@@ -566,8 +566,8 @@ impl<K, V, I> MapVisitor for MapIteratorVisitor<I>
|
|||||||
{
|
{
|
||||||
match self.iter.next() {
|
match self.iter.next() {
|
||||||
Some((key, value)) => {
|
Some((key, value)) => {
|
||||||
let value = try!(serializer.visit_map_elt(key, value));
|
try!(serializer.visit_map_elt(key, value));
|
||||||
Ok(Some(value))
|
Ok(Some(()))
|
||||||
}
|
}
|
||||||
None => Ok(None)
|
None => Ok(None)
|
||||||
}
|
}
|
||||||
@@ -700,3 +700,37 @@ impl<T> Serialize for NonZero<T> where T: Serialize + Zeroable {
|
|||||||
(**self).serialize(serializer)
|
(**self).serialize(serializer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#[cfg(feature = "num-bigint")]
|
||||||
|
impl Serialize for ::num::bigint::BigInt {
|
||||||
|
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer {
|
||||||
|
self.to_str_radix(10).serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "num-bigint")]
|
||||||
|
impl Serialize for ::num::bigint::BigUint {
|
||||||
|
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer {
|
||||||
|
self.to_str_radix(10).serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "num-complex")]
|
||||||
|
impl<T> Serialize for ::num::complex::Complex<T>
|
||||||
|
where T: Serialize + Clone + ::num::Num
|
||||||
|
{
|
||||||
|
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer {
|
||||||
|
(&self.re, &self.im).serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "num-rational")]
|
||||||
|
impl<T> Serialize for ::num::rational::Ratio<T>
|
||||||
|
where T: Serialize + Clone + ::num::Integer + PartialOrd
|
||||||
|
{
|
||||||
|
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error> where S: Serializer {
|
||||||
|
(self.numer(), self.denom()).serialize(serializer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -322,6 +322,7 @@ pub trait Serializer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A trait that is used by a `Serialize` to iterate through a sequence.
|
/// A trait that is used by a `Serialize` to iterate through a sequence.
|
||||||
|
#[cfg_attr(feature = "nightly", allow(len_without_is_empty))]
|
||||||
pub trait SeqVisitor {
|
pub trait SeqVisitor {
|
||||||
/// Serializes a sequence item in the serializer.
|
/// Serializes a sequence item in the serializer.
|
||||||
///
|
///
|
||||||
@@ -337,7 +338,8 @@ pub trait SeqVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trait that is used by a `Serializer` to iterate through a map.
|
/// A trait that is used by a `Serialize` to iterate through a map.
|
||||||
|
#[cfg_attr(feature = "nightly", allow(len_without_is_empty))]
|
||||||
pub trait MapVisitor {
|
pub trait MapVisitor {
|
||||||
/// Serializes a map item in the serializer.
|
/// Serializes a map item in the serializer.
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "serde_codegen"
|
name = "serde_codegen"
|
||||||
version = "0.5.3"
|
version = "0.6.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"
|
||||||
@@ -11,16 +11,17 @@ keywords = ["serde", "serialization"]
|
|||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["with-syntex"]
|
default = ["with-syntex"]
|
||||||
nightly = ["quasi_macros"]
|
nightly = ["clippy", "quasi_macros"]
|
||||||
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 = { verision = "*", optional = true }
|
quasi_codegen = { version = "^0.4.0", optional = true }
|
||||||
syntex = { version = "*", optional = true }
|
syntex = { version = "^0.26.0", optional = true }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
aster = { version = "*", default-features = false }
|
aster = { version = "^0.10.0", default-features = false }
|
||||||
quasi = { verision = "*", default-features = false }
|
clippy = { version = "^0.0.36", optional = true }
|
||||||
quasi_macros = { version = "*", optional = true }
|
quasi = { version = "^0.4.0", default-features = false }
|
||||||
syntex = { version = "*", optional = true }
|
quasi_macros = { version = "^0.4.0", optional = true }
|
||||||
syntex_syntax = { version = "*", optional = true }
|
syntex = { version = "^0.26.0", optional = true }
|
||||||
|
syntex_syntax = { version = "^0.26.0", optional = true }
|
||||||
|
|||||||
+241
-44
@@ -2,10 +2,15 @@ use std::collections::HashMap;
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
|
use syntax::attr;
|
||||||
use syntax::ext::base::ExtCtxt;
|
use syntax::ext::base::ExtCtxt;
|
||||||
|
use syntax::print::pprust::meta_item_to_string;
|
||||||
use syntax::ptr::P;
|
use syntax::ptr::P;
|
||||||
|
|
||||||
|
use aster;
|
||||||
|
|
||||||
/// Represents field name information
|
/// Represents field name information
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum FieldNames {
|
pub enum FieldNames {
|
||||||
Global(P<ast::Expr>),
|
Global(P<ast::Expr>),
|
||||||
Format{
|
Format{
|
||||||
@@ -15,47 +20,20 @@ pub enum FieldNames {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Represents field attribute information
|
/// Represents field attribute information
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct FieldAttrs {
|
pub struct FieldAttrs {
|
||||||
skip_serializing_field: bool,
|
skip_serializing_field: bool,
|
||||||
|
skip_serializing_field_if_empty: bool,
|
||||||
|
skip_serializing_field_if_none: bool,
|
||||||
names: FieldNames,
|
names: FieldNames,
|
||||||
use_default: bool,
|
use_default: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FieldAttrs {
|
impl FieldAttrs {
|
||||||
/// Create a FieldAttr with a single default field name
|
|
||||||
pub fn new(
|
|
||||||
skip_serializing_field: bool,
|
|
||||||
default_value: bool,
|
|
||||||
name: P<ast::Expr>,
|
|
||||||
) -> FieldAttrs {
|
|
||||||
FieldAttrs {
|
|
||||||
skip_serializing_field: skip_serializing_field,
|
|
||||||
names: FieldNames::Global(name),
|
|
||||||
use_default: default_value,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a FieldAttr with format specific field names
|
|
||||||
pub fn new_with_formats(
|
|
||||||
skip_serializing_field: bool,
|
|
||||||
default_value: bool,
|
|
||||||
default_name: P<ast::Expr>,
|
|
||||||
formats: HashMap<P<ast::Expr>, P<ast::Expr>>,
|
|
||||||
) -> FieldAttrs {
|
|
||||||
FieldAttrs {
|
|
||||||
skip_serializing_field: skip_serializing_field,
|
|
||||||
names: FieldNames::Format {
|
|
||||||
formats: formats,
|
|
||||||
default: default_name,
|
|
||||||
},
|
|
||||||
use_default: default_value,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Return a set of formats that the field has attributes for.
|
/// Return a set of formats that the field has attributes for.
|
||||||
pub fn formats(&self) -> HashSet<P<ast::Expr>> {
|
pub fn formats(&self) -> HashSet<P<ast::Expr>> {
|
||||||
match self.names {
|
match self.names {
|
||||||
FieldNames::Format{ref formats, default: _} => {
|
FieldNames::Format { ref formats, .. } => {
|
||||||
let mut set = HashSet::new();
|
let mut set = HashSet::new();
|
||||||
for (fmt, _) in formats.iter() {
|
for (fmt, _) in formats.iter() {
|
||||||
set.insert(fmt.clone());
|
set.insert(fmt.clone());
|
||||||
@@ -70,22 +48,21 @@ impl FieldAttrs {
|
|||||||
///
|
///
|
||||||
/// The resulting expression assumes that `S` refers to a type
|
/// The resulting expression assumes that `S` refers to a type
|
||||||
/// that implements `Serializer`.
|
/// that implements `Serializer`.
|
||||||
pub fn serializer_key_expr(self, cx: &ExtCtxt) -> P<ast::Expr> {
|
pub fn serializer_key_expr(&self, cx: &ExtCtxt) -> P<ast::Expr> {
|
||||||
match self.names {
|
match self.names {
|
||||||
FieldNames::Global(x) => x,
|
FieldNames::Global(ref name) => name.clone(),
|
||||||
FieldNames::Format{formats, default} => {
|
FieldNames::Format { ref formats, ref default } => {
|
||||||
let arms = formats.iter()
|
let arms = formats.iter()
|
||||||
.map(|(fmt, lit)| {
|
.map(|(fmt, lit)| {
|
||||||
quote_arm!(cx, $fmt => { $lit })
|
quote_arm!(cx, $fmt => { $lit })
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
quote_expr!(cx,
|
quote_expr!(cx,
|
||||||
{
|
match S::format() {
|
||||||
match S::format() {
|
$arms
|
||||||
$arms
|
_ => { $default }
|
||||||
_ => { $default }
|
}
|
||||||
}
|
)
|
||||||
})
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,17 +71,17 @@ impl FieldAttrs {
|
|||||||
pub fn default_key_expr(&self) -> &P<ast::Expr> {
|
pub fn default_key_expr(&self) -> &P<ast::Expr> {
|
||||||
match self.names {
|
match self.names {
|
||||||
FieldNames::Global(ref expr) => expr,
|
FieldNames::Global(ref expr) => expr,
|
||||||
FieldNames::Format{formats: _, ref default} => default
|
FieldNames::Format { ref default, .. } => default,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the field name for the field in the specified format.
|
/// Return the field name for the field in the specified format.
|
||||||
pub fn key_expr(&self, format: &P<ast::Expr>) -> &P<ast::Expr> {
|
pub fn key_expr(&self, format: &P<ast::Expr>) -> &P<ast::Expr> {
|
||||||
match self.names {
|
match self.names {
|
||||||
FieldNames::Global(ref expr) =>
|
FieldNames::Global(ref expr) => expr,
|
||||||
expr,
|
FieldNames::Format { ref formats, ref default } => {
|
||||||
FieldNames::Format{ref formats, ref default} =>
|
|
||||||
formats.get(format).unwrap_or(default)
|
formats.get(format).unwrap_or(default)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,4 +94,224 @@ impl FieldAttrs {
|
|||||||
pub fn skip_serializing_field(&self) -> bool {
|
pub fn skip_serializing_field(&self) -> bool {
|
||||||
self.skip_serializing_field
|
self.skip_serializing_field
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn skip_serializing_field_if_empty(&self) -> bool {
|
||||||
|
self.skip_serializing_field_if_empty
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn skip_serializing_field_if_none(&self) -> bool {
|
||||||
|
self.skip_serializing_field_if_none
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct FieldAttrsBuilder<'a> {
|
||||||
|
cx: &'a ExtCtxt<'a>,
|
||||||
|
builder: &'a aster::AstBuilder,
|
||||||
|
skip_serializing_field: bool,
|
||||||
|
skip_serializing_field_if_empty: bool,
|
||||||
|
skip_serializing_field_if_none: bool,
|
||||||
|
name: Option<P<ast::Expr>>,
|
||||||
|
format_rename: HashMap<P<ast::Expr>, P<ast::Expr>>,
|
||||||
|
use_default: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> FieldAttrsBuilder<'a> {
|
||||||
|
pub fn new(cx: &'a ExtCtxt<'a>,
|
||||||
|
builder: &'a aster::AstBuilder) -> FieldAttrsBuilder<'a> {
|
||||||
|
FieldAttrsBuilder {
|
||||||
|
cx: cx,
|
||||||
|
builder: builder,
|
||||||
|
skip_serializing_field: false,
|
||||||
|
skip_serializing_field_if_empty: false,
|
||||||
|
skip_serializing_field_if_none: false,
|
||||||
|
name: None,
|
||||||
|
format_rename: HashMap::new(),
|
||||||
|
use_default: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn field(mut self, field: &ast::StructField) -> Result<FieldAttrsBuilder<'a>, ()> {
|
||||||
|
match field.node.kind {
|
||||||
|
ast::NamedField(name, _) => {
|
||||||
|
self.name = Some(self.builder.expr().str(name));
|
||||||
|
}
|
||||||
|
ast::UnnamedField(_) => { }
|
||||||
|
};
|
||||||
|
|
||||||
|
self.attrs(&field.node.attrs)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn attrs(mut self, attrs: &[ast::Attribute]) -> Result<FieldAttrsBuilder<'a>, ()> {
|
||||||
|
for attr in attrs {
|
||||||
|
self = try!(self.attr(attr));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn attr(mut self, attr: &ast::Attribute) -> Result<FieldAttrsBuilder<'a>, ()> {
|
||||||
|
match attr.node.value.node {
|
||||||
|
ast::MetaList(ref name, ref items) if name == &"serde" => {
|
||||||
|
attr::mark_used(&attr);
|
||||||
|
for item in items {
|
||||||
|
self = try!(self.meta_item(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn meta_item(mut self, meta_item: &P<ast::MetaItem>) -> Result<FieldAttrsBuilder<'a>, ()> {
|
||||||
|
match meta_item.node {
|
||||||
|
ast::MetaNameValue(ref name, ref lit) if name == &"rename" => {
|
||||||
|
let expr = self.builder.expr().build_lit(P(lit.clone()));
|
||||||
|
|
||||||
|
Ok(self.name(expr))
|
||||||
|
}
|
||||||
|
ast::MetaList(ref name, ref items) if name == &"rename" => {
|
||||||
|
for item in items {
|
||||||
|
match item.node {
|
||||||
|
ast::MetaNameValue(ref name, ref lit) => {
|
||||||
|
let name = self.builder.expr().str(name);
|
||||||
|
let expr = self.builder.expr().build_lit(P(lit.clone()));
|
||||||
|
|
||||||
|
self = self.format_rename(name, expr);
|
||||||
|
}
|
||||||
|
_ => { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
ast::MetaWord(ref name) if name == &"default" => {
|
||||||
|
Ok(self.default())
|
||||||
|
}
|
||||||
|
ast::MetaWord(ref name) if name == &"skip_serializing" => {
|
||||||
|
Ok(self.skip_serializing_field())
|
||||||
|
}
|
||||||
|
ast::MetaWord(ref name) if name == &"skip_serializing_if_empty" => {
|
||||||
|
Ok(self.skip_serializing_field_if_empty())
|
||||||
|
}
|
||||||
|
ast::MetaWord(ref name) if name == &"skip_serializing_if_none" => {
|
||||||
|
Ok(self.skip_serializing_field_if_none())
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.cx.span_err(
|
||||||
|
meta_item.span,
|
||||||
|
&format!("unknown serde field attribute `{}`",
|
||||||
|
meta_item_to_string(meta_item)));
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn skip_serializing_field(mut self) -> FieldAttrsBuilder<'a> {
|
||||||
|
self.skip_serializing_field = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn skip_serializing_field_if_empty(mut self) -> FieldAttrsBuilder<'a> {
|
||||||
|
self.skip_serializing_field_if_empty = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn skip_serializing_field_if_none(mut self) -> FieldAttrsBuilder<'a> {
|
||||||
|
self.skip_serializing_field_if_none = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(mut self, name: P<ast::Expr>) -> FieldAttrsBuilder<'a> {
|
||||||
|
self.name = Some(name);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn format_rename(mut self, format: P<ast::Expr>, name: P<ast::Expr>) -> FieldAttrsBuilder<'a> {
|
||||||
|
self.format_rename.insert(format, name);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn default(mut self) -> FieldAttrsBuilder<'a> {
|
||||||
|
self.use_default = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> FieldAttrs {
|
||||||
|
let name = self.name.expect("here");
|
||||||
|
let names = if self.format_rename.is_empty() {
|
||||||
|
FieldNames::Global(name)
|
||||||
|
} else {
|
||||||
|
FieldNames::Format {
|
||||||
|
formats: self.format_rename,
|
||||||
|
default: name,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
FieldAttrs {
|
||||||
|
skip_serializing_field: self.skip_serializing_field,
|
||||||
|
skip_serializing_field_if_empty: self.skip_serializing_field_if_empty,
|
||||||
|
skip_serializing_field_if_none: self.skip_serializing_field_if_none,
|
||||||
|
names: names,
|
||||||
|
use_default: self.use_default,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents container (e.g. struct) attribute information
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ContainerAttrs;
|
||||||
|
|
||||||
|
pub struct ContainerAttrsBuilder<'a> {
|
||||||
|
cx: &'a ExtCtxt<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ContainerAttrsBuilder<'a> {
|
||||||
|
pub fn new(cx: &'a ExtCtxt) -> Self {
|
||||||
|
ContainerAttrsBuilder {
|
||||||
|
cx: cx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn attrs(mut self, attrs: &[ast::Attribute]) -> Result<Self, ()> {
|
||||||
|
for attr in attrs {
|
||||||
|
self = try!(self.attr(attr));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn attr(mut self, attr: &ast::Attribute) -> Result<Self, ()> {
|
||||||
|
match attr.node.value.node {
|
||||||
|
ast::MetaList(ref name, ref items) if name == &"serde" => {
|
||||||
|
attr::mark_used(&attr);
|
||||||
|
for item in items {
|
||||||
|
self = try!(self.meta_item(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn meta_item(self, meta_item: &P<ast::MetaItem>) -> Result<Self, ()> {
|
||||||
|
match meta_item.node {
|
||||||
|
_ => {
|
||||||
|
self.cx.span_err(
|
||||||
|
meta_item.span,
|
||||||
|
&format!("unknown serde container attribute `{}`",
|
||||||
|
meta_item_to_string(meta_item)));
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn build(self) -> ContainerAttrs {
|
||||||
|
ContainerAttrs
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+190
-181
@@ -4,17 +4,14 @@ use aster;
|
|||||||
|
|
||||||
use syntax::ast::{
|
use syntax::ast::{
|
||||||
self,
|
self,
|
||||||
Ident,
|
|
||||||
MetaItem,
|
|
||||||
Item,
|
|
||||||
Expr,
|
|
||||||
StructDef,
|
|
||||||
EnumDef,
|
EnumDef,
|
||||||
|
Ident,
|
||||||
|
Item,
|
||||||
|
MetaItem,
|
||||||
};
|
};
|
||||||
use syntax::codemap::Span;
|
use syntax::codemap::Span;
|
||||||
use syntax::ext::base::{Annotatable, ExtCtxt};
|
use syntax::ext::base::{Annotatable, ExtCtxt};
|
||||||
use syntax::ext::build::AstBuilder;
|
use syntax::ext::build::AstBuilder;
|
||||||
use syntax::owned_slice::OwnedSlice;
|
|
||||||
use syntax::ptr::P;
|
use syntax::ptr::P;
|
||||||
|
|
||||||
use attr;
|
use attr;
|
||||||
@@ -32,7 +29,7 @@ pub fn expand_derive_deserialize(
|
|||||||
_ => {
|
_ => {
|
||||||
cx.span_err(
|
cx.span_err(
|
||||||
meta_item.span,
|
meta_item.span,
|
||||||
"`derive` may only be applied to structs and enums");
|
"`#[derive(Deserialize)]` may only be applied to structs and enums");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -42,7 +39,12 @@ pub fn expand_derive_deserialize(
|
|||||||
let generics = match item.node {
|
let generics = match item.node {
|
||||||
ast::ItemStruct(_, ref generics) => generics,
|
ast::ItemStruct(_, ref generics) => generics,
|
||||||
ast::ItemEnum(_, ref generics) => generics,
|
ast::ItemEnum(_, ref generics) => generics,
|
||||||
_ => cx.bug("expected ItemStruct or ItemEnum in #[derive(Deserialize)]")
|
_ => {
|
||||||
|
cx.span_err(
|
||||||
|
meta_item.span,
|
||||||
|
"`#[derive(Deserialize)]` may only be applied to structs and enums");
|
||||||
|
return;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let impl_generics = builder.from_generics(generics.clone())
|
let impl_generics = builder.from_generics(generics.clone())
|
||||||
@@ -55,18 +57,17 @@ pub fn expand_derive_deserialize(
|
|||||||
.segment(item.ident).with_generics(impl_generics.clone()).build()
|
.segment(item.ident).with_generics(impl_generics.clone()).build()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let body = deserialize_body(
|
let body = match deserialize_body(cx, &builder, &item, &impl_generics, ty.clone()) {
|
||||||
cx,
|
Ok(body) => body,
|
||||||
&builder,
|
Err(()) => {
|
||||||
&item,
|
// An error occured, but it should have been reported already.
|
||||||
&impl_generics,
|
return;
|
||||||
ty.clone(),
|
}
|
||||||
);
|
};
|
||||||
|
|
||||||
let where_clause = &impl_generics.where_clause;
|
let where_clause = &impl_generics.where_clause;
|
||||||
|
|
||||||
let impl_item = quote_item!(cx,
|
let impl_item = quote_item!(cx,
|
||||||
#[automatically_derived]
|
|
||||||
impl $impl_generics ::serde::de::Deserialize for $ty $where_clause {
|
impl $impl_generics ::serde::de::Deserialize for $ty $where_clause {
|
||||||
fn deserialize<__D>(deserializer: &mut __D) -> ::std::result::Result<$ty, __D::Error>
|
fn deserialize<__D>(deserializer: &mut __D) -> ::std::result::Result<$ty, __D::Error>
|
||||||
where __D: ::serde::de::Deserializer,
|
where __D: ::serde::de::Deserializer,
|
||||||
@@ -85,16 +86,21 @@ fn deserialize_body(
|
|||||||
item: &Item,
|
item: &Item,
|
||||||
impl_generics: &ast::Generics,
|
impl_generics: &ast::Generics,
|
||||||
ty: P<ast::Ty>,
|
ty: P<ast::Ty>,
|
||||||
) -> P<ast::Expr> {
|
) -> Result<P<ast::Expr>, ()> {
|
||||||
|
// Note: While we don't have any container attributes, we still want to try to
|
||||||
|
// parse them so we can report a proper error if we get passed an unknown attribute.
|
||||||
|
let _ = try!(field::container_attrs(cx, item));
|
||||||
|
|
||||||
match item.node {
|
match item.node {
|
||||||
ast::ItemStruct(ref struct_def, _) => {
|
ast::ItemStruct(ref variant_data, _) => {
|
||||||
deserialize_item_struct(
|
deserialize_item_struct(
|
||||||
cx,
|
cx,
|
||||||
builder,
|
builder,
|
||||||
item,
|
item,
|
||||||
impl_generics,
|
impl_generics,
|
||||||
ty,
|
ty,
|
||||||
struct_def,
|
item.span,
|
||||||
|
variant_data,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
ast::ItemEnum(ref enum_def, _) => {
|
ast::ItemEnum(ref enum_def, _) => {
|
||||||
@@ -107,7 +113,10 @@ fn deserialize_body(
|
|||||||
enum_def,
|
enum_def,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
_ => cx.bug("expected ItemStruct or ItemEnum in #[derive(Deserialize)]")
|
_ => {
|
||||||
|
cx.span_bug(item.span,
|
||||||
|
"expected ItemStruct or ItemEnum in #[derive(Deserialize)]")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,27 +126,18 @@ fn deserialize_item_struct(
|
|||||||
item: &Item,
|
item: &Item,
|
||||||
impl_generics: &ast::Generics,
|
impl_generics: &ast::Generics,
|
||||||
ty: P<ast::Ty>,
|
ty: P<ast::Ty>,
|
||||||
struct_def: &ast::StructDef,
|
span: Span,
|
||||||
) -> P<ast::Expr> {
|
variant_data: &ast::VariantData,
|
||||||
let mut named_fields = vec![];
|
) -> Result<P<ast::Expr>, ()> {
|
||||||
let mut unnamed_fields = 0;
|
match *variant_data {
|
||||||
|
ast::VariantData::Unit(_) => {
|
||||||
for field in struct_def.fields.iter() {
|
|
||||||
match field.node.kind {
|
|
||||||
ast::NamedField(name, _) => { named_fields.push(name); }
|
|
||||||
ast::UnnamedField(_) => { unnamed_fields += 1; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match (named_fields.is_empty(), unnamed_fields) {
|
|
||||||
(true, 0) => {
|
|
||||||
deserialize_unit_struct(
|
deserialize_unit_struct(
|
||||||
cx,
|
cx,
|
||||||
&builder,
|
&builder,
|
||||||
item.ident,
|
item.ident,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
(true, 1) => {
|
ast::VariantData::Tuple(ref fields, _) if fields.len() == 1 => {
|
||||||
deserialize_newtype_struct(
|
deserialize_newtype_struct(
|
||||||
cx,
|
cx,
|
||||||
&builder,
|
&builder,
|
||||||
@@ -146,29 +146,34 @@ fn deserialize_item_struct(
|
|||||||
ty,
|
ty,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
(true, _) => {
|
ast::VariantData::Tuple(ref fields, _) => {
|
||||||
|
if fields.iter().any(|field| !field.node.kind.is_unnamed()) {
|
||||||
|
cx.span_bug(span, "tuple struct has named fields")
|
||||||
|
}
|
||||||
|
|
||||||
deserialize_tuple_struct(
|
deserialize_tuple_struct(
|
||||||
cx,
|
cx,
|
||||||
&builder,
|
&builder,
|
||||||
item.ident,
|
item.ident,
|
||||||
impl_generics,
|
impl_generics,
|
||||||
ty,
|
ty,
|
||||||
unnamed_fields,
|
fields.len(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
(false, 0) => {
|
ast::VariantData::Struct(ref fields, _) => {
|
||||||
|
if fields.iter().any(|field| field.node.kind.is_unnamed()) {
|
||||||
|
cx.span_bug(span, "struct has unnamed fields")
|
||||||
|
}
|
||||||
|
|
||||||
deserialize_struct(
|
deserialize_struct(
|
||||||
cx,
|
cx,
|
||||||
&builder,
|
&builder,
|
||||||
item.ident,
|
item.ident,
|
||||||
impl_generics,
|
impl_generics,
|
||||||
ty,
|
ty,
|
||||||
struct_def,
|
fields,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
(false, _) => {
|
|
||||||
cx.bug("struct has named and unnamed fields")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,14 +184,14 @@ fn deserialize_visitor(
|
|||||||
trait_generics: &ast::Generics,
|
trait_generics: &ast::Generics,
|
||||||
forward_ty_params: Vec<ast::TyParam>,
|
forward_ty_params: Vec<ast::TyParam>,
|
||||||
forward_tys: Vec<P<ast::Ty>>
|
forward_tys: Vec<P<ast::Ty>>
|
||||||
) -> (P<ast::Item>, P<ast::Ty>, P<ast::Expr>, ast::Generics) {
|
) -> Result<(P<ast::Item>, P<ast::Ty>, P<ast::Expr>, ast::Generics), ()> {
|
||||||
if trait_generics.ty_params.is_empty() && forward_tys.is_empty() {
|
if trait_generics.ty_params.is_empty() && forward_tys.is_empty() {
|
||||||
(
|
Ok((
|
||||||
builder.item().tuple_struct("__Visitor").build(),
|
builder.item().tuple_struct("__Visitor").build(),
|
||||||
builder.ty().id("__Visitor"),
|
builder.ty().id("__Visitor"),
|
||||||
builder.expr().id("__Visitor"),
|
builder.expr().id("__Visitor"),
|
||||||
trait_generics.clone(),
|
trait_generics.clone(),
|
||||||
)
|
))
|
||||||
} else {
|
} else {
|
||||||
let placeholders : Vec<_> = trait_generics.ty_params.iter()
|
let placeholders : Vec<_> = trait_generics.ty_params.iter()
|
||||||
.map(|t| builder.ty().id(t.ident))
|
.map(|t| builder.ty().id(t.ident))
|
||||||
@@ -194,9 +199,9 @@ fn deserialize_visitor(
|
|||||||
let mut trait_generics = trait_generics.clone();
|
let mut trait_generics = trait_generics.clone();
|
||||||
let mut ty_params = forward_ty_params.clone();
|
let mut ty_params = forward_ty_params.clone();
|
||||||
ty_params.extend(trait_generics.ty_params.into_vec());
|
ty_params.extend(trait_generics.ty_params.into_vec());
|
||||||
trait_generics.ty_params = OwnedSlice::from_vec(ty_params);
|
trait_generics.ty_params = P::from_vec(ty_params);
|
||||||
|
|
||||||
(
|
Ok((
|
||||||
builder.item().tuple_struct("__Visitor")
|
builder.item().tuple_struct("__Visitor")
|
||||||
.generics().with(trait_generics.clone()).build()
|
.generics().with(trait_generics.clone()).build()
|
||||||
.with_tys({
|
.with_tys({
|
||||||
@@ -234,7 +239,7 @@ fn deserialize_visitor(
|
|||||||
})
|
})
|
||||||
.build(),
|
.build(),
|
||||||
trait_generics,
|
trait_generics,
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -258,10 +263,10 @@ fn deserialize_unit_struct(
|
|||||||
cx: &ExtCtxt,
|
cx: &ExtCtxt,
|
||||||
builder: &aster::AstBuilder,
|
builder: &aster::AstBuilder,
|
||||||
type_ident: Ident,
|
type_ident: Ident,
|
||||||
) -> P<ast::Expr> {
|
) -> Result<P<ast::Expr>, ()> {
|
||||||
let type_name = builder.expr().str(type_ident);
|
let type_name = builder.expr().str(type_ident);
|
||||||
|
|
||||||
quote_expr!(cx, {
|
Ok(quote_expr!(cx, {
|
||||||
struct __Visitor;
|
struct __Visitor;
|
||||||
|
|
||||||
impl ::serde::de::Visitor for __Visitor {
|
impl ::serde::de::Visitor for __Visitor {
|
||||||
@@ -284,7 +289,7 @@ fn deserialize_unit_struct(
|
|||||||
}
|
}
|
||||||
|
|
||||||
deserializer.visit_unit_struct($type_name, __Visitor)
|
deserializer.visit_unit_struct($type_name, __Visitor)
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_newtype_struct(
|
fn deserialize_newtype_struct(
|
||||||
@@ -293,16 +298,15 @@ fn deserialize_newtype_struct(
|
|||||||
type_ident: Ident,
|
type_ident: Ident,
|
||||||
impl_generics: &ast::Generics,
|
impl_generics: &ast::Generics,
|
||||||
ty: P<ast::Ty>,
|
ty: P<ast::Ty>,
|
||||||
) -> P<ast::Expr> {
|
) -> Result<P<ast::Expr>, ()> {
|
||||||
let where_clause = &impl_generics.where_clause;
|
let where_clause = &impl_generics.where_clause;
|
||||||
|
|
||||||
let (visitor_item, visitor_ty, visitor_expr, visitor_generics) =
|
let (visitor_item, visitor_ty, visitor_expr, visitor_generics) = try!(deserialize_visitor(
|
||||||
deserialize_visitor(
|
builder,
|
||||||
builder,
|
impl_generics,
|
||||||
impl_generics,
|
vec![deserializer_ty_param(builder)],
|
||||||
vec![deserializer_ty_param(builder)],
|
vec![deserializer_ty_arg(builder)],
|
||||||
vec![deserializer_ty_arg(builder)],
|
));
|
||||||
);
|
|
||||||
|
|
||||||
let visit_seq_expr = deserialize_seq(
|
let visit_seq_expr = deserialize_seq(
|
||||||
cx,
|
cx,
|
||||||
@@ -313,7 +317,7 @@ fn deserialize_newtype_struct(
|
|||||||
|
|
||||||
let type_name = builder.expr().str(type_ident);
|
let type_name = builder.expr().str(type_ident);
|
||||||
|
|
||||||
quote_expr!(cx, {
|
Ok(quote_expr!(cx, {
|
||||||
$visitor_item
|
$visitor_item
|
||||||
|
|
||||||
impl $visitor_generics ::serde::de::Visitor for $visitor_ty $where_clause {
|
impl $visitor_generics ::serde::de::Visitor for $visitor_ty $where_clause {
|
||||||
@@ -336,7 +340,7 @@ fn deserialize_newtype_struct(
|
|||||||
}
|
}
|
||||||
|
|
||||||
deserializer.visit_newtype_struct($type_name, $visitor_expr)
|
deserializer.visit_newtype_struct($type_name, $visitor_expr)
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_tuple_struct(
|
fn deserialize_tuple_struct(
|
||||||
@@ -346,16 +350,15 @@ fn deserialize_tuple_struct(
|
|||||||
impl_generics: &ast::Generics,
|
impl_generics: &ast::Generics,
|
||||||
ty: P<ast::Ty>,
|
ty: P<ast::Ty>,
|
||||||
fields: usize,
|
fields: usize,
|
||||||
) -> P<ast::Expr> {
|
) -> Result<P<ast::Expr>, ()> {
|
||||||
let where_clause = &impl_generics.where_clause;
|
let where_clause = &impl_generics.where_clause;
|
||||||
|
|
||||||
let (visitor_item, visitor_ty, visitor_expr, visitor_generics) =
|
let (visitor_item, visitor_ty, visitor_expr, visitor_generics) = try!(deserialize_visitor(
|
||||||
deserialize_visitor(
|
builder,
|
||||||
builder,
|
impl_generics,
|
||||||
impl_generics,
|
vec![deserializer_ty_param(builder)],
|
||||||
vec![deserializer_ty_param(builder)],
|
vec![deserializer_ty_arg(builder)],
|
||||||
vec![deserializer_ty_arg(builder)],
|
));
|
||||||
);
|
|
||||||
|
|
||||||
let visit_seq_expr = deserialize_seq(
|
let visit_seq_expr = deserialize_seq(
|
||||||
cx,
|
cx,
|
||||||
@@ -366,7 +369,7 @@ fn deserialize_tuple_struct(
|
|||||||
|
|
||||||
let type_name = builder.expr().str(type_ident);
|
let type_name = builder.expr().str(type_ident);
|
||||||
|
|
||||||
quote_expr!(cx, {
|
Ok(quote_expr!(cx, {
|
||||||
$visitor_item
|
$visitor_item
|
||||||
|
|
||||||
impl $visitor_generics ::serde::de::Visitor for $visitor_ty $where_clause {
|
impl $visitor_generics ::serde::de::Visitor for $visitor_ty $where_clause {
|
||||||
@@ -381,7 +384,7 @@ fn deserialize_tuple_struct(
|
|||||||
}
|
}
|
||||||
|
|
||||||
deserializer.visit_tuple_struct($type_name, $fields, $visitor_expr)
|
deserializer.visit_tuple_struct($type_name, $fields, $visitor_expr)
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_seq(
|
fn deserialize_seq(
|
||||||
@@ -422,9 +425,9 @@ fn deserialize_struct_as_seq(
|
|||||||
cx: &ExtCtxt,
|
cx: &ExtCtxt,
|
||||||
builder: &aster::AstBuilder,
|
builder: &aster::AstBuilder,
|
||||||
struct_path: ast::Path,
|
struct_path: ast::Path,
|
||||||
struct_def: &StructDef,
|
fields: &[ast::StructField],
|
||||||
) -> P<ast::Expr> {
|
) -> Result<P<ast::Expr>, ()> {
|
||||||
let let_values: Vec<P<ast::Stmt>> = (0 .. struct_def.fields.len())
|
let let_values: Vec<P<ast::Stmt>> = (0 .. fields.len())
|
||||||
.map(|i| {
|
.map(|i| {
|
||||||
let name = builder.id(format!("__field{}", i));
|
let name = builder.id(format!("__field{}", i));
|
||||||
quote_stmt!(cx,
|
quote_stmt!(cx,
|
||||||
@@ -440,13 +443,15 @@ fn deserialize_struct_as_seq(
|
|||||||
|
|
||||||
let result = builder.expr().struct_path(struct_path)
|
let result = builder.expr().struct_path(struct_path)
|
||||||
.with_id_exprs(
|
.with_id_exprs(
|
||||||
struct_def.fields.iter()
|
fields.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, field)| {
|
.map(|(i, field)| {
|
||||||
(
|
(
|
||||||
match field.node.kind {
|
match field.node.kind {
|
||||||
ast::NamedField(name, _) => name.clone(),
|
ast::NamedField(name, _) => name.clone(),
|
||||||
ast::UnnamedField(_) => panic!("struct contains unnamed fields"),
|
ast::UnnamedField(_) => {
|
||||||
|
cx.span_bug(field.span, "struct contains unnamed fields")
|
||||||
|
}
|
||||||
},
|
},
|
||||||
builder.expr().id(format!("__field{}", i)),
|
builder.expr().id(format!("__field{}", i)),
|
||||||
)
|
)
|
||||||
@@ -454,13 +459,13 @@ fn deserialize_struct_as_seq(
|
|||||||
)
|
)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
quote_expr!(cx, {
|
Ok(quote_expr!(cx, {
|
||||||
$let_values
|
$let_values
|
||||||
|
|
||||||
try!(visitor.end());
|
try!(visitor.end());
|
||||||
|
|
||||||
Ok($result)
|
Ok($result)
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_struct(
|
fn deserialize_struct(
|
||||||
@@ -469,36 +474,36 @@ fn deserialize_struct(
|
|||||||
type_ident: Ident,
|
type_ident: Ident,
|
||||||
impl_generics: &ast::Generics,
|
impl_generics: &ast::Generics,
|
||||||
ty: P<ast::Ty>,
|
ty: P<ast::Ty>,
|
||||||
struct_def: &StructDef,
|
fields: &[ast::StructField],
|
||||||
) -> P<ast::Expr> {
|
) -> Result<P<ast::Expr>, ()> {
|
||||||
let where_clause = &impl_generics.where_clause;
|
let where_clause = &impl_generics.where_clause;
|
||||||
|
|
||||||
let (visitor_item, visitor_ty, visitor_expr, visitor_generics) = deserialize_visitor(
|
let (visitor_item, visitor_ty, visitor_expr, visitor_generics) = try!(deserialize_visitor(
|
||||||
builder,
|
builder,
|
||||||
&impl_generics,
|
&impl_generics,
|
||||||
vec![deserializer_ty_param(builder)],
|
vec![deserializer_ty_param(builder)],
|
||||||
vec![deserializer_ty_arg(builder)],
|
vec![deserializer_ty_arg(builder)],
|
||||||
);
|
));
|
||||||
|
|
||||||
let type_path = builder.path().id(type_ident).build();
|
let type_path = builder.path().id(type_ident).build();
|
||||||
|
|
||||||
let visit_seq_expr = deserialize_struct_as_seq(
|
let visit_seq_expr = try!(deserialize_struct_as_seq(
|
||||||
cx,
|
cx,
|
||||||
builder,
|
builder,
|
||||||
type_path.clone(),
|
type_path.clone(),
|
||||||
struct_def
|
fields,
|
||||||
);
|
));
|
||||||
|
|
||||||
let (field_visitor, fields_stmt, visit_map_expr) = deserialize_struct_visitor(
|
let (field_visitor, fields_stmt, visit_map_expr) = try!(deserialize_struct_visitor(
|
||||||
cx,
|
cx,
|
||||||
builder,
|
builder,
|
||||||
struct_def,
|
type_path.clone(),
|
||||||
type_path.clone()
|
fields,
|
||||||
);
|
));
|
||||||
|
|
||||||
let type_name = builder.expr().str(type_ident);
|
let type_name = builder.expr().str(type_ident);
|
||||||
|
|
||||||
quote_expr!(cx, {
|
Ok(quote_expr!(cx, {
|
||||||
$field_visitor
|
$field_visitor
|
||||||
|
|
||||||
$visitor_item
|
$visitor_item
|
||||||
@@ -524,7 +529,7 @@ fn deserialize_struct(
|
|||||||
$fields_stmt
|
$fields_stmt
|
||||||
|
|
||||||
deserializer.visit_struct($type_name, FIELDS, $visitor_expr)
|
deserializer.visit_struct($type_name, FIELDS, $visitor_expr)
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_item_enum(
|
fn deserialize_item_enum(
|
||||||
@@ -534,7 +539,7 @@ fn deserialize_item_enum(
|
|||||||
impl_generics: &ast::Generics,
|
impl_generics: &ast::Generics,
|
||||||
ty: P<ast::Ty>,
|
ty: P<ast::Ty>,
|
||||||
enum_def: &EnumDef,
|
enum_def: &EnumDef,
|
||||||
) -> P<ast::Expr> {
|
) -> Result<P<ast::Expr>, ()> {
|
||||||
let where_clause = &impl_generics.where_clause;
|
let where_clause = &impl_generics.where_clause;
|
||||||
|
|
||||||
let type_name = builder.expr().str(type_ident);
|
let type_name = builder.expr().str(type_ident);
|
||||||
@@ -543,11 +548,13 @@ fn deserialize_item_enum(
|
|||||||
cx,
|
cx,
|
||||||
builder,
|
builder,
|
||||||
enum_def.variants.iter()
|
enum_def.variants.iter()
|
||||||
.map(|variant|
|
.map(|variant| {
|
||||||
attr::FieldAttrs::new(
|
let expr = builder.expr().str(variant.node.name);
|
||||||
false,
|
attr::FieldAttrsBuilder::new(cx, builder)
|
||||||
true,
|
.name(expr)
|
||||||
builder.expr().str(variant.node.name)))
|
.default()
|
||||||
|
.build()
|
||||||
|
})
|
||||||
.collect()
|
.collect()
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -565,35 +572,33 @@ fn deserialize_item_enum(
|
|||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
// Match arms to extract a variant from a string
|
// Match arms to extract a variant from a string
|
||||||
let variant_arms: Vec<_> = enum_def.variants.iter()
|
let mut variant_arms = vec![];
|
||||||
.enumerate()
|
for (i, variant) in enum_def.variants.iter().enumerate() {
|
||||||
.map(|(i, variant)| {
|
let variant_name = builder.pat().enum_()
|
||||||
let variant_name = builder.pat().enum_()
|
.id("__Field").id(format!("__field{}", i)).build()
|
||||||
.id("__Field").id(format!("__field{}", i)).build()
|
.build();
|
||||||
.build();
|
|
||||||
|
|
||||||
let expr = deserialize_variant(
|
let expr = try!(deserialize_variant(
|
||||||
cx,
|
cx,
|
||||||
builder,
|
|
||||||
type_ident,
|
|
||||||
impl_generics,
|
|
||||||
ty.clone(),
|
|
||||||
variant,
|
|
||||||
);
|
|
||||||
|
|
||||||
quote_arm!(cx, $variant_name => { $expr })
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let (visitor_item, visitor_ty, visitor_expr, visitor_generics) =
|
|
||||||
deserialize_visitor(
|
|
||||||
builder,
|
builder,
|
||||||
|
type_ident,
|
||||||
impl_generics,
|
impl_generics,
|
||||||
vec![deserializer_ty_param(builder)],
|
ty.clone(),
|
||||||
vec![deserializer_ty_arg(builder)],
|
variant,
|
||||||
);
|
));
|
||||||
|
|
||||||
quote_expr!(cx, {
|
let arm = quote_arm!(cx, $variant_name => { $expr });
|
||||||
|
variant_arms.push(arm);
|
||||||
|
}
|
||||||
|
|
||||||
|
let (visitor_item, visitor_ty, visitor_expr, visitor_generics) = try!(deserialize_visitor(
|
||||||
|
builder,
|
||||||
|
impl_generics,
|
||||||
|
vec![deserializer_ty_param(builder)],
|
||||||
|
vec![deserializer_ty_arg(builder)],
|
||||||
|
));
|
||||||
|
|
||||||
|
Ok(quote_expr!(cx, {
|
||||||
$variant_visitor
|
$variant_visitor
|
||||||
|
|
||||||
$visitor_item
|
$visitor_item
|
||||||
@@ -613,7 +618,7 @@ fn deserialize_item_enum(
|
|||||||
$variants_stmt
|
$variants_stmt
|
||||||
|
|
||||||
deserializer.visit_enum($type_name, VARIANTS, $visitor_expr)
|
deserializer.visit_enum($type_name, VARIANTS, $visitor_expr)
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_variant(
|
fn deserialize_variant(
|
||||||
@@ -623,23 +628,23 @@ fn deserialize_variant(
|
|||||||
generics: &ast::Generics,
|
generics: &ast::Generics,
|
||||||
ty: P<ast::Ty>,
|
ty: P<ast::Ty>,
|
||||||
variant: &ast::Variant,
|
variant: &ast::Variant,
|
||||||
) -> P<ast::Expr> {
|
) -> Result<P<ast::Expr>, ()> {
|
||||||
let variant_ident = variant.node.name;
|
let variant_ident = variant.node.name;
|
||||||
|
|
||||||
match variant.node.kind {
|
match variant.node.data {
|
||||||
ast::TupleVariantKind(ref args) if args.is_empty() => {
|
ast::VariantData::Unit(_) => {
|
||||||
quote_expr!(cx, {
|
Ok(quote_expr!(cx, {
|
||||||
try!(visitor.visit_unit());
|
try!(visitor.visit_unit());
|
||||||
Ok($type_ident::$variant_ident)
|
Ok($type_ident::$variant_ident)
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
ast::TupleVariantKind(ref args) if args.len() == 1 => {
|
ast::VariantData::Tuple(ref args, _) if args.len() == 1 => {
|
||||||
quote_expr!(cx, {
|
Ok(quote_expr!(cx, {
|
||||||
let val = try!(visitor.visit_newtype());
|
let val = try!(visitor.visit_newtype());
|
||||||
Ok($type_ident::$variant_ident(val))
|
Ok($type_ident::$variant_ident(val))
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
ast::TupleVariantKind(ref args) => {
|
ast::VariantData::Tuple(ref fields, _) => {
|
||||||
deserialize_tuple_variant(
|
deserialize_tuple_variant(
|
||||||
cx,
|
cx,
|
||||||
builder,
|
builder,
|
||||||
@@ -647,10 +652,10 @@ fn deserialize_variant(
|
|||||||
variant_ident,
|
variant_ident,
|
||||||
generics,
|
generics,
|
||||||
ty,
|
ty,
|
||||||
args.len(),
|
fields.len(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
ast::StructVariantKind(ref struct_def) => {
|
ast::VariantData::Struct(ref fields, _) => {
|
||||||
deserialize_struct_variant(
|
deserialize_struct_variant(
|
||||||
cx,
|
cx,
|
||||||
builder,
|
builder,
|
||||||
@@ -658,7 +663,7 @@ fn deserialize_variant(
|
|||||||
variant_ident,
|
variant_ident,
|
||||||
generics,
|
generics,
|
||||||
ty,
|
ty,
|
||||||
struct_def,
|
fields,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -672,16 +677,15 @@ fn deserialize_tuple_variant(
|
|||||||
generics: &ast::Generics,
|
generics: &ast::Generics,
|
||||||
ty: P<ast::Ty>,
|
ty: P<ast::Ty>,
|
||||||
fields: usize,
|
fields: usize,
|
||||||
) -> P<ast::Expr> {
|
) -> Result<P<ast::Expr>, ()> {
|
||||||
let where_clause = &generics.where_clause;
|
let where_clause = &generics.where_clause;
|
||||||
|
|
||||||
let (visitor_item, visitor_ty, visitor_expr, visitor_generics) =
|
let (visitor_item, visitor_ty, visitor_expr, visitor_generics) = try!(deserialize_visitor(
|
||||||
deserialize_visitor(
|
builder,
|
||||||
builder,
|
generics,
|
||||||
generics,
|
vec![deserializer_ty_param(builder)],
|
||||||
vec![deserializer_ty_param(builder)],
|
vec![deserializer_ty_arg(builder)],
|
||||||
vec![deserializer_ty_arg(builder)],
|
));
|
||||||
);
|
|
||||||
|
|
||||||
let visit_seq_expr = deserialize_seq(
|
let visit_seq_expr = deserialize_seq(
|
||||||
cx,
|
cx,
|
||||||
@@ -690,7 +694,7 @@ fn deserialize_tuple_variant(
|
|||||||
fields,
|
fields,
|
||||||
);
|
);
|
||||||
|
|
||||||
quote_expr!(cx, {
|
Ok(quote_expr!(cx, {
|
||||||
$visitor_item
|
$visitor_item
|
||||||
|
|
||||||
impl $visitor_generics ::serde::de::Visitor for $visitor_ty $where_clause {
|
impl $visitor_generics ::serde::de::Visitor for $visitor_ty $where_clause {
|
||||||
@@ -704,7 +708,7 @@ fn deserialize_tuple_variant(
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitor.visit_tuple($fields, $visitor_expr)
|
visitor.visit_tuple($fields, $visitor_expr)
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_struct_variant(
|
fn deserialize_struct_variant(
|
||||||
@@ -714,8 +718,8 @@ fn deserialize_struct_variant(
|
|||||||
variant_ident: ast::Ident,
|
variant_ident: ast::Ident,
|
||||||
generics: &ast::Generics,
|
generics: &ast::Generics,
|
||||||
ty: P<ast::Ty>,
|
ty: P<ast::Ty>,
|
||||||
struct_def: &ast::StructDef,
|
fields: &[ast::StructField],
|
||||||
) -> P<ast::Expr> {
|
) -> Result<P<ast::Expr>, ()> {
|
||||||
let where_clause = &generics.where_clause;
|
let where_clause = &generics.where_clause;
|
||||||
|
|
||||||
let type_path = builder.path()
|
let type_path = builder.path()
|
||||||
@@ -723,29 +727,28 @@ fn deserialize_struct_variant(
|
|||||||
.id(variant_ident)
|
.id(variant_ident)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let visit_seq_expr = deserialize_struct_as_seq(
|
let visit_seq_expr = try!(deserialize_struct_as_seq(
|
||||||
cx,
|
cx,
|
||||||
builder,
|
builder,
|
||||||
type_path.clone(),
|
type_path.clone(),
|
||||||
struct_def
|
fields,
|
||||||
);
|
));
|
||||||
|
|
||||||
let (field_visitor, fields_stmt, field_expr) = deserialize_struct_visitor(
|
let (field_visitor, fields_stmt, field_expr) = try!(deserialize_struct_visitor(
|
||||||
cx,
|
cx,
|
||||||
builder,
|
builder,
|
||||||
struct_def,
|
type_path,
|
||||||
type_path
|
fields,
|
||||||
);
|
));
|
||||||
|
|
||||||
let (visitor_item, visitor_ty, visitor_expr, visitor_generics) =
|
let (visitor_item, visitor_ty, visitor_expr, visitor_generics) = try!(deserialize_visitor(
|
||||||
deserialize_visitor(
|
builder,
|
||||||
builder,
|
generics,
|
||||||
generics,
|
vec![deserializer_ty_param(builder)],
|
||||||
vec![deserializer_ty_param(builder)],
|
vec![deserializer_ty_arg(builder)],
|
||||||
vec![deserializer_ty_arg(builder)],
|
));
|
||||||
);
|
|
||||||
|
|
||||||
quote_expr!(cx, {
|
Ok(quote_expr!(cx, {
|
||||||
$field_visitor
|
$field_visitor
|
||||||
|
|
||||||
$visitor_item
|
$visitor_item
|
||||||
@@ -771,7 +774,7 @@ fn deserialize_struct_variant(
|
|||||||
$fields_stmt
|
$fields_stmt
|
||||||
|
|
||||||
visitor.visit_struct(FIELDS, $visitor_expr)
|
visitor.visit_struct(FIELDS, $visitor_expr)
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_field_visitor(
|
fn deserialize_field_visitor(
|
||||||
@@ -789,7 +792,7 @@ fn deserialize_field_visitor(
|
|||||||
.enum_("__Field")
|
.enum_("__Field")
|
||||||
.with_variants(
|
.with_variants(
|
||||||
field_idents.iter().map(|field_ident| {
|
field_idents.iter().map(|field_ident| {
|
||||||
builder.variant(field_ident).tuple().build()
|
builder.variant(field_ident).unit()
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.build();
|
.build();
|
||||||
@@ -925,29 +928,31 @@ fn deserialize_field_visitor(
|
|||||||
fn deserialize_struct_visitor(
|
fn deserialize_struct_visitor(
|
||||||
cx: &ExtCtxt,
|
cx: &ExtCtxt,
|
||||||
builder: &aster::AstBuilder,
|
builder: &aster::AstBuilder,
|
||||||
struct_def: &ast::StructDef,
|
|
||||||
struct_path: ast::Path,
|
struct_path: ast::Path,
|
||||||
) -> (Vec<P<ast::Item>>, P<ast::Stmt>, P<ast::Expr>) {
|
fields: &[ast::StructField],
|
||||||
|
) -> Result<(Vec<P<ast::Item>>, P<ast::Stmt>, P<ast::Expr>), ()> {
|
||||||
let field_visitor = deserialize_field_visitor(
|
let field_visitor = deserialize_field_visitor(
|
||||||
cx,
|
cx,
|
||||||
builder,
|
builder,
|
||||||
field::struct_field_attrs(cx, builder, struct_def),
|
try!(field::struct_field_attrs(cx, builder, fields)),
|
||||||
);
|
);
|
||||||
|
|
||||||
let visit_map_expr = deserialize_map(
|
let visit_map_expr = try!(deserialize_map(
|
||||||
cx,
|
cx,
|
||||||
builder,
|
builder,
|
||||||
struct_path,
|
struct_path,
|
||||||
struct_def,
|
fields,
|
||||||
);
|
));
|
||||||
|
|
||||||
let fields_expr = builder.expr().addr_of().slice()
|
let fields_expr = builder.expr().addr_of().slice()
|
||||||
.with_exprs(
|
.with_exprs(
|
||||||
struct_def.fields.iter()
|
fields.iter()
|
||||||
.map(|field| {
|
.map(|field| {
|
||||||
match field.node.kind {
|
match field.node.kind {
|
||||||
ast::NamedField(name, _) => builder.expr().str(name),
|
ast::NamedField(name, _) => builder.expr().str(name),
|
||||||
ast::UnnamedField(_) => panic!("struct contains unnamed fields"),
|
ast::UnnamedField(_) => {
|
||||||
|
cx.span_bug(field.span, "struct contains unnamed fields")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
@@ -957,17 +962,17 @@ fn deserialize_struct_visitor(
|
|||||||
const FIELDS: &'static [&'static str] = $fields_expr;
|
const FIELDS: &'static [&'static str] = $fields_expr;
|
||||||
).unwrap();
|
).unwrap();
|
||||||
|
|
||||||
(field_visitor, fields_stmt, visit_map_expr)
|
Ok((field_visitor, fields_stmt, visit_map_expr))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deserialize_map(
|
fn deserialize_map(
|
||||||
cx: &ExtCtxt,
|
cx: &ExtCtxt,
|
||||||
builder: &aster::AstBuilder,
|
builder: &aster::AstBuilder,
|
||||||
struct_path: ast::Path,
|
struct_path: ast::Path,
|
||||||
struct_def: &StructDef,
|
fields: &[ast::StructField],
|
||||||
) -> P<ast::Expr> {
|
) -> Result<P<ast::Expr>, ()> {
|
||||||
// Create the field names for the fields.
|
// Create the field names for the fields.
|
||||||
let field_names: Vec<ast::Ident> = (0 .. struct_def.fields.len())
|
let field_names: Vec<ast::Ident> = (0 .. fields.len())
|
||||||
.map(|i| builder.id(format!("__field{}", i)))
|
.map(|i| builder.id(format!("__field{}", i)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
@@ -987,8 +992,10 @@ fn deserialize_map(
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
let field_attrs = try!(field::struct_field_attrs(cx, builder, fields));
|
||||||
|
|
||||||
let extract_values: Vec<P<ast::Stmt>> = field_names.iter()
|
let extract_values: Vec<P<ast::Stmt>> = field_names.iter()
|
||||||
.zip(field::struct_field_attrs(cx, builder, struct_def).iter())
|
.zip(field_attrs.iter())
|
||||||
.map(|(field_name, field_attr)| {
|
.map(|(field_name, field_attr)| {
|
||||||
let missing_expr = if field_attr.use_default() {
|
let missing_expr = if field_attr.use_default() {
|
||||||
quote_expr!(cx, ::std::default::Default::default())
|
quote_expr!(cx, ::std::default::Default::default())
|
||||||
@@ -1025,13 +1032,15 @@ fn deserialize_map(
|
|||||||
|
|
||||||
let result = builder.expr().struct_path(struct_path)
|
let result = builder.expr().struct_path(struct_path)
|
||||||
.with_id_exprs(
|
.with_id_exprs(
|
||||||
struct_def.fields.iter()
|
fields.iter()
|
||||||
.zip(field_names.iter())
|
.zip(field_names.iter())
|
||||||
.map(|(field, field_name)| {
|
.map(|(field, field_name)| {
|
||||||
(
|
(
|
||||||
match field.node.kind {
|
match field.node.kind {
|
||||||
ast::NamedField(name, _) => name.clone(),
|
ast::NamedField(name, _) => name.clone(),
|
||||||
ast::UnnamedField(_) => panic!("struct contains unnamed fields"),
|
ast::UnnamedField(_) => {
|
||||||
|
cx.span_bug(field.span, "struct contains unnamed fields")
|
||||||
|
}
|
||||||
},
|
},
|
||||||
builder.expr().id(field_name),
|
builder.expr().id(field_name),
|
||||||
)
|
)
|
||||||
@@ -1039,7 +1048,7 @@ fn deserialize_map(
|
|||||||
)
|
)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
quote_expr!(cx, {
|
Ok(quote_expr!(cx, {
|
||||||
$let_values
|
$let_values
|
||||||
|
|
||||||
while let Some(key) = try!(visitor.visit_key()) {
|
while let Some(key) = try!(visitor.visit_key()) {
|
||||||
@@ -1053,5 +1062,5 @@ fn deserialize_map(
|
|||||||
try!(visitor.end());
|
try!(visitor.end());
|
||||||
|
|
||||||
Ok($result)
|
Ok($result)
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-137
@@ -1,147 +1,30 @@
|
|||||||
use std::collections::HashMap;
|
use syntax::ast;
|
||||||
|
use syntax::ext::base::ExtCtxt;
|
||||||
|
|
||||||
use aster;
|
use aster;
|
||||||
|
use attr;
|
||||||
use syntax::ast;
|
|
||||||
use syntax::attr;
|
|
||||||
use syntax::ext::base::ExtCtxt;
|
|
||||||
use syntax::ptr::P;
|
|
||||||
|
|
||||||
use attr::FieldAttrs;
|
|
||||||
|
|
||||||
enum Rename<'a> {
|
|
||||||
None,
|
|
||||||
Global(&'a ast::Lit),
|
|
||||||
Format(HashMap<P<ast::Expr>, &'a ast::Lit>)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rename<'a>(
|
|
||||||
builder: &aster::AstBuilder,
|
|
||||||
mi: &'a ast::MetaItem,
|
|
||||||
) -> Option<Rename<'a>>
|
|
||||||
{
|
|
||||||
match mi.node {
|
|
||||||
ast::MetaNameValue(ref n, ref lit) => {
|
|
||||||
if n == &"rename" {
|
|
||||||
Some(Rename::Global(lit))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
},
|
|
||||||
ast::MetaList(ref n, ref items) => {
|
|
||||||
if n == &"rename" {
|
|
||||||
let mut m = HashMap::new();
|
|
||||||
m.extend(
|
|
||||||
items.iter()
|
|
||||||
.filter_map(
|
|
||||||
|item|
|
|
||||||
match item.node {
|
|
||||||
ast::MetaNameValue(ref n, ref lit) =>
|
|
||||||
Some((builder.expr().str(n),
|
|
||||||
lit)),
|
|
||||||
_ => None
|
|
||||||
}));
|
|
||||||
Some(Rename::Format(m))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
},
|
|
||||||
_ => None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn default_value(mi: &ast::MetaItem) -> bool {
|
|
||||||
if let ast::MetaItem_::MetaWord(ref n) = mi.node {
|
|
||||||
n == &"default"
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn skip_serializing_field(mi: &ast::MetaItem) -> bool {
|
|
||||||
if let ast::MetaItem_::MetaWord(ref n) = mi.node {
|
|
||||||
n == &"skip_serializing"
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn field_attrs<'a>(
|
|
||||||
builder: &aster::AstBuilder,
|
|
||||||
field: &'a ast::StructField,
|
|
||||||
) -> (Rename<'a>, bool, bool) {
|
|
||||||
field.node.attrs.iter()
|
|
||||||
.find(|sa| {
|
|
||||||
if let ast::MetaList(ref n, _) = sa.node.value.node {
|
|
||||||
n == &"serde"
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.and_then(|sa| {
|
|
||||||
if let ast::MetaList(_, ref vals) = sa.node.value.node {
|
|
||||||
attr::mark_used(&sa);
|
|
||||||
Some((
|
|
||||||
vals.iter()
|
|
||||||
.fold(None, |v, mi| v.or(rename(builder, mi)))
|
|
||||||
.unwrap_or(Rename::None),
|
|
||||||
vals.iter().any(|mi| default_value(mi)),
|
|
||||||
vals.iter().any(|mi| skip_serializing_field(mi)),
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Some((Rename::None, false, false))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap_or((Rename::None, false, false))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn struct_field_attrs(
|
pub fn struct_field_attrs(
|
||||||
cx: &ExtCtxt,
|
cx: &ExtCtxt,
|
||||||
builder: &aster::AstBuilder,
|
builder: &aster::AstBuilder,
|
||||||
struct_def: &ast::StructDef,
|
fields: &[ast::StructField],
|
||||||
) -> Vec<FieldAttrs> {
|
) -> Result<Vec<attr::FieldAttrs>, ()> {
|
||||||
struct_def.fields.iter()
|
let mut attrs = vec![];
|
||||||
.map(|field| {
|
for field in fields {
|
||||||
match field_attrs(builder, field) {
|
let builder = attr::FieldAttrsBuilder::new(cx, builder);
|
||||||
(Rename::Global(rename), default_value, skip_serializing_field) =>
|
let builder = try!(builder.field(field));
|
||||||
FieldAttrs::new(
|
let attr = builder.build();
|
||||||
skip_serializing_field,
|
attrs.push(attr);
|
||||||
default_value,
|
}
|
||||||
builder.expr().build_lit(P(rename.clone()))),
|
|
||||||
(Rename::Format(renames), default_value, skip_serializing_field) => {
|
Ok(attrs)
|
||||||
let mut res = HashMap::new();
|
|
||||||
res.extend(
|
|
||||||
renames.into_iter()
|
|
||||||
.map(|(k,v)|
|
|
||||||
(k, builder.expr().build_lit(P(v.clone())))));
|
|
||||||
FieldAttrs::new_with_formats(
|
|
||||||
skip_serializing_field,
|
|
||||||
default_value,
|
|
||||||
default_field_name(cx, builder, field.node.kind),
|
|
||||||
res)
|
|
||||||
},
|
|
||||||
(Rename::None, default_value, skip_serializing_field) => {
|
|
||||||
FieldAttrs::new(
|
|
||||||
skip_serializing_field,
|
|
||||||
default_value,
|
|
||||||
default_field_name(cx, builder, field.node.kind))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_field_name(
|
pub fn container_attrs(
|
||||||
cx: &ExtCtxt,
|
cx: &ExtCtxt,
|
||||||
builder: &aster::AstBuilder,
|
container: &ast::Item,
|
||||||
kind: ast::StructFieldKind,
|
) -> Result<attr::ContainerAttrs, ()> {
|
||||||
) -> P<ast::Expr> {
|
let builder = attr::ContainerAttrsBuilder::new(cx);
|
||||||
match kind {
|
let builder = try!(builder.attrs(container.attrs()));
|
||||||
ast::NamedField(name, _) => {
|
Ok(builder.build())
|
||||||
builder.expr().str(name)
|
|
||||||
}
|
|
||||||
ast::UnnamedField(_) => {
|
|
||||||
cx.bug("struct has named and unnamed fields")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
#![cfg_attr(feature = "nightly", plugin(clippy))]
|
||||||
|
#![cfg_attr(feature = "nightly", allow(used_underscore_binding))]
|
||||||
#![cfg_attr(not(feature = "with-syntex"), feature(rustc_private, plugin))]
|
#![cfg_attr(not(feature = "with-syntex"), feature(rustc_private, plugin))]
|
||||||
#![cfg_attr(not(feature = "with-syntex"), plugin(quasi_macros))]
|
#![cfg_attr(not(feature = "with-syntex"), plugin(quasi_macros))]
|
||||||
|
|
||||||
@@ -14,7 +16,10 @@ extern crate syntex_syntax as syntax;
|
|||||||
extern crate syntax;
|
extern crate syntax;
|
||||||
|
|
||||||
#[cfg(not(feature = "with-syntex"))]
|
#[cfg(not(feature = "with-syntex"))]
|
||||||
extern crate rustc;
|
extern crate rustc_plugin;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "with-syntex"))]
|
||||||
|
use syntax::feature_gate::AttributeType;
|
||||||
|
|
||||||
#[cfg(feature = "with-syntex")]
|
#[cfg(feature = "with-syntex")]
|
||||||
include!(concat!(env!("OUT_DIR"), "/lib.rs"));
|
include!(concat!(env!("OUT_DIR"), "/lib.rs"));
|
||||||
@@ -60,7 +65,7 @@ pub fn register(reg: &mut syntex::Registry) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "with-syntex"))]
|
#[cfg(not(feature = "with-syntex"))]
|
||||||
pub fn register(reg: &mut rustc::plugin::Registry) {
|
pub fn register(reg: &mut rustc_plugin::Registry) {
|
||||||
reg.register_syntax_extension(
|
reg.register_syntax_extension(
|
||||||
syntax::parse::token::intern("derive_Serialize"),
|
syntax::parse::token::intern("derive_Serialize"),
|
||||||
syntax::ext::base::MultiDecorator(
|
syntax::ext::base::MultiDecorator(
|
||||||
@@ -70,4 +75,6 @@ pub fn register(reg: &mut rustc::plugin::Registry) {
|
|||||||
syntax::parse::token::intern("derive_Deserialize"),
|
syntax::parse::token::intern("derive_Deserialize"),
|
||||||
syntax::ext::base::MultiDecorator(
|
syntax::ext::base::MultiDecorator(
|
||||||
Box::new(de::expand_derive_deserialize)));
|
Box::new(de::expand_derive_deserialize)));
|
||||||
|
|
||||||
|
reg.register_attribute("serde".to_owned(), AttributeType::Normal);
|
||||||
}
|
}
|
||||||
|
|||||||
+176
-129
@@ -4,8 +4,6 @@ use syntax::ast::{
|
|||||||
Ident,
|
Ident,
|
||||||
MetaItem,
|
MetaItem,
|
||||||
Item,
|
Item,
|
||||||
Expr,
|
|
||||||
StructDef,
|
|
||||||
};
|
};
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
use syntax::codemap::Span;
|
use syntax::codemap::Span;
|
||||||
@@ -13,7 +11,7 @@ use syntax::ext::base::{Annotatable, ExtCtxt};
|
|||||||
use syntax::ext::build::AstBuilder;
|
use syntax::ext::build::AstBuilder;
|
||||||
use syntax::ptr::P;
|
use syntax::ptr::P;
|
||||||
|
|
||||||
use field::struct_field_attrs;
|
use field;
|
||||||
|
|
||||||
pub fn expand_derive_serialize(
|
pub fn expand_derive_serialize(
|
||||||
cx: &mut ExtCtxt,
|
cx: &mut ExtCtxt,
|
||||||
@@ -27,7 +25,7 @@ pub fn expand_derive_serialize(
|
|||||||
_ => {
|
_ => {
|
||||||
cx.span_err(
|
cx.span_err(
|
||||||
meta_item.span,
|
meta_item.span,
|
||||||
"`derive` may only be applied to structs and enums");
|
"`#[derive(Serialize)]` may only be applied to structs and enums");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -37,7 +35,12 @@ pub fn expand_derive_serialize(
|
|||||||
let generics = match item.node {
|
let generics = match item.node {
|
||||||
ast::ItemStruct(_, ref generics) => generics,
|
ast::ItemStruct(_, ref generics) => generics,
|
||||||
ast::ItemEnum(_, ref generics) => generics,
|
ast::ItemEnum(_, ref generics) => generics,
|
||||||
_ => cx.bug("expected ItemStruct or ItemEnum in #[derive(Serialize)]")
|
_ => {
|
||||||
|
cx.span_err(
|
||||||
|
meta_item.span,
|
||||||
|
"`#[derive(Serialize)]` may only be applied to structs and enums");
|
||||||
|
return;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let impl_generics = builder.from_generics(generics.clone())
|
let impl_generics = builder.from_generics(generics.clone())
|
||||||
@@ -50,18 +53,17 @@ pub fn expand_derive_serialize(
|
|||||||
.segment(item.ident).with_generics(impl_generics.clone()).build()
|
.segment(item.ident).with_generics(impl_generics.clone()).build()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let body = serialize_body(
|
let body = match serialize_body(cx, &builder, &item, &impl_generics, ty.clone()) {
|
||||||
cx,
|
Ok(body) => body,
|
||||||
&builder,
|
Err(()) => {
|
||||||
&item,
|
// An error occured, but it should have been reported already.
|
||||||
&impl_generics,
|
return;
|
||||||
ty.clone(),
|
}
|
||||||
);
|
};
|
||||||
|
|
||||||
let where_clause = &impl_generics.where_clause;
|
let where_clause = &impl_generics.where_clause;
|
||||||
|
|
||||||
let impl_item = quote_item!(cx,
|
let impl_item = quote_item!(cx,
|
||||||
#[automatically_derived]
|
|
||||||
impl $impl_generics ::serde::ser::Serialize for $ty $where_clause {
|
impl $impl_generics ::serde::ser::Serialize for $ty $where_clause {
|
||||||
fn serialize<__S>(&self, serializer: &mut __S) -> ::std::result::Result<(), __S::Error>
|
fn serialize<__S>(&self, serializer: &mut __S) -> ::std::result::Result<(), __S::Error>
|
||||||
where __S: ::serde::ser::Serializer,
|
where __S: ::serde::ser::Serializer,
|
||||||
@@ -80,16 +82,21 @@ fn serialize_body(
|
|||||||
item: &Item,
|
item: &Item,
|
||||||
impl_generics: &ast::Generics,
|
impl_generics: &ast::Generics,
|
||||||
ty: P<ast::Ty>,
|
ty: P<ast::Ty>,
|
||||||
) -> P<ast::Expr> {
|
) -> Result<P<ast::Expr>, ()> {
|
||||||
|
// Note: While we don't have any container attributes, we still want to try to
|
||||||
|
// parse them so we can report a proper error if we get passed an unknown attribute.
|
||||||
|
let _ = try!(field::container_attrs(cx, item));
|
||||||
|
|
||||||
match item.node {
|
match item.node {
|
||||||
ast::ItemStruct(ref struct_def, _) => {
|
ast::ItemStruct(ref variant_data, _) => {
|
||||||
serialize_item_struct(
|
serialize_item_struct(
|
||||||
cx,
|
cx,
|
||||||
builder,
|
builder,
|
||||||
item,
|
item,
|
||||||
impl_generics,
|
impl_generics,
|
||||||
ty,
|
ty,
|
||||||
struct_def,
|
item.span,
|
||||||
|
variant_data,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
ast::ItemEnum(ref enum_def, _) => {
|
ast::ItemEnum(ref enum_def, _) => {
|
||||||
@@ -102,7 +109,10 @@ fn serialize_body(
|
|||||||
enum_def,
|
enum_def,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
_ => cx.bug("expected ItemStruct or ItemEnum in #[derive(Serialize)]")
|
_ => {
|
||||||
|
cx.span_bug(item.span,
|
||||||
|
"expected ItemStruct or ItemEnum in #[derive(Serialize)]");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,57 +122,52 @@ fn serialize_item_struct(
|
|||||||
item: &Item,
|
item: &Item,
|
||||||
impl_generics: &ast::Generics,
|
impl_generics: &ast::Generics,
|
||||||
ty: P<ast::Ty>,
|
ty: P<ast::Ty>,
|
||||||
struct_def: &ast::StructDef,
|
span: Span,
|
||||||
) -> P<ast::Expr> {
|
variant_data: &ast::VariantData,
|
||||||
let mut named_fields = vec![];
|
) -> Result<P<ast::Expr>, ()> {
|
||||||
let mut unnamed_fields = 0;
|
match *variant_data {
|
||||||
|
ast::VariantData::Unit(_) => {
|
||||||
for field in struct_def.fields.iter() {
|
|
||||||
match field.node.kind {
|
|
||||||
ast::NamedField(name, _) => { named_fields.push(name); }
|
|
||||||
ast::UnnamedField(_) => { unnamed_fields += 1; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
match (named_fields.is_empty(), unnamed_fields) {
|
|
||||||
(true, 0) => {
|
|
||||||
serialize_unit_struct(
|
serialize_unit_struct(
|
||||||
cx,
|
cx,
|
||||||
&builder,
|
&builder,
|
||||||
item.ident,
|
item.ident,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
(true, 1) => {
|
ast::VariantData::Tuple(ref fields, _) if fields.len() == 1 => {
|
||||||
serialize_newtype_struct(
|
serialize_newtype_struct(
|
||||||
cx,
|
cx,
|
||||||
&builder,
|
&builder,
|
||||||
item.ident,
|
item.ident,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
(true, _) => {
|
ast::VariantData::Tuple(ref fields, _) => {
|
||||||
|
if fields.iter().any(|field| !field.node.kind.is_unnamed()) {
|
||||||
|
cx.span_bug(span, "tuple struct has named fields")
|
||||||
|
}
|
||||||
|
|
||||||
serialize_tuple_struct(
|
serialize_tuple_struct(
|
||||||
cx,
|
cx,
|
||||||
&builder,
|
&builder,
|
||||||
item.ident,
|
item.ident,
|
||||||
impl_generics,
|
impl_generics,
|
||||||
ty,
|
ty,
|
||||||
unnamed_fields,
|
fields.len(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
(false, 0) => {
|
ast::VariantData::Struct(ref fields, _) => {
|
||||||
|
if fields.iter().any(|field| field.node.kind.is_unnamed()) {
|
||||||
|
cx.span_bug(span, "struct has unnamed fields")
|
||||||
|
}
|
||||||
|
|
||||||
serialize_struct(
|
serialize_struct(
|
||||||
cx,
|
cx,
|
||||||
&builder,
|
&builder,
|
||||||
item.ident,
|
item.ident,
|
||||||
impl_generics,
|
impl_generics,
|
||||||
ty,
|
ty,
|
||||||
struct_def,
|
fields,
|
||||||
named_fields,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
(false, _) => {
|
|
||||||
cx.bug("struct has named and unnamed fields")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,20 +175,24 @@ fn serialize_unit_struct(
|
|||||||
cx: &ExtCtxt,
|
cx: &ExtCtxt,
|
||||||
builder: &aster::AstBuilder,
|
builder: &aster::AstBuilder,
|
||||||
type_ident: Ident
|
type_ident: Ident
|
||||||
) -> P<ast::Expr> {
|
) -> Result<P<ast::Expr>, ()> {
|
||||||
let type_name = builder.expr().str(type_ident);
|
let type_name = builder.expr().str(type_ident);
|
||||||
|
|
||||||
quote_expr!(cx, serializer.visit_unit_struct($type_name))
|
Ok(quote_expr!(cx,
|
||||||
|
serializer.visit_unit_struct($type_name)
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_newtype_struct(
|
fn serialize_newtype_struct(
|
||||||
cx: &ExtCtxt,
|
cx: &ExtCtxt,
|
||||||
builder: &aster::AstBuilder,
|
builder: &aster::AstBuilder,
|
||||||
type_ident: Ident
|
type_ident: Ident
|
||||||
) -> P<ast::Expr> {
|
) -> Result<P<ast::Expr>, ()> {
|
||||||
let type_name = builder.expr().str(type_ident);
|
let type_name = builder.expr().str(type_ident);
|
||||||
|
|
||||||
quote_expr!(cx, serializer.visit_newtype_struct($type_name, &self.0))
|
Ok(quote_expr!(cx,
|
||||||
|
serializer.visit_newtype_struct($type_name, &self.0)
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_tuple_struct(
|
fn serialize_tuple_struct(
|
||||||
@@ -193,7 +202,7 @@ fn serialize_tuple_struct(
|
|||||||
impl_generics: &ast::Generics,
|
impl_generics: &ast::Generics,
|
||||||
ty: P<ast::Ty>,
|
ty: P<ast::Ty>,
|
||||||
fields: usize,
|
fields: usize,
|
||||||
) -> P<ast::Expr> {
|
) -> Result<P<ast::Expr>, ()> {
|
||||||
let (visitor_struct, visitor_impl) = serialize_tuple_struct_visitor(
|
let (visitor_struct, visitor_impl) = serialize_tuple_struct_visitor(
|
||||||
cx,
|
cx,
|
||||||
builder,
|
builder,
|
||||||
@@ -208,7 +217,7 @@ fn serialize_tuple_struct(
|
|||||||
|
|
||||||
let type_name = builder.expr().str(type_ident);
|
let type_name = builder.expr().str(type_ident);
|
||||||
|
|
||||||
quote_expr!(cx, {
|
Ok(quote_expr!(cx, {
|
||||||
$visitor_struct
|
$visitor_struct
|
||||||
$visitor_impl
|
$visitor_impl
|
||||||
serializer.visit_tuple_struct($type_name, Visitor {
|
serializer.visit_tuple_struct($type_name, Visitor {
|
||||||
@@ -216,7 +225,7 @@ fn serialize_tuple_struct(
|
|||||||
state: 0,
|
state: 0,
|
||||||
_structure_ty: ::std::marker::PhantomData::<&$ty>,
|
_structure_ty: ::std::marker::PhantomData::<&$ty>,
|
||||||
})
|
})
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_struct(
|
fn serialize_struct(
|
||||||
@@ -225,10 +234,14 @@ fn serialize_struct(
|
|||||||
type_ident: Ident,
|
type_ident: Ident,
|
||||||
impl_generics: &ast::Generics,
|
impl_generics: &ast::Generics,
|
||||||
ty: P<ast::Ty>,
|
ty: P<ast::Ty>,
|
||||||
struct_def: &StructDef,
|
fields: &[ast::StructField],
|
||||||
fields: Vec<Ident>,
|
) -> Result<P<ast::Expr>, ()> {
|
||||||
) -> P<ast::Expr> {
|
let value_exprs = fields.iter().map(|field| {
|
||||||
let (visitor_struct, visitor_impl) = serialize_struct_visitor(
|
let name = field.node.ident().expect("struct has unnamed field");
|
||||||
|
quote_expr!(cx, &self.value.$name)
|
||||||
|
});
|
||||||
|
|
||||||
|
let (visitor_struct, visitor_impl) = try!(serialize_struct_visitor(
|
||||||
cx,
|
cx,
|
||||||
builder,
|
builder,
|
||||||
ty.clone(),
|
ty.clone(),
|
||||||
@@ -236,14 +249,14 @@ fn serialize_struct(
|
|||||||
.ref_()
|
.ref_()
|
||||||
.lifetime("'__a")
|
.lifetime("'__a")
|
||||||
.build_ty(ty.clone()),
|
.build_ty(ty.clone()),
|
||||||
struct_def,
|
fields,
|
||||||
impl_generics,
|
impl_generics,
|
||||||
fields.iter().map(|field| quote_expr!(cx, &self.value.$field)),
|
value_exprs,
|
||||||
);
|
));
|
||||||
|
|
||||||
let type_name = builder.expr().str(type_ident);
|
let type_name = builder.expr().str(type_ident);
|
||||||
|
|
||||||
quote_expr!(cx, {
|
Ok(quote_expr!(cx, {
|
||||||
$visitor_struct
|
$visitor_struct
|
||||||
$visitor_impl
|
$visitor_impl
|
||||||
serializer.visit_struct($type_name, Visitor {
|
serializer.visit_struct($type_name, Visitor {
|
||||||
@@ -251,7 +264,7 @@ fn serialize_struct(
|
|||||||
state: 0,
|
state: 0,
|
||||||
_structure_ty: ::std::marker::PhantomData::<&$ty>,
|
_structure_ty: ::std::marker::PhantomData::<&$ty>,
|
||||||
})
|
})
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_item_enum(
|
fn serialize_item_enum(
|
||||||
@@ -261,27 +274,28 @@ fn serialize_item_enum(
|
|||||||
impl_generics: &ast::Generics,
|
impl_generics: &ast::Generics,
|
||||||
ty: P<ast::Ty>,
|
ty: P<ast::Ty>,
|
||||||
enum_def: &ast::EnumDef,
|
enum_def: &ast::EnumDef,
|
||||||
) -> P<ast::Expr> {
|
) -> Result<P<ast::Expr>, ()> {
|
||||||
let arms: Vec<ast::Arm> = enum_def.variants.iter()
|
let mut arms = vec![];
|
||||||
.enumerate()
|
|
||||||
.map(|(variant_index, variant)| {
|
|
||||||
serialize_variant(
|
|
||||||
cx,
|
|
||||||
builder,
|
|
||||||
type_ident,
|
|
||||||
impl_generics,
|
|
||||||
ty.clone(),
|
|
||||||
variant,
|
|
||||||
variant_index,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
quote_expr!(cx,
|
for (variant_index, variant) in enum_def.variants.iter().enumerate() {
|
||||||
|
let arm = try!(serialize_variant(
|
||||||
|
cx,
|
||||||
|
builder,
|
||||||
|
type_ident,
|
||||||
|
impl_generics,
|
||||||
|
ty.clone(),
|
||||||
|
variant,
|
||||||
|
variant_index,
|
||||||
|
));
|
||||||
|
|
||||||
|
arms.push(arm);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(quote_expr!(cx,
|
||||||
match *self {
|
match *self {
|
||||||
$arms
|
$arms
|
||||||
}
|
}
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_variant(
|
fn serialize_variant(
|
||||||
@@ -292,18 +306,18 @@ fn serialize_variant(
|
|||||||
ty: P<ast::Ty>,
|
ty: P<ast::Ty>,
|
||||||
variant: &ast::Variant,
|
variant: &ast::Variant,
|
||||||
variant_index: usize,
|
variant_index: usize,
|
||||||
) -> ast::Arm {
|
) -> Result<ast::Arm, ()> {
|
||||||
let type_name = builder.expr().str(type_ident);
|
let type_name = builder.expr().str(type_ident);
|
||||||
let variant_ident = variant.node.name;
|
let variant_ident = variant.node.name;
|
||||||
let variant_name = builder.expr().str(variant_ident);
|
let variant_name = builder.expr().str(variant_ident);
|
||||||
|
|
||||||
match variant.node.kind {
|
match variant.node.data {
|
||||||
ast::TupleVariantKind(ref args) if args.is_empty() => {
|
ast::VariantData::Unit(_) => {
|
||||||
let pat = builder.pat().enum_()
|
let pat = builder.pat().enum_()
|
||||||
.id(type_ident).id(variant_ident).build()
|
.id(type_ident).id(variant_ident).build()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
quote_arm!(cx,
|
Ok(quote_arm!(cx,
|
||||||
$pat => {
|
$pat => {
|
||||||
::serde::ser::Serializer::visit_unit_variant(
|
::serde::ser::Serializer::visit_unit_variant(
|
||||||
serializer,
|
serializer,
|
||||||
@@ -312,16 +326,17 @@ fn serialize_variant(
|
|||||||
$variant_name,
|
$variant_name,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
))
|
||||||
},
|
},
|
||||||
ast::TupleVariantKind(ref args) if args.len() == 1 => {
|
ast::VariantData::Tuple(ref fields, _) if fields.len() == 1 => {
|
||||||
let field = builder.id("__simple_value");
|
let field = builder.id("__simple_value");
|
||||||
let field = builder.pat().ref_id(field);
|
let field = builder.pat().ref_id(field);
|
||||||
let pat = builder.pat().enum_()
|
let pat = builder.pat().enum_()
|
||||||
.id(type_ident).id(variant_ident).build()
|
.id(type_ident).id(variant_ident).build()
|
||||||
.with_pats(Some(field).into_iter())
|
.with_pats(Some(field).into_iter())
|
||||||
.build();
|
.build();
|
||||||
quote_arm!(cx,
|
|
||||||
|
Ok(quote_arm!(cx,
|
||||||
$pat => {
|
$pat => {
|
||||||
::serde::ser::Serializer::visit_newtype_variant(
|
::serde::ser::Serializer::visit_newtype_variant(
|
||||||
serializer,
|
serializer,
|
||||||
@@ -331,16 +346,19 @@ fn serialize_variant(
|
|||||||
__simple_value,
|
__simple_value,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
))
|
||||||
},
|
},
|
||||||
ast::TupleVariantKind(ref args) => {
|
ast::VariantData::Tuple(ref fields, _) => {
|
||||||
let fields: Vec<ast::Ident> = (0 .. args.len())
|
let field_names: Vec<ast::Ident> = (0 .. fields.len())
|
||||||
.map(|i| builder.id(format!("__field{}", i)))
|
.map(|i| builder.id(format!("__field{}", i)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let pat = builder.pat().enum_()
|
let pat = builder.pat().enum_()
|
||||||
.id(type_ident).id(variant_ident).build()
|
.id(type_ident).id(variant_ident).build()
|
||||||
.with_pats(fields.iter().map(|field| builder.pat().ref_id(field)))
|
.with_pats(
|
||||||
|
field_names.iter()
|
||||||
|
.map(|field| builder.pat().ref_id(field))
|
||||||
|
)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let expr = serialize_tuple_variant(
|
let expr = serialize_tuple_variant(
|
||||||
@@ -351,27 +369,29 @@ fn serialize_variant(
|
|||||||
variant_name,
|
variant_name,
|
||||||
generics,
|
generics,
|
||||||
ty,
|
ty,
|
||||||
args,
|
|
||||||
fields,
|
fields,
|
||||||
|
field_names,
|
||||||
);
|
);
|
||||||
|
|
||||||
quote_arm!(cx, $pat => { $expr })
|
Ok(quote_arm!(cx,
|
||||||
|
$pat => { $expr }
|
||||||
|
))
|
||||||
}
|
}
|
||||||
ast::StructVariantKind(ref struct_def) => {
|
ast::VariantData::Struct(ref fields, _) => {
|
||||||
let fields: Vec<_> = (0 .. struct_def.fields.len())
|
let field_names: Vec<_> = (0 .. fields.len())
|
||||||
.map(|i| builder.id(format!("__field{}", i)))
|
.map(|i| builder.id(format!("__field{}", i)))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let pat = builder.pat().struct_()
|
let pat = builder.pat().struct_()
|
||||||
.id(type_ident).id(variant_ident).build()
|
.id(type_ident).id(variant_ident).build()
|
||||||
.with_pats(
|
.with_pats(
|
||||||
fields.iter()
|
field_names.iter()
|
||||||
.zip(struct_def.fields.iter())
|
.zip(fields.iter())
|
||||||
.map(|(id, field)| {
|
.map(|(id, field)| {
|
||||||
let name = match field.node.kind {
|
let name = match field.node.kind {
|
||||||
ast::NamedField(name, _) => name,
|
ast::NamedField(name, _) => name,
|
||||||
ast::UnnamedField(_) => {
|
ast::UnnamedField(_) => {
|
||||||
cx.bug("struct variant has unnamed fields")
|
cx.span_bug(field.span, "struct variant has unnamed fields")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -380,7 +400,7 @@ fn serialize_variant(
|
|||||||
)
|
)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let expr = serialize_struct_variant(
|
let expr = try!(serialize_struct_variant(
|
||||||
cx,
|
cx,
|
||||||
builder,
|
builder,
|
||||||
type_name,
|
type_name,
|
||||||
@@ -388,11 +408,13 @@ fn serialize_variant(
|
|||||||
variant_name,
|
variant_name,
|
||||||
generics,
|
generics,
|
||||||
ty,
|
ty,
|
||||||
struct_def,
|
|
||||||
fields,
|
fields,
|
||||||
);
|
field_names,
|
||||||
|
));
|
||||||
|
|
||||||
quote_arm!(cx, $pat => { $expr })
|
Ok(quote_arm!(cx,
|
||||||
|
$pat => { $expr }
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -405,16 +427,16 @@ fn serialize_tuple_variant(
|
|||||||
variant_name: P<ast::Expr>,
|
variant_name: P<ast::Expr>,
|
||||||
generics: &ast::Generics,
|
generics: &ast::Generics,
|
||||||
structure_ty: P<ast::Ty>,
|
structure_ty: P<ast::Ty>,
|
||||||
args: &[ast::VariantArg],
|
fields: &[ast::StructField],
|
||||||
fields: Vec<Ident>,
|
field_names: Vec<Ident>,
|
||||||
) -> P<ast::Expr> {
|
) -> P<ast::Expr> {
|
||||||
let variant_ty = builder.ty().tuple()
|
let variant_ty = builder.ty().tuple()
|
||||||
.with_tys(
|
.with_tys(
|
||||||
args.iter().map(|arg| {
|
fields.iter().map(|field| {
|
||||||
builder.ty()
|
builder.ty()
|
||||||
.ref_()
|
.ref_()
|
||||||
.lifetime("'__a")
|
.lifetime("'__a")
|
||||||
.build_ty(arg.ty.clone())
|
.build_ty(field.node.ty.clone())
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.build();
|
.build();
|
||||||
@@ -424,13 +446,13 @@ fn serialize_tuple_variant(
|
|||||||
builder,
|
builder,
|
||||||
structure_ty.clone(),
|
structure_ty.clone(),
|
||||||
variant_ty,
|
variant_ty,
|
||||||
args.len(),
|
fields.len(),
|
||||||
generics,
|
generics,
|
||||||
);
|
);
|
||||||
|
|
||||||
let value_expr = builder.expr().tuple()
|
let value_expr = builder.expr().tuple()
|
||||||
.with_exprs(
|
.with_exprs(
|
||||||
fields.iter().map(|field| {
|
field_names.iter().map(|field| {
|
||||||
builder.expr().id(field)
|
builder.expr().id(field)
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
@@ -455,12 +477,12 @@ fn serialize_struct_variant(
|
|||||||
variant_name: P<ast::Expr>,
|
variant_name: P<ast::Expr>,
|
||||||
generics: &ast::Generics,
|
generics: &ast::Generics,
|
||||||
structure_ty: P<ast::Ty>,
|
structure_ty: P<ast::Ty>,
|
||||||
struct_def: &ast::StructDef,
|
fields: &[ast::StructField],
|
||||||
fields: Vec<Ident>,
|
field_names: Vec<Ident>,
|
||||||
) -> P<ast::Expr> {
|
) -> Result<P<ast::Expr>, ()> {
|
||||||
let value_ty = builder.ty().tuple()
|
let value_ty = builder.ty().tuple()
|
||||||
.with_tys(
|
.with_tys(
|
||||||
struct_def.fields.iter().map(|field| {
|
fields.iter().map(|field| {
|
||||||
builder.ty()
|
builder.ty()
|
||||||
.ref_()
|
.ref_()
|
||||||
.lifetime("'__a")
|
.lifetime("'__a")
|
||||||
@@ -471,27 +493,27 @@ fn serialize_struct_variant(
|
|||||||
|
|
||||||
let value_expr = builder.expr().tuple()
|
let value_expr = builder.expr().tuple()
|
||||||
.with_exprs(
|
.with_exprs(
|
||||||
fields.iter().map(|field| {
|
field_names.iter().map(|field| {
|
||||||
builder.expr().id(field)
|
builder.expr().id(field)
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let (visitor_struct, visitor_impl) = serialize_struct_visitor(
|
let (visitor_struct, visitor_impl) = try!(serialize_struct_visitor(
|
||||||
cx,
|
cx,
|
||||||
builder,
|
builder,
|
||||||
structure_ty.clone(),
|
structure_ty.clone(),
|
||||||
value_ty,
|
value_ty,
|
||||||
struct_def,
|
fields,
|
||||||
generics,
|
generics,
|
||||||
(0 .. fields.len()).map(|i| {
|
(0 .. field_names.len()).map(|i| {
|
||||||
builder.expr()
|
builder.expr()
|
||||||
.tup_field(i)
|
.tup_field(i)
|
||||||
.field("value").self_()
|
.field("value").self_()
|
||||||
})
|
})
|
||||||
);
|
));
|
||||||
|
|
||||||
quote_expr!(cx, {
|
Ok(quote_expr!(cx, {
|
||||||
$visitor_struct
|
$visitor_struct
|
||||||
$visitor_impl
|
$visitor_impl
|
||||||
serializer.visit_struct_variant($type_name, $variant_index, $variant_name, Visitor {
|
serializer.visit_struct_variant($type_name, $variant_index, $variant_name, Visitor {
|
||||||
@@ -499,7 +521,7 @@ fn serialize_struct_variant(
|
|||||||
state: 0,
|
state: 0,
|
||||||
_structure_ty: ::std::marker::PhantomData::<&$structure_ty>,
|
_structure_ty: ::std::marker::PhantomData::<&$structure_ty>,
|
||||||
})
|
})
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn serialize_tuple_struct_visitor(
|
fn serialize_tuple_struct_visitor(
|
||||||
@@ -574,29 +596,37 @@ fn serialize_struct_visitor<I>(
|
|||||||
builder: &aster::AstBuilder,
|
builder: &aster::AstBuilder,
|
||||||
structure_ty: P<ast::Ty>,
|
structure_ty: P<ast::Ty>,
|
||||||
variant_ty: P<ast::Ty>,
|
variant_ty: P<ast::Ty>,
|
||||||
struct_def: &StructDef,
|
fields: &[ast::StructField],
|
||||||
generics: &ast::Generics,
|
generics: &ast::Generics,
|
||||||
value_exprs: I,
|
value_exprs: I,
|
||||||
) -> (P<ast::Item>, P<ast::Item>)
|
) -> Result<(P<ast::Item>, P<ast::Item>), ()>
|
||||||
where I: Iterator<Item=P<ast::Expr>>,
|
where I: Iterator<Item=P<ast::Expr>>,
|
||||||
{
|
{
|
||||||
let field_attrs = struct_field_attrs(cx, builder, struct_def);
|
let value_exprs = value_exprs.collect::<Vec<_>>();
|
||||||
|
|
||||||
let len = struct_def.fields.len() - field_attrs.iter()
|
let field_attrs = try!(field::struct_field_attrs(cx, builder, fields));
|
||||||
.fold(0, |sum, field| {
|
|
||||||
sum + if field.skip_serializing_field() { 1 } else { 0 }
|
|
||||||
});
|
|
||||||
|
|
||||||
let arms: Vec<ast::Arm> = field_attrs.into_iter()
|
let arms: Vec<ast::Arm> = field_attrs.iter()
|
||||||
.zip(value_exprs)
|
.zip(value_exprs.iter())
|
||||||
.filter(|&(ref field, _)| !field.skip_serializing_field())
|
.filter(|&(ref field, _)| !field.skip_serializing_field())
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(i, (field, value_expr))| {
|
.map(|(i, (ref field, value_expr))| {
|
||||||
let key_expr = field.serializer_key_expr(cx);
|
let key_expr = field.serializer_key_expr(cx);
|
||||||
|
|
||||||
|
let stmt = if field.skip_serializing_field_if_empty() {
|
||||||
|
quote_stmt!(cx, if ($value_expr).is_empty() { continue; })
|
||||||
|
} else if field.skip_serializing_field_if_none() {
|
||||||
|
quote_stmt!(cx, if ($value_expr).is_none() { continue; })
|
||||||
|
} else {
|
||||||
|
quote_stmt!(cx, {})
|
||||||
|
};
|
||||||
|
|
||||||
quote_arm!(cx,
|
quote_arm!(cx,
|
||||||
$i => {
|
$i => {
|
||||||
self.state += 1;
|
self.state += 1;
|
||||||
Ok(
|
$stmt
|
||||||
|
|
||||||
|
return Ok(
|
||||||
Some(
|
Some(
|
||||||
try!(
|
try!(
|
||||||
serializer.visit_struct_elt(
|
serializer.visit_struct_elt(
|
||||||
@@ -605,7 +635,7 @@ fn serialize_struct_visitor<I>(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -622,7 +652,22 @@ fn serialize_struct_visitor<I>(
|
|||||||
.strip_bounds()
|
.strip_bounds()
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
(
|
let len = field_attrs.iter()
|
||||||
|
.zip(value_exprs.iter())
|
||||||
|
.map(|(field, value_expr)| {
|
||||||
|
if field.skip_serializing_field() {
|
||||||
|
quote_expr!(cx, 0)
|
||||||
|
} else if field.skip_serializing_field_if_empty() {
|
||||||
|
quote_expr!(cx, if ($value_expr).is_empty() { 0 } else { 1 })
|
||||||
|
} else if field.skip_serializing_field_if_none() {
|
||||||
|
quote_expr!(cx, if ($value_expr).is_none() { 0 } else { 1 })
|
||||||
|
} else {
|
||||||
|
quote_expr!(cx, 1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.fold(quote_expr!(cx, 0), |sum, expr| quote_expr!(cx, $sum + $expr));
|
||||||
|
|
||||||
|
Ok((
|
||||||
quote_item!(cx,
|
quote_item!(cx,
|
||||||
struct Visitor $visitor_impl_generics $where_clause {
|
struct Visitor $visitor_impl_generics $where_clause {
|
||||||
state: usize,
|
state: usize,
|
||||||
@@ -640,9 +685,11 @@ fn serialize_struct_visitor<I>(
|
|||||||
fn visit<S>(&mut self, serializer: &mut S) -> ::std::result::Result<Option<()>, S::Error>
|
fn visit<S>(&mut self, serializer: &mut S) -> ::std::result::Result<Option<()>, S::Error>
|
||||||
where S: ::serde::ser::Serializer,
|
where S: ::serde::ser::Serializer,
|
||||||
{
|
{
|
||||||
match self.state {
|
loop {
|
||||||
$arms
|
match self.state {
|
||||||
_ => Ok(None)
|
$arms
|
||||||
|
_ => { return Ok(None); }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -652,5 +699,5 @@ fn serialize_struct_visitor<I>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
).unwrap(),
|
).unwrap(),
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "serde_macros"
|
name = "serde_macros"
|
||||||
version = "0.5.3"
|
version = "0.6.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"
|
||||||
@@ -13,9 +13,11 @@ name = "serde_macros"
|
|||||||
plugin = true
|
plugin = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde_codegen = { version = "*", path = "../serde_codegen", default-features = false, features = ["nightly"] }
|
clippy = "^0.0.36"
|
||||||
|
serde_codegen = { version = "^0.6.10", path = "../serde_codegen", default-features = false, features = ["nightly"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
num = "*"
|
compiletest_rs = "^0.0.11"
|
||||||
rustc-serialize = "*"
|
num = "^0.1.27"
|
||||||
serde = { version = "*", path = "../serde", features = ["nightly"] }
|
rustc-serialize = "^0.3.16"
|
||||||
|
serde = { version = "^0.6.10", path = "../serde", features = ["nightly", "num-impls"] }
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#![feature(custom_attribute, custom_derive, plugin, test)]
|
#![feature(custom_attribute, custom_derive, plugin, test)]
|
||||||
|
#![plugin(clippy)]
|
||||||
#![plugin(serde_macros)]
|
#![plugin(serde_macros)]
|
||||||
|
|
||||||
extern crate num;
|
extern crate num;
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
#![feature(plugin_registrar, rustc_private)]
|
#![feature(plugin, plugin_registrar, rustc_private)]
|
||||||
|
#![plugin(clippy)]
|
||||||
|
|
||||||
extern crate serde_codegen;
|
extern crate serde_codegen;
|
||||||
extern crate rustc;
|
extern crate rustc_plugin;
|
||||||
|
|
||||||
#[plugin_registrar]
|
#[plugin_registrar]
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
pub fn plugin_registrar(reg: &mut rustc::plugin::Registry) {
|
pub fn plugin_registrar(reg: &mut rustc_plugin::Registry) {
|
||||||
serde_codegen::register(reg);
|
serde_codegen::register(reg);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
#![feature(custom_attribute, custom_derive, plugin)]
|
||||||
|
#![plugin(serde_macros)]
|
||||||
|
|
||||||
|
extern crate serde;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
#[serde(abc="xyz")] //~ unknown serde container attribute `abc = "xyz"`
|
||||||
|
struct Foo {
|
||||||
|
x: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
#[serde(abc="xyz")] //~ unknown serde container attribute `abc = "xyz"`
|
||||||
|
struct Foo {
|
||||||
|
x: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct Foo {
|
||||||
|
#[serde(abc="xyz")] //~ unknown serde field attribute `abc = "xyz"`
|
||||||
|
x: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct Foo {
|
||||||
|
#[serde(abc="xyz")] //~ unknown serde field attribute `abc = "xyz"`
|
||||||
|
x: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() { }
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
extern crate compiletest_rs as compiletest;
|
||||||
|
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::env::var;
|
||||||
|
|
||||||
|
fn run_mode(mode: &'static str) {
|
||||||
|
let mut config = compiletest::default_config();
|
||||||
|
|
||||||
|
let cfg_mode = mode.parse().ok().expect("Invalid mode");
|
||||||
|
|
||||||
|
config.target_rustcflags = Some("-L target/debug/ -L target/debug/deps/".to_owned());
|
||||||
|
if let Ok(name) = var::<&str>("TESTNAME") {
|
||||||
|
let s : String = name.to_owned();
|
||||||
|
config.filter = Some(s)
|
||||||
|
}
|
||||||
|
config.mode = cfg_mode;
|
||||||
|
config.src_base = PathBuf::from(format!("tests/{}", mode));
|
||||||
|
|
||||||
|
compiletest::run_tests(&config);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn compile_test() {
|
||||||
|
run_mode("compile-fail");
|
||||||
|
}
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
#![feature(test, custom_attribute, custom_derive, plugin)]
|
#![feature(test, custom_attribute, custom_derive, plugin)]
|
||||||
#![plugin(serde_macros)]
|
#![plugin(serde_macros)]
|
||||||
|
|
||||||
|
extern crate num;
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
extern crate test;
|
extern crate test;
|
||||||
|
|
||||||
include!("../../serde_tests/tests/test.rs.in");
|
include!("../../serde_tests/tests/test.rs.in");
|
||||||
|
|
||||||
|
mod compile_tests;
|
||||||
|
|||||||
+13
-7
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "serde_tests"
|
name = "serde_tests"
|
||||||
version = "0.5.0"
|
version = "0.6.2"
|
||||||
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"
|
||||||
@@ -10,16 +10,22 @@ readme = "README.md"
|
|||||||
keywords = ["serialization"]
|
keywords = ["serialization"]
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
nightly = ["clippy", "serde/nightly"]
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
syntex = { version = "*" }
|
syntex = { version = "^0.26.0" }
|
||||||
syntex_syntax = { version = "*" }
|
syntex_syntax = { version = "^0.26.0" }
|
||||||
serde_codegen = { version = "*", path = "../serde_codegen", features = ["with-syntex"] }
|
serde_codegen = { version = "*", path = "../serde_codegen", features = ["with-syntex"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
num = "*"
|
num = "^0.1.27"
|
||||||
rustc-serialize = "*"
|
rustc-serialize = "^0.3.16"
|
||||||
serde = { version = "*", path = "../serde" }
|
serde = { version = "*", path = "../serde", features = ["num-impls"] }
|
||||||
syntex = "*"
|
syntex = "^0.26.0"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clippy = { version = "^0.0.36", optional = true }
|
||||||
|
|
||||||
[[test]]
|
[[test]]
|
||||||
name = "test"
|
name = "test"
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
#![feature(test)]
|
#![feature(test)]
|
||||||
|
#![cfg_attr(feature = "nightly", feature(plugin))]
|
||||||
|
#![cfg_attr(feature = "nightly", plugin(clippy))]
|
||||||
|
|
||||||
extern crate num;
|
extern crate num;
|
||||||
extern crate rustc_serialize;
|
extern crate rustc_serialize;
|
||||||
|
|||||||
@@ -415,7 +415,7 @@ fn bench_decoder_dog(b: &mut Bencher) {
|
|||||||
#[bench]
|
#[bench]
|
||||||
fn bench_decoder_frog(b: &mut Bencher) {
|
fn bench_decoder_frog(b: &mut Bencher) {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
let animal = Animal::Frog("Henry".to_string(), 349);
|
let animal = Animal::Frog("Henry".to_owned(), 349);
|
||||||
|
|
||||||
let mut d = decoder::AnimalDecoder::new(animal.clone());
|
let mut d = decoder::AnimalDecoder::new(animal.clone());
|
||||||
let value: Animal = Decodable::decode(&mut d).unwrap();
|
let value: Animal = Decodable::decode(&mut d).unwrap();
|
||||||
@@ -439,7 +439,7 @@ fn bench_deserializer_dog(b: &mut Bencher) {
|
|||||||
#[bench]
|
#[bench]
|
||||||
fn bench_deserializer_frog(b: &mut Bencher) {
|
fn bench_deserializer_frog(b: &mut Bencher) {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
let animal = Animal::Frog("Henry".to_string(), 349);
|
let animal = Animal::Frog("Henry".to_owned(), 349);
|
||||||
|
|
||||||
let mut d = deserializer::AnimalDeserializer::new(animal.clone());
|
let mut d = deserializer::AnimalDeserializer::new(animal.clone());
|
||||||
let value: Animal = Deserialize::deserialize(&mut d).unwrap();
|
let value: Animal = Deserialize::deserialize(&mut d).unwrap();
|
||||||
|
|||||||
@@ -614,7 +614,7 @@ mod deserializer {
|
|||||||
fn bench_decoder_0_0(b: &mut Bencher) {
|
fn bench_decoder_0_0(b: &mut Bencher) {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
map.insert("abc".to_string(), Some('c'));
|
map.insert("abc".to_owned(), Some('c'));
|
||||||
|
|
||||||
let outer = Outer {
|
let outer = Outer {
|
||||||
inner: vec!(),
|
inner: vec!(),
|
||||||
@@ -653,11 +653,11 @@ fn bench_decoder_1_0(b: &mut Bencher) {
|
|||||||
fn bench_decoder_1_5(b: &mut Bencher) {
|
fn bench_decoder_1_5(b: &mut Bencher) {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
map.insert("1".to_string(), Some('a'));
|
map.insert("1".to_owned(), Some('a'));
|
||||||
map.insert("2".to_string(), None);
|
map.insert("2".to_owned(), None);
|
||||||
map.insert("3".to_string(), Some('b'));
|
map.insert("3".to_owned(), Some('b'));
|
||||||
map.insert("4".to_string(), None);
|
map.insert("4".to_owned(), None);
|
||||||
map.insert("5".to_string(), Some('c'));
|
map.insert("5".to_owned(), Some('c'));
|
||||||
|
|
||||||
let outer = Outer {
|
let outer = Outer {
|
||||||
inner: vec!(
|
inner: vec!(
|
||||||
@@ -716,11 +716,11 @@ fn bench_deserializer_1_0(b: &mut Bencher) {
|
|||||||
fn bench_deserializer_1_5(b: &mut Bencher) {
|
fn bench_deserializer_1_5(b: &mut Bencher) {
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
map.insert("1".to_string(), Some('a'));
|
map.insert("1".to_owned(), Some('a'));
|
||||||
map.insert("2".to_string(), None);
|
map.insert("2".to_owned(), None);
|
||||||
map.insert("3".to_string(), Some('b'));
|
map.insert("3".to_owned(), Some('b'));
|
||||||
map.insert("4".to_string(), None);
|
map.insert("4".to_owned(), None);
|
||||||
map.insert("5".to_string(), Some('c'));
|
map.insert("5".to_owned(), Some('c'));
|
||||||
|
|
||||||
let outer = Outer {
|
let outer = Outer {
|
||||||
inner: vec!(
|
inner: vec!(
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
#![cfg_attr(feature = "nightly", feature(plugin))]
|
||||||
|
#![cfg_attr(feature = "nightly", plugin(clippy))]
|
||||||
|
|
||||||
|
extern crate num;
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/test.rs"));
|
include!(concat!(env!("OUT_DIR"), "/test.rs"));
|
||||||
|
|||||||
@@ -39,6 +39,20 @@ struct SkipSerializingFields<A: default::Default> {
|
|||||||
b: A,
|
b: A,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||||
|
struct SkipSerializingIfEmptyFields<A: default::Default> {
|
||||||
|
a: i8,
|
||||||
|
#[serde(skip_serializing_if_empty, default)]
|
||||||
|
b: Vec<A>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Deserialize, Serialize)]
|
||||||
|
struct SkipSerializingIfNoneFields<A: default::Default> {
|
||||||
|
a: i8,
|
||||||
|
#[serde(skip_serializing_if_none, default)]
|
||||||
|
b: Option<A>,
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_default() {
|
fn test_default() {
|
||||||
assert_de_tokens(
|
assert_de_tokens(
|
||||||
@@ -169,3 +183,161 @@ fn test_skip_serializing_fields() {
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_skip_serializing_fields_if_empty() {
|
||||||
|
assert_ser_tokens(
|
||||||
|
&SkipSerializingIfEmptyFields::<i32> {
|
||||||
|
a: 1,
|
||||||
|
b: vec![],
|
||||||
|
},
|
||||||
|
&[
|
||||||
|
Token::StructStart("SkipSerializingIfEmptyFields", Some(1)),
|
||||||
|
|
||||||
|
Token::MapSep,
|
||||||
|
Token::Str("a"),
|
||||||
|
Token::I8(1),
|
||||||
|
|
||||||
|
Token::MapEnd,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_de_tokens(
|
||||||
|
&SkipSerializingIfEmptyFields::<i32> {
|
||||||
|
a: 1,
|
||||||
|
b: vec![],
|
||||||
|
},
|
||||||
|
vec![
|
||||||
|
Token::StructStart("SkipSerializingIfEmptyFields", Some(1)),
|
||||||
|
|
||||||
|
Token::MapSep,
|
||||||
|
Token::Str("a"),
|
||||||
|
Token::I8(1),
|
||||||
|
|
||||||
|
Token::MapEnd,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_ser_tokens(
|
||||||
|
&SkipSerializingIfEmptyFields {
|
||||||
|
a: 1,
|
||||||
|
b: vec![2],
|
||||||
|
},
|
||||||
|
&[
|
||||||
|
Token::StructStart("SkipSerializingIfEmptyFields", Some(2)),
|
||||||
|
|
||||||
|
Token::MapSep,
|
||||||
|
Token::Str("a"),
|
||||||
|
Token::I8(1),
|
||||||
|
|
||||||
|
Token::MapSep,
|
||||||
|
Token::Str("b"),
|
||||||
|
Token::SeqStart(Some(1)),
|
||||||
|
Token::SeqSep,
|
||||||
|
Token::I32(2),
|
||||||
|
Token::SeqEnd,
|
||||||
|
|
||||||
|
Token::MapEnd,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_de_tokens(
|
||||||
|
&SkipSerializingIfEmptyFields {
|
||||||
|
a: 1,
|
||||||
|
b: vec![2],
|
||||||
|
},
|
||||||
|
vec![
|
||||||
|
Token::StructStart("SkipSerializingIfEmptyFields", Some(2)),
|
||||||
|
|
||||||
|
Token::MapSep,
|
||||||
|
Token::Str("a"),
|
||||||
|
Token::I8(1),
|
||||||
|
|
||||||
|
Token::MapSep,
|
||||||
|
Token::Str("b"),
|
||||||
|
Token::SeqStart(Some(1)),
|
||||||
|
Token::SeqSep,
|
||||||
|
Token::I32(2),
|
||||||
|
Token::SeqEnd,
|
||||||
|
|
||||||
|
Token::MapEnd,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_skip_serializing_fields_if_none() {
|
||||||
|
assert_ser_tokens(
|
||||||
|
&SkipSerializingIfNoneFields::<i32> {
|
||||||
|
a: 1,
|
||||||
|
b: None,
|
||||||
|
},
|
||||||
|
&[
|
||||||
|
Token::StructStart("SkipSerializingIfNoneFields", Some(1)),
|
||||||
|
|
||||||
|
Token::MapSep,
|
||||||
|
Token::Str("a"),
|
||||||
|
Token::I8(1),
|
||||||
|
|
||||||
|
Token::MapEnd,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_de_tokens(
|
||||||
|
&SkipSerializingIfNoneFields::<i32> {
|
||||||
|
a: 1,
|
||||||
|
b: None,
|
||||||
|
},
|
||||||
|
vec![
|
||||||
|
Token::StructStart("SkipSerializingIfNoneFields", Some(1)),
|
||||||
|
|
||||||
|
Token::MapSep,
|
||||||
|
Token::Str("a"),
|
||||||
|
Token::I8(1),
|
||||||
|
|
||||||
|
Token::MapEnd,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_ser_tokens(
|
||||||
|
&SkipSerializingIfNoneFields {
|
||||||
|
a: 1,
|
||||||
|
b: Some(2),
|
||||||
|
},
|
||||||
|
&[
|
||||||
|
Token::StructStart("SkipSerializingIfNoneFields", Some(2)),
|
||||||
|
|
||||||
|
Token::MapSep,
|
||||||
|
Token::Str("a"),
|
||||||
|
Token::I8(1),
|
||||||
|
|
||||||
|
Token::MapSep,
|
||||||
|
Token::Str("b"),
|
||||||
|
Token::Option(true),
|
||||||
|
Token::I32(2),
|
||||||
|
|
||||||
|
Token::MapEnd,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_de_tokens(
|
||||||
|
&SkipSerializingIfNoneFields {
|
||||||
|
a: 1,
|
||||||
|
b: Some(2),
|
||||||
|
},
|
||||||
|
vec![
|
||||||
|
Token::StructStart("SkipSerializingIfNoneFields", Some(2)),
|
||||||
|
|
||||||
|
Token::MapSep,
|
||||||
|
Token::Str("a"),
|
||||||
|
Token::I8(1),
|
||||||
|
|
||||||
|
Token::MapSep,
|
||||||
|
Token::Str("b"),
|
||||||
|
Token::Option(true),
|
||||||
|
Token::I32(2),
|
||||||
|
|
||||||
|
Token::MapEnd,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
|
||||||
|
|
||||||
|
use num::FromPrimitive;
|
||||||
|
use num::bigint::{BigInt, BigUint};
|
||||||
|
use num::complex::Complex;
|
||||||
|
use num::rational::Ratio;
|
||||||
|
|
||||||
use serde::de::{Deserializer, Visitor};
|
use serde::de::{Deserializer, Visitor};
|
||||||
|
|
||||||
use token::{Token, assert_de_tokens};
|
use token::{Token, assert_de_tokens};
|
||||||
@@ -90,12 +95,12 @@ declare_tests! {
|
|||||||
test_char {
|
test_char {
|
||||||
'a' => vec![Token::Char('a')],
|
'a' => vec![Token::Char('a')],
|
||||||
'a' => vec![Token::Str("a")],
|
'a' => vec![Token::Str("a")],
|
||||||
'a' => vec![Token::String("a".to_string())],
|
'a' => vec![Token::String("a".to_owned())],
|
||||||
}
|
}
|
||||||
test_string {
|
test_string {
|
||||||
"abc".to_string() => vec![Token::Str("abc")],
|
"abc".to_owned() => vec![Token::Str("abc")],
|
||||||
"abc".to_string() => vec![Token::String("abc".to_string())],
|
"abc".to_owned() => vec![Token::String("abc".to_owned())],
|
||||||
"a".to_string() => vec![Token::Char('a')],
|
"a".to_owned() => vec![Token::Char('a')],
|
||||||
}
|
}
|
||||||
test_option {
|
test_option {
|
||||||
None::<i32> => vec![Token::Unit],
|
None::<i32> => vec![Token::Unit],
|
||||||
@@ -555,4 +560,33 @@ declare_tests! {
|
|||||||
Token::Unit,
|
Token::Unit,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
test_num_bigint {
|
||||||
|
BigInt::from_i64(123).unwrap() => vec![Token::Str("123")],
|
||||||
|
BigInt::from_i64(-123).unwrap() => vec![Token::Str("-123")],
|
||||||
|
}
|
||||||
|
test_num_biguint {
|
||||||
|
BigUint::from_i64(123).unwrap() => vec![Token::Str("123")],
|
||||||
|
}
|
||||||
|
test_num_complex {
|
||||||
|
Complex::new(1, 2) => vec![
|
||||||
|
Token::SeqStart(Some(2)),
|
||||||
|
Token::SeqSep,
|
||||||
|
Token::I32(1),
|
||||||
|
|
||||||
|
Token::SeqSep,
|
||||||
|
Token::I32(2),
|
||||||
|
Token::SeqEnd,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
test_num_ratio {
|
||||||
|
Ratio::new(1, 2) => vec![
|
||||||
|
Token::SeqStart(Some(2)),
|
||||||
|
Token::SeqSep,
|
||||||
|
Token::I32(1),
|
||||||
|
|
||||||
|
Token::SeqSep,
|
||||||
|
Token::I32(2),
|
||||||
|
Token::SeqEnd,
|
||||||
|
],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use num::FromPrimitive;
|
||||||
|
use num::bigint::{BigInt, BigUint};
|
||||||
|
use num::complex::Complex;
|
||||||
|
use num::rational::Ratio;
|
||||||
|
|
||||||
use token::Token;
|
use token::Token;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
@@ -58,7 +63,7 @@ declare_ser_tests! {
|
|||||||
}
|
}
|
||||||
test_str {
|
test_str {
|
||||||
"abc" => &[Token::Str("abc")],
|
"abc" => &[Token::Str("abc")],
|
||||||
"abc".to_string() => &[Token::Str("abc")],
|
"abc".to_owned() => &[Token::Str("abc")],
|
||||||
}
|
}
|
||||||
test_option {
|
test_option {
|
||||||
None::<i32> => &[Token::Option(false)],
|
None::<i32> => &[Token::Option(false)],
|
||||||
@@ -259,4 +264,33 @@ declare_ser_tests! {
|
|||||||
Token::MapEnd,
|
Token::MapEnd,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
test_num_bigint {
|
||||||
|
BigInt::from_i64(123).unwrap() => &[Token::Str("123")],
|
||||||
|
BigInt::from_i64(-123).unwrap() => &[Token::Str("-123")],
|
||||||
|
}
|
||||||
|
test_num_biguint {
|
||||||
|
BigUint::from_i64(123).unwrap() => &[Token::Str("123")],
|
||||||
|
}
|
||||||
|
test_num_complex {
|
||||||
|
Complex::new(1, 2) => &[
|
||||||
|
Token::SeqStart(Some(2)),
|
||||||
|
Token::SeqSep,
|
||||||
|
Token::I32(1),
|
||||||
|
|
||||||
|
Token::SeqSep,
|
||||||
|
Token::I32(2),
|
||||||
|
Token::SeqEnd,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
test_num_ratio {
|
||||||
|
Ratio::new(1, 2) => &[
|
||||||
|
Token::SeqStart(Some(2)),
|
||||||
|
Token::SeqSep,
|
||||||
|
Token::I32(1),
|
||||||
|
|
||||||
|
Token::SeqSep,
|
||||||
|
Token::I32(2),
|
||||||
|
Token::SeqEnd,
|
||||||
|
],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -325,7 +325,7 @@ impl de::Error for Error {
|
|||||||
fn end_of_stream() -> Error { Error::EndOfStreamError }
|
fn end_of_stream() -> Error { Error::EndOfStreamError }
|
||||||
|
|
||||||
fn unknown_field(field: &str) -> Error {
|
fn unknown_field(field: &str) -> Error {
|
||||||
Error::UnknownFieldError(field.to_string())
|
Error::UnknownFieldError(field.to_owned())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn missing_field(field: &'static str) -> Error {
|
fn missing_field(field: &'static str) -> Error {
|
||||||
@@ -379,7 +379,6 @@ impl<I> de::Deserializer for Deserializer<I>
|
|||||||
fn visit<V>(&mut self, mut visitor: V) -> Result<V::Value, Error>
|
fn visit<V>(&mut self, mut visitor: V) -> Result<V::Value, Error>
|
||||||
where V: de::Visitor,
|
where V: de::Visitor,
|
||||||
{
|
{
|
||||||
println!("visit {:?}", self.tokens.peek());
|
|
||||||
match self.tokens.next() {
|
match self.tokens.next() {
|
||||||
Some(Token::Bool(v)) => visitor.visit_bool(v),
|
Some(Token::Bool(v)) => visitor.visit_bool(v),
|
||||||
Some(Token::Isize(v)) => visitor.visit_isize(v),
|
Some(Token::Isize(v)) => visitor.visit_isize(v),
|
||||||
|
|||||||
Reference in New Issue
Block a user