Compare commits

...

20 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
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
13 changed files with 272 additions and 5 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "serde"
version = "0.9.10"
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
{
+68
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;
@@ -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
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "serde_derive"
version = "0.9.10"
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)]"
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "serde_test"
version = "0.9.10"
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,
}
+18
View File
@@ -6,6 +6,7 @@ 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;
@@ -878,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")]
@@ -995,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()),
}
}
+11
View File
@@ -6,6 +6,7 @@ use std::net;
use std::path::{Path, PathBuf};
use std::str;
use std::time::Duration;
use std::ffi::CString;
extern crate serde;
@@ -389,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"),
],
}
}