Compare commits

...

35 Commits

Author SHA1 Message Date
David Tolnay dc9445f873 Release 0.9.11 2017-03-05 16:51:44 -08:00
David Tolnay 17bc40ec1c Merge pull request #789 from nox/display
Introduce Serializer::collect_str (fixes #786)
2017-03-05 16:47:22 -08:00
David Tolnay 2184fef82f Add format_args example to collect_str 2017-03-05 16:43:31 -08:00
David Tolnay 7e1b5c6ce4 Neater collect_str variable name for rustdoc 2017-03-05 16:26:05 -08:00
David Tolnay 36da8a5cee Error message geared toward serializer users
The previous message was targeted toward Serializer implementors, which is not
the group that will be seeing this message most often.
2017-03-05 16:24:48 -08:00
David Tolnay fbe85f399d Force no_std formats to implement collect_str in the future 2017-03-05 16:22:02 -08:00
David Tolnay 880b27b19e Identical signature for std and no_std collect_str 2017-03-05 16:17:03 -08:00
David Tolnay cc06f070d1 Use the existing CString error message 2017-03-05 13:59:18 -08:00
David Tolnay 2f988aa5e6 Merge pull request #801 from jonhoo/ffi-strings
impls for null-terminated FFI string types
2017-03-05 13:51:09 -08:00
Jon Gjengset d294a10e83 Only include ByteBuf when ser/de is on for std 2017-03-03 23:48:00 -05:00
Jon Gjengset defcbef7ab Use a non-stupid path for bytes::ByteBuf 2017-03-03 23:28:35 -05:00
Jon Gjengset d90eecd4a2 Add tests for CStr(ing) ser/de 2017-03-03 18:06:04 -05:00
Jon Gjengset 0d6d077e6a Serialize and deserialize CString through [u8] 2017-03-03 18:05:08 -05:00
Jon Gjengset be09fc9bbb Remove unsafe Deserialize impl for CStr
See also https://github.com/rust-lang/rust/issues/40248
2017-03-03 17:34:01 -05:00
Jon Gjengset fc9d78e26b Use serialize_bytes for speed 2017-03-03 17:14:39 -05:00
Jon Gjengset 9f83164c40 Don't serialize trailing NULL 2017-03-03 17:14:28 -05:00
Jon Gjengset 857974ab8a impls for null-terminated FFI string types
Fixes #800.
2017-03-03 16:09:41 -05:00
David Tolnay d70636f4d4 Merge pull request #794 from clarcharr/master
Documentation for serde_test.
2017-03-02 11:53:05 -08:00
Clar Charr 09e467cc4c Documentation for serde_test. 2017-02-28 18:20:27 -05:00
David Tolnay 51ed9c2a40 Release 0.9.10 2017-02-28 12:45:39 -08:00
Anthony Ramine a9a05350a9 Introduce Serializer::collect_str (fixes #786)
The default implementation collects the Display value into a String
and then passes that to Serializer::serialize_str when the std or collections
features are enabled, otherwise it unconditionally returns an error.
2017-02-28 12:11:47 +01:00
David Tolnay fe9ea3b4b4 Fix needless_pass_by_value lint 2017-02-27 19:43:42 -08:00
David Tolnay f944b453c4 Merge pull request #792 from elliottslaughter/fix_serialize_tuple_docs
Fix documentation of serialize_tuple and SerializeTuple
2017-02-27 14:05:15 -08:00
Elliott Slaughter a993630cf9 Fix documentation of serialize_tuple and SerializeTuple. 2017-02-27 14:00:21 -08:00
David Tolnay abc081ce9c Test rename_all attribute 2017-02-25 11:58:34 -08:00
David Tolnay 207940046b Merge pull request #788 from 46bit/issue-140
`rename_all` container attribute to have all children fit a naming convention
2017-02-25 11:38:33 -08:00
David Tolnay 47efbc6d75 Don't need to pass back the input here 2017-02-25 11:32:27 -08:00
David Tolnay 17279e8a4f Simplify case conversion implementation 2017-02-25 11:32:22 -08:00
David Tolnay 06c631db05 Bring in the tests 2017-02-25 11:02:51 -08:00
David Tolnay 7952bad41f Move case conversion to its own file 2017-02-25 11:02:25 -08:00
Michael Mokrysz 3308f81c3a Saving progress on naming convention conversion code to try new Inflector changes. #788 2017-02-25 13:24:49 +00:00
David Tolnay 75e6da02d3 Split up test suite so it compiles in parallel 2017-02-24 15:39:18 -08:00
Michael Mokrysz 84915268ee Minor fixes for pull request. Cheers @dtolnay. 2017-02-24 01:55:31 +00:00
Michael Mokrysz 3b59d47e07 Heavily refactored rename_all and switched to SCREAMING_SNAKE_CASE. 2017-02-23 23:22:54 +00:00
Michael Mokrysz fc94c5399a Implementing rename_all container attribute using Inflector trait. #140 2017-02-23 18:58:41 +00:00
26 changed files with 624 additions and 53 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "serde"
version = "0.9.9"
version = "0.9.11"
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"]
license = "MIT/Apache-2.0"
description = "A generic serialization/deserialization framework"
+17
View File
@@ -25,6 +25,8 @@ use std::net;
#[cfg(feature = "std")]
use std::path;
use core::str;
#[cfg(feature = "std")]
use std::ffi::CString;
#[cfg(feature = "std")]
use std::rc::Rc;
@@ -53,6 +55,9 @@ use de::{Deserialize, Deserializer, EnumVisitor, Error, MapVisitor, SeqVisitor,
VariantVisitor, Visitor};
use de::from_primitive::FromPrimitive;
#[cfg(feature = "std")]
use bytes::ByteBuf;
///////////////////////////////////////////////////////////////////////////////
/// A visitor that produces a `()`.
@@ -295,6 +300,18 @@ impl Deserialize for String {
///////////////////////////////////////////////////////////////////////////////
#[cfg(feature = "std")]
impl Deserialize for CString {
fn deserialize<D>(deserializer: D) -> Result<CString, D::Error>
where D: Deserializer
{
let bytes = try!(ByteBuf::deserialize(deserializer));
CString::new(bytes).map_err(Error::custom)
}
}
///////////////////////////////////////////////////////////////////////////////
struct OptionVisitor<T> {
marker: PhantomData<T>,
}
+24
View File
@@ -22,6 +22,8 @@ use core::ops;
#[cfg(feature = "std")]
use std::path;
#[cfg(feature = "std")]
use std::ffi::{CString, CStr};
#[cfg(feature = "std")]
use std::rc::Rc;
#[cfg(all(feature = "alloc", not(feature = "std")))]
use alloc::rc::Rc;
@@ -98,6 +100,28 @@ impl Serialize for String {
///////////////////////////////////////////////////////////////////////////////
#[cfg(feature = "std")]
impl Serialize for CStr {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer
{
serializer.serialize_bytes(self.to_bytes())
}
}
#[cfg(feature = "std")]
impl Serialize for CString {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer
{
serializer.serialize_bytes(self.to_bytes())
}
}
///////////////////////////////////////////////////////////////////////////////
impl<T> Serialize for Option<T>
where T: Serialize
{
+75 -7
View File
@@ -98,7 +98,11 @@ use std::error;
#[cfg(not(feature = "std"))]
use error;
#[cfg(all(feature = "collections", not(feature = "std")))]
use collections::string::String;
use core::fmt::Display;
#[cfg(any(feature = "std", feature = "collections"))]
use core::fmt::Write;
use core::iter::IntoIterator;
mod impls;
@@ -466,13 +470,13 @@ pub trait Serializer: Sized {
fn serialize_seq_fixed_size(self, size: usize) -> Result<Self::SerializeSeq, Self::Error>;
/// Begin to serialize a tuple. This call must be followed by zero or more
/// calls to `serialize_field`, then a call to `end`.
/// calls to `serialize_element`, then a call to `end`.
///
/// ```rust,ignore
/// let mut tup = serializer.serialize_tuple(3)?;
/// tup.serialize_field(&self.0)?;
/// tup.serialize_field(&self.1)?;
/// tup.serialize_field(&self.2)?;
/// tup.serialize_element(&self.0)?;
/// tup.serialize_element(&self.1)?;
/// tup.serialize_element(&self.2)?;
/// tup.end()
/// ```
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error>;
@@ -616,6 +620,70 @@ pub trait Serializer: Sized {
}
serializer.end()
}
/// Serialize a string produced by an implementation of `Display`.
///
/// The default implementation builds a heap-allocated `String` and
/// delegates to `serialize_str`. Serializers are encouraged to provide a
/// more efficient implementation if possible.
///
/// ```rust
/// # use serde::{Serialize, Serializer};
/// # struct DateTime;
/// # impl DateTime {
/// # fn naive_local(&self) -> () { () }
/// # fn offset(&self) -> () { () }
/// # }
/// impl Serialize for DateTime {
/// fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
/// where S: Serializer
/// {
/// serializer.collect_str(&format_args!("{:?}{:?}",
/// self.naive_local(),
/// self.offset()))
/// }
/// }
/// ```
#[cfg(any(feature = "std", feature = "collections"))]
fn collect_str<T: ?Sized>(self, value: &T) -> Result<Self::Ok, Self::Error>
where T: Display,
{
let mut string = String::new();
write!(string, "{}", value).unwrap();
self.serialize_str(&string)
}
/// Serialize a string produced by an implementation of `Display`.
///
/// The default implementation returns an error unconditionally when
/// compiled with `no_std`.
///
/// ```rust
/// # use serde::{Serialize, Serializer};
/// # struct DateTime;
/// # impl DateTime {
/// # fn naive_local(&self) -> () { () }
/// # fn offset(&self) -> () { () }
/// # }
/// impl Serialize for DateTime {
/// fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
/// where S: Serializer
/// {
/// serializer.collect_str(&format_args!("{:?}{:?}",
/// self.naive_local(),
/// self.offset()))
/// }
/// }
/// ```
#[cfg(not(any(feature = "std", feature = "collections")))]
fn collect_str<T: ?Sized>(self, value: &T) -> Result<Self::Ok, Self::Error>
where T: Display,
{
// TODO https://github.com/serde-rs/serde/issues/805
// Remove this impl and force no_std formats to implement collect_str.
let _ = value;
Err(Error::custom("this no_std format does not support serializing strings with collect_str"))
}
}
/// Returned from `Serializer::serialize_seq` and
@@ -646,9 +714,9 @@ pub trait SerializeSeq {
///
/// ```rust,ignore
/// let mut tup = serializer.serialize_tuple(3)?;
/// tup.serialize_field(&self.0)?;
/// tup.serialize_field(&self.1)?;
/// tup.serialize_field(&self.2)?;
/// tup.serialize_element(&self.0)?;
/// tup.serialize_element(&self.1)?;
/// tup.serialize_element(&self.2)?;
/// tup.end()
/// ```
pub trait SerializeTuple {
+1 -1
View File
@@ -11,7 +11,7 @@ pub fn serialize_tagged_newtype<S, T>(serializer: S,
variant_ident: &'static str,
tag: &'static str,
variant_name: &'static str,
value: T)
value: &T)
-> Result<S::Ok, S::Error>
where S: Serializer,
T: Serialize
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "serde_codegen_internals"
version = "0.14.0"
version = "0.14.1"
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"]
license = "MIT/Apache-2.0"
description = "AST representation used by Serde codegen. Unstable."
+17 -1
View File
@@ -38,7 +38,7 @@ impl<'a> Item<'a> {
pub fn from_ast(cx: &Ctxt, item: &'a syn::MacroInput) -> Item<'a> {
let attrs = attr::Item::from_ast(cx, item);
let body = match item.body {
let mut body = match item.body {
syn::Body::Enum(ref variants) => Body::Enum(enum_from_ast(cx, variants)),
syn::Body::Struct(ref variant_data) => {
let (style, fields) = struct_from_ast(cx, variant_data);
@@ -46,6 +46,22 @@ impl<'a> Item<'a> {
}
};
match body {
Body::Enum(ref mut variants) => {
for ref mut variant in variants {
variant.attrs.rename_by_rule(attrs.rename_all());
for ref mut field in &mut variant.fields {
field.attrs.rename_by_rule(variant.attrs.rename_all());
}
}
}
Body::Struct(_, ref mut fields) => {
for field in fields {
field.attrs.rename_by_rule(attrs.rename_all());
}
}
}
Item {
ident: item.ident.clone(),
attrs: attrs,
+90 -6
View File
@@ -2,6 +2,7 @@ use Ctxt;
use syn;
use syn::MetaItem::{List, NameValue, Word};
use syn::NestedMetaItem::{Literal, MetaItem};
use std::str::FromStr;
// This module handles parsing of `#[serde(...)]` attributes. The entrypoints
// are `attr::Item::from_ast`, `attr::Variant::from_ast`, and
@@ -11,6 +12,8 @@ use syn::NestedMetaItem::{Literal, MetaItem};
// user will see errors simultaneously for all bad attributes in the crate
// rather than just the first.
pub use case::RenameRule;
struct Attr<'c, T> {
cx: &'c Ctxt,
name: &'static str,
@@ -91,6 +94,7 @@ pub struct Item {
name: Name,
deny_unknown_fields: bool,
default: Default,
rename_all: RenameRule,
ser_bound: Option<Vec<syn::WherePredicate>>,
de_bound: Option<Vec<syn::WherePredicate>>,
tag: EnumTag,
@@ -135,6 +139,7 @@ impl Item {
let mut de_name = Attr::none(cx, "rename");
let mut deny_unknown_fields = BoolAttr::none(cx, "deny_unknown_fields");
let mut default = Attr::none(cx, "default");
let mut rename_all = Attr::none(cx, "rename_all");
let mut ser_bound = Attr::none(cx, "bound");
let mut de_bound = Attr::none(cx, "bound");
let mut untagged = BoolAttr::none(cx, "untagged");
@@ -160,6 +165,20 @@ impl Item {
}
}
// Parse `#[serde(rename_all="foo")]`
MetaItem(NameValue(ref name, ref lit)) if name == "rename_all" => {
if let Ok(s) = get_string_from_lit(cx, name.as_ref(), name.as_ref(), lit) {
match RenameRule::from_str(&s) {
Ok(rename_rule) => rename_all.set(rename_rule),
Err(()) => {
cx.error(format!("unknown rename rule for #[serde(rename_all \
= {:?})]",
s))
}
}
}
}
// Parse `#[serde(deny_unknown_fields)]`
MetaItem(Word(ref name)) if name == "deny_unknown_fields" => {
deny_unknown_fields.set_true();
@@ -244,7 +263,8 @@ impl Item {
content.set(s);
}
syn::Body::Struct(_) => {
cx.error("#[serde(content = \"...\")] can only be used on enums")
cx.error("#[serde(content = \"...\")] can only be used on \
enums")
}
}
}
@@ -297,7 +317,10 @@ impl Item {
EnumTag::External
}
(false, Some(tag), Some(content)) => {
EnumTag::Adjacent { tag: tag, content: content }
EnumTag::Adjacent {
tag: tag,
content: content,
}
}
(true, Some(_), Some(_)) => {
cx.error("untagged enum cannot have #[serde(tag = \"...\", content = \"...\")]");
@@ -312,6 +335,7 @@ impl Item {
},
deny_unknown_fields: deny_unknown_fields.get(),
default: default.get().unwrap_or(Default::None),
rename_all: rename_all.get().unwrap_or(RenameRule::None),
ser_bound: ser_bound.get(),
de_bound: de_bound.get(),
tag: tag,
@@ -322,6 +346,10 @@ impl Item {
&self.name
}
pub fn rename_all(&self) -> &RenameRule {
&self.rename_all
}
pub fn deny_unknown_fields(&self) -> bool {
self.deny_unknown_fields
}
@@ -347,6 +375,9 @@ impl Item {
#[derive(Debug)]
pub struct Variant {
name: Name,
ser_renamed: bool,
de_renamed: bool,
rename_all: RenameRule,
skip_deserializing: bool,
skip_serializing: bool,
}
@@ -357,6 +388,7 @@ impl Variant {
let mut de_name = Attr::none(cx, "rename");
let mut skip_deserializing = BoolAttr::none(cx, "skip_deserializing");
let mut skip_serializing = BoolAttr::none(cx, "skip_serializing");
let mut rename_all = Attr::none(cx, "rename_all");
for meta_items in variant.attrs.iter().filter_map(get_serde_meta_items) {
for meta_item in meta_items {
@@ -376,6 +408,21 @@ impl Variant {
de_name.set_opt(de);
}
}
// Parse `#[serde(rename_all="foo")]`
MetaItem(NameValue(ref name, ref lit)) if name == "rename_all" => {
if let Ok(s) = get_string_from_lit(cx, name.as_ref(), name.as_ref(), lit) {
match RenameRule::from_str(&s) {
Ok(rename_rule) => rename_all.set(rename_rule),
Err(()) => {
cx.error(format!("unknown rename rule for #[serde(rename_all \
= {:?})]",
s))
}
}
}
}
// Parse `#[serde(skip_deserializing)]`
MetaItem(Word(ref name)) if name == "skip_deserializing" => {
skip_deserializing.set_true();
@@ -396,11 +443,18 @@ impl Variant {
}
}
let ser_name = ser_name.get();
let ser_renamed = ser_name.is_some();
let de_name = de_name.get();
let de_renamed = de_name.is_some();
Variant {
name: Name {
serialize: ser_name.get().unwrap_or_else(|| variant.ident.to_string()),
deserialize: de_name.get().unwrap_or_else(|| variant.ident.to_string()),
serialize: ser_name.unwrap_or_else(|| variant.ident.to_string()),
deserialize: de_name.unwrap_or_else(|| variant.ident.to_string()),
},
ser_renamed: ser_renamed,
de_renamed: de_renamed,
rename_all: rename_all.get().unwrap_or(RenameRule::None),
skip_deserializing: skip_deserializing.get(),
skip_serializing: skip_serializing.get(),
}
@@ -410,6 +464,19 @@ impl Variant {
&self.name
}
pub fn rename_by_rule(&mut self, rule: &RenameRule) {
if !self.ser_renamed {
self.name.serialize = rule.apply_to_variant(&self.name.serialize);
}
if !self.de_renamed {
self.name.deserialize = rule.apply_to_variant(&self.name.deserialize);
}
}
pub fn rename_all(&self) -> &RenameRule {
&self.rename_all
}
pub fn skip_deserializing(&self) -> bool {
self.skip_deserializing
}
@@ -423,6 +490,8 @@ impl Variant {
#[derive(Debug)]
pub struct Field {
name: Name,
ser_renamed: bool,
de_renamed: bool,
skip_serializing: bool,
skip_deserializing: bool,
skip_serializing_if: Option<syn::Path>,
@@ -571,11 +640,17 @@ impl Field {
default.set_if_none(Default::Default);
}
let ser_name = ser_name.get();
let ser_renamed = ser_name.is_some();
let de_name = de_name.get();
let de_renamed = de_name.is_some();
Field {
name: Name {
serialize: ser_name.get().unwrap_or_else(|| ident.clone()),
deserialize: de_name.get().unwrap_or(ident),
serialize: ser_name.unwrap_or_else(|| ident.clone()),
deserialize: de_name.unwrap_or(ident),
},
ser_renamed: ser_renamed,
de_renamed: de_renamed,
skip_serializing: skip_serializing.get(),
skip_deserializing: skip_deserializing.get(),
skip_serializing_if: skip_serializing_if.get(),
@@ -591,6 +666,15 @@ impl Field {
&self.name
}
pub fn rename_by_rule(&mut self, rule: &RenameRule) {
if !self.ser_renamed {
self.name.serialize = rule.apply_to_field(&self.name.serialize);
}
if !self.de_renamed {
self.name.deserialize = rule.apply_to_field(&self.name.deserialize);
}
}
pub fn skip_serializing(&self) -> bool {
self.skip_serializing
}
+115
View File
@@ -0,0 +1,115 @@
use std::ascii::AsciiExt;
use std::str::FromStr;
use self::RenameRule::*;
#[derive(Debug, PartialEq)]
pub enum RenameRule {
/// Don't apply a default rename rule.
None,
/// Rename direct children to "PascalCase" style, as typically used for enum variants.
PascalCase,
/// Rename direct children to "camelCase" style.
CamelCase,
/// Rename direct children to "snake_case" style, as commonly used for fields.
SnakeCase,
/// Rename direct children to "SCREAMING_SNAKE_CASE" style, as commonly used for constants.
ScreamingSnakeCase,
/// Rename direct children to "kebab-case" style.
KebabCase,
}
impl RenameRule {
pub fn apply_to_variant(&self, variant: &str) -> String {
match *self {
None | PascalCase => variant.to_owned(),
CamelCase => variant[..1].to_ascii_lowercase() + &variant[1..],
SnakeCase => {
let mut snake = String::new();
for (i, ch) in variant.char_indices() {
if i > 0 && ch.is_uppercase() {
snake.push('_');
}
snake.push(ch.to_ascii_lowercase());
}
snake
}
ScreamingSnakeCase => SnakeCase.apply_to_variant(variant).to_ascii_uppercase(),
KebabCase => SnakeCase.apply_to_variant(variant).replace('_', "-"),
}
}
pub fn apply_to_field(&self, field: &str) -> String {
match *self {
None | SnakeCase => field.to_owned(),
PascalCase => {
let mut pascal = String::new();
let mut capitalize = true;
for ch in field.chars() {
if ch == '_' {
capitalize = true;
} else if capitalize {
pascal.push(ch.to_ascii_uppercase());
capitalize = false;
} else {
pascal.push(ch);
}
}
pascal
}
CamelCase => {
let pascal = PascalCase.apply_to_field(field);
pascal[..1].to_ascii_lowercase() + &pascal[1..]
}
ScreamingSnakeCase => field.to_ascii_uppercase(),
KebabCase => field.replace('_', "-"),
}
}
}
impl FromStr for RenameRule {
type Err = ();
fn from_str(rename_all_str: &str) -> Result<Self, Self::Err> {
match rename_all_str {
"PascalCase" => Ok(PascalCase),
"camelCase" => Ok(CamelCase),
"snake_case" => Ok(SnakeCase),
"SCREAMING_SNAKE_CASE" => Ok(ScreamingSnakeCase),
"kebab-case" => Ok(KebabCase),
_ => Err(()),
}
}
}
#[test]
fn rename_variants() {
for &(original, camel, snake, screaming, kebab) in
&[("Outcome", "outcome", "outcome", "OUTCOME", "outcome"),
("VeryTasty", "veryTasty", "very_tasty", "VERY_TASTY", "very-tasty"),
("A", "a", "a", "A", "a"),
("Z42", "z42", "z42", "Z42", "z42")] {
assert_eq!(None.apply_to_variant(original), original);
assert_eq!(PascalCase.apply_to_variant(original), original);
assert_eq!(CamelCase.apply_to_variant(original), camel);
assert_eq!(SnakeCase.apply_to_variant(original), snake);
assert_eq!(ScreamingSnakeCase.apply_to_variant(original), screaming);
assert_eq!(KebabCase.apply_to_variant(original), kebab);
}
}
#[test]
fn rename_fields() {
for &(original, pascal, camel, screaming, kebab) in
&[("outcome", "Outcome", "outcome", "OUTCOME", "outcome"),
("very_tasty", "VeryTasty", "veryTasty", "VERY_TASTY", "very-tasty"),
("a", "A", "a", "A", "a"),
("z42", "Z42", "z42", "Z42", "z42")] {
assert_eq!(None.apply_to_field(original), original);
assert_eq!(PascalCase.apply_to_field(original), pascal);
assert_eq!(CamelCase.apply_to_field(original), camel);
assert_eq!(SnakeCase.apply_to_field(original), original);
assert_eq!(ScreamingSnakeCase.apply_to_field(original), screaming);
assert_eq!(KebabCase.apply_to_field(original), kebab);
}
}
+2
View File
@@ -5,3 +5,5 @@ pub mod attr;
mod ctxt;
pub use ctxt::Ctxt;
mod case;
+2 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "serde_derive"
version = "0.9.9"
version = "0.9.11"
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"]
license = "MIT/Apache-2.0"
description = "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]"
@@ -23,5 +23,5 @@ proc-macro = true
[dependencies]
quote = "0.3.8"
serde_codegen_internals = { version = "=0.14.0", default-features = false, path = "../serde_codegen_internals" }
serde_codegen_internals = { version = "=0.14.1", default-features = false, path = "../serde_codegen_internals" }
syn = { version = "0.11", features = ["visit"] }
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "serde_test"
version = "0.9.9"
version = "0.9.11"
authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"]
license = "MIT/Apache-2.0"
description = "Token De/Serializer for testing De/Serialize implementations"
+5 -2
View File
@@ -7,6 +7,7 @@ use token::Token;
use std::fmt::Debug;
/// Runs both `assert_ser_tokens` and `assert_de_tokens`.
pub fn assert_tokens<T>(value: &T, tokens: &[Token<'static>])
where T: Serialize + Deserialize + PartialEq + Debug
{
@@ -14,6 +15,7 @@ pub fn assert_tokens<T>(value: &T, tokens: &[Token<'static>])
assert_de_tokens(value, tokens);
}
/// Asserts that `value` serializes to the given `tokens`.
pub fn assert_ser_tokens<T>(value: &T, tokens: &[Token])
where T: Serialize
{
@@ -22,7 +24,7 @@ pub fn assert_ser_tokens<T>(value: &T, tokens: &[Token])
assert_eq!(ser.next_token(), None);
}
/// Expect an error serializing `T`.
/// Asserts that `value` serializes to the given `tokens`, and then yields `error`.
pub fn assert_ser_tokens_error<T>(value: &T, tokens: &[Token], error: Error)
where T: Serialize + PartialEq + Debug
{
@@ -32,6 +34,7 @@ pub fn assert_ser_tokens_error<T>(value: &T, tokens: &[Token], error: Error)
assert_eq!(ser.next_token(), None);
}
/// Asserts that the given `tokens` deserialize into `value`.
pub fn assert_de_tokens<T>(value: &T, tokens: &[Token<'static>])
where T: Deserialize + PartialEq + Debug
{
@@ -41,7 +44,7 @@ pub fn assert_de_tokens<T>(value: &T, tokens: &[Token<'static>])
assert_eq!(de.next_token(), None);
}
/// Expect an error deserializing tokens into a `T`.
/// Asserts that the given `tokens` yield `error` when deserializing.
pub fn assert_de_tokens_error<T>(tokens: &[Token<'static>], error: Error)
where T: Deserialize + PartialEq + Debug
{
+4
View File
@@ -7,6 +7,7 @@ use serde::de::value::{ValueDeserializer, MapVisitorDeserializer, SeqVisitorDese
use error::Error;
use token::Token;
/// A `Deserializer` that reads from a list of tokens.
pub struct Deserializer<I>
where I: Iterator<Item = Token<'static>>
{
@@ -16,14 +17,17 @@ pub struct Deserializer<I>
impl<I> Deserializer<I>
where I: Iterator<Item = Token<'static>>
{
/// Creates the deserializer.
pub fn new(tokens: I) -> Deserializer<I> {
Deserializer { tokens: tokens.peekable() }
}
/// Pulls the next token off of the deserializer, ignoring it.
pub fn next_token(&mut self) -> Option<Token<'static>> {
self.tokens.next()
}
/// Pulls the next token off of the deserializer and checks if it matches an expected token.
pub fn expect_token(&mut self, expected: Token) -> Result<(), Error> {
match self.tokens.next() {
Some(token) => {
+8
View File
@@ -5,11 +5,19 @@ use serde::{ser, de};
use token::Token;
/// Error returned by the test `Serializer` and `Deserializer`.
#[derive(Clone, PartialEq, Debug)]
pub enum Error {
/// A custom error.
Message(String),
/// `Deserialize` was expecting a struct of one name, and another was found.
InvalidName(&'static str),
/// `Serialize` generated a token that didn't match the test.
UnexpectedToken(Token<'static>),
/// The expected token list was too short.
EndOfTokens,
}
+3
View File
@@ -5,6 +5,7 @@ use serde::{ser, Serialize};
use error::Error;
use token::Token;
/// A `Serializer` that ensures that a value serializes to a given list of tokens.
pub struct Serializer<'a, I>
where I: Iterator<Item = &'a Token<'a>>
{
@@ -15,6 +16,7 @@ pub struct Serializer<'a, I>
impl<'a, I> Serializer<'a, I>
where I: Iterator<Item = &'a Token<'a>>
{
/// Creates the serializer.
pub fn new(tokens: I) -> Serializer<'a, I> {
Serializer {
tokens: tokens,
@@ -22,6 +24,7 @@ impl<'a, I> Serializer<'a, I>
}
}
/// Pulls the next token off of the serializer, ignoring it.
pub fn next_token(&mut self) -> Option<&'a Token<'a>> {
self.tokens.next()
}
+111
View File
@@ -1,59 +1,170 @@
#[derive(Clone, PartialEq, Debug)]
pub enum Token<'a> {
/// A serialized `bool`.
Bool(bool),
/// A serialized `i8`.
I8(i8),
/// A serialized `i16`.
I16(i16),
/// A serialized `i32`.
I32(i32),
/// A serialized `i64`.
I64(i64),
/// A serialized `u8`.
U8(u8),
/// A serialized `u16`.
U16(u16),
/// A serialized `u32`.
U32(u32),
/// A serialized `u64`.
U64(u64),
/// A serialized `f32`.
F32(f32),
/// A serialized `f64`.
F64(f64),
/// A serialized `char`.
Char(char),
/// A serialized `str`.
Str(&'a str),
/// A serialized `String`.
String(String),
/// A serialized `[u8]`
Bytes(&'a [u8]),
/// A serialized `ByteBuf`
ByteBuf(Vec<u8>),
/// The header to a serialized `Option<T>`.
///
/// `None` is serialized as `Option(false)`, while `Some` is serialized as `Option(true)`, then
/// the value contained in the option.
Option(bool),
/// A serialized `()`.
Unit,
/// A serialized unit struct of the given name.
UnitStruct(&'a str),
/// The header to a serialized newtype struct of the given name.
///
/// Newtype structs are serialized with this header, followed by the value contained in the
/// newtype struct.
StructNewType(&'a str),
/// The header to an enum of the given name.
///
/// This token is only used for deserializers, and ensures that the following tokens are read as
/// an enum. Because this is never emitted by serializers, calling `assert_ser_tokens` or
/// `assert_tokens` will fail if this token is used.
///
/// TODO: Trash this.
EnumStart(&'a str),
/// A unit variant of an enum of the given name, of the given name.
///
/// The first string represents the name of the enum, and the second represents the name of the
/// variant.
EnumUnit(&'a str, &'a str),
/// The header to a newtype variant of an enum of the given name, of the given name.
///
/// The first string represents the name of the enum, and the second represents the name of the
/// variant. The value contained within this enum works the same as `StructNewType`.
EnumNewType(&'a str, &'a str),
/// The header to a sequence of the given length.
///
/// These are serialized via `serialize_seq`, which takes an optional length. After this
/// header is a list of elements, followed by `SeqEnd`.
SeqStart(Option<usize>),
/// The header to an array of the given length.
///
/// These are serialized via `serialize_seq_fized_size`, which requires a length. After this
/// header is a list of elements, followed by `SeqEnd`.
SeqArrayStart(usize),
/// A separator, which occurs *before* every element in a sequence.
///
/// Elements in sequences are represented by a `SeqSep`, followed by the value of the element.
SeqSep,
/// An indicator of the end of a sequence.
SeqEnd,
/// The header to a tuple of the given length, similar to `SeqArrayStart`.
TupleStart(usize),
/// A separator, similar to `SeqSep`.
TupleSep,
/// An indicator of the end of a tuple, similar to `SeqEnd`.
TupleEnd,
/// The header to a tuple struct of the given name and length.
TupleStructStart(&'a str, usize),
/// A separator, similar to `TupleSep`.
TupleStructSep,
/// An indicator of the end of a tuple struct, similar to `TupleEnd`.
TupleStructEnd,
/// The header to a map of the given length.
///
/// These are serialized via `serialize_map`, which takes an optional length. After this header
/// is a list of key-value pairs, followed by `MapEnd`.
MapStart(Option<usize>),
/// A separator, which occurs *before* every key-value pair in a map.
///
/// Elements in maps are represented by a `MapSep`, followed by a serialized key, followed
/// by a serialized value.
MapSep,
/// An indicator of the end of a map.
MapEnd,
/// The header of a struct of the given name and length, similar to `MapStart`.
StructStart(&'a str, usize),
/// A separator, similar to `MapSep`.
StructSep,
/// An indicator of the end of a struct, similar to `MapEnd`.
StructEnd,
/// The header to a tuple variant of an enum of the given name, of the given name and length.
EnumSeqStart(&'a str, &'a str, usize),
/// A separator, similar to `TupleSep`.
EnumSeqSep,
/// An indicator of the end of a tuple variant, similar to `TupleEnd`.
EnumSeqEnd,
/// The header of a struct variant of an enum of the given name, of the given name and length,
/// similar to `StructStart`.
EnumMapStart(&'a str, &'a str, usize),
/// A separator, similar to `StructSep`.
EnumMapSep,
/// An indicator of the end of a struct, similar to `StructEnd`.
EnumMapEnd,
}
+1 -8
View File
@@ -5,10 +5,7 @@ authors = ["Erick Tryzelaar <erick.tryzelaar@gmail.com>"]
publish = false
[features]
unstable-testing = [
"compiletest_rs",
"serde/unstable-testing",
]
unstable-testing = ["compiletest_rs"]
[dev-dependencies]
fnv = "1.0"
@@ -19,7 +16,3 @@ serde_test = { path = "../serde_test" }
[dependencies]
compiletest_rs = { version = "0.2", optional = true }
[[test]]
name = "test"
path = "tests/test.rs"
+2
View File
@@ -1,3 +1,5 @@
#![cfg(feature = "unstable-testing")]
extern crate compiletest_rs as compiletest;
use std::env;
-23
View File
@@ -1,23 +0,0 @@
#![cfg_attr(feature = "unstable-testing", feature(test, non_ascii_idents))]
#[cfg(feature = "unstable-testing")]
extern crate test;
#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_test;
#[macro_use]
mod macros;
mod test_annotations;
mod test_bytes;
mod test_de;
mod test_gen;
mod test_macros;
mod test_ser;
#[cfg(feature = "unstable-testing")]
mod compiletest;
+3
View File
@@ -1,3 +1,6 @@
#[macro_use]
extern crate serde_derive;
extern crate serde;
use self::serde::{Serialize, Serializer, Deserialize, Deserializer};
+3
View File
@@ -1,4 +1,7 @@
extern crate serde;
use serde::bytes::{ByteBuf, Bytes};
extern crate serde_test;
use serde_test::{assert_tokens, assert_ser_tokens, assert_de_tokens, Token};
#[test]
+25
View File
@@ -1,9 +1,14 @@
#[macro_use]
extern crate serde_derive;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::net;
use std::path::PathBuf;
use std::time::Duration;
use std::default::Default;
use std::ffi::CString;
extern crate serde;
use serde::Deserialize;
extern crate fnv;
@@ -17,6 +22,9 @@ use self::serde_test::{
assert_de_tokens_error,
};
#[macro_use]
mod macros;
//////////////////////////////////////////////////////////////////////////
#[derive(Copy, Clone, PartialEq, Debug, Deserialize)]
@@ -871,6 +879,11 @@ declare_tests! {
Token::String("/usr/local/lib".to_owned()),
],
}
test_cstring {
CString::new("abc").unwrap() => &[
Token::Bytes(b"abc"),
],
}
}
#[cfg(feature = "unstable")]
@@ -988,4 +1001,16 @@ declare_error_tests! {
],
Error::Message("invalid length 1, expected an array of length 3".into()),
}
test_cstring_internal_null<CString> {
&[
Token::Bytes(b"a\0c"),
],
Error::Message("nul byte found in provided data at position: 1".into()),
}
test_cstring_internal_null_end<CString> {
&[
Token::Bytes(b"ac\0"),
],
Error::Message("nul byte found in provided data at position: 2".into()),
}
}
+5
View File
@@ -2,6 +2,11 @@
// successfully when there are a variety of generics and non-(de)serializable
// types involved.
#![cfg_attr(feature = "unstable-testing", feature(non_ascii_idents))]
#[macro_use]
extern crate serde_derive;
extern crate serde;
use self::serde::ser::{Serialize, Serializer};
use self::serde::de::{Deserialize, Deserializer};
+89
View File
@@ -1,3 +1,6 @@
#[macro_use]
extern crate serde_derive;
extern crate serde_test;
use self::serde_test::{
Error,
@@ -1222,3 +1225,89 @@ fn test_enum_in_untagged_enum() {
]
);
}
#[test]
fn test_rename_all() {
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "snake_case")]
enum E {
#[serde(rename_all = "camelCase")]
Serialize {
serialize: bool,
serialize_seq: bool,
},
#[serde(rename_all = "kebab-case")]
SerializeSeq {
serialize: bool,
serialize_seq: bool,
},
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
SerializeMap {
serialize: bool,
serialize_seq: bool,
},
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(rename_all = "PascalCase")]
struct S {
serialize: bool,
serialize_seq: bool,
}
assert_tokens(
&E::Serialize { serialize: true, serialize_seq: true },
&[
Token::EnumMapStart("E", "serialize", 2),
Token::EnumMapSep,
Token::Str("serialize"),
Token::Bool(true),
Token::EnumMapSep,
Token::Str("serializeSeq"),
Token::Bool(true),
Token::EnumMapEnd,
]
);
assert_tokens(
&E::SerializeSeq { serialize: true, serialize_seq: true },
&[
Token::EnumMapStart("E", "serialize_seq", 2),
Token::EnumMapSep,
Token::Str("serialize"),
Token::Bool(true),
Token::EnumMapSep,
Token::Str("serialize-seq"),
Token::Bool(true),
Token::EnumMapEnd,
]
);
assert_tokens(
&E::SerializeMap { serialize: true, serialize_seq: true },
&[
Token::EnumMapStart("E", "serialize_map", 2),
Token::EnumMapSep,
Token::Str("SERIALIZE"),
Token::Bool(true),
Token::EnumMapSep,
Token::Str("SERIALIZE_SEQ"),
Token::Bool(true),
Token::EnumMapEnd,
]
);
assert_tokens(
&S { serialize: true, serialize_seq: true },
&[
Token::StructStart("S", 2),
Token::StructSep,
Token::Str("Serialize"),
Token::Bool(true),
Token::StructSep,
Token::Str("SerializeSeq"),
Token::Bool(true),
Token::StructEnd,
]
);
}
+19
View File
@@ -1,8 +1,14 @@
#[macro_use]
extern crate serde_derive;
use std::collections::{BTreeMap, HashMap, HashSet};
use std::net;
use std::path::{Path, PathBuf};
use std::str;
use std::time::Duration;
use std::ffi::CString;
extern crate serde;
extern crate serde_test;
use self::serde_test::{
@@ -18,6 +24,9 @@ use self::fnv::FnvHasher;
#[cfg(feature = "unstable")]
use serde::ser::iterator;
#[macro_use]
mod macros;
//////////////////////////////////////////////////////////////////////////
#[derive(Serialize)]
@@ -381,6 +390,16 @@ declare_tests! {
Token::Str("/usr/local/lib"),
],
}
test_cstring {
CString::new("abc").unwrap() => &[
Token::Bytes(b"abc"),
],
}
test_cstr {
(&*CString::new("abc").unwrap()) => &[
Token::Bytes(b"abc"),
],
}
}