Compare commits

...

26 Commits

Author SHA1 Message Date
Erick Tryzelaar 8f08baf43a feat(cargo): Version bump 2015-12-08 09:57:33 -05:00
Erick Tryzelaar b3b3b7d565 fix(rustup): Sync serde_macros with latest nightly, aster, and quasi 2015-12-08 09:41:57 -05:00
Erick Tryzelaar 1a8a11e924 feat(impls): Add impls for num::{BigInt,BigUint,Complex,Ratio} 2015-12-01 09:03:08 -08:00
Erick Tryzelaar f3f098e7f5 feat(cargo): Version bump 2015-11-28 20:30:36 -08:00
Erick Tryzelaar 09822c99cc fix(rustup): Update serde_codegen to reflect Registry move 2015-11-28 20:17:21 -08:00
Erick Tryzelaar 966b104d48 fix(rustup): nightly rust moved Registry into rustc_plugin 2015-11-28 20:09:54 -08:00
Erick Tryzelaar 59e0d5e081 fix(warning): #[automatically_derived] was removed 2015-11-28 20:09:31 -08:00
Erick Tryzelaar c687ee60ff feat(example): Add an example 2015-11-28 20:09:03 -08:00
Erick Tryzelaar af6fbba9b8 feat(cargo): Bump syntex, aster, and quasi versions 2015-11-22 21:56:44 -08:00
Erick Tryzelaar a577574cfe feat(cargo): Bump syntex, aster, and quasi versions 2015-11-09 13:50:53 -08:00
Erick Tryzelaar 7521db7b0b fix(crates): Bump aster and syntex_syntax 2015-11-01 13:24:26 -08:00
Erick Tryzelaar 72de877ec3 Merge pull request #173 from erickt/update
Update aster, quasi, and syntex
2015-10-17 20:03:15 -07:00
Erick Tryzelaar f872b3fb4b fix(cargo): Update aster, quasi, and syntex 2015-10-17 19:44:07 -07:00
Erick Tryzelaar ddc33ee747 cleanup(whitespace): Fix some whitespace issues 2015-10-14 08:52:25 -07:00
Erick Tryzelaar 612e384b03 Merge pull request #171 from oli-obk/fix_skip_empty_field_ser
$value_expr starting with a & took address of resulting bool
2015-10-12 13:41:23 -07:00
Oliver Schneider 1c88856fc8 $value_expr starting with a & took address of resulting bool 2015-10-12 11:04:50 +02:00
Erick Tryzelaar 534e6d1f4c Merge pull request #167 from pwoolcoc/patch-1
Fix code block end marker
2015-10-06 11:50:54 -07:00
Paul Woolcock 7ad31a01dd Fix code block end marker
Looks like someone accidentally removed the `\`\`\`` from the end of a code block, causing the `Serialization without Macros` section to be formatted like code
2015-10-06 14:04:54 -04:00
Erick Tryzelaar 00cd2900e7 Merge pull request #155 from erickt/skip
Add skip serializing fields if empty or none
2015-10-05 14:51:46 -07:00
Erick Tryzelaar 05a238fac4 Merge pull request #164 from serde-rs/oli-obk-patch-1
add link to the README from the docs
2015-10-05 14:49:46 -07:00
Oliver Schneider 310db21d64 add link to the README from the docs
The small starting page in the docs was noted at https://users.rust-lang.org/t/lets-talk-about-ecosystem-documentation/2791/25
2015-10-05 15:57:01 +02:00
Erick Tryzelaar 72af0896e8 docs(codegen): Document annotations 2015-09-07 16:59:11 -07:00
Erick Tryzelaar c4392ff256 feat(codegen): Add more attributes to skip serializing
These attributes are `#[serde(skip_serializing_if_none)]` and
`#[serde(skip_serializing_if_empty)]`.
2015-09-07 16:58:52 -07:00
Erick Tryzelaar c68ab508c0 refactor(codegen): Simplify parsing attributes 2015-09-07 16:58:52 -07:00
Erick Tryzelaar 76cca814f0 docs(readme): Improve the readme 2015-09-07 16:58:46 -07:00
Erick Tryzelaar 22b69fc5c9 docs(serde): Fix typo 2015-09-07 13:02:53 -07:00
27 changed files with 862 additions and 346 deletions
+43 -6
View File
@@ -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
================================= =================================
+2
View File
@@ -0,0 +1,2 @@
target
Cargo.lock
+18
View File
@@ -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 }
+20
View File
@@ -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
```
+29
View File
@@ -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();
}
+11
View File
@@ -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);
}
+6 -3
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "serde" name = "serde"
version = "0.6.0" version = "0.6.6"
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,8 +10,11 @@ readme = "../README.md"
keywords = ["serde", "serialization"] keywords = ["serde", "serialization"]
[dependencies] [dependencies]
num = "*" num = "^0.1.27"
[features] [features]
nightly = [] nightly = []
num-impls = ["num-bigint", "num-complex", "num-rational"]
num-bigint = ["num/bigint"]
num-complex = ["num/complex"]
num-rational = ["num/rational"]
+84
View File
@@ -895,3 +895,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))
}
}
}
+5
View File
@@ -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;
+3
View File
@@ -5,6 +5,9 @@
//! 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, core, enumset, nonzero, step_trait, zero_one))]
+34
View File
@@ -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)
}
}
+1 -1
View File
@@ -337,7 +337,7 @@ 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.
pub trait MapVisitor { pub trait MapVisitor {
/// Serializes a map item in the serializer. /// Serializes a map item in the serializer.
/// ///
+8 -8
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "serde_codegen" name = "serde_codegen"
version = "0.5.3" version = "0.6.6"
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"
@@ -15,12 +15,12 @@ nightly = ["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 = { verision = "^0.3.9", optional = true }
syntex = { version = "*", optional = true } syntex = { version = "^0.22.0", optional = true }
[dependencies] [dependencies]
aster = { version = "*", default-features = false } aster = { version = "^0.9.0", default-features = false }
quasi = { verision = "*", default-features = false } quasi = { verision = "^0.3.9", default-features = false }
quasi_macros = { version = "*", optional = true } quasi_macros = { version = "^0.3.9", optional = true }
syntex = { version = "*", optional = true } syntex = { version = "^0.22.0", optional = true }
syntex_syntax = { version = "*", optional = true } syntex_syntax = { version = "^0.23.0", optional = true }
+168 -43
View File
@@ -2,10 +2,14 @@ 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::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,43 +19,16 @@ 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 {
@@ -70,22 +47,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 +70,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{formats: _, 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 +93,153 @@ 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> {
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(builder: &'a aster::AstBuilder) -> FieldAttrsBuilder<'a> {
FieldAttrsBuilder {
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) -> 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(self, attrs: &[ast::Attribute]) -> FieldAttrsBuilder<'a> {
attrs.iter().fold(self, FieldAttrsBuilder::attr)
}
pub fn attr(self, attr: &ast::Attribute) -> FieldAttrsBuilder<'a> {
match attr.node.value.node {
ast::MetaList(ref name, ref items) if name == &"serde" => {
attr::mark_used(&attr);
items.iter().fold(self, FieldAttrsBuilder::meta_item)
}
_ => {
self
}
}
}
pub fn meta_item(mut self, meta_item: &P<ast::MetaItem>) -> 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()));
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);
}
_ => { }
}
}
self
}
ast::MetaWord(ref name) if name == &"default" => {
self.default()
}
ast::MetaWord(ref name) if name == &"skip_serializing" => {
self.skip_serializing_field()
}
ast::MetaWord(ref name) if name == &"skip_serializing_if_empty" => {
self.skip_serializing_field_if_empty()
}
ast::MetaWord(ref name) if name == &"skip_serializing_if_none" => {
self.skip_serializing_field_if_none()
}
_ => {
// Ignore unknown meta variables for now.
self
}
}
}
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,
}
}
} }
+60 -65
View File
@@ -4,12 +4,11 @@ use aster;
use syntax::ast::{ use syntax::ast::{
self, self,
Ident,
MetaItem,
Item,
Expr,
StructDef,
EnumDef, EnumDef,
Expr,
Ident,
Item,
MetaItem,
}; };
use syntax::codemap::Span; use syntax::codemap::Span;
use syntax::ext::base::{Annotatable, ExtCtxt}; use syntax::ext::base::{Annotatable, ExtCtxt};
@@ -66,7 +65,6 @@ pub fn expand_derive_deserialize(
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,
@@ -87,14 +85,14 @@ fn deserialize_body(
ty: P<ast::Ty>, ty: P<ast::Ty>,
) -> P<ast::Expr> { ) -> P<ast::Expr> {
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, variant_data,
) )
} }
ast::ItemEnum(ref enum_def, _) => { ast::ItemEnum(ref enum_def, _) => {
@@ -117,27 +115,17 @@ 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, variant_data: &ast::VariantData,
) -> P<ast::Expr> { ) -> P<ast::Expr> {
let mut named_fields = vec![]; match *variant_data {
let mut unnamed_fields = 0; 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 +134,34 @@ fn deserialize_item_struct(
ty, ty,
) )
} }
(true, _) => { ast::VariantData::Tuple(ref fields, _) => {
if fields.iter().any(|field| !field.node.kind.is_unnamed()) {
cx.bug("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.bug("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")
}
} }
} }
@@ -422,9 +415,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> { ) -> 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 +433,13 @@ 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.bug("struct contains unnamed fields"),
}, },
builder.expr().id(format!("__field{}", i)), builder.expr().id(format!("__field{}", i)),
) )
@@ -469,7 +462,7 @@ 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> { ) -> P<ast::Expr> {
let where_clause = &impl_generics.where_clause; let where_clause = &impl_generics.where_clause;
@@ -486,14 +479,14 @@ fn deserialize_struct(
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) = 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);
@@ -543,11 +536,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(builder)
true, .name(expr)
builder.expr().str(variant.node.name))) .default()
.build()
})
.collect() .collect()
); );
@@ -591,7 +586,7 @@ fn deserialize_item_enum(
impl_generics, impl_generics,
vec![deserializer_ty_param(builder)], vec![deserializer_ty_param(builder)],
vec![deserializer_ty_arg(builder)], vec![deserializer_ty_arg(builder)],
); );
quote_expr!(cx, { quote_expr!(cx, {
$variant_visitor $variant_visitor
@@ -626,20 +621,20 @@ fn deserialize_variant(
) -> P<ast::Expr> { ) -> 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, { 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, { 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 +642,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 +653,7 @@ fn deserialize_variant(
variant_ident, variant_ident,
generics, generics,
ty, ty,
struct_def, fields,
) )
} }
} }
@@ -681,7 +676,7 @@ fn deserialize_tuple_variant(
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,
@@ -714,7 +709,7 @@ 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> { ) -> P<ast::Expr> {
let where_clause = &generics.where_clause; let where_clause = &generics.where_clause;
@@ -727,14 +722,14 @@ fn deserialize_struct_variant(
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) = 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) =
@@ -743,7 +738,7 @@ fn deserialize_struct_variant(
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, { quote_expr!(cx, {
$field_visitor $field_visitor
@@ -789,7 +784,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,25 +920,25 @@ 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,
fields: &[ast::StructField],
) -> (Vec<P<ast::Item>>, P<ast::Stmt>, P<ast::Expr>) { ) -> (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), field::struct_field_attrs(cx, builder, fields),
); );
let visit_map_expr = deserialize_map( let visit_map_expr = 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),
@@ -964,10 +959,10 @@ 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> { ) -> 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();
@@ -988,7 +983,7 @@ fn deserialize_map(
.collect(); .collect();
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::struct_field_attrs(cx, builder, fields).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,7 +1020,7 @@ 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)| {
( (
+7 -137
View File
@@ -1,147 +1,17 @@
use std::collections::HashMap; use syntax::ast;
use syntax::ext::base::ExtCtxt;
use aster; use aster;
use attr::{FieldAttrs, FieldAttrsBuilder};
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> { ) -> Vec<FieldAttrs> {
struct_def.fields.iter() fields.iter()
.map(|field| { .map(|field| {
match field_attrs(builder, field) { FieldAttrsBuilder::new(builder).field(field).build()
(Rename::Global(rename), default_value, skip_serializing_field) =>
FieldAttrs::new(
skip_serializing_field,
default_value,
builder.expr().build_lit(P(rename.clone()))),
(Rename::Format(renames), default_value, skip_serializing_field) => {
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() .collect()
} }
fn default_field_name(
cx: &ExtCtxt,
builder: &aster::AstBuilder,
kind: ast::StructFieldKind,
) -> P<ast::Expr> {
match kind {
ast::NamedField(name, _) => {
builder.expr().str(name)
}
ast::UnnamedField(_) => {
cx.bug("struct has named and unnamed fields")
}
}
}
+2 -2
View File
@@ -14,7 +14,7 @@ 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(feature = "with-syntex")] #[cfg(feature = "with-syntex")]
include!(concat!(env!("OUT_DIR"), "/lib.rs")); include!(concat!(env!("OUT_DIR"), "/lib.rs"));
@@ -60,7 +60,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(
+90 -68
View File
@@ -5,7 +5,6 @@ use syntax::ast::{
MetaItem, MetaItem,
Item, Item,
Expr, Expr,
StructDef,
}; };
use syntax::ast; use syntax::ast;
use syntax::codemap::Span; use syntax::codemap::Span;
@@ -61,7 +60,6 @@ pub fn expand_derive_serialize(
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,
@@ -82,14 +80,14 @@ fn serialize_body(
ty: P<ast::Ty>, ty: P<ast::Ty>,
) -> P<ast::Expr> { ) -> P<ast::Expr> {
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, variant_data,
) )
} }
ast::ItemEnum(ref enum_def, _) => { ast::ItemEnum(ref enum_def, _) => {
@@ -112,57 +110,51 @@ 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, variant_data: &ast::VariantData,
) -> P<ast::Expr> { ) -> P<ast::Expr> {
let mut named_fields = vec![]; match *variant_data {
let mut unnamed_fields = 0; 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.bug("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.bug("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")
}
} }
} }
@@ -225,8 +217,7 @@ 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>,
) -> P<ast::Expr> { ) -> P<ast::Expr> {
let (visitor_struct, visitor_impl) = serialize_struct_visitor( let (visitor_struct, visitor_impl) = serialize_struct_visitor(
cx, cx,
@@ -236,9 +227,12 @@ 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)), fields.iter().map(|field| {
let name = field.node.ident().expect("struct has unnamed field");
quote_expr!(cx, &self.value.$name)
})
); );
let type_name = builder.expr().str(type_ident); let type_name = builder.expr().str(type_ident);
@@ -297,8 +291,8 @@ fn serialize_variant(
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();
@@ -314,7 +308,7 @@ fn serialize_variant(
} }
) )
}, },
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_()
@@ -333,14 +327,17 @@ fn serialize_variant(
} }
) )
}, },
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,22 +348,22 @@ fn serialize_variant(
variant_name, variant_name,
generics, generics,
ty, ty,
args,
fields, fields,
field_names,
); );
quote_arm!(cx, $pat => { $expr }) 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,
@@ -388,8 +385,8 @@ fn serialize_variant(
variant_name, variant_name,
generics, generics,
ty, ty,
struct_def,
fields, fields,
field_names,
); );
quote_arm!(cx, $pat => { $expr }) quote_arm!(cx, $pat => { $expr })
@@ -405,16 +402,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 +421,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 +452,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> { ) -> 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,7 +468,7 @@ 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)
}) })
) )
@@ -482,9 +479,9 @@ fn serialize_struct_variant(
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_()
@@ -574,29 +571,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>) ) -> (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 = 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 +610,7 @@ fn serialize_struct_visitor<I>(
) )
) )
) )
) );
} }
) )
}) })
@@ -622,6 +627,21 @@ 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));
( (
quote_item!(cx, quote_item!(cx,
struct Visitor $visitor_impl_generics $where_clause { struct Visitor $visitor_impl_generics $where_clause {
@@ -640,9 +660,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); }
}
} }
} }
+4 -4
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "serde_macros" name = "serde_macros"
version = "0.5.3" version = "0.6.6"
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"
@@ -16,6 +16,6 @@ plugin = true
serde_codegen = { version = "*", path = "../serde_codegen", default-features = false, features = ["nightly"] } serde_codegen = { version = "*", path = "../serde_codegen", default-features = false, features = ["nightly"] }
[dev-dependencies] [dev-dependencies]
num = "*" num = "^0.1.27"
rustc-serialize = "*" rustc-serialize = "^0.3.16"
serde = { version = "*", path = "../serde", features = ["nightly"] } serde = { version = "*", path = "../serde", features = ["nightly", "num-impls"] }
+2 -2
View File
@@ -1,10 +1,10 @@
#![feature(plugin_registrar, rustc_private)] #![feature(plugin_registrar, rustc_private)]
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);
} }
+1
View File
@@ -1,6 +1,7 @@
#![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;
+7 -7
View File
@@ -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"
@@ -11,15 +11,15 @@ keywords = ["serialization"]
build = "build.rs" build = "build.rs"
[build-dependencies] [build-dependencies]
syntex = { version = "*" } syntex = { version = "^0.22.0" }
syntex_syntax = { version = "*" } syntex_syntax = { version = "^0.23.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.22.0"
[[test]] [[test]]
name = "test" name = "test"
+1
View File
@@ -1,3 +1,4 @@
extern crate num;
extern crate serde; extern crate serde;
include!(concat!(env!("OUT_DIR"), "/test.rs")); include!(concat!(env!("OUT_DIR"), "/test.rs"));
+172
View File
@@ -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,
]
);
}
+34
View File
@@ -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};
@@ -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,
],
}
} }
+34
View File
@@ -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;
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@@ -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,
],
}
} }