Compare commits

...

25 Commits

Author SHA1 Message Date
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
12 changed files with 243 additions and 132 deletions
+7 -7
View File
@@ -1,17 +1,17 @@
[package]
name = "serde"
version = "0.3.1"
version = "0.3.3"
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"]
license = "MIT/Apache-2.0"
description = "A serialization/deserialization framework"
repository = "https://github.com/erickt/rust-serde"
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 = "*"
[dev-dependencies]
rustc-serialize = "*"
[dev-dependencies.serde_macros]
path = "serde_macros/"
version = "0.3.1"
serde_macros = { version = "*", path = "serde_macros" }
+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>
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "serde_macros"
version = "0.3.1"
version = "0.3.3"
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"]
license = "MIT/Apache-2.0"
description = "Macros to auto-generate implementations for the serde framework"
+8 -6
View File
@@ -361,8 +361,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(
@@ -596,7 +596,7 @@ fn deserialize_struct_visitor(
let field_visitor = deserialize_field_visitor(
cx,
builder,
field::struct_field_strs(cx, builder, struct_def),
field::struct_field_strs(cx, builder, struct_def, field::Direction::Deserialize),
);
let visit_map_expr = deserialize_map(
@@ -639,9 +639,11 @@ fn deserialize_map(
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 rename = field::field_rename(field, &field::Direction::Deserialize);
let name_str = match (rename, field.node.kind) {
(Some(rename), _) => builder.expr().build_lit(P(rename.clone())),
(None, ast::NamedField(name, _)) => builder.expr().str(name),
(None, ast::UnnamedField(_)) => panic!("struct contains unnamed fields"),
};
let missing_expr = if field::default_value(field) {
+16 -3
View File
@@ -5,7 +5,19 @@ use syntax::ptr::P;
use aster;
fn field_rename(field: &ast::StructField) -> Option<&ast::Lit> {
pub enum Direction {
Serialize,
Deserialize,
}
pub fn field_rename<'a>(
field: &'a ast::StructField,
direction: &Direction,
) -> Option<&'a ast::Lit> {
let dir_attr = match *direction {
Direction::Serialize => "rename_serialize",
Direction::Deserialize => "rename_deserialize",
};
field.node.attrs.iter()
.find(|sa| {
if let ast::MetaList(ref n, _) = sa.node.value.node {
@@ -19,7 +31,7 @@ fn field_rename(field: &ast::StructField) -> Option<&ast::Lit> {
attr::mark_used(&sa);
vals.iter().fold(None, |v, mi| {
if let ast::MetaNameValue(ref n, ref lit) = mi.node {
if n == &"rename" {
if n == &"rename" || n == &dir_attr {
Some(lit)
} else {
v
@@ -38,10 +50,11 @@ pub fn struct_field_strs(
cx: &ExtCtxt,
builder: &aster::AstBuilder,
struct_def: &ast::StructDef,
direction: Direction,
) -> Vec<P<ast::Expr>> {
struct_def.fields.iter()
.map(|field| {
match field_rename(field) {
match field_rename(field, &direction) {
Some(rename) => builder.expr().build_lit(P(rename.clone())),
None => {
match field.node.kind {
+2 -2
View File
@@ -13,7 +13,7 @@ use syntax::ptr::P;
use aster;
use field::struct_field_strs;
use field::{Direction, struct_field_strs};
pub fn expand_derive_serialize(
cx: &mut ExtCtxt,
@@ -517,7 +517,7 @@ fn serialize_struct_visitor<I>(
{
let len = struct_def.fields.len();
let key_exprs = struct_field_strs(cx, builder, struct_def);
let key_exprs = struct_field_strs(cx, builder, struct_def, Direction::Serialize);
let arms: Vec<ast::Arm> = key_exprs.iter()
.zip(value_exprs)
+51
View File
@@ -0,0 +1,51 @@
use std::io;
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(&self) -> &Iter { &self.iter }
/// Unwraps this `LineColIterator`, returning the underlying iterator.
pub fn into_inner(self) -> Iter { self.iter }
}
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)),
}
}
}
+10 -19
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> {
@@ -476,12 +467,12 @@ impl<Iter> de::Deserializer for Deserializer<Iter>
}
}
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 +524,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,
+43 -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,
@@ -295,16 +304,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 +339,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 +351,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 +418,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 +434,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 +461,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 +508,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(())
}
+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;
+17
View File
@@ -20,6 +20,13 @@ struct Rename {
a2: i32,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct DirectionRename {
a1: i32,
#[serde(rename_serialize="a3", rename_deserialize="a4")]
a2: i32,
}
#[test]
fn test_default() {
let deserialized_value: Default = json::from_str(&"{\"a1\":1,\"a2\":2}").unwrap();
@@ -38,3 +45,13 @@ fn test_rename() {
let deserialized_value: Rename = json::from_str(&serialized_value).unwrap();
assert_eq!(value, deserialized_value);
}
#[test]
fn test_direction_rename() {
let value = DirectionRename { a1: 1, a2: 2 };
let serialized_value = json::to_string(&value).unwrap();
assert_eq!(serialized_value, "{\"a1\":1,\"a3\":2}");
let deserialized_value = json::from_str("{\"a1\":1,\"a4\":2}").unwrap();
assert_eq!(value, deserialized_value);
}
+50 -27
View File
@@ -1,4 +1,4 @@
#![feature(custom_derive, plugin, test)]
#![feature(custom_derive, plugin, test, custom_attribute)]
#![plugin(serde_macros)]
extern crate test;
@@ -668,8 +668,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 +681,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,11 +702,11 @@ 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)),
]);
}
@@ -745,8 +745,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 +767,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 +819,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 +870,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 +933,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 +994,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 +1019,26 @@ 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_deserialize="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) });
}