Compare commits

...

68 Commits

Author SHA1 Message Date
Erick Tryzelaar 81d617bd93 The README is in a different location 2015-05-18 23:36:46 -07:00
Erick Tryzelaar 3396388222 Remove redundant import 2015-05-18 23:34:17 -07:00
Erick Tryzelaar ff8c3b3d51 Initial support for syntex 2015-05-18 23:34:17 -07:00
Erick Tryzelaar 3d0efd123f Minor cleanup 2015-05-18 22:49:50 -07:00
Erick Tryzelaar 8ca1b9ac3c default_value doesn't need to be public 2015-05-18 22:49:49 -07:00
Erick Tryzelaar 2c24be90d2 Switch to using MultiDecorator 2015-05-18 22:49:49 -07:00
Erick Tryzelaar 482f92af61 Add a single driver for tests and benchmarks 2015-05-18 22:47:36 -07:00
Erick Tryzelaar 24ac61f9f2 Pull codegen into it's own crate 2015-05-18 22:47:35 -07:00
Erick Tryzelaar 426394cd7b Version bump 2015-05-18 22:47:35 -07:00
Erick Tryzelaar 7c3e95a7c7 Ignore all target and Cargo.lock files 2015-05-18 22:47:35 -07:00
Erick Tryzelaar 9dd5f9dc7a Restructure directories to prep for syntex 2015-05-18 22:47:35 -07:00
Erick Tryzelaar e6776ffc37 Protect against json integer overflow
Closes #75
2015-05-18 22:40:29 -07:00
Erick Tryzelaar adae2bd3c5 Merge pull request #72 from borman/bytestrings
Improved support for byte strings
2015-05-18 22:28:37 -07:00
Erick Tryzelaar 14ca260c26 Merge pull request #70 from oli-obk/xml_requirements
changes needed for xml parsing
2015-05-18 22:25:15 -07:00
Erick Tryzelaar 64588f0cb6 Merge pull request #74 from tomprogrammer/array
Add serialization/deserialization for fixed size arrays
2015-05-18 22:21:59 -07:00
Erick Tryzelaar 50cac7f985 Rewrite Value::lookup to not require an allocation 2015-05-18 22:19:30 -07:00
Erick Tryzelaar 5fe85128c2 Merge pull request #76 from blaenk/json-lookup
implement lookup method for json::Value
2015-05-18 21:50:55 -07:00
Jorge Israel Peña bc236bde34 implement lookup method for json::Value 2015-05-17 21:24:00 -07:00
Thomas Bahn 15794a5ed6 Improve build time 2015-05-16 14:36:28 +02:00
Thomas Bahn 62058962de Add deserialiation implementations for fixed size arrays 2015-05-16 14:19:22 +02:00
Thomas Bahn 7d52366403 Add serialization implementations for fixed size arrays 2015-05-16 14:18:32 +02:00
Erick Tryzelaar ee45eb8340 Merge pull request #69 from hugoduncan/feature/format-specific-field-names
Adds serializer format specific field names
2015-05-14 21:59:05 -07:00
Hugo Duncan 801f37b305 Fix visitors for generic structs 2015-05-14 17:35:21 -04:00
Hugo Duncan bdec0b3e63 Update commas and blocks in match arms 2015-05-14 17:35:16 -04:00
Mikhail Borisov 5c631f3e58 WIP 2015-05-12 15:16:06 +03:00
Mikhail Borisov 5fd9daa865 WIP 2015-05-12 15:03:26 +03:00
Oliver Schneider 0485702a68 update benchmarks 2015-05-11 10:34:58 +02:00
Mikhail Borisov 875610044f Improved support for byte strings 2015-05-09 03:18:13 +03:00
Hugo Duncan ec483fc07d Add doc string for format method 2015-05-08 12:43:12 -04:00
Hugo Duncan 53e6e29571 Fix whitespace 2015-05-08 12:43:12 -04:00
Hugo Duncan c5eed99c6a Make forwarded trait name global 2015-05-08 12:43:12 -04:00
Hugo Duncan fe5176113b Add missing attr.rs file 2015-05-08 12:43:12 -04:00
Hugo Duncan 0f7c67efa7 Factor default attribute lookup into FieldAttrs 2015-05-08 12:43:12 -04:00
Hugo Duncan cd0ee64892 Add constructor functions for FieldAttrs 2015-05-08 12:43:12 -04:00
Hugo Duncan ec3af2cb6a Factor out attr module
Factors out field attribute code into the attr module.
2015-05-08 12:43:12 -04:00
Hugo Duncan 960b68937d Remove commented code and extra newline 2015-05-08 12:43:12 -04:00
Hugo Duncan a1e101b513 Make Value use the 'json' format string 2015-05-08 12:43:11 -04:00
Hugo Duncan c30311153c Rename fmt to format 2015-05-08 12:43:11 -04:00
Hugo Duncan a935ebe8b9 Adds serializer format specific field names
Allows different field names to be used for different external formats.

Field names are specified using the `rename` field attribute, e.g:

    #[serde(rename(xml= "a4", json="a5"))]

Reverts #62

Addresses #61
2015-05-08 12:43:11 -04:00
Oliver Schneider 83ee86122b address comments by erickt 2015-05-08 16:32:46 +02:00
Erick Tryzelaar af752ddcb5 Merge pull request #71 from lifthrasiir/json-split-branch
Replace a redundant `escape` variable with nested matches.
2015-05-07 10:51:57 -07:00
Kang Seonghoon 9550063275 Replace a redundant escape variable with nested matches.
This has a non-trivial performance effect due to the branch prediction,
but is expected to be no slower than the older code and, at least in
some cases, is slightly faster for a string-heavy JSON.
2015-05-08 01:53:24 +09:00
Oliver Schneider c117a680cf changes needed for xml parsing 2015-05-04 16:07:19 +02:00
Erick Tryzelaar 206e19edb4 Fix unquoting arms with latest quasi 2015-05-02 10:44:57 -07:00
Erick Tryzelaar f0c87fbd4c Format flats with Debug to properly print "-0.0". 2015-05-01 07:43:48 -07:00
Erick Tryzelaar c3fe6c9c67 Update the README to point at https://github.com/serde-rs 2015-04-26 18:37:18 -07:00
Erick Tryzelaar e5df4b6653 Simplify PrettyFormatter::new 2015-04-26 12:05:38 -07:00
Erick Tryzelaar 12cf2f0b0b Simplify the return types 2015-04-26 11:01:55 -07:00
Erick Tryzelaar 8d4de2b3db Rename Serializer::new_with_formatter to with_formatter 2015-04-26 10:29:28 -07:00
Erick Tryzelaar e509adcac5 Allow the pretty printer character to be changed
This unfortunately loses the simd-ish whitespace printer, but since
pretty printing shouldn't be on a hot path, this shouldn't really
matter.

Partially addresses #65.
2015-04-26 09:29:06 -07:00
Erick Tryzelaar eb9c860cb4 Rename iterator.rs to iter.rs and expose it. 2015-04-26 09:23:08 -07:00
Erick Tryzelaar cf38b8dae5 Add some docs to LineColIterator, expose the underlying iterator 2015-04-26 09:22:56 -07:00
Erick Tryzelaar 75af81234f LineColIterator doesn't need to be peekable. 2015-04-26 09:21:52 -07:00
Erick Tryzelaar 7cc319acca Bump version to 0.3.2. 2015-04-26 09:21:26 -07:00
Erick Tryzelaar 3b44792ff3 Merge pull request #64 from oli-obk/missing_renamed_field
missing field errors displayed original field name instead of renamed
2015-04-26 08:09:00 -07:00
Erick Tryzelaar 678bad241e Merge pull request #66 from daniellandau/fix/compilation
Fix compilation for latest nightly
2015-04-25 08:13:39 -07:00
Daniel Landau 5b1225cc87 Fix compilation for latest nightly 2015-04-24 22:35:56 +03:00
Oliver Schneider 1748831152 missing field errors displayed original field name instead of renamed
closes #63
2015-04-23 17:28:42 +02:00
Erick Tryzelaar ed1b476a22 Merge pull request #58 from oli-obk/separate_line_col
separate out the line/column counting from character iteration
2015-04-22 11:02:55 -07:00
Erick Tryzelaar 79c59ebae1 Merge pull request #62 from hugoduncan/add-ser-de-rename
Add serialize, deserialize specific rename
2015-04-22 11:01:38 -07:00
Hugo Duncan fd6462f8d1 Add serialize, deserialize specific rename
Adds the rename_serialize and rename_deserialize field attributes to
specify serialisation and deserialisation specific renames.
2015-04-21 17:58:18 -04:00
Oliver Schneider c37f67b0a1 separate out the line/column counting from character iteration 2015-04-16 16:31:56 +02:00
Erick Tryzelaar 195f7380b5 Merge pull request #56 from derhaskell/patch-1
fixes struct serialization example for rust 1.0.0-beta
2015-04-13 06:21:37 -07:00
derhaskell becb8c48e8 fixes typo in deserialization example 2015-04-13 13:37:06 +02:00
derhaskell aa16ecf4d3 Update README.md 2015-04-13 13:28:47 +02:00
derhaskell ddda360fec Update README.md
fixes struct serialization example for rust 1.0.0-beta
2015-04-13 13:27:15 +02:00
Erick Tryzelaar cca72f2dbc Merge pull request #55 from sfackler/master
Add doc link to Cargo.toml and doc attr
2015-04-12 22:22:25 -07:00
Steven Fackler 5013b37c09 Add doc link to Cargo.toml and doc attr
The attribute allows cross-crate rustdoc links.
2015-04-12 22:21:00 -07:00
52 changed files with 1713 additions and 534 deletions
+2 -2
View File
@@ -1,2 +1,2 @@
/target/
/Cargo.lock
target
Cargo.lock
-17
View File
@@ -1,17 +0,0 @@
[package]
name = "serde"
version = "0.3.1"
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"]
license = "MIT/Apache-2.0"
description = "A serialization/deserialization framework"
repository = "https://github.com/erickt/rust-serde"
[dependencies]
num = "*"
[dev-dependencies]
rustc-serialize = "*"
[dev-dependencies.serde_macros]
path = "serde_macros/"
version = "0.3.1"
+33 -32
View File
@@ -1,7 +1,8 @@
Serde Rust Serialization Framework
==================================
[![Build Status](https://travis-ci.org/erickt/rust-serde.png?branch=master)](https://travis-ci.org/erickt/rust-serde)
[![Build Status](https://api.travis-ci.org/serde-rs/serde.png?branch=master)](https://travis-ci.org/serde-rs/serde)
[![Latest Version](https://img.shields.io/crates/v/serde.svg)](https://crates.io/crates/serde)
Serde is a powerful framework that enables serialization libraries to
generically serialize Rust data structures without the overhead of runtime type
@@ -9,7 +10,7 @@ information. In many situations, the 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 type.
Documentation is available at http://erickt.github.io/rust-serde/serde
Documentation is available at http://serde-rs.github.io/serde/serde
Making a Type Serializable
==========================
@@ -17,9 +18,9 @@ Making a Type Serializable
The simplest way to make a type serializable is to use the `serde_macros`
syntax extension, which comes with a `#[derive(Serialize, Deserialize)]`
annotation, which automatically generates implementations of
[Serialize](http://erickt.github.io/rust-serde/serde/ser/trait.Serialize.html)
[Serialize](http://serde-rs.github.io/serde/serde/ser/trait.Serialize.html)
and
[Deserialize](http://erickt.github.io/rust-serde/serde/de/trait.Deserialize.html)
[Deserialize](http://serde-rs.github.io/serde/serde/de/trait.Deserialize.html)
for the annotated type:
```rust
@@ -38,11 +39,11 @@ struct Point {
```
Serde bundles a high performance JSON serializer and deserializer,
[serde::json](http://erickt.github.io/rust-serde/serde/json/index.html),
[serde::json](http://serde-rs.github.io/serde/serde/json/index.html),
which comes with the helper functions
[to_string](http://erickt.github.io/rust-serde/serde/json/ser/fn.to_string.html)
[to_string](http://serde-rs.github.io/serde/serde/json/ser/fn.to_string.html)
and
[from_str](http://erickt.github.io/rust-serde/serde/json/de/fn.from_str.html)
[from_str](http://serde-rs.github.io/serde/serde/json/de/fn.from_str.html)
that make it easy to go to and from JSON:
```rust
@@ -58,19 +59,19 @@ println!("{}", serialized_point); // prints: {"x":1,"y":2}
let deserialize_point: Point = json::from_str(&serialized_point).unwrap();
```
[serde::json](http://erickt.github.io/rust-serde/serde/json/index.html) also
[serde::json](http://serde-rs.github.io/serde/serde/json/index.html) also
supports a generic
[Value](http://erickt.github.io/rust-serde/serde/json/value/enum.Value.html)
[Value](http://serde-rs.github.io/serde/serde/json/value/enum.Value.html)
type, which can represent any JSON value. Also, any
[Serialize](http://erickt.github.io/rust-serde/serde/ser/trait.Serialize.html)
[Serialize](http://serde-rs.github.io/serde/serde/ser/trait.Serialize.html)
and
[Deserialize](http://erickt.github.io/rust-serde/serde/de/trait.Deserialize.html)
[Deserialize](http://serde-rs.github.io/serde/serde/de/trait.Deserialize.html)
can be converted into a
[Value](http://erickt.github.io/rust-serde/serde/json/value/enum.Value.html)
[Value](http://serde-rs.github.io/serde/serde/json/value/enum.Value.html)
with the methods
[to_value](http://erickt.github.io/rust-serde/serde/json/value/fn.to_value.html)
[to_value](http://serde-rs.github.io/serde/serde/json/value/fn.to_value.html)
and
[from_value](http://erickt.github.io/rust-serde/serde/json/value/fn.from_value.html):
[from_value](http://serde-rs.github.io/serde/serde/json/value/fn.from_value.html):
```rust
let point = Point { x: 1, y: 2 };
@@ -86,9 +87,9 @@ Serialization without Macros
Under the covers, Serde extensively uses the Visitor pattern to thread state
between the
[Serializer](http://erickt.github.io/rust-serde/serde/ser/trait.Serializer.html)
[Serializer](http://serde-rs.github.io/serde/serde/ser/trait.Serializer.html)
and
[Serialize](http://erickt.github.io/rust-serde/serde/ser/trait.Serialize.html)
[Serialize](http://serde-rs.github.io/serde/serde/ser/trait.Serialize.html)
without the two having specific information about each other's concrete type.
This has many of the same benefits as frameworks that use runtime type
information without the overhead. In fact, when compiling with optimizations,
@@ -97,7 +98,7 @@ nearly as fast as a hand written serializer format for a specific type.
To see it in action, lets look at how a simple type like `i32` is serialized.
The
[Serializer](http://erickt.github.io/rust-serde/serde/ser/trait.Serializer.html)
[Serializer](http://serde-rs.github.io/serde/serde/ser/trait.Serializer.html)
is threaded through the type:
```rust
@@ -112,9 +113,9 @@ impl serde::Serialize for i32 {
As you can see it's pretty simple. More complex types like `BTreeMap` need to
pass a
[MapVisitor](http://erickt.github.io/rust-serde/serde/ser/trait.MapVisitor.html)
[MapVisitor](http://serde-rs.github.io/serde/serde/ser/trait.MapVisitor.html)
to the
[Serializer](http://erickt.github.io/rust-serde/serde/ser/trait.Serializer.html)
[Serializer](http://serde-rs.github.io/serde/serde/ser/trait.Serializer.html)
in order to walk through the type:
```rust
@@ -198,18 +199,18 @@ struct PointMapVisitor<'a> {
state: u8,
}
impl<'a> serde::ser::MapVisitor for PointMapVisitor {
fn visit<S>(&mut self, serializer: &mut S) -> Result<Option(), S::Error>
impl<'a> serde::ser::MapVisitor for PointMapVisitor<'a> {
fn visit<S>(&mut self, serializer: &mut S) -> Result<Option<()>, S::Error>
where S: serde::Serializer
{
match self.state {
0 => {
self.state += 1;
Ok(Some(try!(serializer.visit_map_elt("x", &self.x)))
Ok(Some(try!(serializer.visit_map_elt("x", &self.value.x))))
}
1 => {
self.state += 1;
Ok(Some(try!(serializer.visit_map_elt("y", &self.y))))
Ok(Some(try!(serializer.visit_map_elt("y", &self.value.y))))
}
_ => {
Ok(None)
@@ -224,16 +225,16 @@ Deserialization without Macros
Deserialization is a little more complicated since there's a bit more error
handling that needs to occur. Let's start with the simple `i32`
[Deserialize](http://erickt.github.io/rust-serde/serde/de/trait.Deserialize.html)
[Deserialize](http://serde-rs.github.io/serde/serde/de/trait.Deserialize.html)
implementation. It passes a
[Visitor](http://erickt.github.io/rust-serde/serde/de/trait.Visitor.html) to the
[Deserializer](http://erickt.github.io/rust-serde/serde/de/trait.Deserializer.html).
The [Visitor](http://erickt.github.io/rust-serde/serde/de/trait.Visitor.html)
[Visitor](http://serde-rs.github.io/serde/serde/de/trait.Visitor.html) to the
[Deserializer](http://serde-rs.github.io/serde/serde/de/trait.Deserializer.html).
The [Visitor](http://serde-rs.github.io/serde/serde/de/trait.Visitor.html)
can create the `i32` from a variety of different types:
```rust
impl Deserialize for i32 {
fn deserialize<D>(deserializer: &mut D) -> Result<$ty, D::Error>
fn deserialize<D>(deserializer: &mut D) -> Result<i32, D::Error>
where D: serde::Deserializer,
{
deserializer.visit(I32Visitor)
@@ -263,9 +264,9 @@ impl serde::de::Visitor for I32Visitor {
Since it's possible for this type to get passed an unexpected type, we need a
way to error out. This is done by way of the
[Error](http://erickt.github.io/rust-serde/serde/de/trait.Error.html) trait,
[Error](http://serde-rs.github.io/serde/serde/de/trait.Error.html) trait,
which allows a
[Deserialize](http://erickt.github.io/rust-serde/serde/de/trait.Deserialize.html)
[Deserialize](http://serde-rs.github.io/serde/serde/de/trait.Deserialize.html)
to generate an error for a few common error conditions. Here's how it could be used:
```rust
@@ -282,9 +283,9 @@ to generate an error for a few common error conditions. Here's how it could be u
```
Maps follow a similar pattern as before, and use a
[MapVisitor](http://erickt.github.io/rust-serde/serde/de/trait.MapVisitor.html)
[MapVisitor](http://serde-rs.github.io/serde/serde/de/trait.MapVisitor.html)
to walk through the values generated by the
[Deserializer](http://erickt.github.io/rust-serde/serde/de/trait.Deserializer.html).
[Deserializer](http://serde-rs.github.io/serde/serde/de/trait.Deserializer.html).
```rust
impl<K, V> serde::Deserialize for BTreeMap<K, V>
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "serde"
version = "0.4.0"
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"]
license = "MIT/Apache-2.0"
description = "A generic serialization/deserialization framework"
repository = "https://github.com/serde-rs/serde"
documentation = "http://serde-rs.github.io/serde/serde"
readme = "../README.md"
keywords = ["serialization"]
[dependencies]
num = "*"
+94
View File
@@ -0,0 +1,94 @@
use std::cmp;
use std::io;
use std::slice;
trait IntoBufRead {
type IntoBuf: io::BufRead + BufReadExt;
fn into_buf_read(self) -> Self::IntoBuf;
}
trait BufReadExt {
fn get_buf(&self) -> &[u8];
fn read_u8(&mut self) -> io::Result<Option<u8>>;
}
struct SliceReader<'a> {
buf: &'a [u8],
}
impl<'a> io::Read for SliceReader<'a> {
#[inline]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let amt = cmp::min(buf.len(), self.buf.len());
let (a, b) = self.buf.split_at(amt);
slice::bytes::copy_memory(buf, a);
*self.buf = b;
Ok(amt)
}
}
impl<'a> io::BufRead for SliceReader<'a> {
fn fill_buf(&mut self) -> io::Result<&[u8]> { Ok(*self) }
fn consume(&mut self, amt: usize) { *self.buf = &self.buf[amt..]; }
}
impl<'a> BufReadExt for SliceReader<'a> {
fn get_buf(&self) -> &[u8] { self.buf }
fn read_u8(&mut self) -> io::Result<Option<u8>> {
let byte = self.buf.get(0);
*self.buf = &self.buf[1..];
byte
}
}
struct BufReader<R> {
inner: R,
buf: io::Cursor<Vec<u8>>,
}
impl<R> BufReader<R> where R: io::Read {
fn new(inner: R) -> Self {
BufferedReader::with_capacity(io::DEFAULT_BUF_SIZE, inner)
}
fn new(cap: usize, inner: R) -> Self {
BufferedReader {
inner: inner,
buf: io::Cursor::new(Vec::with_capacity(cap)),
}
}
fn into_inner(self) -> R {
self.inner
}
}
impl<R> Read for BufReader<R> where R: io::Read {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
// If we don't have any buffered data and we're doing a massive read
// (larger than our internal buffer), bypass our internal buffer
// entirely.
if self.buf.get_ref().len() == self.buf.position() as usize &&
buf.len() >= self.buf.get_ref().capacity() {
return self.inner.read(buf);
}
try!(self.fill_buf());
self.buf.read(buf)
}
}
impl<R> BufReadExt for BufReader<R> {
fn get_buf(&self) -> &[u8] {
self.buf.get_ref()
}
fn read_u8(&mut self) -> io::Result<Option<u8>> {
if self.buf.get_ref().len() == self.buf.position() as usize {
}
let byte = self.buf.get(0);
*self.buf = &self.buf[1..];
byte
}
}
+43 -4
View File
@@ -1,6 +1,8 @@
//! Helper module to enable serializing bytes more efficiently
use std::ops;
use std::fmt;
use std::ascii;
use ser;
use de;
@@ -8,10 +10,17 @@ use de;
///////////////////////////////////////////////////////////////////////////////
/// `Bytes` wraps a `&[u8]` in order to serialize into a byte array.
#[derive(Clone, Copy, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct Bytes<'a> {
bytes: &'a [u8],
}
impl<'a> fmt::Debug for Bytes<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "b\"{}\"", escape_bytestring(self.bytes))
}
}
impl<'a> From<&'a [u8]> for Bytes<'a> {
fn from(bytes: &'a [u8]) -> Self {
Bytes {
@@ -28,6 +37,12 @@ impl<'a> From<&'a Vec<u8>> for Bytes<'a> {
}
}
impl<'a> Into<&'a [u8]> for Bytes<'a> {
fn into(self) -> &'a [u8] {
self.bytes
}
}
impl<'a> ops::Deref for Bytes<'a> {
type Target = [u8];
@@ -46,7 +61,7 @@ impl<'a> ser::Serialize for Bytes<'a> {
///////////////////////////////////////////////////////////////////////////////
/// `ByteBuf` wraps a `Vec<u8>` in order to hook into serialize and from deserialize a byte array.
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
#[derive(Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct ByteBuf {
bytes: Vec<u8>,
}
@@ -65,10 +80,22 @@ impl ByteBuf {
}
}
impl<T> From<T> for ByteBuf where T: Into<Vec<u8>> {
fn from(bytes: T) -> Self {
impl fmt::Debug for ByteBuf {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "b\"{}\"", escape_bytestring(self.bytes.as_ref()))
}
}
impl Into<Vec<u8>> for ByteBuf {
fn into(self) -> Vec<u8> {
self.bytes
}
}
impl From<Vec<u8>> for ByteBuf {
fn from(bytes: Vec<u8>) -> Self {
ByteBuf {
bytes: bytes.into(),
bytes: bytes,
}
}
}
@@ -172,3 +199,15 @@ impl de::Deserialize for ByteBuf {
deserializer.visit_bytes(ByteBufVisitor)
}
}
///////////////////////////////////////////////////////////////////////////////
fn escape_bytestring(bytes: &[u8]) -> String {
let mut result = String::new();
for &b in bytes {
for esc in ascii::escape_default(b) {
result.push(esc as char);
}
}
result
}
+173 -3
View File
@@ -3,6 +3,7 @@ use std::hash::Hash;
use std::marker::PhantomData;
use std::path;
use std::rc::Rc;
use std::str;
use std::sync::Arc;
use num::FromPrimitive;
@@ -56,6 +57,16 @@ impl Visitor for BoolVisitor {
{
Ok(v)
}
fn visit_str<E>(&mut self, s: &str) -> Result<bool, E>
where E: Error,
{
match s.trim() {
"true" => Ok(true),
"false" => Ok(false),
_ => Err(Error::syntax_error()),
}
}
}
impl Deserialize for bool {
@@ -96,7 +107,7 @@ impl<T> PrimitiveVisitor<T> {
}
impl<
T: Deserialize + FromPrimitive
T: Deserialize + FromPrimitive + str::FromStr
> Visitor for PrimitiveVisitor<T> {
type Value = T;
@@ -112,6 +123,13 @@ impl<
impl_deserialize_num_method!(u64, visit_u64, from_u64);
impl_deserialize_num_method!(f32, visit_f32, from_f32);
impl_deserialize_num_method!(f64, visit_f64, from_f64);
#[inline]
fn visit_str<E>(&mut self, v: &str) -> Result<T, E>
where E: Error,
{
str::FromStr::from_str(v.trim()).or(Err(Error::syntax_error()))
}
}
macro_rules! impl_deserialize_num {
@@ -198,6 +216,24 @@ impl Visitor for StringVisitor {
{
Ok(v)
}
fn visit_bytes<E>(&mut self, v: &[u8]) -> Result<String, E>
where E: Error,
{
match str::from_utf8(v) {
Ok(s) => Ok(s.to_string()),
Err(_) => Err(Error::syntax_error()),
}
}
fn visit_byte_buf<'a, E>(&mut self, v: Vec<u8>) -> Result<String, E>
where E: Error,
{
match String::from_utf8(v) {
Ok(s) => Ok(s),
Err(_) => Err(Error::syntax_error()),
}
}
}
impl Deserialize for String {
@@ -394,12 +430,146 @@ impl<T> Deserialize for Vec<T>
fn deserialize<D>(deserializer: &mut D) -> Result<Vec<T>, D::Error>
where D: Deserializer,
{
deserializer.visit(VecVisitor::new())
deserializer.visit_seq(VecVisitor::new())
}
}
///////////////////////////////////////////////////////////////////////////////
struct ArrayVisitor0<T> {
marker: PhantomData<T>,
}
impl<T> ArrayVisitor0<T> {
pub fn new() -> Self {
ArrayVisitor0 {
marker: PhantomData,
}
}
}
impl<T> Visitor for ArrayVisitor0<T> where T: Deserialize + Default {
type Value = [T; 0];
#[inline]
fn visit_unit<E>(&mut self) -> Result<[T; 0], E>
where E: Error,
{
Ok([T::default(); 0])
}
#[inline]
fn visit_seq<V>(&mut self, mut visitor: V) -> Result<[T; 0], V::Error>
where V: SeqVisitor,
{
try!(visitor.end());
Ok([T::default(); 0])
}
}
impl<T> Deserialize for [T; 0]
where T: Deserialize + Default
{
fn deserialize<D>(deserializer: &mut D) -> Result<[T; 0], D::Error>
where D: Deserializer,
{
deserializer.visit(ArrayVisitor0::new())
}
}
macro_rules! array_impls {
($($visitor:ident, $len:expr => ($($name:ident),+),)+) => {
$(
struct $visitor<T> {
marker: PhantomData<T>,
}
impl<T> $visitor<T> {
pub fn new() -> Self {
$visitor {
marker: PhantomData
}
}
}
impl<T> Visitor for $visitor<T> where T: Deserialize {
type Value = [T; $len];
#[inline]
fn visit_seq<V>(&mut self, mut visitor: V) -> Result<[T; $len], V::Error>
where V: SeqVisitor,
{
$(
let $name = match try!(visitor.visit()) {
Some(val) => val,
None => { return Err(Error::end_of_stream_error()); }
};
)+;
try!(visitor.end());
Ok([$($name,)+])
}
}
impl<T> Deserialize for [T; $len]
where T: Deserialize,
{
fn deserialize<D>(deserializer: &mut D) -> Result<[T; $len], D::Error>
where D: Deserializer,
{
deserializer.visit($visitor::new())
}
}
)+
}
}
array_impls! {
ArrayVisitor1, 1 => (a),
ArrayVisitor2, 2 => (a, b),
ArrayVisitor3, 3 => (a, b, c),
ArrayVisitor4, 4 => (a, b, c, d),
ArrayVisitor5, 5 => (a, b, c, d, e),
ArrayVisitor6, 6 => (a, b, c, d, e, f),
ArrayVisitor7, 7 => (a, b, c, d, e, f, g),
ArrayVisitor8, 8 => (a, b, c, d, e, f, g, h),
ArrayVisitor9, 9 => (a, b, c, d, e, f, g, h, i),
ArrayVisitor10, 10 => (a, b, c, d, e, f, g, h, i, j),
ArrayVisitor11, 11 => (a, b, c, d, e, f, g, h, i, j, k),
ArrayVisitor12, 12 => (a, b, c, d, e, f, g, h, i, j, k, l),
ArrayVisitor13, 13 => (a, b, c, d, e, f, g, h, i, j, k, l, m),
ArrayVisitor14, 14 => (a, b, c, d, e, f, g, h, i, j, k, l, m, n),
ArrayVisitor15, 15 => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o),
ArrayVisitor16, 16 => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p),
ArrayVisitor17, 17 => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q),
ArrayVisitor18, 18 => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r),
ArrayVisitor19, 19 => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s),
ArrayVisitor20, 20 => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s ,t),
ArrayVisitor21, 21 => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u),
ArrayVisitor22, 22 => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v),
ArrayVisitor23, 23 => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w),
ArrayVisitor24, 24 => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x),
ArrayVisitor25, 25 => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x,
y),
ArrayVisitor26, 26 => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x,
y, z),
ArrayVisitor27, 27 => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x,
y, z, aa),
ArrayVisitor28, 28 => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x,
y, z, aa, ab),
ArrayVisitor29, 29 => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x,
y, z, aa, ab, ac),
ArrayVisitor30, 30 => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x,
y, z, aa, ab, ac, ad),
ArrayVisitor31, 31 => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x,
y, z, aa, ab, ac, ad, ae),
ArrayVisitor32, 32 => (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x,
y, z, aa, ab, ac, ad, ae, af),
}
///////////////////////////////////////////////////////////////////////////////
macro_rules! tuple_impls {
() => {};
($($visitor:ident => ($($name:ident),+),)+) => {
@@ -438,7 +608,7 @@ macro_rules! tuple_impls {
fn deserialize<D>(deserializer: &mut D) -> Result<($($name,)+), D::Error>
where D: Deserializer,
{
deserializer.visit($visitor { marker: PhantomData })
deserializer.visit_seq($visitor { marker: PhantomData })
}
}
)+
+12 -3
View File
@@ -113,12 +113,21 @@ pub trait Deserializer {
{
self.visit(visitor)
}
/// Specify a format string for the deserializer.
///
/// The deserializer format is used to determine which format
/// specific field attributes should be used with the
/// deserializer.
fn format() -> &'static str {
""
}
}
///////////////////////////////////////////////////////////////////////////////
pub trait Visitor {
type Value;
type Value: Deserialize;
fn visit_bool<E>(&mut self, _v: bool) -> Result<Self::Value, E>
where E: Error,
@@ -263,10 +272,10 @@ pub trait Visitor {
Err(Error::syntax_error())
}
fn visit_byte_buf<E>(&mut self, _v: Vec<u8>) -> Result<Self::Value, E>
fn visit_byte_buf<E>(&mut self, v: Vec<u8>) -> Result<Self::Value, E>
where E: Error,
{
Err(Error::syntax_error())
self.visit_bytes(&v)
}
}
+54
View File
@@ -12,6 +12,7 @@ use std::hash::Hash;
use std::vec;
use de;
use bytes;
///////////////////////////////////////////////////////////////////////////////
@@ -409,3 +410,56 @@ impl<K, V> ValueDeserializer for HashMap<K, V>
MapDeserializer::new(self.into_iter(), len)
}
}
///////////////////////////////////////////////////////////////////////////////
impl<'a> ValueDeserializer for bytes::Bytes<'a>
{
type Deserializer = BytesDeserializer<'a>;
fn into_deserializer(self) -> BytesDeserializer<'a> {
BytesDeserializer(Some(self.into()))
}
}
pub struct BytesDeserializer<'a> (Option<&'a [u8]>);
impl<'a> de::Deserializer for BytesDeserializer<'a> {
type Error = Error;
fn visit<V>(&mut self, mut visitor: V) -> Result<V::Value, Error>
where V: de::Visitor,
{
match self.0.take() {
Some(bytes) => visitor.visit_bytes(bytes),
None => Err(de::Error::end_of_stream_error()),
}
}
}
///////////////////////////////////////////////////////////////////////////////
impl ValueDeserializer for bytes::ByteBuf
{
type Deserializer = ByteBufDeserializer;
fn into_deserializer(self) -> Self::Deserializer {
ByteBufDeserializer(Some(self.into()))
}
}
pub struct ByteBufDeserializer(Option<Vec<u8>>);
impl de::Deserializer for ByteBufDeserializer {
type Error = Error;
fn visit<V>(&mut self, mut visitor: V) -> Result<V::Value, Error>
where V: de::Visitor,
{
match self.0.take() {
Some(bytes) => visitor.visit_byte_buf(bytes),
None => Err(de::Error::end_of_stream_error()),
}
}
}
+57
View File
@@ -0,0 +1,57 @@
use std::io;
use std::iter::Peekable;
pub struct LineColIterator<Iter: Iterator<Item=io::Result<u8>>> {
iter: Iter,
line: usize,
col: usize,
}
impl<Iter: Iterator<Item=io::Result<u8>>> LineColIterator<Iter> {
pub fn new(iter: Iter) -> LineColIterator<Iter> {
LineColIterator {
iter: iter,
line: 1,
col: 0,
}
}
/// Report the current line inside the iterator.
pub fn line(&self) -> usize { self.line }
/// Report the current column inside the iterator.
pub fn col(&self) -> usize { self.col }
/// Gets a reference to the underlying iterator.
pub fn get_ref(&self) -> &Iter { &self.iter }
/// Gets a mutable reference to the underlying iterator.
pub fn get_mut(&mut self) -> &mut Iter { &mut self.iter }
/// Unwraps this `LineColIterator`, returning the underlying iterator.
pub fn into_inner(self) -> Iter { self.iter }
}
impl<Iter: Iterator<Item=io::Result<u8>>> LineColIterator<Peekable<Iter>> {
/// peeks at the next value
pub fn peek(&mut self) -> Option<&io::Result<u8>> { self.iter.peek() }
}
impl<Iter: Iterator<Item=io::Result<u8>>> Iterator for LineColIterator<Iter> {
type Item = io::Result<u8>;
fn next(&mut self) -> Option<io::Result<u8>> {
match self.iter.next() {
None => None,
Some(Ok(b'\n')) => {
self.line += 1;
self.col = 0;
Some(Ok(b'\n'))
},
Some(Ok(c)) => {
self.col += 1;
Some(Ok(c))
},
Some(Err(e)) => Some(Err(e)),
}
}
}
+90 -87
View File
@@ -3,13 +3,13 @@ use std::io;
use std::str;
use de;
use iter::LineColIterator;
use super::error::{Error, ErrorCode};
pub struct Deserializer<Iter> {
rdr: Iter,
pub struct Deserializer<Iter: Iterator<Item=io::Result<u8>>> {
rdr: LineColIterator<Iter>,
ch: Option<u8>,
line: usize,
col: usize,
str_buf: Vec<u8>,
}
@@ -20,10 +20,8 @@ impl<Iter> Deserializer<Iter>
#[inline]
pub fn new(rdr: Iter) -> Result<Deserializer<Iter>, Error> {
let mut deserializer = Deserializer {
rdr: rdr,
rdr: LineColIterator::new(rdr),
ch: None,
line: 1,
col: 0,
str_buf: Vec::with_capacity(128),
};
@@ -53,13 +51,6 @@ impl<Iter> Deserializer<Iter>
None => None,
};
if self.ch_is(b'\n') {
self.line += 1;
self.col = 1;
} else {
self.col += 1;
}
Ok(())
}
@@ -73,7 +64,7 @@ impl<Iter> Deserializer<Iter>
}
fn error(&mut self, reason: ErrorCode) -> Error {
Error::SyntaxError(reason, self.line, self.col)
Error::SyntaxError(reason, self.rdr.line(), self.rdr.col())
}
fn parse_whitespace(&mut self) -> Result<(), Error> {
@@ -189,7 +180,7 @@ impl<Iter> Deserializer<Iter>
}
fn parse_integer(&mut self) -> Result<u64, Error> {
let mut res = 0;
let mut accum: u64 = 0;
match self.ch_or_null() {
b'0' => {
@@ -207,8 +198,17 @@ impl<Iter> Deserializer<Iter>
while !self.eof() {
match self.ch_or_null() {
c @ b'0' ... b'9' => {
res *= 10;
res += (c as u64) - (b'0' as u64);
macro_rules! try_or_invalid {
($e: expr) => {
match $e {
Some(v) => v,
None => { return Err(self.error(ErrorCode::InvalidNumber)); }
}
}
}
accum = try_or_invalid!(accum.checked_mul(10));
accum = try_or_invalid!(accum.checked_add((c as u64) - ('0' as u64)));
try!(self.bump());
}
_ => break,
@@ -218,7 +218,7 @@ impl<Iter> Deserializer<Iter>
_ => { return Err(self.error(ErrorCode::InvalidNumber)); }
}
Ok(res)
Ok(accum)
}
fn parse_decimal(&mut self, res: f64) -> Result<f64, Error> {
@@ -316,89 +316,87 @@ impl<Iter> Deserializer<Iter>
fn parse_string(&mut self) -> Result<(), Error> {
self.str_buf.clear();
let mut escape = false;
loop {
let ch = match try!(self.next_char()) {
Some(ch) => ch,
None => { return Err(self.error(ErrorCode::EOFWhileParsingString)); }
};
if escape {
match ch {
b'"' => self.str_buf.push(b'"'),
b'\\' => self.str_buf.push(b'\\'),
b'/' => self.str_buf.push(b'/'),
b'b' => self.str_buf.push(b'\x08'),
b'f' => self.str_buf.push(b'\x0c'),
b'n' => self.str_buf.push(b'\n'),
b'r' => self.str_buf.push(b'\r'),
b't' => self.str_buf.push(b'\t'),
b'u' => {
let c = match try!(self.decode_hex_escape()) {
0xDC00 ... 0xDFFF => {
return Err(self.error(ErrorCode::LoneLeadingSurrogateInHexEscape));
}
match ch {
b'"' => {
try!(self.bump());
return Ok(());
}
b'\\' => {
let ch = match try!(self.next_char()) {
Some(ch) => ch,
None => { return Err(self.error(ErrorCode::EOFWhileParsingString)); }
};
// Non-BMP characters are encoded as a sequence of
// two hex escapes, representing UTF-16 surrogates.
n1 @ 0xD800 ... 0xDBFF => {
match (try!(self.next_char()), try!(self.next_char())) {
(Some(b'\\'), Some(b'u')) => (),
_ => {
return Err(self.error(ErrorCode::UnexpectedEndOfHexEscape));
}
}
let n2 = try!(self.decode_hex_escape());
if n2 < 0xDC00 || n2 > 0xDFFF {
match ch {
b'"' => self.str_buf.push(b'"'),
b'\\' => self.str_buf.push(b'\\'),
b'/' => self.str_buf.push(b'/'),
b'b' => self.str_buf.push(b'\x08'),
b'f' => self.str_buf.push(b'\x0c'),
b'n' => self.str_buf.push(b'\n'),
b'r' => self.str_buf.push(b'\r'),
b't' => self.str_buf.push(b'\t'),
b'u' => {
let c = match try!(self.decode_hex_escape()) {
0xDC00 ... 0xDFFF => {
return Err(self.error(ErrorCode::LoneLeadingSurrogateInHexEscape));
}
let n = (((n1 - 0xD800) as u32) << 10 |
(n2 - 0xDC00) as u32) + 0x1_0000;
// Non-BMP characters are encoded as a sequence of
// two hex escapes, representing UTF-16 surrogates.
n1 @ 0xD800 ... 0xDBFF => {
match (try!(self.next_char()), try!(self.next_char())) {
(Some(b'\\'), Some(b'u')) => (),
_ => {
return Err(self.error(ErrorCode::UnexpectedEndOfHexEscape));
}
}
match char::from_u32(n as u32) {
Some(c) => c,
None => {
return Err(self.error(ErrorCode::InvalidUnicodeCodePoint));
let n2 = try!(self.decode_hex_escape());
if n2 < 0xDC00 || n2 > 0xDFFF {
return Err(self.error(ErrorCode::LoneLeadingSurrogateInHexEscape));
}
let n = (((n1 - 0xD800) as u32) << 10 |
(n2 - 0xDC00) as u32) + 0x1_0000;
match char::from_u32(n as u32) {
Some(c) => c,
None => {
return Err(self.error(ErrorCode::InvalidUnicodeCodePoint));
}
}
}
}
n => {
match char::from_u32(n as u32) {
Some(c) => c,
None => {
return Err(self.error(ErrorCode::InvalidUnicodeCodePoint));
n => {
match char::from_u32(n as u32) {
Some(c) => c,
None => {
return Err(self.error(ErrorCode::InvalidUnicodeCodePoint));
}
}
}
}
};
};
// FIXME: this allocation is required in order to be compatible with stable
// rust, which doesn't support encoding a `char` into a stack buffer.
let buf = c.to_string();
self.str_buf.extend(buf.bytes());
}
_ => {
return Err(self.error(ErrorCode::InvalidEscape));
// FIXME: this allocation is required in order to be compatible with stable
// rust, which doesn't support encoding a `char` into a stack buffer.
let buf = c.to_string();
self.str_buf.extend(buf.bytes());
}
_ => {
return Err(self.error(ErrorCode::InvalidEscape));
}
}
}
escape = false;
} else {
match ch {
b'"' => {
try!(self.bump());
return Ok(());
}
b'\\' => {
escape = true;
}
ch => {
self.str_buf.push(ch);
}
ch => {
self.str_buf.push(ch);
}
}
}
@@ -474,14 +472,19 @@ impl<Iter> de::Deserializer for Deserializer<Iter>
Err(self.error(ErrorCode::ExpectedSomeValue))
}
}
#[inline]
fn format() -> &'static str {
"json"
}
}
struct SeqVisitor<'a, Iter: 'a> {
struct SeqVisitor<'a, Iter: 'a + Iterator<Item=io::Result<u8>>> {
de: &'a mut Deserializer<Iter>,
first: bool,
}
impl<'a, Iter> SeqVisitor<'a, Iter> {
impl<'a, Iter: Iterator<Item=io::Result<u8>>> SeqVisitor<'a, Iter> {
fn new(de: &'a mut Deserializer<Iter>) -> Self {
SeqVisitor {
de: de,
@@ -533,12 +536,12 @@ impl<'a, Iter> de::SeqVisitor for SeqVisitor<'a, Iter>
}
}
struct MapVisitor<'a, Iter: 'a> {
struct MapVisitor<'a, Iter: 'a + Iterator<Item=io::Result<u8>>> {
de: &'a mut Deserializer<Iter>,
first: bool,
}
impl<'a, Iter> MapVisitor<'a, Iter> {
impl<'a, Iter: Iterator<Item=io::Result<u8>>> MapVisitor<'a, Iter> {
fn new(de: &'a mut Deserializer<Iter>) -> Self {
MapVisitor {
de: de,
+48 -32
View File
@@ -1,5 +1,5 @@
use std::io;
use std::num::{Float, FpCategory};
use std::num::FpCategory;
use std::string::FromUtf8Error;
use ser;
@@ -17,11 +17,20 @@ pub struct Serializer<W, F=CompactFormatter> {
impl<W> Serializer<W>
where W: io::Write,
{
/// Creates a new JSON visitr whose output will be written to the writer
/// specified.
/// Creates a new JSON serializer.
#[inline]
pub fn new(writer: W) -> Serializer<W> {
Serializer::new_with_formatter(writer, CompactFormatter)
pub fn new(writer: W) -> Self {
Serializer::with_formatter(writer, CompactFormatter)
}
}
impl<'a, W> Serializer<W, PrettyFormatter<'a>>
where W: io::Write,
{
/// Creates a new JSON pretty print serializer.
#[inline]
pub fn pretty(writer: W) -> Self {
Serializer::with_formatter(writer, PrettyFormatter::new())
}
}
@@ -29,10 +38,10 @@ impl<W, F> Serializer<W, F>
where W: io::Write,
F: Formatter,
{
/// Creates a new JSON visitr whose output will be written to the writer
/// Creates a new JSON visitor whose output will be written to the writer
/// specified.
#[inline]
pub fn new_with_formatter(writer: W, formatter: F) -> Serializer<W, F> {
pub fn with_formatter(writer: W, formatter: F) -> Self {
Serializer {
writer: writer,
formatter: formatter,
@@ -247,6 +256,11 @@ impl<W, F> ser::Serializer for Serializer<W, F>
try!(self.formatter.colon(&mut self.writer));
value.serialize(self)
}
#[inline]
fn format() -> &'static str {
"json"
}
}
pub trait Formatter {
@@ -295,16 +309,29 @@ impl Formatter for CompactFormatter {
}
}
pub struct PrettyFormatter {
pub struct PrettyFormatter<'a> {
current_indent: usize,
indent: usize,
indent: &'a [u8],
}
impl Formatter for PrettyFormatter {
impl<'a> PrettyFormatter<'a> {
fn new() -> Self {
PrettyFormatter::with_indent(b" ")
}
fn with_indent(indent: &'a [u8]) -> Self {
PrettyFormatter {
current_indent: 0,
indent: indent,
}
}
}
impl<'a> Formatter for PrettyFormatter<'a> {
fn open<W>(&mut self, writer: &mut W, ch: u8) -> io::Result<()>
where W: io::Write,
{
self.current_indent += self.indent;
self.current_indent += 1;
writer.write_all(&[ch])
}
@@ -317,7 +344,7 @@ impl Formatter for PrettyFormatter {
try!(writer.write_all(b",\n"));
}
spaces(writer, self.current_indent)
indent(writer, self.current_indent, self.indent)
}
fn colon<W>(&mut self, writer: &mut W) -> io::Result<()>
@@ -329,9 +356,9 @@ impl Formatter for PrettyFormatter {
fn close<W>(&mut self, writer: &mut W, ch: u8) -> io::Result<()>
where W: io::Write,
{
self.current_indent -= self.indent;
self.current_indent -= 1;
try!(writer.write(b"\n"));
try!(spaces(writer, self.current_indent));
try!(indent(writer, self.current_indent, self.indent));
writer.write_all(&[ch])
}
@@ -396,7 +423,7 @@ fn fmt_f32_or_null<W>(wr: &mut W, value: f32) -> io::Result<()>
match value.classify() {
FpCategory::Nan | FpCategory::Infinite => wr.write_all(b"null"),
_ => {
let s = value.to_string();
let s = format!("{:?}", value);
try!(wr.write_all(s.as_bytes()));
if !s.contains('.') {
try!(wr.write_all(b".0"))
@@ -412,7 +439,7 @@ fn fmt_f64_or_null<W>(wr: &mut W, value: f64) -> io::Result<()>
match value.classify() {
FpCategory::Nan | FpCategory::Infinite => wr.write_all(b"null"),
_ => {
let s = value.to_string();
let s = format!("{:?}", value);
try!(wr.write_all(s.as_bytes()));
if !s.contains('.') {
try!(wr.write_all(b".0"))
@@ -439,10 +466,7 @@ pub fn to_writer_pretty<W, T>(writer: &mut W, value: &T) -> io::Result<()>
where W: io::Write,
T: ser::Serialize,
{
let mut ser = Serializer::new_with_formatter(writer, PrettyFormatter {
current_indent: 0,
indent: 2,
});
let mut ser = Serializer::pretty(writer);
try!(value.serialize(&mut ser));
Ok(())
}
@@ -489,20 +513,12 @@ pub fn to_string_pretty<T>(value: &T) -> Result<String, FromUtf8Error>
String::from_utf8(vec)
}
fn spaces<W>(wr: &mut W, mut n: usize) -> io::Result<()>
fn indent<W>(wr: &mut W, n: usize, s: &[u8]) -> io::Result<()>
where W: io::Write,
{
const LEN: usize = 16;
const BUF: &'static [u8; LEN] = &[b' '; 16];
while n >= LEN {
try!(wr.write_all(BUF));
n -= LEN;
for _ in 0 .. n {
try!(wr.write_all(s));
}
if n > 0 {
wr.write_all(&BUF[..n])
} else {
Ok(())
}
Ok(())
}
+34 -2
View File
@@ -37,8 +37,30 @@ impl Value {
/// Otherwise, it will return the `Value` associated with the final key.
pub fn find_path<'a>(&'a self, keys: &[&str]) -> Option<&'a Value>{
let mut target = self;
for key in keys.iter() {
match target.find(*key) {
for key in keys {
match target.find(key) {
Some(t) => { target = t; },
None => return None
}
}
Some(target)
}
/// Looks up a value by path.
///
/// This is a convenience method that splits the path by `'.'`
/// and then feeds the sequence of keys into the `find_path`
/// method.
///
/// ``` ignore
/// let obj: Value = json::from_str(r#"{"x": {"a": 1}}"#).unwrap();
///
/// assert!(obj.lookup("x.a").unwrap() == &Value::U64(1));
/// ```
pub fn lookup<'a>(&'a self, path: &'a str) -> Option<&'a Value> {
let mut target = self;
for key in path.split('.') {
match target.find(key) {
Some(t) => { target = t; },
None => return None
}
@@ -571,6 +593,11 @@ impl ser::Serializer for Serializer {
Ok(())
}
#[inline]
fn format() -> &'static str {
"json"
}
}
pub struct Deserializer {
@@ -677,6 +704,11 @@ impl de::Deserializer for Deserializer {
None => Ok(value)
}
}
#[inline]
fn format() -> &'static str {
"json"
}
}
struct SeqDeserializer<'a> {
+5 -3
View File
@@ -5,13 +5,15 @@
//! 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
//! type.
#![doc(html_root_url="http://erickt.github.io/rust-serde")]
extern crate num;
pub use ser::{Serialize, Serializer};
pub use de::{Deserialize, Deserializer, Error};
pub mod ser;
pub mod de;
pub mod json;
pub mod bytes;
pub mod de;
pub mod iter;
pub mod json;
pub mod ser;
@@ -130,6 +130,53 @@ impl<'a, T> Serialize for &'a [T]
}
}
macro_rules! array_impls {
($len:expr) => {
impl<T> Serialize for [T; $len] where T: Serialize {
#[inline]
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
where S: Serializer,
{
serializer.visit_seq(SeqIteratorVisitor::new(self.iter(), Some($len)))
}
}
}
}
array_impls!(0);
array_impls!(1);
array_impls!(2);
array_impls!(3);
array_impls!(4);
array_impls!(5);
array_impls!(6);
array_impls!(7);
array_impls!(8);
array_impls!(9);
array_impls!(10);
array_impls!(11);
array_impls!(12);
array_impls!(13);
array_impls!(14);
array_impls!(15);
array_impls!(16);
array_impls!(17);
array_impls!(18);
array_impls!(19);
array_impls!(20);
array_impls!(21);
array_impls!(22);
array_impls!(23);
array_impls!(24);
array_impls!(25);
array_impls!(26);
array_impls!(27);
array_impls!(28);
array_impls!(29);
array_impls!(30);
array_impls!(31);
array_impls!(32);
impl<T> Serialize for Vec<T> where T: Serialize {
#[inline]
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
+9 -1
View File
@@ -104,7 +104,7 @@ pub trait Serializer {
fn visit_str(&mut self, value: &str) -> Result<(), Self::Error>;
/// `visit_bytes` is a hook that enables those serialization formats that support serializing
/// byte slices separately from generic arrays. By default it serializes as a regular array.
/// byte slices separately from generic arrays. By default it serializes as a regular array.
#[inline]
fn visit_bytes(&mut self, value: &[u8]) -> Result<(), Self::Error> {
self.visit_seq(impls::SeqIteratorVisitor::new(value.iter(), Some(value.len())))
@@ -179,6 +179,14 @@ pub trait Serializer {
fn visit_map_elt<K, V>(&mut self, key: K, value: V) -> Result<(), Self::Error>
where K: Serialize,
V: Serialize;
/// Specify a format string for the serializer.
///
/// The serializer format is used to determine which format
/// specific field attributes should be used with the serializer.
fn format() -> &'static str {
""
}
}
pub trait SeqVisitor {
+24
View File
@@ -0,0 +1,24 @@
[package]
name = "serde_codegen"
version = "0.4.0"
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"]
license = "MIT/Apache-2.0"
description = "Macros to auto-generate implementations for the serde framework"
repository = "https://github.com/erickt/rust-serde"
build = "build.rs"
[features]
default = ["with-syntex"]
nightly = ["quasi_macros"]
with-syntex = ["quasi/with-syntex", "quasi_codegen/with-syntex", "syntex", "syntex_syntax"]
[build-dependencies]
quasi_codegen = { verision = "*", optional = true }
syntex = { version = "*", optional = true }
[dependencies]
aster = { version = "*", default-features = false }
quasi = { verision = "*", default-features = false }
quasi_macros = { version = "*", optional = true }
syntex = { version = "*", optional = true }
syntex_syntax = { version = "*", optional = true }
+28
View File
@@ -0,0 +1,28 @@
#[cfg(feature = "with-syntex")]
mod inner {
extern crate syntex;
extern crate quasi_codegen;
use std::env;
use std::path::Path;
pub fn main() {
let out_dir = env::var_os("OUT_DIR").unwrap();
let mut registry = syntex::Registry::new();
quasi_codegen::register(&mut registry);
let src = Path::new("src/lib.rs.in");
let dst = Path::new(&out_dir).join("lib.rs");
registry.expand("", &src, &dst).unwrap();
}
}
#[cfg(not(feature = "with-syntex"))]
mod inner {
pub fn main() {}
}
fn main() {
inner::main();
}
+108
View File
@@ -0,0 +1,108 @@
use std::collections::HashMap;
use std::collections::HashSet;
use syntax::ast;
use syntax::ext::base::ExtCtxt;
use syntax::ptr::P;
/// Represents field name information
pub enum FieldNames {
Global(P<ast::Expr>),
Format{
formats: HashMap<P<ast::Expr>, P<ast::Expr>>,
default: P<ast::Expr>,
}
}
/// Represents field attribute information
pub struct FieldAttrs {
names: FieldNames,
use_default: bool,
}
impl FieldAttrs {
/// Create a FieldAttr with a single default field name
pub fn new(default_value: bool, name: P<ast::Expr>) -> FieldAttrs {
FieldAttrs {
names: FieldNames::Global(name),
use_default: default_value,
}
}
/// Create a FieldAttr with format specific field names
pub fn new_with_formats(
default_value: bool,
default_name: P<ast::Expr>,
formats: HashMap<P<ast::Expr>, P<ast::Expr>>,
) -> FieldAttrs {
FieldAttrs {
names: FieldNames::Format {
formats: formats,
default: default_name,
},
use_default: default_value,
}
}
/// Return a set of formats that the field has attributes for.
pub fn formats(&self) -> HashSet<P<ast::Expr>> {
match self.names {
FieldNames::Format{ref formats, default: _} => {
let mut set = HashSet::new();
for (fmt, _) in formats.iter() {
set.insert(fmt.clone());
};
set
},
_ => HashSet::new()
}
}
/// Return an expression for the field key name for serialisation.
///
/// The resulting expression assumes that `S` refers to a type
/// that implements `Serializer`.
pub fn serializer_key_expr(self, cx: &ExtCtxt) -> P<ast::Expr> {
match self.names {
FieldNames::Global(x) => x,
FieldNames::Format{formats, default} => {
let arms = formats.iter()
.map(|(fmt, lit)| {
quote_arm!(cx, $fmt => { $lit })
})
.collect::<Vec<_>>();
quote_expr!(cx,
{
match S::format() {
$arms
_ => { $default }
}
})
},
}
}
/// Return the default field name for the field.
pub fn default_key_expr(&self) -> &P<ast::Expr> {
match self.names {
FieldNames::Global(ref expr) => expr,
FieldNames::Format{formats: _, ref default} => default
}
}
/// Return the field name for the field in the specified format.
pub fn key_expr(&self, format: &P<ast::Expr>) -> &P<ast::Expr> {
match self.names {
FieldNames::Global(ref expr) =>
expr,
FieldNames::Format{ref formats, ref default} =>
formats.get(format).unwrap_or(default)
}
}
/// Predicate for using a field's default value
pub fn use_default(&self) -> bool {
self.use_default
}
}
@@ -1,4 +1,9 @@
use std::collections::HashSet;
use aster;
use syntax::ast::{
self,
Ident,
MetaItem,
Item,
@@ -6,23 +11,32 @@ use syntax::ast::{
StructDef,
EnumDef,
};
use syntax::ast;
use syntax::codemap::Span;
use syntax::ext::base::ExtCtxt;
use syntax::ext::base::{Annotatable, ExtCtxt};
use syntax::ext::build::AstBuilder;
use syntax::owned_slice::OwnedSlice;
use syntax::ptr::P;
use aster;
use attr;
use field;
pub fn expand_derive_deserialize(
cx: &mut ExtCtxt,
span: Span,
_mitem: &MetaItem,
item: &Item,
push: &mut FnMut(P<ast::Item>)
meta_item: &MetaItem,
annotatable: Annotatable,
push: &mut FnMut(Annotatable)
) {
let item = match annotatable {
Annotatable::Item(item) => item,
_ => {
cx.span_err(
meta_item.span,
"`derive` may only be applied to structs and enums");
return;
}
};
let builder = aster::AstBuilder::new().span(span);
let generics = match item.node {
@@ -44,7 +58,7 @@ pub fn expand_derive_deserialize(
let body = deserialize_body(
cx,
&builder,
item,
&item,
&impl_generics,
ty.clone(),
);
@@ -62,7 +76,7 @@ pub fn expand_derive_deserialize(
}
).unwrap();
push(impl_item)
push(Annotatable::Item(impl_item))
}
fn deserialize_body(
@@ -154,14 +168,25 @@ fn deserialize_item_struct(
fn deserialize_visitor(
builder: &aster::AstBuilder,
trait_generics: &ast::Generics,
) -> (P<ast::Item>, P<ast::Ty>, P<ast::Expr>) {
if trait_generics.ty_params.is_empty() {
forward_ty_params: Vec<ast::TyParam>,
forward_tys: Vec<P<ast::Ty>>
) -> (P<ast::Item>, P<ast::Ty>, P<ast::Expr>, ast::Generics) {
if trait_generics.ty_params.is_empty() && forward_tys.is_empty() {
(
builder.item().tuple_struct("__Visitor").build(),
builder.ty().id("__Visitor"),
builder.expr().id("__Visitor"),
trait_generics.clone(),
)
} else {
let placeholders : Vec<_> = trait_generics.ty_params.iter()
.map(|t| builder.ty().id(t.ident))
.collect();
let mut trait_generics = trait_generics.clone();
let mut ty_params = forward_ty_params.clone();
ty_params.extend(trait_generics.ty_params.into_vec());
trait_generics.ty_params = OwnedSlice::from_vec(ty_params);
(
builder.item().tuple_struct("__Visitor")
.generics().with(trait_generics.clone()).build()
@@ -174,17 +199,38 @@ fn deserialize_visitor(
builder.ty().path()
.segment("__Visitor").with_generics(trait_generics.clone()).build()
.build(),
builder.expr().call().id("__Visitor")
builder.expr().call()
.path().segment("__Visitor")
.with_tys(forward_tys)
.with_tys(placeholders)
.build().build()
.with_args(
trait_generics.ty_params.iter().map(|_| {
builder.expr().phantom_data()
})
)
.build(),
trait_generics,
)
}
}
fn deserializer_ty_param(builder: &aster::AstBuilder) -> ast::TyParam {
builder.ty_param("__D")
.trait_bound(builder.path()
.global()
.segment("serde").build()
.segment("de").build()
.id("Deserializer")
.build())
.build()
.build()
}
fn deserializer_ty_arg(builder: &aster::AstBuilder) -> P<ast::Ty>{
builder.ty().id("__D")
}
fn deserialize_unit_struct(
cx: &ExtCtxt,
builder: &aster::AstBuilder,
@@ -228,10 +274,13 @@ fn deserialize_tuple_struct(
) -> P<ast::Expr> {
let where_clause = &impl_generics.where_clause;
let (visitor_item, visitor_ty, visitor_expr) = deserialize_visitor(
builder,
impl_generics,
);
let (visitor_item, visitor_ty, visitor_expr, visitor_generics) =
deserialize_visitor(
builder,
impl_generics,
vec![deserializer_ty_param(builder)],
vec![deserializer_ty_arg(builder)],
);
let visit_seq_expr = deserialize_seq(
cx,
@@ -245,7 +294,7 @@ fn deserialize_tuple_struct(
quote_expr!(cx, {
$visitor_item
impl $impl_generics ::serde::de::Visitor for $visitor_ty $where_clause {
impl $visitor_generics ::serde::de::Visitor for $visitor_ty $where_clause {
type Value = $ty;
fn visit_seq<__V>(&mut self, mut visitor: __V) -> ::std::result::Result<$ty, __V::Error>
@@ -270,7 +319,7 @@ fn deserialize_seq(
let name = builder.id(format!("__field{}", i));
quote_stmt!(cx,
let $name = match try!(visitor.visit()) {
Some(value) => value,
Some(value) => { value },
None => {
return Err(::serde::de::Error::end_of_stream_error());
}
@@ -303,10 +352,13 @@ fn deserialize_struct(
) -> P<ast::Expr> {
let where_clause = &impl_generics.where_clause;
let (visitor_item, visitor_ty, visitor_expr) = deserialize_visitor(
builder,
impl_generics,
);
let (visitor_item, visitor_ty, visitor_expr, visitor_generics) =
deserialize_visitor(
builder,
&impl_generics,
vec![deserializer_ty_param(builder)],
vec![deserializer_ty_arg(builder)],
);
let (field_visitor, visit_map_expr) = deserialize_struct_visitor(
cx,
@@ -322,7 +374,7 @@ fn deserialize_struct(
$visitor_item
impl $impl_generics ::serde::de::Visitor for $visitor_ty $where_clause {
impl $visitor_generics ::serde::de::Visitor for $visitor_ty $where_clause {
type Value = $ty;
#[inline]
@@ -353,7 +405,10 @@ fn deserialize_item_enum(
cx,
builder,
enum_def.variants.iter()
.map(|variant| builder.expr().str(variant.node.name))
.map(|variant|
attr::FieldAttrs::new(
true,
builder.expr().str(variant.node.name)))
.collect()
);
@@ -361,8 +416,8 @@ fn deserialize_item_enum(
let variant_arms: Vec<_> = enum_def.variants.iter()
.enumerate()
.map(|(i, variant)| {
let variant_name = builder.expr().path()
.id("__Field").id(format!("__field{}", i))
let variant_name = builder.pat().enum_()
.id("__Field").id(format!("__field{}", i)).build()
.build();
let expr = deserialize_variant(
@@ -378,17 +433,20 @@ fn deserialize_item_enum(
})
.collect();
let (visitor_item, visitor_ty, visitor_expr) = deserialize_visitor(
builder,
impl_generics,
);
let (visitor_item, visitor_ty, visitor_expr, visitor_generics) =
deserialize_visitor(
builder,
impl_generics,
vec![deserializer_ty_param(builder)],
vec![deserializer_ty_arg(builder)],
);
quote_expr!(cx, {
$variant_visitor
$visitor_item
impl $impl_generics ::serde::de::EnumVisitor for $visitor_ty $where_clause {
impl $visitor_generics ::serde::de::EnumVisitor for $visitor_ty $where_clause {
type Value = $ty;
fn visit<__V>(&mut self, mut visitor: __V) -> ::std::result::Result<$ty, __V::Error>
@@ -457,10 +515,13 @@ fn deserialize_tuple_variant(
) -> P<ast::Expr> {
let where_clause = &generics.where_clause;
let (visitor_item, visitor_ty, visitor_expr) = deserialize_visitor(
builder,
generics,
);
let (visitor_item, visitor_ty, visitor_expr, visitor_generics) =
deserialize_visitor(
builder,
generics,
vec![deserializer_ty_param(builder)],
vec![deserializer_ty_arg(builder)],
);
let visit_seq_expr = deserialize_seq(
cx,
@@ -472,7 +533,7 @@ fn deserialize_tuple_variant(
quote_expr!(cx, {
$visitor_item
impl $generics ::serde::de::Visitor for $visitor_ty $where_clause {
impl $visitor_generics ::serde::de::Visitor for $visitor_ty $where_clause {
type Value = $ty;
fn visit_seq<__V>(&mut self, mut visitor: __V) -> ::std::result::Result<$ty, __V::Error>
@@ -504,17 +565,20 @@ fn deserialize_struct_variant(
builder.path().id(type_ident).id(variant_ident).build(),
);
let (visitor_item, visitor_ty, visitor_expr) = deserialize_visitor(
builder,
generics,
);
let (visitor_item, visitor_ty, visitor_expr, visitor_generics) =
deserialize_visitor(
builder,
generics,
vec![deserializer_ty_param(builder)],
vec![deserializer_ty_arg(builder)],
);
quote_expr!(cx, {
$field_visitor
$visitor_item
impl $generics ::serde::de::Visitor for $visitor_ty $where_clause {
impl $visitor_generics ::serde::de::Visitor for $visitor_ty $where_clause {
type Value = $ty;
fn visit_map<__V>(&mut self, mut visitor: __V) -> ::std::result::Result<$ty, __V::Error>
@@ -531,10 +595,10 @@ fn deserialize_struct_variant(
fn deserialize_field_visitor(
cx: &ExtCtxt,
builder: &aster::AstBuilder,
field_exprs: Vec<P<ast::Expr>>,
field_attrs: Vec<attr::FieldAttrs>,
) -> Vec<P<ast::Item>> {
// Create the field names for the fields.
let field_idents: Vec<ast::Ident> = (0 .. field_exprs.len())
let field_idents: Vec<ast::Ident> = (0 .. field_attrs.len())
.map(|i| builder.id(format!("__field{}", i)))
.collect();
@@ -548,43 +612,106 @@ fn deserialize_field_visitor(
)
.build();
// A set of all the formats that have specialized field attributes
let formats = field_attrs.iter()
.fold(HashSet::new(), |mut set, field_expr| {
set.extend(field_expr.formats());
set
});
// Match arms to extract a field from a string
let field_arms: Vec<_> = field_idents.iter()
.zip(field_exprs.into_iter())
let default_field_arms: Vec<_> = field_idents.iter()
.zip(field_attrs.iter())
.map(|(field_ident, field_expr)| {
quote_arm!(cx, $field_expr => { Ok(__Field::$field_ident) })
let expr = field_expr.default_key_expr();
quote_arm!(cx, $expr => { Ok(__Field::$field_ident) })
})
.collect();
vec![
field_enum,
let body = if formats.is_empty() {
// No formats specific attributes, so no match on format required
quote_expr!(cx,
match value {
$default_field_arms
_ => { Err(::serde::de::Error::unknown_field_error(value)) }
})
} else {
let field_arms : Vec<_> = formats.iter()
.map(|fmt| {
field_idents.iter()
.zip(field_attrs.iter())
.map(|(field_ident, field_expr)| {
let expr = field_expr.key_expr(fmt);
quote_arm!(cx, $expr => { Ok(__Field::$field_ident) })
})
.collect::<Vec<_>>()
})
.collect();
quote_item!(cx,
impl ::serde::de::Deserialize for __Field {
#[inline]
fn deserialize<D>(deserializer: &mut D) -> ::std::result::Result<__Field, D::Error>
where D: ::serde::de::Deserializer,
{
struct __FieldVisitor;
impl ::serde::de::Visitor for __FieldVisitor {
type Value = __Field;
fn visit_str<E>(&mut self, value: &str) -> ::std::result::Result<__Field, E>
where E: ::serde::de::Error,
{
match value {
$field_arms
_ => Err(::serde::de::Error::unknown_field_error(value)),
}
let fmt_matches : Vec<_> = formats.iter()
.zip(field_arms.iter())
.map(|(ref fmt, ref arms)| {
quote_arm!(cx, $fmt => {
match value {
$arms
_ => {
Err(::serde::de::Error::unknown_field_error(value))
}
}})
})
.collect();
quote_expr!(cx,
match __D::format() {
$fmt_matches
_ => match value {
$default_field_arms
_ => { Err(::serde::de::Error::unknown_field_error(value)) }
}
})
};
let impl_item = quote_item!(cx,
impl ::serde::de::Deserialize for __Field {
#[inline]
fn deserialize<D>(deserializer: &mut D) -> ::std::result::Result<__Field, D::Error>
where D: ::serde::de::Deserializer,
{
use std::marker::PhantomData;
struct __FieldVisitor<D> {
phantom: PhantomData<D>
}
impl<__D> ::serde::de::Visitor for __FieldVisitor<__D>
where __D: ::serde::de::Deserializer
{
type Value = __Field;
fn visit_str<E>(&mut self, value: &str) -> ::std::result::Result<__Field, E>
where E: ::serde::de::Error,
{
$body
}
deserializer.visit(__FieldVisitor)
fn visit_bytes<E>(&mut self, value: &[u8]) -> ::std::result::Result<__Field, E>
where E: ::serde::de::Error,
{
// TODO: would be better to generate a byte string literal match
match ::std::str::from_utf8(value) {
Ok(s) => self.visit_str(s),
_ => Err(::serde::de::Error::syntax_error()),
}
}
}
deserializer.visit(
__FieldVisitor::<D>{ phantom: PhantomData })
}
).unwrap(),
]
}
).unwrap();
vec![field_enum, impl_item]
}
fn deserialize_struct_visitor(
@@ -596,7 +723,7 @@ fn deserialize_struct_visitor(
let field_visitor = deserialize_field_visitor(
cx,
builder,
field::struct_field_strs(cx, builder, struct_def),
field::struct_field_attrs(cx, builder, struct_def),
);
let visit_map_expr = deserialize_map(
@@ -637,23 +764,36 @@ fn deserialize_map(
.collect();
let extract_values: Vec<P<ast::Stmt>> = field_names.iter()
.zip(struct_def.fields.iter())
.map(|(field_name, field)| {
let name_str = match field.node.kind {
ast::NamedField(name, _) => builder.expr().str(name),
ast::UnnamedField(_) => panic!("struct contains unnamed fields"),
};
let missing_expr = if field::default_value(field) {
.zip(field::struct_field_attrs(cx, builder, struct_def).iter())
.map(|(field_name, field_attr)| {
let missing_expr = if field_attr.use_default() {
quote_expr!(cx, ::std::default::Default::default())
} else {
quote_expr!(cx, try!(visitor.missing_field($name_str)))
let formats = field_attr.formats();
let arms : Vec<_> = formats.iter()
.map(|format| {
let key_expr = field_attr.key_expr(format);
quote_arm!(cx, $format => { $key_expr })
})
.collect();
let default = field_attr.default_key_expr();
if arms.is_empty() {
quote_expr!(cx, try!(visitor.missing_field($default)))
} else {
quote_expr!(
cx,
try!(visitor.missing_field(
match __D::format() {
$arms
_ => { $default }
})))
}
};
quote_stmt!(cx,
let $field_name = match $field_name {
Some($field_name) => $field_name,
None => $missing_expr,
None => $missing_expr
};
).unwrap()
})
+133
View File
@@ -0,0 +1,133 @@
use std::collections::HashMap;
use aster;
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 field_attrs<'a>(
builder: &aster::AstBuilder,
field: &'a ast::StructField,
) -> (Rename<'a>, 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))))
} else {
Some((Rename::None, false))
}
})
.unwrap_or((Rename::None, false))
}
pub fn struct_field_attrs(
cx: &ExtCtxt,
builder: &aster::AstBuilder,
struct_def: &ast::StructDef,
) -> Vec<FieldAttrs> {
struct_def.fields.iter()
.map(|field| {
match field_attrs(builder, field) {
(Rename::Global(rename), default_value) =>
FieldAttrs::new(
default_value,
builder.expr().build_lit(P(rename.clone()))),
(Rename::Format(renames), default_value) => {
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(
default_value,
default_field_name(cx, builder, field.node.kind),
res)
},
(Rename::None, default_value) => {
FieldAttrs::new(
default_value,
default_field_name(cx, builder, field.node.kind))
}
}
})
.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")
}
}
}
+73
View File
@@ -0,0 +1,73 @@
#![cfg_attr(not(feature = "with-syntex"), feature(rustc_private, plugin))]
#![cfg_attr(not(feature = "with-syntex"), plugin(quasi_macros))]
extern crate aster;
extern crate quasi;
#[cfg(feature = "with-syntex")]
extern crate syntex;
#[cfg(feature = "with-syntex")]
extern crate syntex_syntax as syntax;
#[cfg(not(feature = "with-syntex"))]
extern crate syntax;
#[cfg(not(feature = "with-syntex"))]
extern crate rustc;
#[cfg(feature = "with-syntex")]
include!(concat!(env!("OUT_DIR"), "/lib.rs"));
#[cfg(not(feature = "with-syntex"))]
include!("lib.rs.in");
#[cfg(feature = "with-syntex")]
pub fn register(reg: &mut syntex::Registry) {
use syntax::{ast, fold};
reg.add_attr("feature(custom_derive)");
reg.add_attr("feature(custom_attribute)");
reg.add_decorator("derive_Serialize", ser::expand_derive_serialize);
reg.add_decorator("derive_Deserialize", de::expand_derive_deserialize);
reg.add_post_expansion_pass(strip_attributes);
/// Strip the serde attributes from the crate.
#[cfg(feature = "with-syntex")]
fn strip_attributes(krate: ast::Crate) -> ast::Crate {
/// Helper folder that strips the serde attributes after the extensions have been expanded.
struct StripAttributeFolder;
impl fold::Folder for StripAttributeFolder {
fn fold_attribute(&mut self, attr: ast::Attribute) -> Option<ast::Attribute> {
match attr.node.value.node {
ast::MetaList(ref n, _) if n == &"serde" => { return None; }
_ => {}
}
Some(attr)
}
fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
fold::noop_fold_mac(mac, self)
}
}
fold::Folder::fold_crate(&mut StripAttributeFolder, krate)
}
}
#[cfg(not(feature = "with-syntex"))]
pub fn register(reg: &mut rustc::plugin::Registry) {
reg.register_syntax_extension(
syntax::parse::token::intern("derive_Serialize"),
syntax::ext::base::MultiDecorator(
Box::new(ser::expand_derive_serialize)));
reg.register_syntax_extension(
syntax::parse::token::intern("derive_Deserialize"),
syntax::ext::base::MultiDecorator(
Box::new(de::expand_derive_deserialize)));
}
+4
View File
@@ -0,0 +1,4 @@
mod attr;
mod de;
mod field;
mod ser;
@@ -1,3 +1,5 @@
use aster;
use syntax::ast::{
Ident,
MetaItem,
@@ -7,21 +9,29 @@ use syntax::ast::{
};
use syntax::ast;
use syntax::codemap::Span;
use syntax::ext::base::ExtCtxt;
use syntax::ext::base::{Annotatable, ExtCtxt};
use syntax::ext::build::AstBuilder;
use syntax::ptr::P;
use aster;
use field::struct_field_strs;
use field::struct_field_attrs;
pub fn expand_derive_serialize(
cx: &mut ExtCtxt,
span: Span,
_mitem: &MetaItem,
item: &Item,
push: &mut FnMut(P<ast::Item>)
meta_item: &MetaItem,
annotatable: Annotatable,
push: &mut FnMut(Annotatable)
) {
let item = match annotatable {
Annotatable::Item(item) => item,
_ => {
cx.span_err(
meta_item.span,
"`derive` may only be applied to structs and enums");
return;
}
};
let builder = aster::AstBuilder::new().span(span);
let generics = match item.node {
@@ -43,7 +53,7 @@ pub fn expand_derive_serialize(
let body = serialize_body(
cx,
&builder,
item,
&item,
&impl_generics,
ty.clone(),
);
@@ -61,7 +71,7 @@ pub fn expand_derive_serialize(
}
).unwrap();
push(impl_item)
push(Annotatable::Item(impl_item))
}
fn serialize_body(
@@ -276,7 +286,7 @@ fn serialize_variant(
$type_name,
$variant_name,
)
},
}
)
}
ast::TupleVariantKind(ref args) => {
@@ -488,11 +498,11 @@ fn serialize_tuple_struct_visitor(
$where_clause {
#[inline]
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 {
$arms
_ => Ok(None),
_ => Ok(None)
}
}
@@ -517,12 +527,13 @@ fn serialize_struct_visitor<I>(
{
let len = struct_def.fields.len();
let key_exprs = struct_field_strs(cx, builder, struct_def);
let field_attrs = struct_field_attrs(cx, builder, struct_def);
let arms: Vec<ast::Arm> = key_exprs.iter()
let arms: Vec<ast::Arm> = field_attrs.into_iter()
.zip(value_exprs)
.enumerate()
.map(|(i, (key_expr, value_expr))| {
.map(|(i, (field, value_expr))| {
let key_expr = field.serializer_key_expr(cx);
quote_arm!(cx,
$i => {
self.state += 1;
@@ -571,7 +582,7 @@ fn serialize_struct_visitor<I>(
{
match self.state {
$arms
_ => Ok(None),
_ => Ok(None)
}
}
+7 -4
View File
@@ -1,6 +1,6 @@
[package]
name = "serde_macros"
version = "0.3.1"
version = "0.4.0"
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"]
license = "MIT/Apache-2.0"
description = "Macros to auto-generate implementations for the serde framework"
@@ -11,6 +11,9 @@ name = "serde_macros"
plugin = true
[dependencies]
aster = "*"
quasi = "*"
quasi_macros = "*"
serde_codegen = { version = "*", path = "../serde_codegen", default-features = false, features = ["nightly"] }
[dev-dependencies]
num = "*"
rustc-serialize = "*"
serde = { version = "*", path = "../serde" }
+9
View File
@@ -0,0 +1,9 @@
#![feature(custom_attribute, custom_derive, plugin, test)]
#![plugin(serde_macros)]
extern crate num;
extern crate rustc_serialize;
extern crate serde;
extern crate test;
include!("../../serde_tests/benches/bench.rs.in");
-83
View File
@@ -1,83 +0,0 @@
use syntax::ast;
use syntax::attr;
use syntax::ext::base::ExtCtxt;
use syntax::ptr::P;
use aster;
fn field_rename(field: &ast::StructField) -> Option<&ast::Lit> {
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);
vals.iter().fold(None, |v, mi| {
if let ast::MetaNameValue(ref n, ref lit) = mi.node {
if n == &"rename" {
Some(lit)
} else {
v
}
} else {
v
}
})
} else {
None
}
})
}
pub fn struct_field_strs(
cx: &ExtCtxt,
builder: &aster::AstBuilder,
struct_def: &ast::StructDef,
) -> Vec<P<ast::Expr>> {
struct_def.fields.iter()
.map(|field| {
match field_rename(field) {
Some(rename) => builder.expr().build_lit(P(rename.clone())),
None => {
match field.node.kind {
ast::NamedField(name, _) => {
builder.expr().str(name)
}
ast::UnnamedField(_) => {
cx.bug("struct has named and unnamed fields")
}
}
}
}
})
.collect()
}
pub fn default_value(field: &ast::StructField) -> bool {
field.node.attrs.iter()
.any(|sa| {
if let ast::MetaItem_::MetaList(ref n, ref vals) = sa.node.value.node {
if n == &"serde" {
attr::mark_used(&sa);
vals.iter()
.map(|mi|
if let ast::MetaItem_::MetaWord(ref n) = mi.node {
n == &"default"
} else {
false
})
.any(|x| x)
} else {
false
}
}
else {
false
}
})
}
+4 -21
View File
@@ -1,27 +1,10 @@
#![feature(custom_derive, plugin, plugin_registrar, rustc_private, unboxed_closures)]
#![plugin(quasi_macros)]
#![feature(plugin_registrar, rustc_private)]
extern crate aster;
extern crate quasi;
extern crate serde_codegen;
extern crate rustc;
extern crate syntax;
use syntax::ext::base::Decorator;
use syntax::parse::token;
use rustc::plugin::Registry;
mod ser;
mod de;
mod field;
#[plugin_registrar]
#[doc(hidden)]
pub fn plugin_registrar(reg: &mut Registry) {
reg.register_syntax_extension(
token::intern("derive_Serialize"),
Decorator(Box::new(ser::expand_derive_serialize)));
reg.register_syntax_extension(
token::intern("derive_Deserialize"),
Decorator(Box::new(de::expand_derive_deserialize)));
pub fn plugin_registrar(reg: &mut rustc::plugin::Registry) {
serde_codegen::register(reg);
}
+7
View File
@@ -0,0 +1,7 @@
#![feature(test, custom_attribute, custom_derive, plugin)]
#![plugin(serde_macros)]
extern crate serde;
extern crate test;
include!("../../serde_tests/tests/test.rs.in");
+30
View File
@@ -0,0 +1,30 @@
[package]
name = "serde_tests"
version = "0.4.0"
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"]
license = "MIT/Apache-2.0"
description = "A generic serialization/deserialization framework"
repository = "https://github.com/serde-rs/serde"
documentation = "http://serde-rs.github.io/serde/serde"
readme = "README.md"
keywords = ["serialization"]
build = "build.rs"
[build-dependencies]
syntex = { version = "*", optional = true }
syntex_syntax = { version = "*" }
serde_codegen = { version = "*", path = "../serde_codegen", features = ["with-syntex"] }
[dev-dependencies]
num = "*"
rustc-serialize = "*"
serde = { version = "*", path = "../serde" }
syntex = "*"
[[test]]
name = "test"
path = "tests/test.rs"
[[bench]]
name = "bench"
path = "benches/bench.rs"
+8
View File
@@ -0,0 +1,8 @@
#![feature(test)]
extern crate num;
extern crate rustc_serialize;
extern crate serde;
extern crate test;
include!(concat!(env!("OUT_DIR"), "/bench.rs"));
+5
View File
@@ -0,0 +1,5 @@
mod bench_enum;
mod bench_log;
mod bench_map;
mod bench_struct;
mod bench_vec;
@@ -1,18 +1,8 @@
#![feature(custom_derive, plugin, test)]
#![plugin(serde_macros)]
extern crate serde;
extern crate rustc_serialize;
extern crate test;
use test::Bencher;
use rustc_serialize::{Decoder, Decodable};
use serde;
use serde::de::{Deserializer, Deserialize};
use Animal::{Dog, Frog};
//////////////////////////////////////////////////////////////////////////////
#[derive(Clone, PartialEq, Debug, RustcDecodable, Deserialize)]
@@ -408,7 +398,7 @@ mod deserializer {
#[bench]
fn bench_decoder_dog(b: &mut Bencher) {
b.iter(|| {
let animal = Dog;
let animal = Animal::Dog;
let mut d = decoder::AnimalDecoder::new(animal.clone());
let value: Animal = Decodable::decode(&mut d).unwrap();
@@ -420,7 +410,7 @@ fn bench_decoder_dog(b: &mut Bencher) {
#[bench]
fn bench_decoder_frog(b: &mut Bencher) {
b.iter(|| {
let animal = Frog("Henry".to_string(), 349);
let animal = Animal::Frog("Henry".to_string(), 349);
let mut d = decoder::AnimalDecoder::new(animal.clone());
let value: Animal = Decodable::decode(&mut d).unwrap();
@@ -432,7 +422,7 @@ fn bench_decoder_frog(b: &mut Bencher) {
#[bench]
fn bench_deserializer_dog(b: &mut Bencher) {
b.iter(|| {
let animal = Dog;
let animal = Animal::Dog;
let mut d = deserializer::AnimalDeserializer::new(animal.clone());
let value: Animal = Deserialize::deserialize(&mut d).unwrap();
@@ -444,7 +434,7 @@ fn bench_deserializer_dog(b: &mut Bencher) {
#[bench]
fn bench_deserializer_frog(b: &mut Bencher) {
b.iter(|| {
let animal = Frog("Henry".to_string(), 349);
let animal = Animal::Frog("Henry".to_string(), 349);
let mut d = deserializer::AnimalDeserializer::new(animal.clone());
let value: Animal = Deserialize::deserialize(&mut d).unwrap();
@@ -1,20 +1,14 @@
#![feature(custom_derive, collections, plugin, test)]
#![allow(non_camel_case_types)]
#![plugin(serde_macros)]
extern crate num;
extern crate rustc_serialize;
extern crate serde;
extern crate test;
use std::io::{self, Read, Write};
use num::FromPrimitive;
use test::Bencher;
use rustc_serialize;
use serde::de::{self, Deserialize, Deserializer};
use serde::json::ser::escape_str;
use serde::json;
use serde::ser::{self, Serialize, Serializer};
use std::str::FromStr;
use rustc_serialize::Encodable;
@@ -31,6 +25,7 @@ struct Http {
request_uri: String,
}
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug, PartialEq)]
enum HttpProtocol {
HTTP_PROTOCOL_UNKNOWN,
@@ -53,6 +48,11 @@ impl rustc_serialize::Decodable for HttpProtocol {
}
}
impl FromStr for HttpProtocol {
type Err = ();
fn from_str(s: &str) -> Result<HttpProtocol, ()> { unimplemented!() }
}
impl FromPrimitive for HttpProtocol {
fn from_i64(i: i64) -> Option<HttpProtocol> {
FromPrimitive::from_u64(i as u64)
@@ -86,6 +86,7 @@ impl de::Deserialize for HttpProtocol {
}
}
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug, PartialEq)]
enum HttpMethod {
METHOD_UNKNOWN,
@@ -101,6 +102,11 @@ enum HttpMethod {
PATCH,
}
impl FromStr for HttpMethod {
type Err = ();
fn from_str(s: &str) -> Result<HttpMethod, ()> { unimplemented!() }
}
impl FromPrimitive for HttpMethod {
fn from_i64(i: i64) -> Option<HttpMethod> {
FromPrimitive::from_u64(i as u64)
@@ -157,6 +163,7 @@ impl de::Deserialize for HttpMethod {
}
}
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug, PartialEq)]
enum CacheStatus {
CACHESTATUS_UNKNOWN,
@@ -165,6 +172,11 @@ enum CacheStatus {
Hit,
}
impl FromStr for CacheStatus {
type Err = ();
fn from_str(s: &str) -> Result<CacheStatus, ()> { unimplemented!() }
}
impl FromPrimitive for CacheStatus {
fn from_i64(i: i64) -> Option<CacheStatus> {
FromPrimitive::from_u64(i as u64)
@@ -222,6 +234,7 @@ struct Origin {
protocol: OriginProtocol,
}
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug, PartialEq)]
enum OriginProtocol {
ORIGIN_PROTOCOL_UNKNOWN,
@@ -229,6 +242,11 @@ enum OriginProtocol {
HTTPS,
}
impl FromStr for OriginProtocol {
type Err = ();
fn from_str(s: &str) -> Result<OriginProtocol, ()> { unimplemented!() }
}
impl FromPrimitive for OriginProtocol {
fn from_i64(i: i64) -> Option<OriginProtocol> {
FromPrimitive::from_u64(i as u64)
@@ -277,6 +295,7 @@ impl de::Deserialize for OriginProtocol {
}
}
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug, PartialEq)]
enum ZonePlan {
ZONEPLAN_UNKNOWN,
@@ -286,6 +305,11 @@ enum ZonePlan {
ENT,
}
impl FromStr for ZonePlan {
type Err = ();
fn from_str(s: &str) -> Result<ZonePlan, ()> { unimplemented!() }
}
impl FromPrimitive for ZonePlan {
fn from_i64(i: i64) -> Option<ZonePlan> {
FromPrimitive::from_u64(i as u64)
@@ -596,6 +620,11 @@ enum Country {
ZW,
}
impl FromStr for Country {
type Err = ();
fn from_str(s: &str) -> Result<Country, ()> { unimplemented!() }
}
impl FromPrimitive for Country {
fn from_i64(i: i64) -> Option<Country> {
FromPrimitive::from_u64(i as u64)
@@ -987,12 +1016,20 @@ impl MyMemWriter0 {
impl Write for MyMemWriter0 {
#[cfg(feature = "nightly")]
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.buf.push_all(buf);
Ok(buf.len())
}
#[cfg(not(feature = "nightly"))]
#[inline]
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.buf.extend(buf.iter().cloned());
Ok(buf.len())
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
Ok(())
@@ -1,16 +1,10 @@
#![feature(custom_derive, plugin, test)]
#![plugin(serde_macros)]
extern crate serde;
extern crate rustc_serialize;
extern crate test;
use std::fmt::Debug;
use std::collections::HashMap;
use test::Bencher;
use rustc_serialize::{Decoder, Decodable};
use serde;
use serde::de::{Deserializer, Deserialize};
//////////////////////////////////////////////////////////////////////////////
@@ -1,15 +1,9 @@
#![feature(custom_derive, plugin, test)]
#![plugin(serde_macros)]
extern crate serde;
extern crate rustc_serialize;
extern crate test;
use std::collections::HashMap;
use test::Bencher;
use rustc_serialize::{Decoder, Decodable};
use serde;
use serde::de::{Deserializer, Deserialize};
//////////////////////////////////////////////////////////////////////////////
@@ -34,8 +28,6 @@ pub struct Outer {
pub enum Error {
EndOfStream,
SyntaxError,
UnexpectedName,
ConversionError,
MissingField,
OtherError,
}
@@ -1,15 +1,9 @@
#![feature(plugin, test)]
#![plugin(serde_macros)]
extern crate serde;
extern crate rustc_serialize;
extern crate test;
use std::fmt::Debug;
use test::Bencher;
use rustc_serialize::{Decoder, Decodable};
use serde;
use serde::de::{Deserializer, Deserialize};
//////////////////////////////////////////////////////////////////////////////
+22
View File
@@ -0,0 +1,22 @@
extern crate syntex;
extern crate serde_codegen;
use std::env;
use std::path::Path;
fn main() {
let out_dir = env::var_os("OUT_DIR").unwrap();
for &(src, dst) in &[
("tests/test.rs.in", "test.rs"),
("benches/bench.rs.in", "bench.rs"),
] {
let src = Path::new(src);
let dst = Path::new(&out_dir).join(dst);
let mut registry = syntex::Registry::new();
serde_codegen::register(&mut registry);
registry.expand("", &src, &dst).unwrap();
}
}
+6
View File
@@ -0,0 +1,6 @@
#![feature(test)]
extern crate serde;
extern crate test;
include!(concat!(env!("OUT_DIR"), "/test.rs"));
+7
View File
@@ -0,0 +1,7 @@
mod test_annotations;
mod test_bytes;
mod test_de;
mod test_json;
mod test_json_builder;
mod test_macros;
mod test_ser;
+73
View File
@@ -0,0 +1,73 @@
use serde::json;
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Default {
a1: i32,
#[serde(default)]
a2: i32,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Rename {
a1: i32,
#[serde(rename="a3")]
a2: i32,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct FormatRename {
a1: i32,
#[serde(rename(xml= "a4", json="a5"))]
a2: i32,
}
#[derive(Debug, PartialEq, Deserialize, Serialize)]
enum SerEnum<A> {
Map {
a: i8,
#[serde(rename(xml= "c", json="d"))]
b: A,
},
}
#[test]
fn test_default() {
let deserialized_value: Default = json::from_str(&"{\"a1\":1,\"a2\":2}").unwrap();
assert_eq!(deserialized_value, Default { a1: 1, a2: 2 });
let deserialized_value: Default = json::from_str(&"{\"a1\":1}").unwrap();
assert_eq!(deserialized_value, Default { a1: 1, a2: 0 });
}
#[test]
fn test_rename() {
let value = Rename { a1: 1, a2: 2 };
let serialized_value = json::to_string(&value).unwrap();
assert_eq!(serialized_value, "{\"a1\":1,\"a3\":2}");
let deserialized_value: Rename = json::from_str(&serialized_value).unwrap();
assert_eq!(value, deserialized_value);
}
#[test]
fn test_format_rename() {
let value = FormatRename { a1: 1, a2: 2 };
let serialized_value = json::to_string(&value).unwrap();
assert_eq!(serialized_value, "{\"a1\":1,\"a5\":2}");
let deserialized_value = json::from_str("{\"a1\":1,\"a5\":2}").unwrap();
assert_eq!(value, deserialized_value);
}
#[test]
fn test_enum_format_rename() {
let s1 = String::new();
let value = SerEnum::Map { a: 0i8, b: s1 };
let serialized_value = json::to_string(&value).unwrap();
let ans = "{\"Map\":{\"a\":0,\"d\":\"\"}}";
assert_eq!(serialized_value, ans);
let deserialized_value = json::from_str(ans).unwrap();
assert_eq!(value, deserialized_value);
}
@@ -1,9 +1,4 @@
#![feature(custom_derive, plugin, test)]
#![plugin(serde_macros)]
extern crate test;
extern crate serde;
use serde;
use serde::Serialize;
use serde::bytes::{ByteBuf, Bytes};
use serde::json;
@@ -1,9 +1,3 @@
#![feature(custom_derive, plugin, test)]
#![plugin(serde_macros)]
extern crate test;
extern crate serde;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::iter;
use std::vec;
@@ -652,6 +646,46 @@ declare_tests! {
Token::SeqEnd,
],
}
test_array {
[0; 0] => vec![
Token::Unit,
],
[0; 0] => vec![
Token::SeqStart(0),
Token::SeqEnd,
],
([0; 0], [1], [2, 3]) => vec![
Token::SeqStart(3),
Token::SeqSep,
Token::SeqStart(0),
Token::SeqEnd,
Token::SeqSep,
Token::SeqStart(1),
Token::SeqSep,
Token::I32(1),
Token::SeqEnd,
Token::SeqSep,
Token::SeqStart(2),
Token::SeqSep,
Token::I32(2),
Token::SeqSep,
Token::I32(3),
Token::SeqEnd,
Token::SeqEnd,
],
[0; 0] => vec![
Token::Name("Anything"),
Token::Unit,
],
[0; 0] => vec![
Token::Name("Anything"),
Token::SeqStart(0),
Token::SeqEnd,
],
}
test_tuple {
(1,) => vec![
Token::SeqStart(1),
@@ -1,9 +1,3 @@
#![feature(custom_derive, plugin, test)]
#![plugin(serde_macros)]
extern crate test;
extern crate serde;
use std::fmt::Debug;
use std::collections::BTreeMap;
@@ -668,8 +662,8 @@ fn test_parse_err<T>(errors: Vec<(&'static str, Error)>)
#[test]
fn test_parse_null() {
test_parse_err::<()>(vec![
("n", Error::SyntaxError(ErrorCode::ExpectedSomeIdent, 1, 2)),
("nul", Error::SyntaxError(ErrorCode::ExpectedSomeIdent, 1, 4)),
("n", Error::SyntaxError(ErrorCode::ExpectedSomeIdent, 1, 1)),
("nul", Error::SyntaxError(ErrorCode::ExpectedSomeIdent, 1, 3)),
("nulla", Error::SyntaxError(ErrorCode::TrailingCharacters, 1, 5)),
]);
@@ -681,9 +675,9 @@ fn test_parse_null() {
#[test]
fn test_parse_bool() {
test_parse_err::<bool>(vec![
("t", Error::SyntaxError(ErrorCode::ExpectedSomeIdent, 1, 2)),
("t", Error::SyntaxError(ErrorCode::ExpectedSomeIdent, 1, 1)),
("truz", Error::SyntaxError(ErrorCode::ExpectedSomeIdent, 1, 4)),
("f", Error::SyntaxError(ErrorCode::ExpectedSomeIdent, 1, 2)),
("f", Error::SyntaxError(ErrorCode::ExpectedSomeIdent, 1, 1)),
("faz", Error::SyntaxError(ErrorCode::ExpectedSomeIdent, 1, 3)),
("truea", Error::SyntaxError(ErrorCode::TrailingCharacters, 1, 5)),
("falsea", Error::SyntaxError(ErrorCode::TrailingCharacters, 1, 6)),
@@ -702,12 +696,13 @@ fn test_parse_number_errors() {
test_parse_err::<f64>(vec![
("+", Error::SyntaxError(ErrorCode::ExpectedSomeValue, 1, 1)),
(".", Error::SyntaxError(ErrorCode::ExpectedSomeValue, 1, 1)),
("-", Error::SyntaxError(ErrorCode::InvalidNumber, 1, 2)),
("-", Error::SyntaxError(ErrorCode::InvalidNumber, 1, 1)),
("00", Error::SyntaxError(ErrorCode::InvalidNumber, 1, 2)),
("1.", Error::SyntaxError(ErrorCode::InvalidNumber, 1, 3)),
("1e", Error::SyntaxError(ErrorCode::InvalidNumber, 1, 3)),
("1e+", Error::SyntaxError(ErrorCode::InvalidNumber, 1, 4)),
("1.", Error::SyntaxError(ErrorCode::InvalidNumber, 1, 2)),
("1e", Error::SyntaxError(ErrorCode::InvalidNumber, 1, 2)),
("1e+", Error::SyntaxError(ErrorCode::InvalidNumber, 1, 3)),
("1a", Error::SyntaxError(ErrorCode::TrailingCharacters, 1, 2)),
("777777777777777777777777777", Error::SyntaxError(ErrorCode::InvalidNumber, 1, 20)),
]);
}
@@ -745,8 +740,8 @@ fn test_parse_f64() {
#[test]
fn test_parse_string() {
test_parse_err::<String>(vec![
("\"", Error::SyntaxError(ErrorCode::EOFWhileParsingString, 1, 2)),
("\"lol", Error::SyntaxError(ErrorCode::EOFWhileParsingString, 1, 5)),
("\"", Error::SyntaxError(ErrorCode::EOFWhileParsingString, 1, 1)),
("\"lol", Error::SyntaxError(ErrorCode::EOFWhileParsingString, 1, 4)),
("\"lol\"a", Error::SyntaxError(ErrorCode::TrailingCharacters, 1, 6)),
]);
@@ -767,10 +762,10 @@ fn test_parse_string() {
#[test]
fn test_parse_list() {
test_parse_err::<Vec<f64>>(vec![
("[", Error::SyntaxError(ErrorCode::EOFWhileParsingValue, 1, 2)),
("[ ", Error::SyntaxError(ErrorCode::EOFWhileParsingValue, 1, 3)),
("[1", Error::SyntaxError(ErrorCode::EOFWhileParsingList, 1, 3)),
("[1,", Error::SyntaxError(ErrorCode::EOFWhileParsingValue, 1, 4)),
("[", Error::SyntaxError(ErrorCode::EOFWhileParsingValue, 1, 1)),
("[ ", Error::SyntaxError(ErrorCode::EOFWhileParsingValue, 1, 2)),
("[1", Error::SyntaxError(ErrorCode::EOFWhileParsingList, 1, 2)),
("[1,", Error::SyntaxError(ErrorCode::EOFWhileParsingValue, 1, 3)),
("[1,]", Error::SyntaxError(ErrorCode::ExpectedSomeValue, 1, 4)),
("[1 2]", Error::SyntaxError(ErrorCode::ExpectedListCommaOrEnd, 1, 4)),
("[]a", Error::SyntaxError(ErrorCode::TrailingCharacters, 1, 3)),
@@ -819,17 +814,17 @@ fn test_parse_list() {
#[test]
fn test_parse_object() {
test_parse_err::<BTreeMap<String, u32>>(vec![
("{", Error::SyntaxError(ErrorCode::EOFWhileParsingValue, 1, 2)),
("{ ", Error::SyntaxError(ErrorCode::EOFWhileParsingValue, 1, 3)),
("{", Error::SyntaxError(ErrorCode::EOFWhileParsingValue, 1, 1)),
("{ ", Error::SyntaxError(ErrorCode::EOFWhileParsingValue, 1, 2)),
("{1", Error::SyntaxError(ErrorCode::KeyMustBeAString, 1, 2)),
("{ \"a\"", Error::SyntaxError(ErrorCode::EOFWhileParsingObject, 1, 6)),
("{\"a\"", Error::SyntaxError(ErrorCode::EOFWhileParsingObject, 1, 5)),
("{\"a\" ", Error::SyntaxError(ErrorCode::EOFWhileParsingObject, 1, 6)),
("{ \"a\"", Error::SyntaxError(ErrorCode::EOFWhileParsingObject, 1, 5)),
("{\"a\"", Error::SyntaxError(ErrorCode::EOFWhileParsingObject, 1, 4)),
("{\"a\" ", Error::SyntaxError(ErrorCode::EOFWhileParsingObject, 1, 5)),
("{\"a\" 1", Error::SyntaxError(ErrorCode::ExpectedColon, 1, 6)),
("{\"a\":", Error::SyntaxError(ErrorCode::EOFWhileParsingValue, 1, 6)),
("{\"a\":1", Error::SyntaxError(ErrorCode::EOFWhileParsingObject, 1, 7)),
("{\"a\":", Error::SyntaxError(ErrorCode::EOFWhileParsingValue, 1, 5)),
("{\"a\":1", Error::SyntaxError(ErrorCode::EOFWhileParsingObject, 1, 6)),
("{\"a\":1 1", Error::SyntaxError(ErrorCode::ExpectedObjectCommaOrEnd, 1, 8)),
("{\"a\":1,", Error::SyntaxError(ErrorCode::EOFWhileParsingValue, 1, 8)),
("{\"a\":1,", Error::SyntaxError(ErrorCode::EOFWhileParsingValue, 1, 7)),
("{}a", Error::SyntaxError(ErrorCode::TrailingCharacters, 1, 3)),
]);
@@ -870,8 +865,8 @@ fn test_parse_object() {
#[test]
fn test_parse_struct() {
test_parse_err::<Outer>(vec![
("5", Error::SyntaxError(ErrorCode::ExpectedSomeValue, 1, 2)),
("\"hello\"", Error::SyntaxError(ErrorCode::ExpectedSomeValue, 1, 8)),
("5", Error::SyntaxError(ErrorCode::ExpectedSomeValue, 1, 1)),
("\"hello\"", Error::SyntaxError(ErrorCode::ExpectedSomeValue, 1, 7)),
("{\"inner\": true}", Error::SyntaxError(ErrorCode::ExpectedSomeValue, 1, 15)),
]);
@@ -933,7 +928,7 @@ fn test_parse_option() {
fn test_parse_enum_errors() {
test_parse_err::<Animal>(vec![
("{}", Error::SyntaxError(ErrorCode::ExpectedSomeValue, 1, 2)),
("{\"Dog\":", Error::SyntaxError(ErrorCode::EOFWhileParsingValue, 1, 8)),
("{\"Dog\":", Error::SyntaxError(ErrorCode::EOFWhileParsingValue, 1, 7)),
("{\"Dog\":}", Error::SyntaxError(ErrorCode::ExpectedSomeValue, 1, 8)),
("{\"unknown\":[]}", Error::SyntaxError(ErrorCode::UnknownField("unknown".to_string()), 1, 11)),
("{\"Dog\":{}}", Error::SyntaxError(ErrorCode::ExpectedSomeValue, 1, 9)),
@@ -994,7 +989,7 @@ fn test_parse_trailing_whitespace() {
#[test]
fn test_multiline_errors() {
test_parse_err::<BTreeMap<String, String>>(vec![
("{\n \"foo\":\n \"bar\"", Error::SyntaxError(ErrorCode::EOFWhileParsingObject, 3, 8)),
("{\n \"foo\":\n \"bar\"", Error::SyntaxError(ErrorCode::EOFWhileParsingObject, 3, 6)),
]);
}
@@ -1019,3 +1014,67 @@ fn test_missing_field() {
))).unwrap();
assert_eq!(value, Foo { x: Some(5) });
}
#[test]
fn test_missing_renamed_field() {
#[derive(Debug, PartialEq, Deserialize)]
struct Foo {
#[serde(rename="y")]
x: Option<u32>,
}
let value: Foo = from_str("{}").unwrap();
assert_eq!(value, Foo { x: None });
let value: Foo = from_str("{\"y\": 5}").unwrap();
assert_eq!(value, Foo { x: Some(5) });
let value: Foo = from_value(Value::Object(treemap!())).unwrap();
assert_eq!(value, Foo { x: None });
let value: Foo = from_value(Value::Object(treemap!(
"y".to_string() => Value::I64(5)
))).unwrap();
assert_eq!(value, Foo { x: Some(5) });
}
#[test]
fn test_missing_fmt_renamed_field() {
#[derive(Debug, PartialEq, Deserialize)]
struct Foo {
#[serde(rename(json="y"))]
x: Option<u32>,
}
let value: Foo = from_str("{}").unwrap();
assert_eq!(value, Foo { x: None });
let value: Foo = from_str("{\"y\": 5}").unwrap();
assert_eq!(value, Foo { x: Some(5) });
let value: Foo = from_value(Value::Object(treemap!())).unwrap();
assert_eq!(value, Foo { x: None });
let value : Foo = from_value(Value::Object(treemap!(
"y".to_string() => Value::I64(5)
))).unwrap();
assert_eq!(value, Foo { x: Some(5) });
}
#[test]
fn test_find_path() {
let obj: Value = json::from_str(r#"{"x": {"a": 1}, "y": 2}"#).unwrap();
assert!(obj.find_path(&["x", "a"]).unwrap() == &Value::U64(1));
assert!(obj.find_path(&["y"]).unwrap() == &Value::U64(2));
assert!(obj.find_path(&["z"]).is_none());
}
#[test]
fn test_lookup() {
let obj: Value = json::from_str(r#"{"x": {"a": 1}, "y": 2}"#).unwrap();
assert!(obj.lookup("x.a").unwrap() == &Value::U64(1));
assert!(obj.lookup("y").unwrap() == &Value::U64(2));
assert!(obj.lookup("z").is_none());
}
@@ -1,5 +1,3 @@
extern crate serde;
use std::collections::BTreeMap;
use serde::json::value::Value;
@@ -1,8 +1,3 @@
#![feature(custom_derive, plugin)]
#![plugin(serde_macros)]
extern crate serde;
use std::collections::BTreeMap;
use serde::json::{self, Value};
@@ -1,9 +1,3 @@
#![feature(custom_derive, plugin, test)]
#![plugin(serde_macros)]
extern crate test;
extern crate serde;
use std::vec;
use std::collections::BTreeMap;
@@ -380,6 +374,24 @@ declare_tests! {
Token::SeqEnd,
],
}
test_array {
[0; 0] => vec![
Token::SeqStart(Some(0)),
Token::SeqEnd,
],
[1, 2, 3] => vec![
Token::SeqStart(Some(3)),
Token::SeqSep,
Token::I32(1),
Token::SeqSep,
Token::I32(2),
Token::SeqSep,
Token::I32(3),
Token::SeqEnd,
],
}
test_vec {
Vec::<isize>::new() => vec![
Token::SeqStart(Some(0)),
-40
View File
@@ -1,40 +0,0 @@
#![feature(custom_attribute, custom_derive, plugin, test)]
#![plugin(serde_macros)]
extern crate test;
extern crate serde;
use serde::json;
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Default {
a1: i32,
#[serde(default)]
a2: i32,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Rename {
a1: i32,
#[serde(rename="a3")]
a2: i32,
}
#[test]
fn test_default() {
let deserialized_value: Default = json::from_str(&"{\"a1\":1,\"a2\":2}").unwrap();
assert_eq!(deserialized_value, Default { a1: 1, a2: 2 });
let deserialized_value: Default = json::from_str(&"{\"a1\":1}").unwrap();
assert_eq!(deserialized_value, Default { a1: 1, a2: 0 });
}
#[test]
fn test_rename() {
let value = Rename { a1: 1, a2: 2 };
let serialized_value = json::to_string(&value).unwrap();
assert_eq!(serialized_value, "{\"a1\":1,\"a3\":2}");
let deserialized_value: Rename = json::from_str(&serialized_value).unwrap();
assert_eq!(value, deserialized_value);
}