Modularize rustc-serialize support
* Add rustc-serialize as a default feature * Make room for for serde support
This commit is contained in:
parent
cd33b87bf9
commit
2d49247b92
|
@ -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: |
|
||||
|
|
|
@ -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"
|
||||
|
|
205
src/decoder/mod.rs
Normal file
205
src/decoder/mod.rs
Normal file
|
@ -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<Value>,
|
||||
cur_field: Option<String>,
|
||||
}
|
||||
|
||||
/// 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<String>,
|
||||
/// 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<T: ::rustc_serialize::Decodable>(toml: Value) -> Option<T> {
|
||||
::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<T: ::serde::Deserialize>(toml: Value) -> Option<T> {
|
||||
::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<T: ::rustc_serialize::Decodable>(s: &str) -> Option<T> {
|
||||
::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<T: ::serde::Deserialize>(s: &str) -> Option<T> {
|
||||
::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<Value>, 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<Value>) -> 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",
|
||||
}
|
||||
}
|
||||
}
|
311
src/decoder/rustc_serialize.rs
Normal file
311
src/decoder/rustc_serialize.rs
Normal file
|
@ -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<usize, DecodeError> {
|
||||
self.read_i64().map(|i| i as usize)
|
||||
}
|
||||
fn read_u64(&mut self) -> Result<u64, DecodeError> {
|
||||
self.read_i64().map(|i| i as u64)
|
||||
}
|
||||
fn read_u32(&mut self) -> Result<u32, DecodeError> {
|
||||
self.read_i64().map(|i| i as u32)
|
||||
}
|
||||
fn read_u16(&mut self) -> Result<u16, DecodeError> {
|
||||
self.read_i64().map(|i| i as u16)
|
||||
}
|
||||
fn read_u8(&mut self) -> Result<u8, DecodeError> {
|
||||
self.read_i64().map(|i| i as u8)
|
||||
}
|
||||
fn read_isize(&mut self) -> Result<isize, DecodeError> {
|
||||
self.read_i64().map(|i| i as isize)
|
||||
}
|
||||
fn read_i64(&mut self) -> Result<i64, DecodeError> {
|
||||
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<i32, DecodeError> {
|
||||
self.read_i64().map(|i| i as i32)
|
||||
}
|
||||
fn read_i16(&mut self) -> Result<i16, DecodeError> {
|
||||
self.read_i64().map(|i| i as i16)
|
||||
}
|
||||
fn read_i8(&mut self) -> Result<i8, DecodeError> {
|
||||
self.read_i64().map(|i| i as i8)
|
||||
}
|
||||
fn read_bool(&mut self) -> Result<bool, DecodeError> {
|
||||
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<f64, DecodeError> {
|
||||
match self.toml {
|
||||
Some(Value::Float(f)) => Ok(f),
|
||||
ref found => Err(self.mismatch("float", found)),
|
||||
}
|
||||
}
|
||||
fn read_f32(&mut self) -> Result<f32, DecodeError> {
|
||||
self.read_f64().map(|f| f as f32)
|
||||
}
|
||||
fn read_char(&mut self) -> Result<char, DecodeError> {
|
||||
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<String, DecodeError> {
|
||||
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<T, F>(&mut self, _name: &str, f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
|
||||
{
|
||||
f(self)
|
||||
}
|
||||
|
||||
fn read_enum_variant<T, F>(&mut self, names: &[&str], mut f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnMut(&mut Decoder, usize) -> Result<T, DecodeError>
|
||||
{
|
||||
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<T, F>(&mut self, _a_idx: usize, f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
|
||||
{
|
||||
f(self)
|
||||
}
|
||||
|
||||
fn read_enum_struct_variant<T, F>(&mut self, _names: &[&str], _f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnMut(&mut Decoder, usize) -> Result<T, DecodeError>
|
||||
{
|
||||
panic!()
|
||||
}
|
||||
fn read_enum_struct_variant_field<T, F>(&mut self,
|
||||
_f_name: &str,
|
||||
_f_idx: usize,
|
||||
_f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
|
||||
{
|
||||
panic!()
|
||||
}
|
||||
|
||||
fn read_struct<T, F>(&mut self, _s_name: &str, _len: usize, f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
|
||||
{
|
||||
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<T, F>(&mut self, f_name: &str, _f_idx: usize, f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
|
||||
{
|
||||
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<T, F>(&mut self, tuple_len: usize, f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
|
||||
{
|
||||
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<T, F>(&mut self, a_idx: usize, f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
|
||||
{
|
||||
self.read_seq_elt(a_idx, f)
|
||||
}
|
||||
|
||||
fn read_tuple_struct<T, F>(&mut self, _s_name: &str, _len: usize, _f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
|
||||
{
|
||||
panic!()
|
||||
}
|
||||
fn read_tuple_struct_arg<T, F>(&mut self, _a_idx: usize, _f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
|
||||
{
|
||||
panic!()
|
||||
}
|
||||
|
||||
// Specialized types:
|
||||
fn read_option<T, F>(&mut self, mut f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnMut(&mut Decoder, bool) -> Result<T, DecodeError>
|
||||
{
|
||||
match self.toml {
|
||||
Some(..) => f(self, true),
|
||||
None => f(self, false),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_seq<T, F>(&mut self, f: F) -> Result<T, DecodeError>
|
||||
where F: FnOnce(&mut Decoder, usize) -> Result<T, DecodeError>
|
||||
{
|
||||
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<T, F>(&mut self, idx: usize, f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
|
||||
{
|
||||
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<T, F>(&mut self, f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnOnce(&mut Decoder, usize) -> Result<T, DecodeError>
|
||||
{
|
||||
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<T, F>(&mut self, idx: usize, f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
|
||||
{
|
||||
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<T, F>(&mut self, idx: usize, f: F)
|
||||
-> Result<T, DecodeError>
|
||||
where F: FnOnce(&mut Decoder) -> Result<T, DecodeError>
|
||||
{
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
210
src/encoder/mod.rs
Normal file
210
src/encoder/mod.rs
Normal file
|
@ -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" }
|
||||
}
|
689
src/encoder/rustc_serialize.rs
Normal file
689
src/encoder/rustc_serialize.rs
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
70
src/lib.rs
70
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<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(())
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Value {
|
||||
type Err = Vec<ParserError>;
|
||||
fn from_str(s: &str) -> Result<Value, Vec<ParserError>> {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
1279
src/serialization.rs
1279
src/serialization.rs
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue