From 41563ee01b4eb3481207a88bbb2e3a54d6e96b2b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 2 Apr 2015 17:07:26 -0700 Subject: wip --- Cargo.toml | 2 + src/decoder/mod.rs | 1 + src/decoder/serde.rs | 152 ++++++++++++++++ src/encoder/mod.rs | 1 + src/encoder/serde.rs | 84 +++++++++ src/lib.rs | 1 + tests/serde.rs | 476 +++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 717 insertions(+) create mode 100644 src/decoder/serde.rs create mode 100644 src/encoder/serde.rs create mode 100644 tests/serde.rs diff --git a/Cargo.toml b/Cargo.toml index 102f63d..da4f3c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,9 +17,11 @@ facilitate deserializing and serializing Rust structures. [dependencies] rustc-serialize = { optional = true, version = "0.3.0" } +serde = { optional = true, git = "https://github.com/alexcrichton/rust-serde", branch = "tweak-some-impls" } [features] default = ["rustc-serialize"] [dev-dependencies] rustc-serialize = "0.3" +serde_macros = { git = "https://github.com/alexcrichton/rust-serde", branch = "tweak-some-impls" } diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs index 675e0ca..a3cc29a 100644 --- a/src/decoder/mod.rs +++ b/src/decoder/mod.rs @@ -5,6 +5,7 @@ use Value; use self::DecodeErrorKind::*; #[cfg(feature = "rustc-serialize")] mod rustc_serialize; +#[cfg(feature = "serde")] mod serde; /// A structure to transform TOML values into Rust values. /// diff --git a/src/decoder/serde.rs b/src/decoder/serde.rs new file mode 100644 index 0000000..6f60892 --- /dev/null +++ b/src/decoder/serde.rs @@ -0,0 +1,152 @@ +use serde::de; +use Value; +use super::{Decoder, DecodeError, DecodeErrorKind}; + +struct DecodeValue(Value); +struct MapVisitor(I, Option); +struct SubDecoder(Decoder); + +fn se2toml(err: de::value::Error, ty: &'static str) -> DecodeError { + match err { + de::value::Error::SyntaxError => de::Error::syntax_error(), + de::value::Error::EndOfStreamError => de::Error::end_of_stream_error(), + de::value::Error::MissingFieldError(s) => { + DecodeError { + field: Some(s.to_string()), + kind: DecodeErrorKind::ExpectedField(Some(ty)), + } + } + } +} + +impl de::Deserializer for Decoder { + type Error = DecodeError; + + fn visit(&mut self, mut visitor: V) -> Result + where V: de::Visitor + { + match self.toml.take() { + Some(Value::String(s)) => { + visitor.visit_string(s).map_err(|e| se2toml(e, "string")) + } + Some(Value::Integer(i)) => { + visitor.visit_i64(i).map_err(|e| se2toml(e, "integer")) + } + Some(Value::Float(f)) => { + visitor.visit_f64(f).map_err(|e| se2toml(e, "float")) + } + Some(Value::Boolean(b)) => { + visitor.visit_bool(b).map_err(|e| se2toml(e, "bool")) + } + Some(Value::Datetime(s)) => { + visitor.visit_string(s).map_err(|e| se2toml(e, "date")) + } + Some(Value::Array(a)) => { + let len = a.len(); + let iter = a.into_iter().map(DecodeValue); + let e = visitor.visit_seq(de::value::SeqDeserializer::new(iter, + len)); + e.map_err(|e| se2toml(e, "array")) + } + Some(Value::Table(t)) => { + visitor.visit_map(MapVisitor(t.into_iter(), None)) + } + None => Err(de::Error::end_of_stream_error()), + } + } + + fn visit_option(&mut self, mut visitor: V) -> Result + where V: de::Visitor + { + if self.toml.is_none() { + visitor.visit_none() + } else { + visitor.visit_some(self) + } + } + + fn visit_seq(&mut self, mut visitor: V) -> Result + where V: de::Visitor, + { + if self.toml.is_none() { + let iter = None::.into_iter(); + let e = visitor.visit_seq(de::value::SeqDeserializer::new(iter, 0)); + e.map_err(|e| se2toml(e, "array")) + } else { + self.visit(visitor) + } + } +} + +impl de::Error for DecodeError { + fn syntax_error() -> DecodeError { + DecodeError { field: None, kind: DecodeErrorKind::SyntaxError } + } + fn end_of_stream_error() -> DecodeError { + DecodeError { field: None, kind: DecodeErrorKind::EndOfStream } + } + fn missing_field_error(name: &'static str) -> DecodeError { + DecodeError { + field: Some(name.to_string()), + kind: DecodeErrorKind::ExpectedField(None), + } + } +} + +impl de::Deserializer for SubDecoder { + type Error = de::value::Error; + + fn visit(&mut self, visitor: V) -> Result + where V: de::Visitor + { + self.0.visit(visitor).map_err(|e| { + match e.kind { + DecodeErrorKind::SyntaxError => de::value::Error::SyntaxError, + DecodeErrorKind::EndOfStream => de::value::Error::EndOfStreamError, + _ => de::value::Error::SyntaxError, + } + }) + } +} + +impl de::value::ValueDeserializer for DecodeValue { + type Deserializer = SubDecoder; + + fn into_deserializer(self) -> SubDecoder { + SubDecoder(Decoder::new(self.0)) + } +} + +impl de::MapVisitor for MapVisitor + where I: Iterator +{ + type Error = DecodeError; + + fn visit_key(&mut self) -> Result, DecodeError> + where K: de::Deserialize + { + match self.0.next() { + Some((k, v)) => { + self.1 = Some(v); + de::Deserialize::deserialize(&mut Decoder::new(Value::String(k))) + .map(|v| Some(v)) + } + None => Ok(None), + } + + } + + fn visit_value(&mut self) -> Result + where V: de::Deserialize + { + match self.1.take() { + Some(t) => de::Deserialize::deserialize(&mut Decoder::new(t)), + None => Err(de::Error::end_of_stream_error()) + } + } + + fn end(&mut self) -> Result<(), DecodeError> { + Ok(()) + } + +} diff --git a/src/encoder/mod.rs b/src/encoder/mod.rs index 21185f4..8137c33 100644 --- a/src/encoder/mod.rs +++ b/src/encoder/mod.rs @@ -6,6 +6,7 @@ use std::mem; use {Value, Table}; #[cfg(feature = "rustc-serialize")] mod rustc_serialize; +#[cfg(feature = "serde")] mod serde; /// A structure to transform Rust values into TOML values. /// diff --git a/src/encoder/serde.rs b/src/encoder/serde.rs new file mode 100644 index 0000000..87a742e --- /dev/null +++ b/src/encoder/serde.rs @@ -0,0 +1,84 @@ +use serde::ser; +use Value; +use super::{Encoder, Error}; + +impl ser::Serializer for Encoder { + type Error = Error; + + fn visit_bool(&mut self, v: bool) -> Result<(), Error> { + self.emit_value(Value::Boolean(v)) + } + fn visit_i64(&mut self, v: i64) -> Result<(), Error> { + self.emit_value(Value::Integer(v)) + } + fn visit_u64(&mut self, v: u64) -> Result<(), Error> { + self.visit_i64(v as i64) + } + fn visit_f64(&mut self, v: f64) -> Result<(), Error> { + self.emit_value(Value::Float(v)) + } + fn visit_str(&mut self, value: &str) -> Result<(), Error> { + self.emit_value(Value::String(value.to_string())) + } + fn visit_unit(&mut self) -> Result<(), Error> { + Ok(()) + } + fn visit_none(&mut self) -> Result<(), Error> { + self.emit_none() + } + fn visit_some(&mut self, value: V) -> Result<(), Error> + where V: ser::Serialize + { + value.serialize(self) + } + fn visit_seq(&mut self, mut visitor: V) -> Result<(), Error> + where V: ser::SeqVisitor + { + self.seq(|me| { + while try!(visitor.visit(me)).is_some() {} + Ok(()) + }) + } + fn visit_seq_elt(&mut self, value: T) -> Result<(), Error> + where T: ser::Serialize + { + value.serialize(self) + } + fn visit_map(&mut self, mut visitor: V) -> Result<(), Error> + where V: ser::MapVisitor + { + self.table(|me| { + while try!(visitor.visit(me)).is_some() {} + Ok(()) + }) + } + fn visit_map_elt(&mut self, key: K, value: V) -> Result<(), Error> + where K: ser::Serialize, V: ser::Serialize + { + try!(self.table_key(|me| key.serialize(me))); + try!(value.serialize(self)); + Ok(()) + } +} + +impl ser::Serialize for Value { + fn serialize(&self, e: &mut E) -> Result<(), E::Error> + where E: ser::Serializer + { + match *self { + Value::String(ref s) => e.visit_str(s), + Value::Integer(i) => e.visit_i64(i), + Value::Float(f) => e.visit_f64(f), + Value::Boolean(b) => e.visit_bool(b), + Value::Datetime(ref s) => e.visit_str(s), + Value::Array(ref a) => { + e.visit_seq(ser::impls::SeqIteratorVisitor::new(a.iter(), + Some(a.len()))) + } + Value::Table(ref t) => { + e.visit_map(ser::impls::MapIteratorVisitor::new(t.iter(), + Some(t.len()))) + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 0196fbc..81083e6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,6 +40,7 @@ #![cfg_attr(test, deny(warnings))] #[cfg(feature = "rustc-serialize")] extern crate rustc_serialize; +#[cfg(feature = "serde")] extern crate serde; use std::collections::BTreeMap; use std::str::FromStr; diff --git a/tests/serde.rs b/tests/serde.rs new file mode 100644 index 0000000..7e3c325 --- /dev/null +++ b/tests/serde.rs @@ -0,0 +1,476 @@ +#![cfg(feature = "serde")] +#![feature(custom_derive, plugin)] +#![plugin(serde_macros)] + + +extern crate serde; +extern crate toml; + +use std::collections::{BTreeMap, HashSet}; +use serde::{Deserialize, Serialize, Deserializer}; + +use toml::{Encoder, Decoder, DecodeError}; +use toml::Value; +use toml::Value::{Table, Integer, Array, Float}; + +macro_rules! t { + ($e:expr) => (match $e { + Ok(t) => t, + Err(e) => panic!("{} failed with {}", stringify!($e), e), + }) +} + +macro_rules! encode( ($t:expr) => ({ + let mut e = Encoder::new(); + t!($t.serialize(&mut e)); + e.toml +}) ); + +macro_rules! decode( ($t:expr) => ({ + let mut d = Decoder::new($t); + t!(Deserialize::deserialize(&mut d)) +}) ); + +macro_rules! map( ($($k:ident, $v:expr),*) => ({ + let mut _m = BTreeMap::new(); + $(_m.insert(stringify!($k).to_string(), $v);)* + _m +}) ); + +#[test] +fn smoke() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Foo { a: isize } + + let v = Foo { a: 2 }; + assert_eq!(encode!(v), map! { a, Integer(2) }); + assert_eq!(v, decode!(Table(encode!(v)))); +} + +#[test] +fn smoke_hyphen() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Foo { a_b: isize } + + let v = Foo { a_b: 2 }; + assert_eq!(encode!(v), map! { a_b, Integer(2) }); + assert_eq!(v, decode!(Table(encode!(v)))); + + let mut m = BTreeMap::new(); + m.insert("a-b".to_string(), Integer(2)); + assert_eq!(v, decode!(Table(encode!(v)))); +} + +#[test] +fn nested() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Foo { a: isize, b: Bar } + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Bar { a: String } + + let v = Foo { a: 2, b: Bar { a: "test".to_string() } }; + assert_eq!(encode!(v), + map! { + a, Integer(2), + b, Table(map! { + a, Value::String("test".to_string()) + }) + }); + assert_eq!(v, decode!(Table(encode!(v)))); +} + +#[test] +fn application_decode_error() { + #[derive(PartialEq, Debug)] + struct Range10(usize); + impl Deserialize for Range10 { + fn deserialize(d: &mut D) -> Result { + let x: usize = try!(Deserialize::deserialize(d)); + if x > 10 { + Err(serde::de::Error::syntax_error()) + } else { + Ok(Range10(x)) + } + } + } + let mut d_good = Decoder::new(Integer(5)); + let mut d_bad1 = Decoder::new(Value::String("not an isize".to_string())); + let mut d_bad2 = Decoder::new(Integer(11)); + + assert_eq!(Ok(Range10(5)), Deserialize::deserialize(&mut d_good)); + + let err1: Result = Deserialize::deserialize(&mut d_bad1); + assert!(err1.is_err()); + let err2: Result = Deserialize::deserialize(&mut d_bad2); + assert!(err2.is_err()); +} + +#[test] +fn array() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Foo { a: Vec } + + let v = Foo { a: vec![1, 2, 3, 4] }; + assert_eq!(encode!(v), + map! { + a, Array(vec![ + Integer(1), + Integer(2), + Integer(3), + Integer(4) + ]) + }); + assert_eq!(v, decode!(Table(encode!(v)))); +} + +#[test] +fn tuple() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Foo { a: (isize, isize, isize, isize) } + + let v = Foo { a: (1, 2, 3, 4) }; + assert_eq!(encode!(v), + map! { + a, Array(vec![ + Integer(1), + Integer(2), + Integer(3), + Integer(4) + ]) + }); + assert_eq!(v, decode!(Table(encode!(v)))); +} + +#[test] +fn inner_structs_with_options() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Foo { + a: Option>, + b: Bar, + } + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Bar { + a: String, + b: f64, + } + + let v = Foo { + a: Some(Box::new(Foo { + a: None, + b: Bar { a: "foo".to_string(), b: 4.5 }, + })), + b: Bar { a: "bar".to_string(), b: 1.0 }, + }; + assert_eq!(encode!(v), + map! { + a, Table(map! { + b, Table(map! { + a, Value::String("foo".to_string()), + b, Float(4.5) + }) + }), + b, Table(map! { + a, Value::String("bar".to_string()), + b, Float(1.0) + }) + }); + assert_eq!(v, decode!(Table(encode!(v)))); +} + +#[test] +fn hashmap() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Foo { + map: BTreeMap, + set: HashSet, + } + + let v = Foo { + map: { + let mut m = BTreeMap::new(); + m.insert("foo".to_string(), 10); + m.insert("bar".to_string(), 4); + m + }, + set: { + let mut s = HashSet::new(); + s.insert('a'); + s + }, + }; + assert_eq!(encode!(v), + map! { + map, Table(map! { + foo, Integer(10), + bar, Integer(4) + }), + set, Array(vec![Value::String("a".to_string())]) + } + ); + assert_eq!(v, decode!(Table(encode!(v)))); +} + +#[test] +fn tuple_struct() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Foo(isize, String, f64); + + let v = Foo(1, "foo".to_string(), 4.5); + assert_eq!( + encode!(v), + map! { + _field0, Integer(1), + _field1, Value::String("foo".to_string()), + _field2, Float(4.5) + } + ); + assert_eq!(v, decode!(Table(encode!(v)))); +} + +#[test] +fn table_array() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Foo { a: Vec, } + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Bar { a: isize } + + let v = Foo { a: vec![Bar { a: 1 }, Bar { a: 2 }] }; + assert_eq!( + encode!(v), + map! { + a, Array(vec![ + Table(map!{ a, Integer(1) }), + Table(map!{ a, Integer(2) }), + ]) + } + ); + assert_eq!(v, decode!(Table(encode!(v)))); +} + +#[test] +fn type_errors() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Foo { bar: isize } + + let mut d = Decoder::new(Table(map! { + bar, Float(1.0) + })); + let a: Result = Deserialize::deserialize(&mut d); + match a { + Ok(..) => panic!("should not have decoded"), + Err(e) => { + assert_eq!(format!("{}", e), + "expected a value of type `integer`, but \ + found a value of type `float` for the key `bar`"); + } + } +} + +#[test] +fn missing_errors() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Foo { bar: isize } + + let mut d = Decoder::new(Table(map! { + })); + let a: Result = Deserialize::deserialize(&mut d); + match a { + Ok(..) => panic!("should not have decoded"), + Err(e) => { + assert_eq!(format!("{}", e), + "expected a value of type `integer` for the key `bar`"); + } + } +} + +#[test] +fn parse_enum() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Foo { a: E } + #[derive(Serialize, Deserialize, PartialEq, Debug)] + enum E { + Bar(isize), + Baz(f64), + Last(Foo2), + } + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Foo2 { + test: String, + } + + let v = Foo { a: E::Bar(10) }; + assert_eq!( + encode!(v), + map! { a, Integer(10) } + ); + assert_eq!(v, decode!(Table(encode!(v)))); + + let v = Foo { a: E::Baz(10.2) }; + assert_eq!( + encode!(v), + map! { a, Float(10.2) } + ); + assert_eq!(v, decode!(Table(encode!(v)))); + + let v = Foo { a: E::Last(Foo2 { test: "test".to_string() }) }; + assert_eq!( + encode!(v), + map! { a, Table(map! { test, Value::String("test".to_string()) }) } + ); + assert_eq!(v, decode!(Table(encode!(v)))); +} + +#[test] +fn unused_fields() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Foo { a: isize } + + let v = Foo { a: 2 }; + let mut d = Decoder::new(Table(map! { + a, Integer(2), + b, Integer(5) + })); + assert_eq!(v, t!(Deserialize::deserialize(&mut d))); + + assert_eq!(d.toml, Some(Table(map! { + b, Integer(5) + }))); +} + +#[test] +fn unused_fields2() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Foo { a: Bar } + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Bar { a: isize } + + let v = Foo { a: Bar { a: 2 } }; + let mut d = Decoder::new(Table(map! { + a, Table(map! { + a, Integer(2), + b, Integer(5) + }) + })); + assert_eq!(v, t!(Deserialize::deserialize(&mut d))); + + assert_eq!(d.toml, Some(Table(map! { + a, Table(map! { + b, Integer(5) + }) + }))); +} + +#[test] +fn unused_fields3() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Foo { a: Bar } + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Bar { a: isize } + + let v = Foo { a: Bar { a: 2 } }; + let mut d = Decoder::new(Table(map! { + a, Table(map! { + a, Integer(2) + }) + })); + assert_eq!(v, t!(Deserialize::deserialize(&mut d))); + + assert_eq!(d.toml, None); +} + +#[test] +fn unused_fields4() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Foo { a: BTreeMap } + + let v = Foo { a: map! { a, "foo".to_string() } }; + let mut d = Decoder::new(Table(map! { + a, Table(map! { + a, Value::String("foo".to_string()) + }) + })); + assert_eq!(v, t!(Deserialize::deserialize(&mut d))); + + assert_eq!(d.toml, None); +} + +#[test] +fn unused_fields5() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Foo { a: Vec } + + let v = Foo { a: vec!["a".to_string()] }; + let mut d = Decoder::new(Table(map! { + a, Array(vec![Value::String("a".to_string())]) + })); + assert_eq!(v, t!(Deserialize::deserialize(&mut d))); + + assert_eq!(d.toml, None); +} + +#[test] +fn unused_fields6() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Foo { a: Option> } + + let v = Foo { a: Some(vec![]) }; + let mut d = Decoder::new(Table(map! { + a, Array(vec![]) + })); + assert_eq!(v, t!(Deserialize::deserialize(&mut d))); + + assert_eq!(d.toml, None); +} + +#[test] +fn unused_fields7() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Foo { a: Vec } + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Bar { a: isize } + + let v = Foo { a: vec![Bar { a: 1 }] }; + let mut d = Decoder::new(Table(map! { + a, Array(vec![Table(map! { + a, Integer(1), + b, Integer(2) + })]) + })); + assert_eq!(v, t!(Deserialize::deserialize(&mut d))); + + assert_eq!(d.toml, Some(Table(map! { + a, Array(vec![Table(map! { + b, Integer(2) + })]) + }))); +} + +#[test] +fn empty_arrays() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Foo { a: Vec } + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Bar; + + let v = Foo { a: vec![] }; + let mut d = Decoder::new(Table(map! {})); + assert_eq!(v, t!(Deserialize::deserialize(&mut d))); +} + +#[test] +fn empty_arrays2() { + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Foo { a: Option> } + #[derive(Serialize, Deserialize, PartialEq, Debug)] + struct Bar; + + let v = Foo { a: None }; + let mut d = Decoder::new(Table(map! {})); + assert_eq!(v, t!(Deserialize::deserialize(&mut d))); + + let v = Foo { a: Some(vec![]) }; + let mut d = Decoder::new(Table(map! { + a, Array(vec![]) + })); + assert_eq!(v, t!(Deserialize::deserialize(&mut d))); +} -- cgit v1.2.3