Repository: pyfisch/cbor
Pull request #49 (2017-10-19 21:12:20)

Implement to_value serialization
I have ported `to_value` serialization from `serde_json` in the way that would keep `Value` structure high-level, using `Object` where it was possible instead of `Bytes` sequences as the default `cbor` serializer does when it outputs the serialized data into a byte stream.

This makes impossible to perform `Object -> Value -> SerializedString -> Object` conversion without intermediate deserialization to `Value` because layouts of initial `Object` and the `Value` are incompatible, but it keeps the `Value` useful for inspection and conversions.

The `Value` with byte streams instead of the most of complex types is not very useful. 


Patch:
diff --git a/src/error.rs b/src/error.rs
index 70aba64..ebd2920 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -13,6 +13,18 @@ pub struct Error(Box<ErrorImpl>);
 /// Alias for a `Result` with the error type `serde_cbor::Error`.
 pub type Result<T> = result::Result<T, Error>;
 
+/// Categorizes the cause of a `serde_cbor::Error`.
+pub enum Category {
+    /// The error was caused by a failure to read or write bytes on an IO stream.
+    Io,
+    /// The error was caused by input that was not syntactically valid CBOR.
+    Syntax,
+    /// The error was caused by input data that was semantically correct.
+    Data,
+    /// The error was causeed by prematurely reaching the end of the input data.
+    Eof,
+}
+
 impl Error {
     /// The byte offset at which the error occurred.
     pub fn offset(&self) -> u64 {
@@ -29,6 +41,58 @@ impl Error {
             offset: 0,
         }))
     }
+
+    /// Categorizes the cause of this error.
+    pub fn classify(&self) -> Category {
+        match self.0.code {
+            ErrorCode::Message(_) => Category::Data,
+            ErrorCode::Io(_) => Category::Io,
+            ErrorCode::EofWhileParsingValue |
+            ErrorCode::EofWhileParsingArray |
+            ErrorCode::EofWhileParsingMap => Category::Eof,
+            ErrorCode::NumberOutOfRange |
+            ErrorCode::LengthOutOfRange |
+            ErrorCode::InvalidUtf8 |
+            ErrorCode::UnassignedCode |
+            ErrorCode::UnexpectedCode |
+            ErrorCode::TrailingData |
+            ErrorCode::ArrayTooShort |
+            ErrorCode::ArrayTooLong |
+            ErrorCode::RecursionLimitExceeded => Category::Syntax,
+        }
+    }
+
+    /// Returns true if this error was caused by a failure to read or write bytes on an IO stream.
+    pub fn is_io(&self) -> bool {
+        match self.classify() {
+            Category::Io => true,
+            _ => false,
+        }
+    }
+
+    /// Returns true if this error was caused by input that was not syntactically valid CBOR.
+    pub fn is_syntax(&self) -> bool {
+        match self.classify() {
+            Category::Syntax => true,
+            _ => false,
+        }
+    }
+
+    /// Returns true if this error was caused by data that was semantically incorrect.
+    pub fn is_data(&self) -> bool {
+        match self.classify() {
+            Category::Data => true,
+            _ => false,
+        }
+    }
+
+    /// Returns true if this error was caused by prematurely reaching the end of the input data.
+    pub fn is_eof(&self) -> bool {
+        match self.classify() {
+            Category::Eof => true,
+            _ => false,
+        }
+    }
 }
 
 impl error::Error for Error {
diff --git a/src/lib.rs b/src/lib.rs
index 49e0340..71e5f5d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -144,4 +144,4 @@ pub use de::{from_slice, from_reader, Deserializer};
 #[doc(inline)]
 pub use ser::{to_writer, to_vec, Serializer};
 #[doc(inline)]
-pub use value::{Value, ObjectKey};
+pub use value::{Value, ObjectKey, to_value};
diff --git a/src/ser.rs b/src/ser.rs
index fe0d88f..8599893 100644
--- a/src/ser.rs
+++ b/src/ser.rs
@@ -438,10 +438,7 @@ where
     #[inline]
     fn serialize_struct(self, _name: &'static str, len: usize) -> Result<StructSerializer<'a, W>> {
         self.write_u64(5, len as u64)?;
-        Ok(StructSerializer {
-            ser: self,
-            idx: 0,
-        })
+        Ok(StructSerializer { ser: self, idx: 0 })
     }
 
     #[inline]
@@ -524,7 +521,7 @@ where
 #[doc(hidden)]
 pub struct StructSerializer<'a, W: 'a> {
     ser: &'a mut Serializer<W>,
-    idx: u32
+    idx: u32,
 }
 
 impl<'a, W> StructSerializer<'a, W>
diff --git a/src/value/mod.rs b/src/value/mod.rs
new file mode 100644
index 0000000..7da104e
--- /dev/null
+++ b/src/value/mod.rs
@@ -0,0 +1,7 @@
+//! CBOR values, keys and serialization routines.
+
+pub mod value;
+pub mod ser;
+
+pub use self::value::{ObjectKey, Value};
+pub use self::ser::to_value;
diff --git a/src/value/ser.rs b/src/value/ser.rs
new file mode 100644
index 0000000..6e29d06
--- /dev/null
+++ b/src/value/ser.rs
@@ -0,0 +1,418 @@
+//! Value serialization routines
+
+// Copyright 2017 Serde Developers
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::collections::BTreeMap;
+
+use serde::{self, Serialize};
+use error::Error;
+
+use value::Value;
+use value::ObjectKey;
+
+struct Serializer;
+
+impl serde::Serializer for Serializer {
+    type Ok = Value;
+    type Error = Error;
+
+    type SerializeSeq = SerializeVec;
+    type SerializeTuple = SerializeVec;
+    type SerializeTupleStruct = SerializeVec;
+    type SerializeTupleVariant = SerializeTupleVariant;
+    type SerializeMap = SerializeMap;
+    type SerializeStruct = SerializeMap;
+    type SerializeStructVariant = SerializeStructVariant;
+
+    #[inline]
+    fn serialize_bool(self, value: bool) -> Result<Value, Error> {
+        Ok(Value::Bool(value))
+    }
+
+    #[inline]
+    fn serialize_i8(self, value: i8) -> Result<Value, Error> {
+        self.serialize_i64(value as i64)
+    }
+
+    #[inline]
+    fn serialize_i16(self, value: i16) -> Result<Value, Error> {
+        self.serialize_i64(value as i64)
+    }
+
+    #[inline]
+    fn serialize_i32(self, value: i32) -> Result<Value, Error> {
+        self.serialize_i64(value as i64)
+    }
+
+    fn serialize_i64(self, value: i64) -> Result<Value, Error> {
+        Ok(Value::I64(value))
+    }
+
+    #[inline]
+    fn serialize_u8(self, value: u8) -> Result<Value, Error> {
+        self.serialize_u64(value as u64)
+    }
+
+    #[inline]
+    fn serialize_u16(self, value: u16) -> Result<Value, Error> {
+        self.serialize_u64(value as u64)
+    }
+
+    #[inline]
+    fn serialize_u32(self, value: u32) -> Result<Value, Error> {
+        self.serialize_u64(value as u64)
+    }
+
+    #[inline]
+    fn serialize_u64(self, value: u64) -> Result<Value, Error> {
+        Ok(Value::U64(value))
+    }
+
+    #[inline]
+    fn serialize_f32(self, value: f32) -> Result<Value, Error> {
+        self.serialize_f64(value as f64)
+    }
+
+    #[inline]
+    fn serialize_f64(self, value: f64) -> Result<Value, Error> {
+        Ok(Value::F64(value))
+    }
+
+    #[inline]
+    fn serialize_char(self, value: char) -> Result<Value, Error> {
+        let mut s = String::new();
+        s.push(value);
+        self.serialize_str(&s)
+    }
+
+    #[inline]
+    fn serialize_str(self, value: &str) -> Result<Value, Error> {
+        Ok(Value::String(value.to_owned()))
+    }
+
+    fn serialize_bytes(self, value: &[u8]) -> Result<Value, Error> {
+        Ok(Value::Bytes(value.iter().map(|&x| x).collect()))
+    }
+
+    #[inline]
+    fn serialize_unit(self) -> Result<Value, Error> {
+        Ok(Value::Null)
+    }
+
+    #[inline]
+    fn serialize_unit_struct(self, _name: &'static str) -> Result<Value, Error> {
+        self.serialize_unit()
+    }
+
+    #[inline]
+    fn serialize_unit_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+    ) -> Result<Value, Error> {
+        self.serialize_str(variant)
+    }
+
+    #[inline]
+    fn serialize_newtype_struct<T: ?Sized>(
+        self,
+        _name: &'static str,
+        value: &T,
+    ) -> Result<Value, Error>
+    where
+        T: Serialize,
+    {
+        value.serialize(self)
+    }
+
+    fn serialize_newtype_variant<T: ?Sized>(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        value: &T,
+    ) -> Result<Value, Error>
+    where
+        T: Serialize,
+    {
+        let mut values = BTreeMap::new();
+        values.insert(ObjectKey::from(variant.to_owned()), try!(to_value(&value)));
+        Ok(Value::Object(values))
+    }
+
+    #[inline]
+    fn serialize_none(self) -> Result<Value, Error> {
+        self.serialize_unit()
+    }
+
+    #[inline]
+    fn serialize_some<T: ?Sized>(self, value: &T) -> Result<Value, Error>
+    where
+        T: Serialize,
+    {
+        value.serialize(self)
+    }
+
+    fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Error> {
+        Ok(SerializeVec { vec: Vec::with_capacity(len.unwrap_or(0)) })
+    }
+
+    fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Error> {
+        self.serialize_seq(Some(len))
+    }
+
+    fn serialize_tuple_struct(
+        self,
+        _name: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeTupleStruct, Error> {
+        self.serialize_tuple(len)
+    }
+
+    fn serialize_tuple_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeTupleVariant, Error> {
+        Ok(SerializeTupleVariant {
+            name: String::from(variant),
+            vec: Vec::with_capacity(len),
+        })
+    }
+
+    fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Error> {
+        Ok(SerializeMap {
+            map: BTreeMap::new(),
+            next_key: None,
+        })
+    }
+
+    fn serialize_struct(
+        self,
+        _name: &'static str,
+        len: usize,
+    ) -> Result<Self::SerializeStruct, Error> {
+        self.serialize_map(Some(len))
+    }
+
+    fn serialize_struct_variant(
+        self,
+        _name: &'static str,
+        _variant_index: u32,
+        variant: &'static str,
+        _len: usize,
+    ) -> Result<Self::SerializeStructVariant, Error> {
+        Ok(SerializeStructVariant {
+            name: String::from(variant),
+            map: BTreeMap::new(),
+        })
+    }
+}
+
+#[doc(hidden)]
+pub struct SerializeVec {
+    vec: Vec<Value>,
+}
+
+#[doc(hidden)]
+pub struct SerializeTupleVariant {
+    name: String,
+    vec: Vec<Value>,
+}
+
+#[doc(hidden)]
+pub struct SerializeMap {
+    map: BTreeMap<ObjectKey, Value>,
+    next_key: Option<ObjectKey>,
+}
+
+#[doc(hidden)]
+pub struct SerializeStructVariant {
+    name: String,
+    map: BTreeMap<ObjectKey, Value>,
+}
+
+impl serde::ser::SerializeSeq for SerializeVec {
+    type Ok = Value;
+    type Error = Error;
+
+    fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), Error>
+    where
+        T: Serialize,
+    {
+        self.vec.push(try!(to_value(&value)));
+        Ok(())
+    }
+
+    fn end(self) -> Result<Value, Error> {
+        Ok(Value::Array(self.vec))
+    }
+}
+
+impl serde::ser::SerializeTuple for SerializeVec {
+    type Ok = Value;
+    type Error = Error;
+
+    fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), Error>
+    where
+        T: Serialize,
+    {
+        serde::ser::SerializeSeq::serialize_element(self, value)
+    }
+
+    fn end(self) -> Result<Value, Error> {
+        serde::ser::SerializeSeq::end(self)
+    }
+}
+
+impl serde::ser::SerializeTupleStruct for SerializeVec {
+    type Ok = Value;
+    type Error = Error;
+
+    fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), Error>
+    where
+        T: Serialize,
+    {
+        serde::ser::SerializeSeq::serialize_element(self, value)
+    }
+
+    fn end(self) -> Result<Value, Error> {
+        serde::ser::SerializeSeq::end(self)
+    }
+}
+
+impl serde::ser::SerializeTupleVariant for SerializeTupleVariant {
+    type Ok = Value;
+    type Error = Error;
+
+    fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), Error>
+    where
+        T: Serialize,
+    {
+        self.vec.push(try!(to_value(&value)));
+        Ok(())
+    }
+
+    fn end(self) -> Result<Value, Error> {
+        let mut object = BTreeMap::new();
+
+        object.insert(ObjectKey::from(self.name), Value::Array(self.vec));
+
+        Ok(Value::Object(object))
+    }
+}
+
+impl serde::ser::SerializeMap for SerializeMap {
+    type Ok = Value;
+    type Error = Error;
+
+    fn serialize_key<T: ?Sized>(&mut self, key: &T) -> Result<(), Error>
+    where
+        T: Serialize,
+    {
+        self.next_key = Some(ObjectKey::from(try!(to_value(&key))));
+        Ok(())
+    }
+
+    fn serialize_value<T: ?Sized>(&mut self, value: &T) -> Result<(), Error>
+    where
+        T: Serialize,
+    {
+        let key = self.next_key.take();
+        // Panic because this indicates a bug in the program rather than an
+        // expected failure.
+        let key = key.expect("serialize_value called before serialize_key");
+        self.map.insert(key, try!(to_value(&value)));
+        Ok(())
+    }
+
+    fn end(self) -> Result<Value, Error> {
+        Ok(Value::Object(self.map))
+    }
+}
+
+impl serde::ser::SerializeStruct for SerializeMap {
+    type Ok = Value;
+    type Error = Error;
+
+    fn serialize_field<T: ?Sized>(&mut self, key: &'static str, value: &T) -> Result<(), Error>
+    where
+        T: Serialize,
+    {
+        try!(serde::ser::SerializeMap::serialize_key(self, key));
+        serde::ser::SerializeMap::serialize_value(self, value)
+    }
+
+    fn end(self) -> Result<Value, Error> {
+        serde::ser::SerializeMap::end(self)
+    }
+}
+
+impl serde::ser::SerializeStructVariant for SerializeStructVariant {
+    type Ok = Value;
+    type Error = Error;
+
+    fn serialize_field<T: ?Sized>(&mut self, key: &'static str, value: &T) -> Result<(), Error>
+    where
+        T: Serialize,
+    {
+        self.map.insert(
+            ObjectKey::from(String::from(key)),
+            try!(to_value(&value)),
+        );
+        Ok(())
+    }
+
+    fn end(self) -> Result<Value, Error> {
+        let mut object = BTreeMap::new();
+
+        object.insert(ObjectKey::from(self.name), Value::Object(self.map));
+
+        Ok(Value::Object(object))
+    }
+}
+
+/// Convert a `T` into `serde_cbor::Value` which is an enum that can represent
+/// any valid CBOR data.
+///
+/// ```rust
+/// extern crate serde;
+///
+/// #[macro_use]
+/// extern crate serde_derive;
+/// extern crate serde_cbor;
+///
+/// use std::error::Error;
+///
+/// #[derive(Serialize)]
+/// struct User {
+///     fingerprint: String,
+///     location: String,
+/// }
+///
+/// fn main() {
+///     let u = User {
+///         fingerprint: "0xF9BA143B95FF6D82".to_owned(),
+///         location: "Menlo Park, CA".to_owned(),
+///     };
+///
+///     let v = serde_cbor::to_value(u).unwrap();
+/// }
+/// ```
+#[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
+// Taking by value is more friendly to iterator adapters, option and result
+pub fn to_value<T>(value: T) -> Result<Value, Error>
+where
+    T: Serialize,
+{
+    value.serialize(Serializer)
+}


Test patch:
diff --git a/src/value.rs b/src/value/value.rs
similarity index 100%
rename from src/value.rs
rename to src/value/value.rs
diff --git a/tests/value.rs b/tests/value.rs
new file mode 100644
index 0000000..9f66993
--- /dev/null
+++ b/tests/value.rs
@@ -0,0 +1,75 @@
+#[macro_use]
+extern crate serde_derive;
+extern crate serde_cbor;
+
+use std::collections::BTreeMap;
+
+#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
+struct TupleStruct(String, i32, u64);
+
+#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
+struct UnitStruct;
+
+#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
+struct Struct<'a> {
+    tuple_struct: TupleStruct,
+    tuple: (String, f32, f64),
+    map: BTreeMap<String, String>,
+    bytes: &'a [u8],
+    array: Vec<String>,
+    unit_array: Vec<UnitStruct>,
+}
+
+use std::iter::FromIterator;
+use serde_cbor::Value;
+
+#[test]
+fn serde() {
+    let tuple_struct = TupleStruct(format!("test"), -60, 3000);
+
+    let tuple = (format!("hello"), -50.0040957, -12.094635556478);
+
+    let map = BTreeMap::from_iter(
+        [
+            (format!("key1"), format!("value1")),
+            (format!("key2"), format!("value2")),
+            (format!("key3"), format!("value3")),
+            (format!("key4"), format!("value4")),
+        ].into_iter()
+            .cloned(),
+    );
+
+    let bytes = b"test byte string";
+
+    let array = vec![format!("one"), format!("two"), format!("three")];
+    let unit_array = vec![UnitStruct, UnitStruct, UnitStruct];
+
+    let data = Struct {
+        tuple_struct,
+        tuple,
+        map,
+        bytes,
+        array,
+        unit_array,
+    };
+
+    let value = serde_cbor::to_value(data.clone()).unwrap();
+    println!("{:?}", value);
+
+    let data_ser = serde_cbor::to_vec(&value).unwrap();
+    let data_de_value: Value = serde_cbor::from_slice(&data_ser).unwrap();
+
+    for ((k1, v1), (k2, v2)) in
+        value.as_object().unwrap().iter().zip(
+            data_de_value
+                .as_object()
+                .unwrap()
+                .iter(),
+        )
+    {
+        assert_eq!(k1, k2);
+        assert_eq!(v1, v2);
+    }
+
+    assert_eq!(value, data_de_value);
+}