Compare commits

...

25 Commits

Author SHA1 Message Date
Simon Persson a5d0703e44 Bump clippy dependency to compile on 1.8. 2016-01-23 16:15:31 -08:00
Erick Tryzelaar 0a32cea26e feat(impls): Allow options to be deserialized from units 2016-01-23 16:07:50 -08:00
Erick Tryzelaar da4e37d3f5 docs(tradeoffs): Add section that describes tradeoffs for option types 2016-01-20 11:15:43 -08:00
Erick Tryzelaar 3f9cbc157a fix(cargo): serde_codegen shouldn't depend on '*' serde dependencies 2016-01-18 13:15:09 -08:00
Erick Tryzelaar a51f930101 feat(cargo): Version bump 2016-01-18 13:11:16 -08:00
Erick Tryzelaar 77edd8e544 fix(clippy): Switch to using or_else when we get a str parse error 2016-01-18 13:07:30 -08:00
Erick Tryzelaar 8087b7cec6 fix(cargo): Bump clippy version 2016-01-18 13:00:21 -08:00
Erick Tryzelaar 8df841f048 fix(errors): Report errors on unknown #[serde(...)] attributes
Closes #51, #175, and #187
2016-01-18 12:39:46 -08:00
Erick Tryzelaar bfa2b69193 feat(clippy): Use clippy for it's extra lints 2016-01-18 12:24:03 -08:00
Erick Tryzelaar fbcf905c9f Merge pull request #210 from erickt/v0.6.x
feat(errors): Don't panic if annotating a non-struct/enum
2016-01-16 15:06:07 -08:00
Erick Tryzelaar 979a4bcd88 feat(errors): Don't panic if annotating a non-struct/enum
This also improves the error spans when there's an internal
error.

Closes #206.
2016-01-16 14:51:11 -08:00
Erick Tryzelaar 342ea25290 feat(cargo): Version bump 2016-01-13 07:05:32 -08:00
Erick Tryzelaar 8d8f17982a feat(cargo): Version bump 2016-01-13 06:59:30 -08:00
Erick Tryzelaar dd3233ac85 fix(cargo): Fix typos 2016-01-10 11:50:35 -08:00
Erick Tryzelaar 9b57f60c2a fix(cargo): Version bump 2016-01-07 14:50:18 -08:00
Erick Tryzelaar 8038832a79 fix(cargo): Version bump 2016-01-07 14:48:40 -08:00
Erick Tryzelaar 072ff149f5 fix(rustup): Update to latest rust; silence some warnings 2015-12-24 12:12:03 -05:00
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
33 changed files with 848 additions and 276 deletions
+63
View File
@@ -579,6 +579,69 @@ impl serde::de::Visitor for PointVisitor {
} }
``` ```
Design Considerations and tradeoffs for Serializers and Deserializers
=====================================================================
Serde serialization and deserialization implementations are written in such a
way that they err on being able to represent more values, and also provide
better error messages when they are passed an incorrect type to deserialize
from. For example, by default, it is a syntax error to deserialize a `String`
into an `Option<String>`. This is implemented such that it is possible to
distinguish between the values `None` and `Some(())`, if the serialization
format supports option types.
However, many formats do not have option types, and represents optional values
as either a `null`, or some other value. Serde `Serializer`s and
`Deserializer`s can opt-in support for this. For serialization, this is pretty
easy. Simply implement these methods:
```rust
...
fn visit_none(&mut self) -> Result<(), Self::Error> {
self.visit_unit()
}
fn visit_some<T>(&mut self, value: T) -> Result<(), Self::Error> {
value.serialize(self)
}
...
```
For deserialization, this can be implemented by way of the
`Deserializer::visit_option` hook, which presumes that there is some ability to peek at what is the
next value in the serialized token stream. This following example is from
[serde_tests::TokenDeserializer](https://github.com/serde-rs/serde/blob/master/serde_tests/tests/token.rs#L435-L454),
where it checks to see if the next value is an `Option`, a `()`, or some other
value:
```rust
...
fn visit_option<V>(&mut self, mut visitor: V) -> Result<V::Value, Error>
where V: de::Visitor,
{
match self.tokens.peek() {
Some(&Token::Option(false)) => {
self.tokens.next();
visitor.visit_none()
}
Some(&Token::Option(true)) => {
self.tokens.next();
visitor.visit_some(self)
}
Some(&Token::Unit) => {
self.tokens.next();
visitor.visit_none()
}
Some(_) => visitor.visit_some(self),
None => Err(Error::EndOfStreamError),
}
}
...
```
Annotations Annotations
=========== ===========
+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);
}
+10 -5
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "serde" name = "serde"
version = "0.6.1" version = "0.6.11"
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,8 +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 = "^0.1.27"
[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.37", optional = true }
num = { version = "^0.1.27", default-features = false }
+97 -4
View File
@@ -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) {
@@ -281,6 +283,13 @@ impl<
> Visitor for OptionVisitor<T> { > Visitor for OptionVisitor<T> {
type Value = Option<T>; type Value = Option<T>;
#[inline]
fn visit_unit<E>(&mut self) -> Result<Option<T>, E>
where E: Error,
{
Ok(None)
}
#[inline] #[inline]
fn visit_none<E>(&mut self) -> Result<Option<T>, E> fn visit_none<E>(&mut self) -> Result<Option<T>, E>
where E: Error, where E: Error,
@@ -895,3 +904,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;
+4 -1
View File
@@ -10,7 +10,10 @@
//! [github repository](https://github.com/serde-rs/serde) //! [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
View File
@@ -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)
}
}
+2
View File
@@ -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.
/// ///
@@ -338,6 +339,7 @@ pub trait SeqVisitor {
} }
/// A trait that is used by a `Serialize` 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.
/// ///
+10 -9
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "serde_codegen" name = "serde_codegen"
version = "0.6.4" version = "0.6.11"
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 = "^0.3.8", optional = true } quasi_codegen = { version = "^0.4.0", optional = true }
syntex = { version = "^0.22.0", optional = true } syntex = { version = "^0.26.0", optional = true }
[dependencies] [dependencies]
aster = { version = "^0.8.0", default-features = false } aster = { version = "^0.10.0", default-features = false }
quasi = { verision = "^0.3.8", default-features = false } clippy = { version = "^0.0.37", optional = true }
quasi_macros = { version = "^0.3.8", optional = true } quasi = { version = "^0.4.0", default-features = false }
syntex = { version = "^0.22.0", optional = true } quasi_macros = { version = "^0.4.0", optional = true }
syntex_syntax = { version = "^0.22.0", optional = true } syntex = { version = "^0.26.0", optional = true }
syntex_syntax = { version = "^0.26.0", optional = true }
+90 -18
View File
@@ -4,6 +4,7 @@ use std::collections::HashSet;
use syntax::ast; use syntax::ast;
use syntax::attr; 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; use aster;
@@ -32,7 +33,7 @@ impl FieldAttrs {
/// 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,7 +71,7 @@ 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,
} }
} }
@@ -104,6 +105,7 @@ impl FieldAttrs {
} }
pub struct FieldAttrsBuilder<'a> { pub struct FieldAttrsBuilder<'a> {
cx: &'a ExtCtxt<'a>,
builder: &'a aster::AstBuilder, builder: &'a aster::AstBuilder,
skip_serializing_field: bool, skip_serializing_field: bool,
skip_serializing_field_if_empty: bool, skip_serializing_field_if_empty: bool,
@@ -114,8 +116,10 @@ pub struct FieldAttrsBuilder<'a> {
} }
impl<'a> FieldAttrsBuilder<'a> { impl<'a> FieldAttrsBuilder<'a> {
pub fn new(builder: &'a aster::AstBuilder) -> FieldAttrsBuilder<'a> { pub fn new(cx: &'a ExtCtxt<'a>,
builder: &'a aster::AstBuilder) -> FieldAttrsBuilder<'a> {
FieldAttrsBuilder { FieldAttrsBuilder {
cx: cx,
builder: builder, builder: builder,
skip_serializing_field: false, skip_serializing_field: false,
skip_serializing_field_if_empty: false, skip_serializing_field_if_empty: false,
@@ -126,7 +130,7 @@ impl<'a> FieldAttrsBuilder<'a> {
} }
} }
pub fn field(mut self, field: &ast::StructField) -> FieldAttrsBuilder<'a> { pub fn field(mut self, field: &ast::StructField) -> Result<FieldAttrsBuilder<'a>, ()> {
match field.node.kind { match field.node.kind {
ast::NamedField(name, _) => { ast::NamedField(name, _) => {
self.name = Some(self.builder.expr().str(name)); self.name = Some(self.builder.expr().str(name));
@@ -137,28 +141,36 @@ impl<'a> FieldAttrsBuilder<'a> {
self.attrs(&field.node.attrs) self.attrs(&field.node.attrs)
} }
pub fn attrs(self, attrs: &[ast::Attribute]) -> FieldAttrsBuilder<'a> { pub fn attrs(mut self, attrs: &[ast::Attribute]) -> Result<FieldAttrsBuilder<'a>, ()> {
attrs.iter().fold(self, FieldAttrsBuilder::attr) for attr in attrs {
self = try!(self.attr(attr));
}
Ok(self)
} }
pub fn attr(self, attr: &ast::Attribute) -> FieldAttrsBuilder<'a> { pub fn attr(mut self, attr: &ast::Attribute) -> Result<FieldAttrsBuilder<'a>, ()> {
match attr.node.value.node { match attr.node.value.node {
ast::MetaList(ref name, ref items) if name == &"serde" => { ast::MetaList(ref name, ref items) if name == &"serde" => {
attr::mark_used(&attr); attr::mark_used(&attr);
items.iter().fold(self, FieldAttrsBuilder::meta_item) for item in items {
self = try!(self.meta_item(item));
}
Ok(self)
} }
_ => { _ => {
self Ok(self)
} }
} }
} }
pub fn meta_item(mut self, meta_item: &P<ast::MetaItem>) -> FieldAttrsBuilder<'a> { pub fn meta_item(mut self, meta_item: &P<ast::MetaItem>) -> Result<FieldAttrsBuilder<'a>, ()> {
match meta_item.node { match meta_item.node {
ast::MetaNameValue(ref name, ref lit) if name == &"rename" => { ast::MetaNameValue(ref name, ref lit) if name == &"rename" => {
let expr = self.builder.expr().build_lit(P(lit.clone())); let expr = self.builder.expr().build_lit(P(lit.clone()));
self.name(expr) Ok(self.name(expr))
} }
ast::MetaList(ref name, ref items) if name == &"rename" => { ast::MetaList(ref name, ref items) if name == &"rename" => {
for item in items { for item in items {
@@ -172,23 +184,27 @@ impl<'a> FieldAttrsBuilder<'a> {
_ => { } _ => { }
} }
} }
self
Ok(self)
} }
ast::MetaWord(ref name) if name == &"default" => { ast::MetaWord(ref name) if name == &"default" => {
self.default() Ok(self.default())
} }
ast::MetaWord(ref name) if name == &"skip_serializing" => { ast::MetaWord(ref name) if name == &"skip_serializing" => {
self.skip_serializing_field() Ok(self.skip_serializing_field())
} }
ast::MetaWord(ref name) if name == &"skip_serializing_if_empty" => { ast::MetaWord(ref name) if name == &"skip_serializing_if_empty" => {
self.skip_serializing_field_if_empty() Ok(self.skip_serializing_field_if_empty())
} }
ast::MetaWord(ref name) if name == &"skip_serializing_if_none" => { ast::MetaWord(ref name) if name == &"skip_serializing_if_none" => {
self.skip_serializing_field_if_none() Ok(self.skip_serializing_field_if_none())
} }
_ => { _ => {
// Ignore unknown meta variables for now. self.cx.span_err(
self meta_item.span,
&format!("unknown serde field attribute `{}`",
meta_item_to_string(meta_item)));
Err(())
} }
} }
} }
@@ -243,3 +259,59 @@ impl<'a> FieldAttrsBuilder<'a> {
} }
} }
} }
/// 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
}
}
+140 -127
View File
@@ -5,7 +5,6 @@ use aster;
use syntax::ast::{ use syntax::ast::{
self, self,
EnumDef, EnumDef,
Expr,
Ident, Ident,
Item, Item,
MetaItem, MetaItem,
@@ -13,7 +12,6 @@ use syntax::ast::{
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;
@@ -31,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;
} }
}; };
@@ -41,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())
@@ -54,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,
@@ -84,7 +86,11 @@ 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 variant_data, _) => { ast::ItemStruct(ref variant_data, _) => {
deserialize_item_struct( deserialize_item_struct(
@@ -93,6 +99,7 @@ fn deserialize_body(
item, item,
impl_generics, impl_generics,
ty, ty,
item.span,
variant_data, variant_data,
) )
} }
@@ -106,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)]")
}
} }
} }
@@ -116,8 +126,9 @@ fn deserialize_item_struct(
item: &Item, item: &Item,
impl_generics: &ast::Generics, impl_generics: &ast::Generics,
ty: P<ast::Ty>, ty: P<ast::Ty>,
span: Span,
variant_data: &ast::VariantData, variant_data: &ast::VariantData,
) -> P<ast::Expr> { ) -> Result<P<ast::Expr>, ()> {
match *variant_data { match *variant_data {
ast::VariantData::Unit(_) => { ast::VariantData::Unit(_) => {
deserialize_unit_struct( deserialize_unit_struct(
@@ -137,7 +148,7 @@ fn deserialize_item_struct(
} }
ast::VariantData::Tuple(ref fields, _) => { ast::VariantData::Tuple(ref fields, _) => {
if fields.iter().any(|field| !field.node.kind.is_unnamed()) { if fields.iter().any(|field| !field.node.kind.is_unnamed()) {
cx.bug("tuple struct has named fields") cx.span_bug(span, "tuple struct has named fields")
} }
deserialize_tuple_struct( deserialize_tuple_struct(
@@ -151,7 +162,7 @@ fn deserialize_item_struct(
} }
ast::VariantData::Struct(ref fields, _) => { ast::VariantData::Struct(ref fields, _) => {
if fields.iter().any(|field| field.node.kind.is_unnamed()) { if fields.iter().any(|field| field.node.kind.is_unnamed()) {
cx.bug("struct has unnamed fields") cx.span_bug(span, "struct has unnamed fields")
} }
deserialize_struct( deserialize_struct(
@@ -173,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))
@@ -188,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({
@@ -228,7 +239,7 @@ fn deserialize_visitor(
}) })
.build(), .build(),
trait_generics, trait_generics,
) ))
} }
} }
@@ -252,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 {
@@ -278,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(
@@ -287,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,
@@ -307,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 {
@@ -330,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(
@@ -340,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,
@@ -360,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 {
@@ -375,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(
@@ -417,7 +426,7 @@ fn deserialize_struct_as_seq(
builder: &aster::AstBuilder, builder: &aster::AstBuilder,
struct_path: ast::Path, struct_path: ast::Path,
fields: &[ast::StructField], fields: &[ast::StructField],
) -> P<ast::Expr> { ) -> Result<P<ast::Expr>, ()> {
let let_values: Vec<P<ast::Stmt>> = (0 .. 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));
@@ -440,7 +449,9 @@ fn deserialize_struct_as_seq(
( (
match field.node.kind { match field.node.kind {
ast::NamedField(name, _) => name.clone(), ast::NamedField(name, _) => name.clone(),
ast::UnnamedField(_) => cx.bug("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)),
) )
@@ -448,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(
@@ -464,35 +475,35 @@ fn deserialize_struct(
impl_generics: &ast::Generics, impl_generics: &ast::Generics,
ty: P<ast::Ty>, ty: P<ast::Ty>,
fields: &[ast::StructField], 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(),
fields, 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,
type_path.clone(), type_path.clone(),
fields, 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
@@ -518,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(
@@ -528,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);
@@ -539,7 +550,7 @@ fn deserialize_item_enum(
enum_def.variants.iter() enum_def.variants.iter()
.map(|variant| { .map(|variant| {
let expr = builder.expr().str(variant.node.name); let expr = builder.expr().str(variant.node.name);
attr::FieldAttrsBuilder::new(builder) attr::FieldAttrsBuilder::new(cx, builder)
.name(expr) .name(expr)
.default() .default()
.build() .build()
@@ -561,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
@@ -609,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(
@@ -619,21 +628,21 @@ 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.data { match variant.node.data {
ast::VariantData::Unit(_) => { 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::VariantData::Tuple(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::VariantData::Tuple(ref fields, _) => { ast::VariantData::Tuple(ref fields, _) => {
deserialize_tuple_variant( deserialize_tuple_variant(
@@ -668,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,
@@ -686,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 {
@@ -700,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(
@@ -711,7 +719,7 @@ fn deserialize_struct_variant(
generics: &ast::Generics, generics: &ast::Generics,
ty: P<ast::Ty>, ty: P<ast::Ty>,
fields: &[ast::StructField], 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()
@@ -719,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(),
fields, 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,
type_path, type_path,
fields, 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
@@ -767,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(
@@ -923,19 +930,19 @@ fn deserialize_struct_visitor(
builder: &aster::AstBuilder, builder: &aster::AstBuilder,
struct_path: ast::Path, struct_path: ast::Path,
fields: &[ast::StructField], fields: &[ast::StructField],
) -> (Vec<P<ast::Item>>, P<ast::Stmt>, P<ast::Expr>) { ) -> 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, fields), 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,
fields, fields,
); ));
let fields_expr = builder.expr().addr_of().slice() let fields_expr = builder.expr().addr_of().slice()
.with_exprs( .with_exprs(
@@ -943,7 +950,9 @@ fn deserialize_struct_visitor(
.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")
}
} }
}) })
) )
@@ -953,7 +962,7 @@ 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(
@@ -961,7 +970,7 @@ fn deserialize_map(
builder: &aster::AstBuilder, builder: &aster::AstBuilder,
struct_path: ast::Path, struct_path: ast::Path,
fields: &[ast::StructField], 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 .. 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)))
@@ -983,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, fields).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())
@@ -1027,7 +1038,9 @@ fn deserialize_map(
( (
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),
) )
@@ -1035,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()) {
@@ -1049,5 +1062,5 @@ fn deserialize_map(
try!(visitor.end()); try!(visitor.end());
Ok($result) Ok($result)
}) }))
} }
+21 -8
View File
@@ -2,16 +2,29 @@ use syntax::ast;
use syntax::ext::base::ExtCtxt; use syntax::ext::base::ExtCtxt;
use aster; use aster;
use attr::{FieldAttrs, FieldAttrsBuilder}; use attr;
pub fn struct_field_attrs( pub fn struct_field_attrs(
_cx: &ExtCtxt, cx: &ExtCtxt,
builder: &aster::AstBuilder, builder: &aster::AstBuilder,
fields: &[ast::StructField], fields: &[ast::StructField],
) -> Vec<FieldAttrs> { ) -> Result<Vec<attr::FieldAttrs>, ()> {
fields.iter() let mut attrs = vec![];
.map(|field| { for field in fields {
FieldAttrsBuilder::new(builder).field(field).build() let builder = attr::FieldAttrsBuilder::new(cx, builder);
}) let builder = try!(builder.field(field));
.collect() let attr = builder.build();
attrs.push(attr);
}
Ok(attrs)
}
pub fn container_attrs(
cx: &ExtCtxt,
container: &ast::Item,
) -> Result<attr::ContainerAttrs, ()> {
let builder = attr::ContainerAttrsBuilder::new(cx);
let builder = try!(builder.attrs(container.attrs()));
Ok(builder.build())
} }
+9 -2
View File
@@ -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);
} }
+93 -69
View File
@@ -4,7 +4,6 @@ use syntax::ast::{
Ident, Ident,
MetaItem, MetaItem,
Item, Item,
Expr,
}; };
use syntax::ast; use syntax::ast;
use syntax::codemap::Span; use syntax::codemap::Span;
@@ -12,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,
@@ -26,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;
} }
}; };
@@ -36,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())
@@ -49,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,
@@ -79,7 +82,11 @@ 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 variant_data, _) => { ast::ItemStruct(ref variant_data, _) => {
serialize_item_struct( serialize_item_struct(
@@ -88,6 +95,7 @@ fn serialize_body(
item, item,
impl_generics, impl_generics,
ty, ty,
item.span,
variant_data, variant_data,
) )
} }
@@ -101,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)]");
}
} }
} }
@@ -111,8 +122,9 @@ fn serialize_item_struct(
item: &Item, item: &Item,
impl_generics: &ast::Generics, impl_generics: &ast::Generics,
ty: P<ast::Ty>, ty: P<ast::Ty>,
span: Span,
variant_data: &ast::VariantData, variant_data: &ast::VariantData,
) -> P<ast::Expr> { ) -> Result<P<ast::Expr>, ()> {
match *variant_data { match *variant_data {
ast::VariantData::Unit(_) => { ast::VariantData::Unit(_) => {
serialize_unit_struct( serialize_unit_struct(
@@ -130,7 +142,7 @@ fn serialize_item_struct(
} }
ast::VariantData::Tuple(ref fields, _) => { ast::VariantData::Tuple(ref fields, _) => {
if fields.iter().any(|field| !field.node.kind.is_unnamed()) { if fields.iter().any(|field| !field.node.kind.is_unnamed()) {
cx.bug("tuple struct has named fields") cx.span_bug(span, "tuple struct has named fields")
} }
serialize_tuple_struct( serialize_tuple_struct(
@@ -144,7 +156,7 @@ fn serialize_item_struct(
} }
ast::VariantData::Struct(ref fields, _) => { ast::VariantData::Struct(ref fields, _) => {
if fields.iter().any(|field| field.node.kind.is_unnamed()) { if fields.iter().any(|field| field.node.kind.is_unnamed()) {
cx.bug("struct has unnamed fields") cx.span_bug(span, "struct has unnamed fields")
} }
serialize_struct( serialize_struct(
@@ -163,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(
@@ -186,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,
@@ -201,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 {
@@ -209,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(
@@ -219,8 +235,13 @@ fn serialize_struct(
impl_generics: &ast::Generics, impl_generics: &ast::Generics,
ty: P<ast::Ty>, ty: P<ast::Ty>,
fields: &[ast::StructField], fields: &[ast::StructField],
) -> P<ast::Expr> { ) -> Result<P<ast::Expr>, ()> {
let (visitor_struct, visitor_impl) = serialize_struct_visitor( let value_exprs = fields.iter().map(|field| {
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(),
@@ -230,15 +251,12 @@ fn serialize_struct(
.build_ty(ty.clone()), .build_ty(ty.clone()),
fields, fields,
impl_generics, impl_generics,
fields.iter().map(|field| { value_exprs,
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);
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 {
@@ -246,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(
@@ -256,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(
@@ -287,7 +306,7 @@ 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);
@@ -298,7 +317,7 @@ fn serialize_variant(
.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,
@@ -307,7 +326,7 @@ fn serialize_variant(
$variant_name, $variant_name,
) )
} }
) ))
}, },
ast::VariantData::Tuple(ref fields, _) if fields.len() == 1 => { ast::VariantData::Tuple(ref fields, _) if fields.len() == 1 => {
let field = builder.id("__simple_value"); let field = builder.id("__simple_value");
@@ -316,7 +335,8 @@ fn serialize_variant(
.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,
@@ -326,7 +346,7 @@ fn serialize_variant(
__simple_value, __simple_value,
) )
} }
) ))
}, },
ast::VariantData::Tuple(ref fields, _) => { ast::VariantData::Tuple(ref fields, _) => {
let field_names: Vec<ast::Ident> = (0 .. fields.len()) let field_names: Vec<ast::Ident> = (0 .. fields.len())
@@ -353,7 +373,9 @@ fn serialize_variant(
field_names, field_names,
); );
quote_arm!(cx, $pat => { $expr }) Ok(quote_arm!(cx,
$pat => { $expr }
))
} }
ast::VariantData::Struct(ref fields, _) => { ast::VariantData::Struct(ref fields, _) => {
let field_names: Vec<_> = (0 .. fields.len()) let field_names: Vec<_> = (0 .. fields.len())
@@ -369,7 +391,7 @@ fn serialize_variant(
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")
} }
}; };
@@ -378,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,9 +410,11 @@ fn serialize_variant(
ty, ty,
fields, fields,
field_names, field_names,
); ));
quote_arm!(cx, $pat => { $expr }) Ok(quote_arm!(cx,
$pat => { $expr }
))
} }
} }
} }
@@ -455,7 +479,7 @@ fn serialize_struct_variant(
structure_ty: P<ast::Ty>, structure_ty: P<ast::Ty>,
fields: &[ast::StructField], fields: &[ast::StructField],
field_names: 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(
fields.iter().map(|field| { fields.iter().map(|field| {
@@ -475,7 +499,7 @@ fn serialize_struct_variant(
) )
.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(),
@@ -487,9 +511,9 @@ fn serialize_struct_variant(
.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 {
@@ -497,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(
@@ -575,12 +599,12 @@ fn serialize_struct_visitor<I>(
fields: &[ast::StructField], 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 value_exprs = value_exprs.collect::<Vec<_>>(); let value_exprs = value_exprs.collect::<Vec<_>>();
let field_attrs = struct_field_attrs(cx, builder, fields); let field_attrs = try!(field::struct_field_attrs(cx, builder, fields));
let arms: Vec<ast::Arm> = field_attrs.iter() let arms: Vec<ast::Arm> = field_attrs.iter()
.zip(value_exprs.iter()) .zip(value_exprs.iter())
@@ -643,7 +667,7 @@ fn serialize_struct_visitor<I>(
}) })
.fold(quote_expr!(cx, 0), |sum, expr| quote_expr!(cx, $sum + $expr)); .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,
@@ -675,5 +699,5 @@ fn serialize_struct_visitor<I>(
} }
} }
).unwrap(), ).unwrap(),
) ))
} }
+5 -3
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "serde_macros" name = "serde_macros"
version = "0.6.1" version = "0.6.11"
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.37"
serde_codegen = { version = "^0.6.10", path = "../serde_codegen", default-features = false, features = ["nightly"] }
[dev-dependencies] [dev-dependencies]
compiletest_rs = "^0.0.11"
num = "^0.1.27" num = "^0.1.27"
rustc-serialize = "^0.3.16" rustc-serialize = "^0.3.16"
serde = { version = "*", path = "../serde", features = ["nightly"] } serde = { version = "^0.6.10", path = "../serde", features = ["nightly", "num-impls"] }
+1
View File
@@ -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;
+4 -3
View File
@@ -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() { }
+25
View File
@@ -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");
}
+3
View File
@@ -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;
+11 -5
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "serde_tests" name = "serde_tests"
version = "0.6.2" version = "0.6.3"
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 = "^0.22.0" } syntex = { version = "^0.26.0" }
syntex_syntax = { version = "^0.22.0" } 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 = "^0.1.27" num = "^0.1.27"
rustc-serialize = "^0.3.16" rustc-serialize = "^0.3.16"
serde = { version = "*", path = "../serde" } serde = { version = "*", path = "../serde", features = ["num-impls"] }
syntex = "^0.22.0" syntex = "^0.26.0"
[dependencies]
clippy = { version = "^0.0.37", optional = true }
[[test]] [[test]]
name = "test" name = "test"
+2
View File
@@ -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;
+2 -2
View File
@@ -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();
+11 -11
View File
@@ -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!(
+4
View File
@@ -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"));
+38 -4
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};
@@ -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,
],
}
} }
+35 -1
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;
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@@ -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,
],
}
} }
+1 -2
View File
@@ -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),