diff options
author | Alex Crichton <alex@alexcrichton.com> | 2015-04-02 17:04:17 -0700 |
---|---|---|
committer | Alex Crichton <alex@alexcrichton.com> | 2015-04-02 17:07:37 -0700 |
commit | 2d49247b92f74cf760202b7d1a08d61804f45bca (patch) | |
tree | e000d46eeda80b4cee5e5ac79a36f00970ca4a6a /src/encoder | |
parent | cd33b87bf9a7f1e08fc73c436db51def4850855a (diff) | |
download | milf-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.rs | 210 | ||||
-rw-r--r-- | src/encoder/rustc_serialize.rs | 689 |
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); + } +} |