aboutsummaryrefslogtreecommitdiff
path: root/src/encoder
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2015-04-02 17:04:17 -0700
committerAlex Crichton <alex@alexcrichton.com>2015-04-02 17:07:37 -0700
commit2d49247b92f74cf760202b7d1a08d61804f45bca (patch)
treee000d46eeda80b4cee5e5ac79a36f00970ca4a6a /src/encoder
parentcd33b87bf9a7f1e08fc73c436db51def4850855a (diff)
downloadmilf-rs-2d49247b92f74cf760202b7d1a08d61804f45bca.tar.gz
milf-rs-2d49247b92f74cf760202b7d1a08d61804f45bca.zip
Modularize rustc-serialize support
* Add rustc-serialize as a default feature * Make room for for serde support
Diffstat (limited to 'src/encoder')
-rw-r--r--src/encoder/mod.rs210
-rw-r--r--src/encoder/rustc_serialize.rs689
2 files changed, 899 insertions, 0 deletions
diff --git a/src/encoder/mod.rs b/src/encoder/mod.rs
new file mode 100644
index 0000000..21185f4
--- /dev/null
+++ b/src/encoder/mod.rs
@@ -0,0 +1,210 @@
+use std::collections::BTreeMap;
+use std::error;
+use std::fmt;
+use std::mem;
+
+use {Value, Table};
+
+#[cfg(feature = "rustc-serialize")] mod rustc_serialize;
+
+/// A structure to transform Rust values into TOML values.
+///
+/// This encoder implements the serialization `Encoder` interface, allowing
+/// `Encodable` rust types to be fed into the encoder. The output of this
+/// encoder is a TOML `Table` structure. The resulting TOML can be stringified
+/// if necessary.
+///
+/// # Example
+///
+/// ```
+/// extern crate rustc_serialize;
+/// extern crate toml;
+///
+/// # fn main() {
+/// use toml::{Encoder, Value};
+/// use rustc_serialize::Encodable;
+///
+/// #[derive(RustcEncodable)]
+/// struct MyStruct { foo: isize, bar: String }
+/// let my_struct = MyStruct { foo: 4, bar: "hello!".to_string() };
+///
+/// let mut e = Encoder::new();
+/// my_struct.encode(&mut e).unwrap();
+///
+/// assert_eq!(e.toml.get(&"foo".to_string()), Some(&Value::Integer(4)))
+/// # }
+/// ```
+pub struct Encoder {
+ /// Output TOML that is emitted. The current version of this encoder forces
+ /// the top-level representation of a structure to be a table.
+ ///
+ /// This field can be used to extract the return value after feeding a value
+ /// into this `Encoder`.
+ pub toml: Table,
+ state: State,
+}
+
+/// Enumeration of errors which can occur while encoding a rust value into a
+/// TOML value.
+#[allow(missing_copy_implementations)]
+#[derive(Debug)]
+pub enum Error {
+ /// Indication that a key was needed when a value was emitted, but no key
+ /// was previously emitted.
+ NeedsKey,
+ /// Indication that a key was emitted, but not value was emitted.
+ NoValue,
+ /// Indicates that a map key was attempted to be emitted at an invalid
+ /// location.
+ InvalidMapKeyLocation,
+ /// Indicates that a type other than a string was attempted to be used as a
+ /// map key type.
+ InvalidMapKeyType,
+}
+
+#[derive(PartialEq)]
+enum State {
+ Start,
+ NextKey(String),
+ NextArray(Vec<Value>),
+ NextMapKey,
+}
+
+impl Encoder {
+ /// Constructs a new encoder which will emit to the given output stream.
+ pub fn new() -> Encoder {
+ Encoder { state: State::Start, toml: BTreeMap::new() }
+ }
+
+ fn emit_value(&mut self, v: Value) -> Result<(), Error> {
+ match mem::replace(&mut self.state, State::Start) {
+ State::NextKey(key) => { self.toml.insert(key, v); Ok(()) }
+ State::NextArray(mut vec) => {
+ // TODO: validate types
+ vec.push(v);
+ self.state = State::NextArray(vec);
+ Ok(())
+ }
+ State::NextMapKey => {
+ match v {
+ Value::String(s) => { self.state = State::NextKey(s); Ok(()) }
+ _ => Err(Error::InvalidMapKeyType)
+ }
+ }
+ _ => Err(Error::NeedsKey)
+ }
+ }
+
+ fn emit_none(&mut self) -> Result<(), Error> {
+ match mem::replace(&mut self.state, State::Start) {
+ State::Start => unreachable!(),
+ State::NextKey(_) => Ok(()),
+ State::NextArray(..) => panic!("how to encode None in an array?"),
+ State::NextMapKey => Err(Error::InvalidMapKeyLocation),
+ }
+ }
+
+ fn seq<F>(&mut self, f: F) -> Result<(), Error>
+ where F: FnOnce(&mut Encoder) -> Result<(), Error>
+ {
+ let old = mem::replace(&mut self.state, State::NextArray(Vec::new()));
+ try!(f(self));
+ match mem::replace(&mut self.state, old) {
+ State::NextArray(v) => self.emit_value(Value::Array(v)),
+ _ => unreachable!(),
+ }
+ }
+
+ fn table<F>(&mut self, f: F) -> Result<(), Error>
+ where F: FnOnce(&mut Encoder) -> Result<(), Error>
+ {
+ match mem::replace(&mut self.state, State::Start) {
+ State::NextKey(key) => {
+ let mut nested = Encoder::new();
+ try!(f(&mut nested));
+ self.toml.insert(key, Value::Table(nested.toml));
+ Ok(())
+ }
+ State::NextArray(mut arr) => {
+ let mut nested = Encoder::new();
+ try!(f(&mut nested));
+ arr.push(Value::Table(nested.toml));
+ self.state = State::NextArray(arr);
+ Ok(())
+ }
+ State::Start => f(self),
+ State::NextMapKey => Err(Error::InvalidMapKeyLocation),
+ }
+ }
+
+ fn table_key<F>(&mut self, f: F) -> Result<(), Error>
+ where F: FnOnce(&mut Encoder) -> Result<(), Error>
+ {
+ match mem::replace(&mut self.state, State::NextMapKey) {
+ State::Start => {}
+ _ => return Err(Error::InvalidMapKeyLocation),
+ }
+ try!(f(self));
+ match self.state {
+ State::NextKey(_) => Ok(()),
+ _ => Err(Error::InvalidMapKeyLocation),
+ }
+ }
+}
+
+/// Encodes an encodable value into a TOML value.
+///
+/// This function expects the type given to represent a TOML table in some form.
+/// If encoding encounters an error, then this function will fail the task.
+#[cfg(feature = "rustc-serialize")]
+pub fn encode<T: ::rustc_serialize::Encodable>(t: &T) -> Value {
+ let mut e = Encoder::new();
+ t.encode(&mut e).unwrap();
+ Value::Table(e.toml)
+}
+
+/// Encodes an encodable value into a TOML value.
+///
+/// This function expects the type given to represent a TOML table in some form.
+/// If encoding encounters an error, then this function will fail the task.
+#[cfg(all(not(feature = "rustc-serialize"), feature = "serde"))]
+pub fn encode<T: ::serde::Serialize>(t: &T) -> Value {
+ let mut e = Encoder::new();
+ t.deserialize(&mut e).unwrap();
+ Value::Table(e.toml)
+}
+
+/// Encodes an encodable value into a TOML string.
+///
+/// This function expects the type given to represent a TOML table in some form.
+/// If encoding encounters an error, then this function will fail the task.
+#[cfg(feature = "rustc-serialize")]
+pub fn encode_str<T: ::rustc_serialize::Encodable>(t: &T) -> String {
+ encode(t).to_string()
+}
+
+/// Encodes an encodable value into a TOML string.
+///
+/// This function expects the type given to represent a TOML table in some form.
+/// If encoding encounters an error, then this function will fail the task.
+#[cfg(all(not(feature = "rustc-serialize"), feature = "serde"))]
+pub fn encode_str<T: ::serde::Serialize>(t: &T) -> String {
+ encode(t).to_string()
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ Error::NeedsKey => write!(f, "need a key to encode"),
+ Error::NoValue => write!(f, "not value to emit for a previous key"),
+ Error::InvalidMapKeyLocation => write!(f, "a map cannot be emitted \
+ at this location"),
+ Error::InvalidMapKeyType => write!(f, "only strings can be used as \
+ key types"),
+ }
+ }
+}
+
+impl error::Error for Error {
+ fn description(&self) -> &str { "TOML encoding error" }
+}
diff --git a/src/encoder/rustc_serialize.rs b/src/encoder/rustc_serialize.rs
new file mode 100644
index 0000000..ab5e90f
--- /dev/null
+++ b/src/encoder/rustc_serialize.rs
@@ -0,0 +1,689 @@
+use std::mem;
+
+use rustc_serialize;
+use Value;
+use super::{Encoder, Error, State};
+use super::Error::*;
+
+impl rustc_serialize::Encoder for Encoder {
+ type Error = Error;
+
+ fn emit_nil(&mut self) -> Result<(), Error> { Ok(()) }
+ fn emit_usize(&mut self, v: usize) -> Result<(), Error> {
+ self.emit_i64(v as i64)
+ }
+ fn emit_u8(&mut self, v: u8) -> Result<(), Error> {
+ self.emit_i64(v as i64)
+ }
+ fn emit_u16(&mut self, v: u16) -> Result<(), Error> {
+ self.emit_i64(v as i64)
+ }
+ fn emit_u32(&mut self, v: u32) -> Result<(), Error> {
+ self.emit_i64(v as i64)
+ }
+ fn emit_u64(&mut self, v: u64) -> Result<(), Error> {
+ self.emit_i64(v as i64)
+ }
+ fn emit_isize(&mut self, v: isize) -> Result<(), Error> {
+ self.emit_i64(v as i64)
+ }
+ fn emit_i8(&mut self, v: i8) -> Result<(), Error> {
+ self.emit_i64(v as i64)
+ }
+ fn emit_i16(&mut self, v: i16) -> Result<(), Error> {
+ self.emit_i64(v as i64)
+ }
+ fn emit_i32(&mut self, v: i32) -> Result<(), Error> {
+ self.emit_i64(v as i64)
+ }
+ fn emit_i64(&mut self, v: i64) -> Result<(), Error> {
+ self.emit_value(Value::Integer(v))
+ }
+ fn emit_bool(&mut self, v: bool) -> Result<(), Error> {
+ self.emit_value(Value::Boolean(v))
+ }
+ fn emit_f32(&mut self, v: f32) -> Result<(), Error> { self.emit_f64(v as f64) }
+ fn emit_f64(&mut self, v: f64) -> Result<(), Error> {
+ self.emit_value(Value::Float(v))
+ }
+ fn emit_char(&mut self, v: char) -> Result<(), Error> {
+ self.emit_str(&*format!("{}", v))
+ }
+ fn emit_str(&mut self, v: &str) -> Result<(), Error> {
+ self.emit_value(Value::String(format!("{}", v)))
+ }
+ fn emit_enum<F>(&mut self, _name: &str, f: F)
+ -> Result<(), Error>
+ where F: FnOnce(&mut Encoder) -> Result<(), Error>
+ {
+ f(self)
+ }
+ fn emit_enum_variant<F>(&mut self, _v_name: &str, _v_id: usize,
+ _len: usize, f: F) -> Result<(), Error>
+ where F: FnOnce(&mut Encoder) -> Result<(), Error>
+ {
+ f(self)
+ }
+ fn emit_enum_variant_arg<F>(&mut self, _a_idx: usize, f: F)
+ -> Result<(), Error>
+ where F: FnOnce(&mut Encoder) -> Result<(), Error>
+ {
+ f(self)
+ }
+ fn emit_enum_struct_variant<F>(&mut self, _v_name: &str, _v_id: usize,
+ _len: usize,
+ _f: F)
+ -> Result<(), Error>
+ where F: FnOnce(&mut Encoder) -> Result<(), Error>
+ {
+ panic!()
+ }
+ fn emit_enum_struct_variant_field<F>(&mut self,
+ _f_name: &str,
+ _f_idx: usize,
+ _f: F)
+ -> Result<(), Error>
+ where F: FnOnce(&mut Encoder) -> Result<(), Error>
+ {
+ panic!()
+ }
+ fn emit_struct<F>(&mut self, _name: &str, _len: usize, f: F)
+ -> Result<(), Error>
+ where F: FnOnce(&mut Encoder) -> Result<(), Error>
+ {
+ self.table(f)
+ }
+ fn emit_struct_field<F>(&mut self, f_name: &str, _f_idx: usize, f: F)
+ -> Result<(), Error>
+ where F: FnOnce(&mut Encoder) -> Result<(), Error>
+ {
+ let old = mem::replace(&mut self.state,
+ State::NextKey(format!("{}", f_name)));
+ try!(f(self));
+ if self.state != State::Start {
+ return Err(NoValue)
+ }
+ self.state = old;
+ Ok(())
+ }
+ fn emit_tuple<F>(&mut self, len: usize, f: F)
+ -> Result<(), Error>
+ where F: FnOnce(&mut Encoder) -> Result<(), Error>
+ {
+ self.emit_seq(len, f)
+ }
+ fn emit_tuple_arg<F>(&mut self, idx: usize, f: F)
+ -> Result<(), Error>
+ where F: FnOnce(&mut Encoder) -> Result<(), Error>
+ {
+ self.emit_seq_elt(idx, f)
+ }
+ fn emit_tuple_struct<F>(&mut self, _name: &str, _len: usize, _f: F)
+ -> Result<(), Error>
+ where F: FnOnce(&mut Encoder) -> Result<(), Error>
+ {
+ unimplemented!()
+ }
+ fn emit_tuple_struct_arg<F>(&mut self, _f_idx: usize, _f: F)
+ -> Result<(), Error>
+ where F: FnOnce(&mut Encoder) -> Result<(), Error>
+ {
+ unimplemented!()
+ }
+ fn emit_option<F>(&mut self, f: F)
+ -> Result<(), Error>
+ where F: FnOnce(&mut Encoder) -> Result<(), Error>
+ {
+ f(self)
+ }
+ fn emit_option_none(&mut self) -> Result<(), Error> {
+ self.emit_none()
+ }
+ fn emit_option_some<F>(&mut self, f: F) -> Result<(), Error>
+ where F: FnOnce(&mut Encoder) -> Result<(), Error>
+ {
+ f(self)
+ }
+ fn emit_seq<F>(&mut self, _len: usize, f: F)
+ -> Result<(), Error>
+ where F: FnOnce(&mut Encoder) -> Result<(), Error>
+ {
+ self.seq(f)
+ }
+ fn emit_seq_elt<F>(&mut self, _idx: usize, f: F)
+ -> Result<(), Error>
+ where F: FnOnce(&mut Encoder) -> Result<(), Error>
+ {
+ f(self)
+ }
+ fn emit_map<F>(&mut self, len: usize, f: F)
+ -> Result<(), Error>
+ where F: FnOnce(&mut Encoder) -> Result<(), Error>
+ {
+ self.emit_struct("foo", len, f)
+ }
+ fn emit_map_elt_key<F>(&mut self, _idx: usize, f: F) -> Result<(), Error>
+ where F: FnOnce(&mut Encoder) -> Result<(), Error>
+ {
+ self.table_key(f)
+ }
+ fn emit_map_elt_val<F>(&mut self, _idx: usize, f: F) -> Result<(), Error>
+ where F: FnOnce(&mut Encoder) -> Result<(), Error>
+ {
+ f(self)
+ }
+}
+
+impl rustc_serialize::Encodable for Value {
+ fn encode<E>(&self, e: &mut E) -> Result<(), E::Error>
+ where E: rustc_serialize::Encoder
+ {
+ match *self {
+ Value::String(ref s) => e.emit_str(s),
+ Value::Integer(i) => e.emit_i64(i),
+ Value::Float(f) => e.emit_f64(f),
+ Value::Boolean(b) => e.emit_bool(b),
+ Value::Datetime(ref s) => e.emit_str(s),
+ Value::Array(ref a) => {
+ e.emit_seq(a.len(), |e| {
+ for item in a {
+ try!(item.encode(e));
+ }
+ Ok(())
+ })
+ }
+ Value::Table(ref t) => {
+ e.emit_map(t.len(), |e| {
+ for (i, (key, value)) in t.iter().enumerate() {
+ try!(e.emit_map_elt_key(i, |e| e.emit_str(key)));
+ try!(e.emit_map_elt_val(i, |e| value.encode(e)));
+ }
+ Ok(())
+ })
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use std::collections::{BTreeMap, HashSet};
+ use rustc_serialize::{self, Encodable, Decodable};
+
+ use {Encoder, Decoder, DecodeError};
+ use Value;
+ use Value::{Table, Integer, Array, Float};
+
+ macro_rules! encode( ($t:expr) => ({
+ let mut e = Encoder::new();
+ $t.encode(&mut e).unwrap();
+ e.toml
+ }) );
+
+ macro_rules! decode( ($t:expr) => ({
+ let mut d = Decoder::new($t);
+ Decodable::decode(&mut d).unwrap()
+ }) );
+
+ macro_rules! map( ($($k:ident, $v:expr),*) => ({
+ let mut _m = BTreeMap::new();
+ $(_m.insert(stringify!($k).to_string(), $v);)*
+ _m
+ }) );
+
+ #[test]
+ fn smoke() {
+ #[derive(RustcEncodable, RustcDecodable, 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(RustcEncodable, RustcDecodable, 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(RustcEncodable, RustcDecodable, PartialEq, Debug)]
+ struct Foo { a: isize, b: Bar }
+ #[derive(RustcEncodable, RustcDecodable, 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 Decodable for Range10 {
+ fn decode<D: rustc_serialize::Decoder>(d: &mut D) -> Result<Range10, D::Error> {
+ let x: usize = try!(Decodable::decode(d));
+ if x > 10 {
+ Err(d.error("Value out of range!"))
+ } 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)), Decodable::decode(&mut d_good));
+
+ let err1: Result<Range10, _> = Decodable::decode(&mut d_bad1);
+ assert!(err1.is_err());
+ let err2: Result<Range10, _> = Decodable::decode(&mut d_bad2);
+ assert!(err2.is_err());
+ }
+
+ #[test]
+ fn array() {
+ #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
+ struct Foo { a: Vec<isize> }
+
+ 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(RustcEncodable, RustcDecodable, 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(RustcEncodable, RustcDecodable, PartialEq, Debug)]
+ struct Foo {
+ a: Option<Box<Foo>>,
+ b: Bar,
+ }
+ #[derive(RustcEncodable, RustcDecodable, 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(RustcEncodable, RustcDecodable, PartialEq, Debug)]
+ struct Foo {
+ map: BTreeMap<String, isize>,
+ set: HashSet<char>,
+ }
+
+ 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(RustcEncodable, RustcDecodable, 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(RustcEncodable, RustcDecodable, PartialEq, Debug)]
+ struct Foo { a: Vec<Bar>, }
+ #[derive(RustcEncodable, RustcDecodable, 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(RustcEncodable, RustcDecodable, PartialEq, Debug)]
+ struct Foo { bar: isize }
+
+ let mut d = Decoder::new(Table(map! {
+ bar, Float(1.0)
+ }));
+ let a: Result<Foo, DecodeError> = Decodable::decode(&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(RustcEncodable, RustcDecodable, PartialEq, Debug)]
+ struct Foo { bar: isize }
+
+ let mut d = Decoder::new(Table(map! {
+ }));
+ let a: Result<Foo, DecodeError> = Decodable::decode(&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(RustcEncodable, RustcDecodable, PartialEq, Debug)]
+ struct Foo { a: E }
+ #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
+ enum E {
+ Bar(isize),
+ Baz(f64),
+ Last(Foo2),
+ }
+ #[derive(RustcEncodable, RustcDecodable, 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(RustcEncodable, RustcDecodable, 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, Decodable::decode(&mut d).unwrap());
+
+ assert_eq!(d.toml, Some(Table(map! {
+ b, Integer(5)
+ })));
+ }
+
+ #[test]
+ fn unused_fields2() {
+ #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
+ struct Foo { a: Bar }
+ #[derive(RustcEncodable, RustcDecodable, 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, Decodable::decode(&mut d).unwrap());
+
+ assert_eq!(d.toml, Some(Table(map! {
+ a, Table(map! {
+ b, Integer(5)
+ })
+ })));
+ }
+
+ #[test]
+ fn unused_fields3() {
+ #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
+ struct Foo { a: Bar }
+ #[derive(RustcEncodable, RustcDecodable, 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, Decodable::decode(&mut d).unwrap());
+
+ assert_eq!(d.toml, None);
+ }
+
+ #[test]
+ fn unused_fields4() {
+ #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
+ struct Foo { a: BTreeMap<String, String> }
+
+ 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, Decodable::decode(&mut d).unwrap());
+
+ assert_eq!(d.toml, None);
+ }
+
+ #[test]
+ fn unused_fields5() {
+ #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
+ struct Foo { a: Vec<String> }
+
+ 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, Decodable::decode(&mut d).unwrap());
+
+ assert_eq!(d.toml, None);
+ }
+
+ #[test]
+ fn unused_fields6() {
+ #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
+ struct Foo { a: Option<Vec<String>> }
+
+ let v = Foo { a: Some(vec![]) };
+ let mut d = Decoder::new(Table(map! {
+ a, Array(vec![])
+ }));
+ assert_eq!(v, Decodable::decode(&mut d).unwrap());
+
+ assert_eq!(d.toml, None);
+ }
+
+ #[test]
+ fn unused_fields7() {
+ #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
+ struct Foo { a: Vec<Bar> }
+ #[derive(RustcEncodable, RustcDecodable, 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, Decodable::decode(&mut d).unwrap());
+
+ assert_eq!(d.toml, Some(Table(map! {
+ a, Array(vec![Table(map! {
+ b, Integer(2)
+ })])
+ })));
+ }
+
+ #[test]
+ fn empty_arrays() {
+ #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
+ struct Foo { a: Vec<Bar> }
+ #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
+ struct Bar;
+
+ let v = Foo { a: vec![] };
+ let mut d = Decoder::new(Table(map! {}));
+ assert_eq!(v, Decodable::decode(&mut d).unwrap());
+ }
+
+ #[test]
+ fn empty_arrays2() {
+ #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
+ struct Foo { a: Option<Vec<Bar>> }
+ #[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
+ struct Bar;
+
+ let v = Foo { a: None };
+ let mut d = Decoder::new(Table(map! {}));
+ assert_eq!(v, Decodable::decode(&mut d).unwrap());
+
+ let v = Foo { a: Some(vec![]) };
+ let mut d = Decoder::new(Table(map! {
+ a, Array(vec![])
+ }));
+ assert_eq!(v, Decodable::decode(&mut d).unwrap());
+ }
+
+ #[test]
+ fn round_trip() {
+ let toml = r#"
+ [test]
+ foo = "bar"
+
+ [[values]]
+ foo = "baz"
+
+ [[values]]
+ foo = "qux"
+ "#;
+
+ let value: Value = toml.parse().unwrap();
+ let val2 = ::encode_str(&value).parse().unwrap();
+ assert_eq!(value, val2);
+ }
+}