From 2d49247b92f74cf760202b7d1a08d61804f45bca Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 2 Apr 2015 17:04:17 -0700 Subject: [PATCH] Modularize rustc-serialize support * Add rustc-serialize as a default feature * Make room for for serde support --- .travis.yml | 2 + Cargo.toml | 8 +- src/decoder/mod.rs | 205 +++++ src/decoder/rustc_serialize.rs | 311 ++++++++ src/encoder/mod.rs | 210 ++++++ src/encoder/rustc_serialize.rs | 689 +++++++++++++++++ src/lib.rs | 70 +- src/serialization.rs | 1279 -------------------------------- 8 files changed, 1434 insertions(+), 1340 deletions(-) create mode 100644 src/decoder/mod.rs create mode 100644 src/decoder/rustc_serialize.rs create mode 100644 src/encoder/mod.rs create mode 100644 src/encoder/rustc_serialize.rs delete mode 100644 src/serialization.rs diff --git a/.travis.yml b/.travis.yml index 41b485d..145528e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,9 @@ language: rust sudo: false script: - cargo build --verbose + - cargo build --verbose --no-default-features - cargo test --verbose + - cargo test --verbose --no-default-features - rustdoc --test README.md -L target - cargo doc --no-deps after_success: | diff --git a/Cargo.toml b/Cargo.toml index cf33dc1..102f63d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,4 +16,10 @@ facilitate deserializing and serializing Rust structures. """ [dependencies] -rustc-serialize = "0.3.0" +rustc-serialize = { optional = true, version = "0.3.0" } + +[features] +default = ["rustc-serialize"] + +[dev-dependencies] +rustc-serialize = "0.3" diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs new file mode 100644 index 0000000..675e0ca --- /dev/null +++ b/src/decoder/mod.rs @@ -0,0 +1,205 @@ +use std::error; +use std::fmt; + +use Value; +use self::DecodeErrorKind::*; + +#[cfg(feature = "rustc-serialize")] mod rustc_serialize; + +/// A structure to transform TOML values into Rust values. +/// +/// This decoder implements the serialization `Decoder` interface, allowing +/// `Decodable` types to be generated by this decoder. The input is any +/// arbitrary TOML value. +pub struct Decoder { + /// The TOML value left over after decoding. This can be used to inspect + /// whether fields were decoded or not. + pub toml: Option, + cur_field: Option, +} + +/// Description for errors which can occur while decoding a type. +#[derive(PartialEq, Debug)] +pub struct DecodeError { + /// Field that this error applies to. + pub field: Option, + /// The type of error which occurred while decoding, + pub kind: DecodeErrorKind, +} + +/// Enumeration of possible errors which can occur while decoding a structure. +#[derive(PartialEq, Debug)] +pub enum DecodeErrorKind { + /// An error flagged by the application, e.g. value out of range + ApplicationError(String), + /// A field was expected, but none was found. + ExpectedField(/* type */ Option<&'static str>), + /// A field was found, but it had the wrong type. + ExpectedType(/* expected */ &'static str, /* found */ &'static str), + /// The nth map key was expected, but none was found. + ExpectedMapKey(usize), + /// The nth map element was expected, but none was found. + ExpectedMapElement(usize), + /// An enum decoding was requested, but no variants were supplied + NoEnumVariants, + /// The unit type was being decoded, but a non-zero length string was found + NilTooLong, + /// There was an error with the syntactical structure of the TOML. + SyntaxError, + /// The end of the TOML input was reached too soon + EndOfStream, +} + +/// Decodes a TOML value into a decodable type. +/// +/// This function will consume the given TOML value and attempt to decode it +/// into the type specified. If decoding fails, `None` will be returned. If a +/// finer-grained error is desired, then it is recommended to use `Decodable` +/// directly. +#[cfg(feature = "rustc-serialize")] +pub fn decode(toml: Value) -> Option { + ::rustc_serialize::Decodable::decode(&mut Decoder::new(toml)).ok() +} + +/// Decodes a TOML value into a decodable type. +/// +/// This function will consume the given TOML value and attempt to decode it +/// into the type specified. If decoding fails, `None` will be returned. If a +/// finer-grained error is desired, then it is recommended to use `Decodable` +/// directly. +#[cfg(all(not(feature = "rustc-serialize"), feature = "serde"))] +pub fn decode(toml: Value) -> Option { + ::serde::Deserialize::deserialize(&mut Decoder::new(toml)).ok() +} + +/// Decodes a string into a toml-encoded value. +/// +/// This function will parse the given string into a TOML value, and then parse +/// the TOML value into the desired type. If any error occurs `None` is return. +/// +/// If more fine-grained errors are desired, these steps should be driven +/// manually. +#[cfg(feature = "rustc-serialize")] +pub fn decode_str(s: &str) -> Option { + ::Parser::new(s).parse().and_then(|t| decode(Value::Table(t))) +} + +/// Decodes a string into a toml-encoded value. +/// +/// This function will parse the given string into a TOML value, and then parse +/// the TOML value into the desired type. If any error occurs `None` is return. +/// +/// If more fine-grained errors are desired, these steps should be driven +/// manually. +#[cfg(all(not(feature = "rustc-serialize"), feature = "serde"))] +pub fn decode_str(s: &str) -> Option { + ::Parser::new(s).parse().and_then(|t| decode(Value::Table(t))) +} + +impl Decoder { + /// Creates a new decoder, consuming the TOML value to decode. + /// + /// This decoder can be passed to the `Decodable` methods or driven + /// manually. + pub fn new(toml: Value) -> Decoder { + Decoder { toml: Some(toml), cur_field: None } + } + + fn sub_decoder(&self, toml: Option, field: &str) -> Decoder { + Decoder { + toml: toml, + cur_field: if field.len() == 0 { + self.cur_field.clone() + } else { + match self.cur_field { + None => Some(format!("{}", field)), + Some(ref s) => Some(format!("{}.{}", s, field)) + } + } + } + } + + fn err(&self, kind: DecodeErrorKind) -> DecodeError { + DecodeError { + field: self.cur_field.clone(), + kind: kind, + } + } + + fn mismatch(&self, expected: &'static str, + found: &Option) -> DecodeError{ + match *found { + Some(ref val) => self.err(ExpectedType(expected, val.type_str())), + None => self.err(ExpectedField(Some(expected))), + } + } +} + +impl fmt::Display for DecodeError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + try!(match self.kind { + ApplicationError(ref err) => { + write!(f, "{}", err) + } + ExpectedField(expected_type) => { + match expected_type { + Some("table") => write!(f, "expected a section"), + Some(e) => write!(f, "expected a value of type `{}`", e), + None => write!(f, "expected a value"), + } + } + ExpectedType(expected, found) => { + fn humanize(s: &str) -> String { + if s == "section" { + format!("a section") + } else { + format!("a value of type `{}`", s) + } + } + write!(f, "expected {}, but found {}", + humanize(expected), + humanize(found)) + } + ExpectedMapKey(idx) => { + write!(f, "expected at least {} keys", idx + 1) + } + ExpectedMapElement(idx) => { + write!(f, "expected at least {} elements", idx + 1) + } + NoEnumVariants => { + write!(f, "expected an enum variant to decode to") + } + NilTooLong => { + write!(f, "expected 0-length string") + } + SyntaxError => { + write!(f, "syntax error") + } + EndOfStream => { + write!(f, "end of stream") + } + }); + match self.field { + Some(ref s) => { + write!(f, " for the key `{}`", s) + } + None => Ok(()) + } + } +} + +impl error::Error for DecodeError { + fn description(&self) -> &str { + match self.kind { + ApplicationError(ref s) => &**s, + ExpectedField(..) => "expected a field", + ExpectedType(..) => "expected a type", + ExpectedMapKey(..) => "expected a map key", + ExpectedMapElement(..) => "expected a map element", + NoEnumVariants => "no enum variants to decode to", + NilTooLong => "nonzero length string representing nil", + SyntaxError => "syntax error", + EndOfStream => "end of stream", + } + } +} diff --git a/src/decoder/rustc_serialize.rs b/src/decoder/rustc_serialize.rs new file mode 100644 index 0000000..6e8fe59 --- /dev/null +++ b/src/decoder/rustc_serialize.rs @@ -0,0 +1,311 @@ +use rustc_serialize; +use std::mem; + +use super::{Decoder, DecodeError}; +use super::DecodeErrorKind::*; +use Value; + +impl rustc_serialize::Decoder for Decoder { + type Error = DecodeError; + fn read_nil(&mut self) -> Result<(), DecodeError> { + match self.toml { + Some(Value::String(ref s)) if s.len() == 0 => {} + Some(Value::String(..)) => return Err(self.err(NilTooLong)), + ref found => return Err(self.mismatch("string", found)), + } + self.toml.take(); + Ok(()) + } + fn read_usize(&mut self) -> Result { + self.read_i64().map(|i| i as usize) + } + fn read_u64(&mut self) -> Result { + self.read_i64().map(|i| i as u64) + } + fn read_u32(&mut self) -> Result { + self.read_i64().map(|i| i as u32) + } + fn read_u16(&mut self) -> Result { + self.read_i64().map(|i| i as u16) + } + fn read_u8(&mut self) -> Result { + self.read_i64().map(|i| i as u8) + } + fn read_isize(&mut self) -> Result { + self.read_i64().map(|i| i as isize) + } + fn read_i64(&mut self) -> Result { + match self.toml { + Some(Value::Integer(i)) => { self.toml.take(); Ok(i) } + ref found => Err(self.mismatch("integer", found)), + } + } + fn read_i32(&mut self) -> Result { + self.read_i64().map(|i| i as i32) + } + fn read_i16(&mut self) -> Result { + self.read_i64().map(|i| i as i16) + } + fn read_i8(&mut self) -> Result { + self.read_i64().map(|i| i as i8) + } + fn read_bool(&mut self) -> Result { + match self.toml { + Some(Value::Boolean(b)) => { self.toml.take(); Ok(b) } + ref found => Err(self.mismatch("bool", found)), + } + } + fn read_f64(&mut self) -> Result { + match self.toml { + Some(Value::Float(f)) => Ok(f), + ref found => Err(self.mismatch("float", found)), + } + } + fn read_f32(&mut self) -> Result { + self.read_f64().map(|f| f as f32) + } + fn read_char(&mut self) -> Result { + let ch = match self.toml { + Some(Value::String(ref s)) if s.chars().count() == 1 => + s.chars().next().unwrap(), + ref found => return Err(self.mismatch("string", found)), + }; + self.toml.take(); + Ok(ch) + } + fn read_str(&mut self) -> Result { + match self.toml.take() { + Some(Value::String(s)) => Ok(s), + found => { + let err = Err(self.mismatch("string", &found)); + self.toml = found; + err + } + } + } + + // Compound types: + fn read_enum(&mut self, _name: &str, f: F) + -> Result + where F: FnOnce(&mut Decoder) -> Result + { + f(self) + } + + fn read_enum_variant(&mut self, names: &[&str], mut f: F) + -> Result + where F: FnMut(&mut Decoder, usize) -> Result + { + let mut first_error = None; + for i in 0..names.len() { + let mut d = self.sub_decoder(self.toml.clone(), ""); + match f(&mut d, i) { + Ok(t) => { self.toml = d.toml; return Ok(t) } + Err(e) => { + if first_error.is_none() { + first_error = Some(e); + } + } + } + } + Err(first_error.unwrap_or_else(|| self.err(NoEnumVariants))) + } + fn read_enum_variant_arg(&mut self, _a_idx: usize, f: F) + -> Result + where F: FnOnce(&mut Decoder) -> Result + { + f(self) + } + + fn read_enum_struct_variant(&mut self, _names: &[&str], _f: F) + -> Result + where F: FnMut(&mut Decoder, usize) -> Result + { + panic!() + } + fn read_enum_struct_variant_field(&mut self, + _f_name: &str, + _f_idx: usize, + _f: F) + -> Result + where F: FnOnce(&mut Decoder) -> Result + { + panic!() + } + + fn read_struct(&mut self, _s_name: &str, _len: usize, f: F) + -> Result + where F: FnOnce(&mut Decoder) -> Result + { + match self.toml { + Some(Value::Table(..)) => { + let ret = try!(f(self)); + match self.toml { + Some(Value::Table(ref t)) if t.len() == 0 => {} + _ => return Ok(ret) + } + self.toml.take(); + Ok(ret) + } + ref found => Err(self.mismatch("table", found)), + } + } + fn read_struct_field(&mut self, f_name: &str, _f_idx: usize, f: F) + -> Result + where F: FnOnce(&mut Decoder) -> Result + { + let field = format!("{}", f_name); + let toml = match self.toml { + Some(Value::Table(ref mut table)) => { + table.remove(&field) + .or_else(|| table.remove(&f_name.replace("_", "-"))) + }, + ref found => return Err(self.mismatch("table", found)), + }; + let mut d = self.sub_decoder(toml, f_name); + let ret = try!(f(&mut d)); + if let Some(value) = d.toml { + if let Some(Value::Table(ref mut table)) = self.toml { + table.insert(field, value); + } + } + Ok(ret) + } + + fn read_tuple(&mut self, tuple_len: usize, f: F) + -> Result + where F: FnOnce(&mut Decoder) -> Result + { + self.read_seq(move |d, len| { + assert!(len == tuple_len, + "expected tuple of length `{}`, found tuple \ + of length `{}`", tuple_len, len); + f(d) + }) + } + fn read_tuple_arg(&mut self, a_idx: usize, f: F) + -> Result + where F: FnOnce(&mut Decoder) -> Result + { + self.read_seq_elt(a_idx, f) + } + + fn read_tuple_struct(&mut self, _s_name: &str, _len: usize, _f: F) + -> Result + where F: FnOnce(&mut Decoder) -> Result + { + panic!() + } + fn read_tuple_struct_arg(&mut self, _a_idx: usize, _f: F) + -> Result + where F: FnOnce(&mut Decoder) -> Result + { + panic!() + } + + // Specialized types: + fn read_option(&mut self, mut f: F) + -> Result + where F: FnMut(&mut Decoder, bool) -> Result + { + match self.toml { + Some(..) => f(self, true), + None => f(self, false), + } + } + + fn read_seq(&mut self, f: F) -> Result + where F: FnOnce(&mut Decoder, usize) -> Result + { + let len = match self.toml { + Some(Value::Array(ref arr)) => arr.len(), + None => 0, + ref found => return Err(self.mismatch("array", found)), + }; + let ret = try!(f(self, len)); + match self.toml { + Some(Value::Array(ref mut arr)) => { + arr.retain(|slot| slot.as_integer() != Some(0)); + if arr.len() != 0 { return Ok(ret) } + } + _ => return Ok(ret) + } + self.toml.take(); + Ok(ret) + } + fn read_seq_elt(&mut self, idx: usize, f: F) + -> Result + where F: FnOnce(&mut Decoder) -> Result + { + let toml = match self.toml { + Some(Value::Array(ref mut arr)) => { + mem::replace(&mut arr[idx], Value::Integer(0)) + } + ref found => return Err(self.mismatch("array", found)), + }; + let mut d = self.sub_decoder(Some(toml), ""); + let ret = try!(f(&mut d)); + match d.toml { + Some(toml) => match self.toml { + Some(Value::Array(ref mut arr)) => arr[idx] = toml, + _ => {} + }, + _ => {} + } + Ok(ret) + } + + fn read_map(&mut self, f: F) + -> Result + where F: FnOnce(&mut Decoder, usize) -> Result + { + let len = match self.toml { + Some(Value::Table(ref table)) => table.len(), + ref found => return Err(self.mismatch("table", found)), + }; + let ret = try!(f(self, len)); + self.toml.take(); + Ok(ret) + } + fn read_map_elt_key(&mut self, idx: usize, f: F) + -> Result + where F: FnOnce(&mut Decoder) -> Result + { + match self.toml { + Some(Value::Table(ref table)) => { + match table.iter().skip(idx).next() { + Some((key, _)) => { + let val = Value::String(format!("{}", key)); + f(&mut self.sub_decoder(Some(val), &**key)) + } + None => Err(self.err(ExpectedMapKey(idx))), + } + } + ref found => Err(self.mismatch("table", found)), + } + } + fn read_map_elt_val(&mut self, idx: usize, f: F) + -> Result + where F: FnOnce(&mut Decoder) -> Result + { + match self.toml { + Some(Value::Table(ref table)) => { + match table.iter().skip(idx).next() { + Some((_, value)) => { + // XXX: this shouldn't clone + f(&mut self.sub_decoder(Some(value.clone()), "")) + } + None => Err(self.err(ExpectedMapElement(idx))), + } + } + ref found => Err(self.mismatch("table", found)), + } + } + + fn error(&mut self, err: &str) -> DecodeError { + DecodeError { + field: self.cur_field.clone(), + kind: ApplicationError(format!("{}", err)) + } + } +} 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), + 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(&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(&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(&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: &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: &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: &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: &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(&mut self, _name: &str, f: F) + -> Result<(), Error> + where F: FnOnce(&mut Encoder) -> Result<(), Error> + { + f(self) + } + fn emit_enum_variant(&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(&mut self, _a_idx: usize, f: F) + -> Result<(), Error> + where F: FnOnce(&mut Encoder) -> Result<(), Error> + { + f(self) + } + fn emit_enum_struct_variant(&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(&mut self, + _f_name: &str, + _f_idx: usize, + _f: F) + -> Result<(), Error> + where F: FnOnce(&mut Encoder) -> Result<(), Error> + { + panic!() + } + fn emit_struct(&mut self, _name: &str, _len: usize, f: F) + -> Result<(), Error> + where F: FnOnce(&mut Encoder) -> Result<(), Error> + { + self.table(f) + } + fn emit_struct_field(&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(&mut self, len: usize, f: F) + -> Result<(), Error> + where F: FnOnce(&mut Encoder) -> Result<(), Error> + { + self.emit_seq(len, f) + } + fn emit_tuple_arg(&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(&mut self, _name: &str, _len: usize, _f: F) + -> Result<(), Error> + where F: FnOnce(&mut Encoder) -> Result<(), Error> + { + unimplemented!() + } + fn emit_tuple_struct_arg(&mut self, _f_idx: usize, _f: F) + -> Result<(), Error> + where F: FnOnce(&mut Encoder) -> Result<(), Error> + { + unimplemented!() + } + fn emit_option(&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(&mut self, f: F) -> Result<(), Error> + where F: FnOnce(&mut Encoder) -> Result<(), Error> + { + f(self) + } + fn emit_seq(&mut self, _len: usize, f: F) + -> Result<(), Error> + where F: FnOnce(&mut Encoder) -> Result<(), Error> + { + self.seq(f) + } + fn emit_seq_elt(&mut self, _idx: usize, f: F) + -> Result<(), Error> + where F: FnOnce(&mut Encoder) -> Result<(), Error> + { + f(self) + } + fn emit_map(&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(&mut self, _idx: usize, f: F) -> Result<(), Error> + where F: FnOnce(&mut Encoder) -> Result<(), Error> + { + self.table_key(f) + } + fn emit_map_elt_val(&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(&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: &mut D) -> Result { + 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 = Decodable::decode(&mut d_bad1); + assert!(err1.is_err()); + let err2: Result = Decodable::decode(&mut d_bad2); + assert!(err2.is_err()); + } + + #[test] + fn array() { + #[derive(RustcEncodable, RustcDecodable, 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(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>, + 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, + 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(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, } + #[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 = 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 = 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 } + + 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 } + + 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> } + + 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 } + #[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 } + #[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> } + #[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); + } +} diff --git a/src/lib.rs b/src/lib.rs index da2ddbc..0196fbc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,26 +39,25 @@ #![deny(missing_docs)] #![cfg_attr(test, deny(warnings))] -extern crate rustc_serialize; +#[cfg(feature = "rustc-serialize")] extern crate rustc_serialize; use std::collections::BTreeMap; use std::str::FromStr; use std::string; pub use parser::{Parser, ParserError}; -pub use serialization::{Encoder, encode, encode_str}; -pub use serialization::{Decoder, decode, decode_str}; -pub use serialization::Error; -pub use serialization::Error::{NeedsKey, NoValue}; -pub use serialization::Error::{InvalidMapKeyLocation, InvalidMapKeyType}; -pub use serialization::{DecodeError, DecodeErrorKind}; -pub use serialization::DecodeErrorKind::{ApplicationError, ExpectedField}; -pub use serialization::DecodeErrorKind::{ExpectedMapElement, ExpectedMapKey, NoEnumVariants}; -pub use serialization::DecodeErrorKind::{ExpectedType, NilTooLong}; + +#[cfg(any(feature = "rustc-serialize", feature = "serde"))] +pub use self::encoder::{Encoder, Error, encode, encode_str}; +#[cfg(any(feature = "rustc-serialize", feature = "serde"))] +pub use self::decoder::{Decoder, DecodeError, DecodeErrorKind, decode, decode_str}; mod parser; mod display; -mod serialization; +#[cfg(any(feature = "rustc-serialize", feature = "serde"))] +mod encoder; +#[cfg(any(feature = "rustc-serialize", feature = "serde"))] +mod decoder; /// Representation of a TOML value. #[derive(PartialEq, Clone, Debug)] @@ -203,37 +202,6 @@ impl Value { } } -impl rustc_serialize::Encodable for Value { - fn encode(&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(()) - }) - } - } - } -} - impl FromStr for Value { type Err = Vec; fn from_str(s: &str) -> Result> { @@ -292,22 +260,4 @@ mod tests { let foo = value.lookup("values.str.foo"); assert!(foo.is_none()); } - - #[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); - } } diff --git a/src/serialization.rs b/src/serialization.rs deleted file mode 100644 index 565b4e7..0000000 --- a/src/serialization.rs +++ /dev/null @@ -1,1279 +0,0 @@ -use std::collections::BTreeMap; -use std::mem; -use std::fmt; -use std::error::Error as StdError; - -use rustc_serialize; -use {Value, Parser}; -use Table as TomlTable; -use Value::{Table, Array, Integer, Float, Boolean}; - -use self::EncoderState::{Start, NextKey, NextArray, NextMapKey}; -use self::Error::{NeedsKey, NoValue, InvalidMapKeyLocation, InvalidMapKeyType}; -use self::DecodeErrorKind::{ApplicationError, ExpectedField, ExpectedType, ExpectedMapKey}; -use self::DecodeErrorKind::{ExpectedMapElement, NoEnumVariants, NilTooLong}; - -/// 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: TomlTable, - state: EncoderState, -} - -/// A structure to transform TOML values into Rust values. -/// -/// This decoder implements the serialization `Decoder` interface, allowing -/// `Decodable` types to be generated by this decoder. The input is any -/// arbitrary TOML value. -pub struct Decoder { - /// The TOML value left over after decoding. This can be used to inspect - /// whether fields were decoded or not. - pub toml: Option, - cur_field: Option, -} - -/// 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, -} - -/// Description for errors which can occur while decoding a type. -#[derive(PartialEq, Debug)] -pub struct DecodeError { - /// Field that this error applies to. - pub field: Option, - /// The type of error which occurred while decoding, - pub kind: DecodeErrorKind, -} - -/// Enumeration of possible errors which can occur while decoding a structure. -#[derive(PartialEq, Debug)] -pub enum DecodeErrorKind { - /// An error flagged by the application, e.g. value out of range - ApplicationError(String), - /// A field was expected, but none was found. - ExpectedField(/* type */ &'static str), - /// A field was found, but it had the wrong type. - ExpectedType(/* expected */ &'static str, /* found */ &'static str), - /// The nth map key was expected, but none was found. - ExpectedMapKey(usize), - /// The nth map element was expected, but none was found. - ExpectedMapElement(usize), - /// An enum decoding was requested, but no variants were supplied - NoEnumVariants, - /// The unit type was being decoded, but a non-zero length string was found - NilTooLong -} - -#[derive(PartialEq)] -enum EncoderState { - Start, - NextKey(String), - NextArray(Vec), - NextMapKey, -} - -/// 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. -pub fn encode(t: &T) -> Value { - let mut e = Encoder::new(); - t.encode(&mut e).unwrap(); - 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. -pub fn encode_str(t: &T) -> String { - format!("{}", encode(t)) -} - -impl Encoder { - /// Constructs a new encoder which will emit to the given output stream. - pub fn new() -> Encoder { - Encoder { state: Start, toml: BTreeMap::new() } - } - - fn emit_value(&mut self, v: Value) -> Result<(), Error> { - match mem::replace(&mut self.state, Start) { - NextKey(key) => { self.toml.insert(key, v); Ok(()) } - NextArray(mut vec) => { - // TODO: validate types - vec.push(v); - self.state = NextArray(vec); - Ok(()) - } - NextMapKey => { - match v { - Value::String(s) => { self.state = NextKey(s); Ok(()) } - _ => Err(InvalidMapKeyType) - } - } - _ => Err(NeedsKey) - } - } -} - -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(Integer(v)) - } - fn emit_bool(&mut self, v: bool) -> Result<(), Error> { - self.emit_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(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(&mut self, _name: &str, f: F) - -> Result<(), Error> - where F: FnOnce(&mut Encoder) -> Result<(), Error> - { - f(self) - } - fn emit_enum_variant(&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(&mut self, _a_idx: usize, f: F) - -> Result<(), Error> - where F: FnOnce(&mut Encoder) -> Result<(), Error> - { - f(self) - } - fn emit_enum_struct_variant(&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(&mut self, - _f_name: &str, - _f_idx: usize, - _f: F) - -> Result<(), Error> - where F: FnOnce(&mut Encoder) -> Result<(), Error> - { - panic!() - } - fn emit_struct(&mut self, _name: &str, _len: usize, f: F) - -> Result<(), Error> - where F: FnOnce(&mut Encoder) -> Result<(), Error> - { - match mem::replace(&mut self.state, Start) { - NextKey(key) => { - let mut nested = Encoder::new(); - try!(f(&mut nested)); - self.toml.insert(key, Table(nested.toml)); - Ok(()) - } - NextArray(mut arr) => { - let mut nested = Encoder::new(); - try!(f(&mut nested)); - arr.push(Table(nested.toml)); - self.state = NextArray(arr); - Ok(()) - } - Start => f(self), - NextMapKey => Err(InvalidMapKeyLocation), - } - } - fn emit_struct_field(&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, - NextKey(format!("{}", f_name))); - try!(f(self)); - if self.state != Start { - return Err(NoValue) - } - self.state = old; - Ok(()) - } - fn emit_tuple(&mut self, len: usize, f: F) - -> Result<(), Error> - where F: FnOnce(&mut Encoder) -> Result<(), Error> - { - self.emit_seq(len, f) - } - fn emit_tuple_arg(&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(&mut self, _name: &str, _len: usize, _f: F) - -> Result<(), Error> - where F: FnOnce(&mut Encoder) -> Result<(), Error> - { - unimplemented!() - } - fn emit_tuple_struct_arg(&mut self, _f_idx: usize, _f: F) - -> Result<(), Error> - where F: FnOnce(&mut Encoder) -> Result<(), Error> - { - unimplemented!() - } - fn emit_option(&mut self, f: F) - -> Result<(), Error> - where F: FnOnce(&mut Encoder) -> Result<(), Error> - { - f(self) - } - fn emit_option_none(&mut self) -> Result<(), Error> { - match mem::replace(&mut self.state, Start) { - Start => unreachable!(), - NextKey(_) => Ok(()), - NextArray(..) => panic!("how to encode None in an array?"), - NextMapKey => Err(InvalidMapKeyLocation), - } - } - fn emit_option_some(&mut self, f: F) - -> Result<(), Error> - where F: FnOnce(&mut Encoder) -> Result<(), Error> - { - f(self) - } - fn emit_seq(&mut self, _len: usize, f: F) - -> Result<(), Error> - where F: FnOnce(&mut Encoder) -> Result<(), Error> - { - let old = mem::replace(&mut self.state, NextArray(Vec::new())); - try!(f(self)); - match mem::replace(&mut self.state, old) { - NextArray(v) => self.emit_value(Array(v)), - _ => unreachable!(), - } - } - fn emit_seq_elt(&mut self, _idx: usize, f: F) - -> Result<(), Error> - where F: FnOnce(&mut Encoder) -> Result<(), Error> - { - f(self) - } - fn emit_map(&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(&mut self, _idx: usize, f: F) - -> Result<(), Error> - where F: FnOnce(&mut Encoder) -> Result<(), Error> - { - match mem::replace(&mut self.state, NextMapKey) { - Start => {} - _ => return Err(InvalidMapKeyLocation), - } - try!(f(self)); - match self.state { - NextKey(_) => Ok(()), - _ => Err(InvalidMapKeyLocation), - } - } - fn emit_map_elt_val(&mut self, _idx: usize, f: F) - -> Result<(), Error> - where F: FnOnce(&mut Encoder) -> Result<(), Error> - { - f(self) - } -} - -/// Decodes a TOML value into a decodable type. -/// -/// This function will consume the given TOML value and attempt to decode it -/// into the type specified. If decoding fails, `None` will be returned. If a -/// finer-grained error is desired, then it is recommended to use `Decodable` -/// directly. -pub fn decode(toml: Value) - -> Option -{ - rustc_serialize::Decodable::decode(&mut Decoder::new(toml)).ok() -} - -/// Decodes a string into a toml-encoded value. -/// -/// This function will parse the given string into a TOML value, and then parse -/// the TOML value into the desired type. If any error occurs `None` is return. -/// If more fine-grained errors are desired, these steps should be driven -/// manually. -pub fn decode_str(s: &str) - -> Option -{ - Parser::new(s).parse().and_then(|t| decode(Table(t))) -} - -impl Decoder { - /// Creates a new decoder, consuming the TOML value to decode. - /// - /// This decoder can be passed to the `Decodable` methods or driven - /// manually. - pub fn new(toml: Value) -> Decoder { - Decoder { toml: Some(toml), cur_field: None } - } - - fn sub_decoder(&self, toml: Option, field: &str) -> Decoder { - Decoder { - toml: toml, - cur_field: if field.len() == 0 { - self.cur_field.clone() - } else { - match self.cur_field { - None => Some(format!("{}", field)), - Some(ref s) => Some(format!("{}.{}", s, field)) - } - } - } - } - - fn err(&self, kind: DecodeErrorKind) -> DecodeError { - DecodeError { - field: self.cur_field.clone(), - kind: kind, - } - } - - fn mismatch(&self, expected: &'static str, - found: &Option) -> DecodeError{ - match *found { - Some(ref val) => self.err(ExpectedType(expected, val.type_str())), - None => self.err(ExpectedField(expected)), - } - } -} - -impl rustc_serialize::Decoder for Decoder { - type Error = DecodeError; - fn read_nil(&mut self) -> Result<(), DecodeError> { - match self.toml { - Some(Value::String(ref s)) if s.len() == 0 => {} - Some(Value::String(..)) => return Err(self.err(NilTooLong)), - ref found => return Err(self.mismatch("string", found)), - } - self.toml.take(); - Ok(()) - } - fn read_usize(&mut self) -> Result { - self.read_i64().map(|i| i as usize) - } - fn read_u64(&mut self) -> Result { - self.read_i64().map(|i| i as u64) - } - fn read_u32(&mut self) -> Result { - self.read_i64().map(|i| i as u32) - } - fn read_u16(&mut self) -> Result { - self.read_i64().map(|i| i as u16) - } - fn read_u8(&mut self) -> Result { - self.read_i64().map(|i| i as u8) - } - fn read_isize(&mut self) -> Result { - self.read_i64().map(|i| i as isize) - } - fn read_i64(&mut self) -> Result { - match self.toml { - Some(Integer(i)) => { self.toml.take(); Ok(i) } - ref found => Err(self.mismatch("integer", found)), - } - } - fn read_i32(&mut self) -> Result { - self.read_i64().map(|i| i as i32) - } - fn read_i16(&mut self) -> Result { - self.read_i64().map(|i| i as i16) - } - fn read_i8(&mut self) -> Result { - self.read_i64().map(|i| i as i8) - } - fn read_bool(&mut self) -> Result { - match self.toml { - Some(Boolean(b)) => { self.toml.take(); Ok(b) } - ref found => Err(self.mismatch("bool", found)), - } - } - fn read_f64(&mut self) -> Result { - match self.toml { - Some(Float(f)) => Ok(f), - ref found => Err(self.mismatch("float", found)), - } - } - fn read_f32(&mut self) -> Result { - self.read_f64().map(|f| f as f32) - } - fn read_char(&mut self) -> Result { - let ch = match self.toml { - Some(Value::String(ref s)) if s.chars().count() == 1 => - s.chars().next().unwrap(), - ref found => return Err(self.mismatch("string", found)), - }; - self.toml.take(); - Ok(ch) - } - fn read_str(&mut self) -> Result { - match self.toml.take() { - Some(Value::String(s)) => Ok(s), - found => { - let err = Err(self.mismatch("string", &found)); - self.toml = found; - err - } - } - } - - // Compound types: - fn read_enum(&mut self, _name: &str, f: F) - -> Result - where F: FnOnce(&mut Decoder) -> Result - { - f(self) - } - - fn read_enum_variant(&mut self, names: &[&str], mut f: F) - -> Result - where F: FnMut(&mut Decoder, usize) -> Result - { - let mut first_error = None; - for i in 0..names.len() { - let mut d = self.sub_decoder(self.toml.clone(), ""); - match f(&mut d, i) { - Ok(t) => { self.toml = d.toml; return Ok(t) } - Err(e) => { - if first_error.is_none() { - first_error = Some(e); - } - } - } - } - Err(first_error.unwrap_or_else(|| self.err(NoEnumVariants))) - } - fn read_enum_variant_arg(&mut self, _a_idx: usize, f: F) - -> Result - where F: FnOnce(&mut Decoder) -> Result - { - f(self) - } - - fn read_enum_struct_variant(&mut self, _names: &[&str], _f: F) - -> Result - where F: FnMut(&mut Decoder, usize) -> Result - { - panic!() - } - fn read_enum_struct_variant_field(&mut self, - _f_name: &str, - _f_idx: usize, - _f: F) - -> Result - where F: FnOnce(&mut Decoder) -> Result - { - panic!() - } - - fn read_struct(&mut self, _s_name: &str, _len: usize, f: F) - -> Result - where F: FnOnce(&mut Decoder) -> Result - { - match self.toml { - Some(Table(..)) => { - let ret = try!(f(self)); - match self.toml { - Some(Table(ref t)) if t.len() == 0 => {} - _ => return Ok(ret) - } - self.toml.take(); - Ok(ret) - } - ref found => Err(self.mismatch("table", found)), - } - } - fn read_struct_field(&mut self, f_name: &str, _f_idx: usize, f: F) - -> Result - where F: FnOnce(&mut Decoder) -> Result - { - let field = format!("{}", f_name); - let toml = match self.toml { - Some(Table(ref mut table)) => { - table.remove(&field) - .or_else(|| table.remove(&f_name.replace("_", "-"))) - }, - ref found => return Err(self.mismatch("table", found)), - }; - let mut d = self.sub_decoder(toml, f_name); - let ret = try!(f(&mut d)); - match d.toml { - Some(value) => match self.toml { - Some(Table(ref mut table)) => { table.insert(field, value); } - _ => {} - }, - None => {} - } - Ok(ret) - } - - fn read_tuple(&mut self, tuple_len: usize, f: F) - -> Result - where F: FnOnce(&mut Decoder) -> Result - { - self.read_seq(move |d, len| { - assert!(len == tuple_len, - "expected tuple of length `{}`, found tuple \ - of length `{}`", tuple_len, len); - f(d) - }) - } - fn read_tuple_arg(&mut self, a_idx: usize, f: F) - -> Result - where F: FnOnce(&mut Decoder) -> Result - { - self.read_seq_elt(a_idx, f) - } - - fn read_tuple_struct(&mut self, _s_name: &str, _len: usize, _f: F) - -> Result - where F: FnOnce(&mut Decoder) -> Result - { - panic!() - } - fn read_tuple_struct_arg(&mut self, _a_idx: usize, _f: F) - -> Result - where F: FnOnce(&mut Decoder) -> Result - { - panic!() - } - - // Specialized types: - fn read_option(&mut self, mut f: F) - -> Result - where F: FnMut(&mut Decoder, bool) -> Result - { - match self.toml { - Some(..) => f(self, true), - None => f(self, false), - } - } - - fn read_seq(&mut self, f: F) - -> Result - where F: FnOnce(&mut Decoder, usize) -> Result - { - let len = match self.toml { - Some(Array(ref arr)) => arr.len(), - None => 0, - ref found => return Err(self.mismatch("array", found)), - }; - let ret = try!(f(self, len)); - match self.toml { - Some(Array(ref mut arr)) => { - arr.retain(|slot| slot.as_integer() != Some(0)); - if arr.len() != 0 { return Ok(ret) } - } - _ => return Ok(ret) - } - self.toml.take(); - Ok(ret) - } - fn read_seq_elt(&mut self, idx: usize, f: F) - -> Result - where F: FnOnce(&mut Decoder) -> Result - { - let toml = match self.toml { - Some(Array(ref mut arr)) => mem::replace(&mut arr[idx], Integer(0)), - ref found => return Err(self.mismatch("array", found)), - }; - let mut d = self.sub_decoder(Some(toml), ""); - let ret = try!(f(&mut d)); - match d.toml { - Some(toml) => match self.toml { - Some(Array(ref mut arr)) => arr[idx] = toml, - _ => {} - }, - _ => {} - } - Ok(ret) - } - - fn read_map(&mut self, f: F) - -> Result - where F: FnOnce(&mut Decoder, usize) -> Result - { - let len = match self.toml { - Some(Table(ref table)) => table.len(), - ref found => return Err(self.mismatch("table", found)), - }; - let ret = try!(f(self, len)); - self.toml.take(); - Ok(ret) - } - fn read_map_elt_key(&mut self, idx: usize, f: F) - -> Result - where F: FnOnce(&mut Decoder) -> Result - { - match self.toml { - Some(Table(ref table)) => { - match table.iter().skip(idx).next() { - Some((key, _)) => { - let val = Value::String(format!("{}", key)); - f(&mut self.sub_decoder(Some(val), &**key)) - } - None => Err(self.err(ExpectedMapKey(idx))), - } - } - ref found => Err(self.mismatch("table", found)), - } - } - fn read_map_elt_val(&mut self, idx: usize, f: F) - -> Result - where F: FnOnce(&mut Decoder) -> Result - { - match self.toml { - Some(Table(ref table)) => { - match table.iter().skip(idx).next() { - Some((_, value)) => { - // XXX: this shouldn't clone - f(&mut self.sub_decoder(Some(value.clone()), "")) - } - None => Err(self.err(ExpectedMapElement(idx))), - } - } - ref found => Err(self.mismatch("table", found)), - } - } - - fn error(&mut self, err: &str) -> DecodeError { - DecodeError { - field: self.cur_field.clone(), - kind: ApplicationError(format!("{}", err)) - } - } -} - -impl fmt::Display for DecodeError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - try!(match self.kind { - ApplicationError(ref err) => { - write!(f, "{}", err) - } - ExpectedField(expected_type) => { - if expected_type == "table" { - write!(f, "expected a section") - } else { - write!(f, "expected a value of type `{}`", expected_type) - } - } - ExpectedType(expected, found) => { - fn humanize(s: &str) -> String { - if s == "section" { - format!("a section") - } else { - format!("a value of type `{}`", s) - } - } - write!(f, "expected {}, but found {}", - humanize(expected), - humanize(found)) - } - ExpectedMapKey(idx) => { - write!(f, "expected at least {} keys", idx + 1) - } - ExpectedMapElement(idx) => { - write!(f, "expected at least {} elements", idx + 1) - } - NoEnumVariants => { - write!(f, "expected an enum variant to decode to") - } - NilTooLong => { - write!(f, "expected 0-length string") - } - }); - match self.field { - Some(ref s) => { - write!(f, " for the key `{}`", s) - } - None => Ok(()) - } - } -} - -impl StdError for DecodeError { - fn description(&self) -> &str { - match self.kind { - ApplicationError(ref s) => &**s, - ExpectedField(..) => "expected a field", - ExpectedType(..) => "expected a type", - ExpectedMapKey(..) => "expected a map key", - ExpectedMapElement(..) => "expected a map element", - NoEnumVariants => "no enum variants to decode to", - NilTooLong => "nonzero length string representing nil", - } - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - NeedsKey => write!(f, "need a key to encode"), - NoValue => write!(f, "not value to emit for a previous key"), - InvalidMapKeyLocation => write!(f, "a map cannot be emitted at \ - this location"), - InvalidMapKeyType => write!(f, "only strings can be used as \ - key types"), - } - } -} - -impl StdError for Error { - fn description(&self) -> &str { "TOML encoding error" } -} - -#[cfg(test)] -mod tests { - use std::collections::{BTreeMap, HashSet}; - use rustc_serialize::{self, Encodable, Decodable}; - - use super::{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: &mut D) -> Result { - 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 = Decodable::decode(&mut d_bad1); - assert!(err1.is_err()); - let err2: Result = Decodable::decode(&mut d_bad2); - assert!(err2.is_err()); - } - - #[test] - fn array() { - #[derive(RustcEncodable, RustcDecodable, 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(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>, - 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, - 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(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, } - #[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 = 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 = 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 } - - 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 } - - 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> } - - 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 } - #[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 } - #[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> } - #[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()); - } -}