Rewrite crate with serde support from ground up

This commit completely rewrites this crate from the ground up,
supporting serde at the lowest levels as I believe serde support was
intended to do. This is a major change from the previous versions of
this crate, with a summary of changes being:

* Serialization directly to TOML is now supported without going through
  a `Value` first.

* Deserialization directly from TOML is now supported without going
  through a `Value`. Note that due to the TOML format some values still
  are buffered in intermediate memory, but overall this should be at a
  minimum now.

* The API of `Value` was overhauled to match the API of
  `serde_json::Value`. The changes here were to:

  * Add `is_*` accessors
  * Add `get` and `get_mut` for one-field lookups.
  * Implement panicking lookups through `Index`

  The old `index` methods are now gone in favor of `get` and `Index`
  implementations.

* A `Datetime` type has been added to represent a TOML datetime in a
  first-class fashion. Currently this type provides no accessors other
  than a `Display` implementation, but the idea is that this will grow
  support over time for decomposing the date.

* Support for the `rustc-serialize` crate has been dropped, that'll stay
  on the 0.2 and 0.1 release trains.

* This crate no longer supports the detection of unused fields, for that though
  you can use the `serde_ignored` crate on crates.io
This commit is contained in:
Alex Crichton 2017-01-29 16:53:20 -08:00
parent 4c358121bb
commit e256931e9b
32 changed files with 5535 additions and 5521 deletions

View file

@ -7,11 +7,7 @@ sudo: false
before_script: before_script:
- pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH
script: script:
- cargo build --verbose - cargo test
- cargo build --verbose --no-default-features
- cargo build --verbose --features serde --no-default-features
- cargo test --verbose --features serde
- cargo test --verbose --manifest-path serde-tests/Cargo.toml
- rustdoc --test README.md -L target - rustdoc --test README.md -L target
- cargo doc --no-deps - cargo doc --no-deps
after_success: after_success:

View file

@ -1,5 +1,4 @@
[package] [package]
name = "toml" name = "toml"
version = "0.2.1" version = "0.2.1"
authors = ["Alex Crichton <alex@alexcrichton.com>"] authors = ["Alex Crichton <alex@alexcrichton.com>"]
@ -16,11 +15,8 @@ facilitate deserializing and serializing Rust structures.
""" """
[dependencies] [dependencies]
rustc-serialize = { optional = true, version = "0.3.0" } serde = "0.9.6"
serde = { optional = true, version = "0.8" }
[features]
default = ["rustc-serialize"]
[dev-dependencies] [dev-dependencies]
rustc-serialize = "0.3" serde_derive = "0.9"
serde_json = "0.9"

View file

@ -1,4 +1,4 @@
//! An example showing off the usage of `RustcDecodable` to automatically decode //! An example showing off the usage of `Deserialize` to automatically decode
//! TOML into a Rust `struct` //! TOML into a Rust `struct`
//! //!
//! Note that this works similarly with `serde` as well. //! Note that this works similarly with `serde` as well.
@ -6,11 +6,12 @@
#![deny(warnings)] #![deny(warnings)]
extern crate toml; extern crate toml;
extern crate rustc_serialize; #[macro_use]
extern crate serde_derive;
/// This is what we're going to decode into. Each field is optional, meaning /// This is what we're going to decode into. Each field is optional, meaning
/// that it doesn't have to be present in TOML. /// that it doesn't have to be present in TOML.
#[derive(Debug, RustcDecodable)] #[derive(Debug, Deserialize)]
struct Config { struct Config {
global_string: Option<String>, global_string: Option<String>,
global_integer: Option<u64>, global_integer: Option<u64>,
@ -22,13 +23,13 @@ struct Config {
/// table. /// table.
/// ///
/// Again, each field is optional, meaning they don't have to be present. /// Again, each field is optional, meaning they don't have to be present.
#[derive(Debug, RustcDecodable)] #[derive(Debug, Deserialize)]
struct ServerConfig { struct ServerConfig {
ip: Option<String>, ip: Option<String>,
port: Option<u64>, port: Option<u64>,
} }
#[derive(Debug, RustcDecodable)] #[derive(Debug, Deserialize)]
struct PeerConfig { struct PeerConfig {
ip: Option<String>, ip: Option<String>,
port: Option<u64>, port: Option<u64>,
@ -51,11 +52,6 @@ fn main() {
ip = "127.0.0.1" ip = "127.0.0.1"
"#; "#;
// Use the `decode_str` convenience here to decode a TOML string directly let decoded: Config = toml::from_str(toml_str).unwrap();
// into the `Config` struct.
//
// Note that the errors reported here won't necessarily be the best, but you
// can get higher fidelity errors working with `toml::Parser` directly.
let decoded: Config = toml::decode_str(toml_str).unwrap();
println!("{:#?}", decoded); println!("{:#?}", decoded);
} }

View file

@ -1,57 +1,51 @@
#![deny(warnings)] #![deny(warnings)]
extern crate toml; extern crate toml;
extern crate rustc_serialize; extern crate serde_json;
use std::fs::File; use std::fs::File;
use std::env; use std::env;
use std::io; use std::io;
use std::io::prelude::*; use std::io::prelude::*;
use toml::Value; use toml::Value as Toml;
use rustc_serialize::json::Json; use serde_json::Value as Json;
fn main() { fn main() {
let mut args = env::args(); let mut args = env::args();
let mut input = String::new(); let mut input = String::new();
let filename = if args.len() > 1 { if args.len() > 1 {
let name = args.nth(1).unwrap(); let name = args.nth(1).unwrap();
File::open(&name).and_then(|mut f| { File::open(&name).and_then(|mut f| {
f.read_to_string(&mut input) f.read_to_string(&mut input)
}).unwrap(); }).unwrap();
name
} else { } else {
io::stdin().read_to_string(&mut input).unwrap(); io::stdin().read_to_string(&mut input).unwrap();
"<stdin>".to_string() }
};
let mut parser = toml::Parser::new(&input); match input.parse() {
let toml = match parser.parse() { Ok(toml) => {
Some(toml) => toml, let json = convert(toml);
None => { println!("{}", serde_json::to_string_pretty(&json).unwrap());
for err in &parser.errors {
let (loline, locol) = parser.to_linecol(err.lo);
let (hiline, hicol) = parser.to_linecol(err.hi);
println!("{}:{}:{}-{}:{} error: {}",
filename, loline, locol, hiline, hicol, err.desc);
} }
return Err(error) => println!("failed to parse TOML: {}", error),
} }
};
let json = convert(Value::Table(toml));
println!("{}", json.pretty());
} }
fn convert(toml: Value) -> Json { fn convert(toml: Toml) -> Json {
match toml { match toml {
Value::String(s) => Json::String(s), Toml::String(s) => Json::String(s),
Value::Integer(i) => Json::I64(i), Toml::Integer(i) => Json::Number(i.into()),
Value::Float(f) => Json::F64(f), Toml::Float(f) => {
Value::Boolean(b) => Json::Boolean(b), let n = serde_json::Number::from_f64(f)
Value::Array(arr) => Json::Array(arr.into_iter().map(convert).collect()), .expect("float infinite and nan not allowed");
Value::Table(table) => Json::Object(table.into_iter().map(|(k, v)| { Json::Number(n)
}
Toml::Boolean(b) => Json::Bool(b),
Toml::Array(arr) => Json::Array(arr.into_iter().map(convert).collect()),
Toml::Table(table) => Json::Object(table.into_iter().map(|(k, v)| {
(k, convert(v)) (k, convert(v))
}).collect()), }).collect()),
Value::Datetime(dt) => Json::String(dt), Toml::Datetime(dt) => Json::String(dt.to_string()),
} }
} }

View file

@ -1,20 +0,0 @@
[package]
name = "serde-tests"
version = "0.1.0"
authors = ["Alex Crichton <alex@alexcrichton.com>"]
build = "build.rs"
[dependencies]
serde = "0.8"
toml = { path = "..", features = ["serde"] }
[build-dependencies]
serde_codegen = "0.8"
[lib]
name = "serde_tests"
path = "lib.rs"
[[test]]
name = "serde"
path = "test.rs"

View file

@ -1,13 +0,0 @@
extern crate serde_codegen;
use std::env;
use std::path::Path;
fn main() {
let out_dir = env::var_os("OUT_DIR").unwrap();
let src = Path::new("test.rs.in");
let dst = Path::new(&out_dir).join("test.rs");
serde_codegen::expand(&src, &dst).unwrap();
}

View file

@ -1 +0,0 @@
// intentionally blank

View file

@ -1 +0,0 @@
include!(concat!(env!("OUT_DIR"), "/test.rs"));

View file

@ -1,482 +0,0 @@
extern crate serde;
extern crate toml;
use std::collections::{BTreeMap, HashSet};
use serde::{Deserialize, Serialize, Deserializer};
use toml::{Encoder, Decoder, DecodeError};
use toml::Value;
use toml::Value::{Table, Integer, Array, Float};
macro_rules! t {
($e:expr) => (match $e {
Ok(t) => t,
Err(e) => panic!("{} failed with {}", stringify!($e), e),
})
}
macro_rules! encode( ($t:expr) => ({
let mut e = Encoder::new();
t!($t.serialize(&mut e));
e.toml
}) );
macro_rules! decode( ($t:expr) => ({
let mut d = Decoder::new($t);
t!(Deserialize::deserialize(&mut d))
}) );
macro_rules! map( ($($k:ident, $v:expr),*) => ({
let mut _m = BTreeMap::new();
$(_m.insert(stringify!($k).to_string(), $v);)*
_m
}) );
#[test]
fn smoke() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Foo { a: isize }
let v = Foo { a: 2 };
assert_eq!(encode!(v), map! { a, Integer(2) });
assert_eq!(v, decode!(Table(encode!(v))));
}
#[test]
fn smoke_hyphen() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Foo { a_b: isize }
let v = Foo { a_b: 2 };
assert_eq!(encode!(v), map! { a_b, Integer(2) });
assert_eq!(v, decode!(Table(encode!(v))));
let mut m = BTreeMap::new();
m.insert("a-b".to_string(), Integer(2));
assert_eq!(v, decode!(Table(encode!(v))));
}
#[test]
fn nested() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Foo { a: isize, b: Bar }
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Bar { a: String }
let v = Foo { a: 2, b: Bar { a: "test".to_string() } };
assert_eq!(encode!(v),
map! {
a, Integer(2),
b, Table(map! {
a, Value::String("test".to_string())
})
});
assert_eq!(v, decode!(Table(encode!(v))));
}
#[test]
fn application_decode_error() {
#[derive(PartialEq, Debug)]
struct Range10(usize);
impl Deserialize for Range10 {
fn deserialize<D: Deserializer>(d: &mut D) -> Result<Range10, D::Error> {
let x: usize = try!(Deserialize::deserialize(d));
if x > 10 {
Err(serde::de::Error::custom("more than 10"))
} else {
Ok(Range10(x))
}
}
}
let mut d_good = Decoder::new(Integer(5));
let mut d_bad1 = Decoder::new(Value::String("not an isize".to_string()));
let mut d_bad2 = Decoder::new(Integer(11));
assert_eq!(Ok(Range10(5)), Deserialize::deserialize(&mut d_good));
let err1: Result<Range10, _> = Deserialize::deserialize(&mut d_bad1);
assert!(err1.is_err());
let err2: Result<Range10, _> = Deserialize::deserialize(&mut d_bad2);
assert!(err2.is_err());
}
#[test]
fn array() {
#[derive(Serialize, Deserialize, 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(Serialize, Deserialize, PartialEq, Debug)]
struct Foo { a: (isize, isize, isize, isize) }
let v = Foo { a: (1, 2, 3, 4) };
assert_eq!(encode!(v),
map! {
a, Array(vec![
Integer(1),
Integer(2),
Integer(3),
Integer(4)
])
});
assert_eq!(v, decode!(Table(encode!(v))));
}
#[test]
fn inner_structs_with_options() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Foo {
a: Option<Box<Foo>>,
b: Bar,
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Bar {
a: String,
b: f64,
}
let v = Foo {
a: Some(Box::new(Foo {
a: None,
b: Bar { a: "foo".to_string(), b: 4.5 },
})),
b: Bar { a: "bar".to_string(), b: 1.0 },
};
assert_eq!(encode!(v),
map! {
a, Table(map! {
b, Table(map! {
a, Value::String("foo".to_string()),
b, Float(4.5)
})
}),
b, Table(map! {
a, Value::String("bar".to_string()),
b, Float(1.0)
})
});
assert_eq!(v, decode!(Table(encode!(v))));
}
#[test]
fn hashmap() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Foo {
map: BTreeMap<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(Serialize, Deserialize, PartialEq, Debug)]
struct Foo(isize, String, f64);
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Bar {
whee: Foo,
}
let v = Bar {
whee: Foo(1, "foo".to_string(), 4.5)
};
assert_eq!(
encode!(v),
map! {
whee, Value::Array(vec![
Integer(1),
Value::String("foo".to_string()),
Float(4.5),
])
}
);
assert_eq!(v, decode!(Table(encode!(v))));
}
#[test]
fn table_array() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Foo { a: Vec<Bar>, }
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Bar { a: isize }
let v = Foo { a: vec![Bar { a: 1 }, Bar { a: 2 }] };
assert_eq!(
encode!(v),
map! {
a, Array(vec![
Table(map!{ a, Integer(1) }),
Table(map!{ a, Integer(2) }),
])
}
);
assert_eq!(v, decode!(Table(encode!(v))));
}
#[test]
fn type_errors() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Foo { bar: isize }
let mut d = Decoder::new(Table(map! {
bar, Float(1.0)
}));
let a: Result<Foo, DecodeError> = Deserialize::deserialize(&mut d);
// serde uses FromPrimitive, that's why this works
match a {
Ok(..) => panic!("should not have decoded"),
Err(e) => {
assert_eq!(format!("{}", e),
"expected a value of type `integer`, but \
found a value of type `float` for the key `bar`");
}
}
}
#[test]
fn missing_errors() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Foo { bar: isize }
let mut d = Decoder::new(Table(map! {
}));
let a: Result<Foo, DecodeError> = Deserialize::deserialize(&mut d);
match a {
Ok(..) => panic!("should not have decoded"),
Err(e) => {
assert_eq!(format!("{}", e),
"expected a value for the key `bar`");
}
}
}
#[test]
fn parse_enum() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Foo { a: E }
#[derive(Serialize, Deserialize, PartialEq, Debug)]
enum E {
Bar(isize),
Baz(f64),
Last(Foo2),
}
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Foo2 {
test: String,
}
let v = Foo { a: E::Bar(10) };
// technically serde is correct here. a single element tuple still is a
// tuple and therefor a sequence
assert_eq!(
encode!(v),
map! { a, Integer(10) }
);
assert_eq!(v, decode!(Table(encode!(v))));
let v = Foo { a: E::Baz(10.2) };
assert_eq!(
encode!(v),
map! { a, Float(10.2) }
);
assert_eq!(v, decode!(Table(encode!(v))));
let v = Foo { a: E::Last(Foo2 { test: "test".to_string() }) };
assert_eq!(
encode!(v),
map! { a, Table(map! { test, Value::String("test".to_string()) }) }
);
assert_eq!(v, decode!(Table(encode!(v))));
}
#[test]
fn unused_fields() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Foo { a: isize }
let v = Foo { a: 2 };
let mut d = Decoder::new(Table(map! {
a, Integer(2),
b, Integer(5)
}));
assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
assert_eq!(d.toml, Some(Table(map! {
b, Integer(5)
})));
}
#[test]
fn unused_fields2() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Foo { a: Bar }
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Bar { a: isize }
let v = Foo { a: Bar { a: 2 } };
let mut d = Decoder::new(Table(map! {
a, Table(map! {
a, Integer(2),
b, Integer(5)
})
}));
assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
assert_eq!(d.toml, Some(Table(map! {
a, Table(map! {
b, Integer(5)
})
})));
}
#[test]
fn unused_fields3() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Foo { a: Bar }
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Bar { a: isize }
let v = Foo { a: Bar { a: 2 } };
let mut d = Decoder::new(Table(map! {
a, Table(map! {
a, Integer(2)
})
}));
assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
assert_eq!(d.toml, None);
}
#[test]
fn unused_fields4() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Foo { a: BTreeMap<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, t!(Deserialize::deserialize(&mut d)));
assert_eq!(d.toml, None);
}
#[test]
fn unused_fields5() {
#[derive(Serialize, Deserialize, 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, t!(Deserialize::deserialize(&mut d)));
assert_eq!(d.toml, None);
}
#[test]
fn unused_fields6() {
#[derive(Serialize, Deserialize, 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, t!(Deserialize::deserialize(&mut d)));
assert_eq!(d.toml, None);
}
#[test]
fn unused_fields7() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Foo { a: Vec<Bar> }
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Bar { a: isize }
let v = Foo { a: vec![Bar { a: 1 }] };
let mut d = Decoder::new(Table(map! {
a, Array(vec![Table(map! {
a, Integer(1),
b, Integer(2)
})])
}));
assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
assert_eq!(d.toml, Some(Table(map! {
a, Array(vec![Table(map! {
b, Integer(2)
})])
})));
}
#[test]
fn empty_arrays() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Foo { a: Vec<Bar> }
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Bar;
let v = Foo { a: vec![] };
let mut d = Decoder::new(Table(map! {}));
assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
}
#[test]
fn empty_arrays2() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Foo { a: Option<Vec<Bar>> }
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Bar;
let v = Foo { a: None };
let mut d = Decoder::new(Table(map! {}));
assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
let v = Foo { a: Some(vec![]) };
let mut d = Decoder::new(Table(map! {
a, Array(vec![])
}));
assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
}

424
src/datetime.rs Normal file
View file

@ -0,0 +1,424 @@
use std::fmt;
use std::str::{self, FromStr};
use std::error;
use serde::{de, ser};
/// A parsed TOML datetime value
///
/// This structure is intended to represent the datetime primitive type that can
/// be encoded into TOML documents. This type is a parsed version that contains
/// all metadata internally.
///
/// Currently this type is intentionally conservative and only supports
/// `to_string` as an accessor. Over time though it's intended that it'll grow
/// more support!
///
/// Note that if you're using `Deserialize` to deserialize a TOML document, you
/// can use this as a placeholder for where you're expecting a datetime to be
/// specified.
///
/// Also note though that while this type implements `Serialize` and
/// `Deserialize` it's only recommended to use this type with the TOML format,
/// otherwise encoded in other formats it may look a little odd.
#[derive(PartialEq, Clone)]
pub struct Datetime {
date: Option<Date>,
time: Option<Time>,
offset: Option<Offset>,
}
/// Error returned from parsing a `Datetime` in the `FromStr` implementation.
#[derive(Debug, Clone)]
pub struct DatetimeParseError {
_private: (),
}
// Currently serde itself doesn't have a datetime type, so we map our `Datetime`
// to a special valid in the serde data model. Namely one with thiese special
// fields/struct names.
//
// In general the TOML encoder/decoder will catch this and not literally emit
// these strings but rather emit datetimes as they're intended.
pub const SERDE_STRUCT_FIELD_NAME: &'static str = "$__toml_private_datetime";
pub const SERDE_STRUCT_NAME: &'static str = "$__toml_private_Datetime";
#[derive(PartialEq, Clone)]
struct Date {
year: u16,
month: u8,
day: u8,
}
#[derive(PartialEq, Clone)]
struct Time {
hour: u8,
minute: u8,
second: u8,
secfract: Option<f64>,
}
#[derive(PartialEq, Clone)]
enum Offset {
Z,
Custom { hours: i8, minutes: u8 },
}
impl fmt::Debug for Datetime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl fmt::Display for Datetime {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let Some(ref date) = self.date {
write!(f, "{}", date)?;
}
if let Some(ref time) = self.time {
if self.date.is_some() {
write!(f, "T")?;
}
write!(f, "{}", time)?;
}
if let Some(ref offset) = self.offset {
write!(f, "{}", offset)?;
}
Ok(())
}
}
impl fmt::Display for Date {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:04}-{:02}-{:02}", self.year, self.month, self.day)
}
}
impl fmt::Display for Time {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:02}:{:02}:{:02}", self.hour, self.minute, self.second)?;
if let Some(i) = self.secfract {
let s = format!("{}", i);
write!(f, "{}", s.trim_left_matches("0"))?;
}
Ok(())
}
}
impl fmt::Display for Offset {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Offset::Z => write!(f, "Z"),
Offset::Custom { hours, minutes } => {
write!(f, "{:+03}:{:02}", hours, minutes)
}
}
}
}
impl FromStr for Datetime {
type Err = DatetimeParseError;
fn from_str(date: &str) -> Result<Datetime, DatetimeParseError> {
// Accepted formats:
//
// 0000-00-00T00:00:00.00Z
// 0000-00-00T00:00:00.00
// 0000-00-00
// 00:00:00.00
if date.len() < 3 {
return Err(DatetimeParseError { _private: () })
}
let mut offset_allowed = true;
let mut chars = date.chars();
// First up, parse the full date if we can
let full_date = if chars.clone().nth(2) == Some(':') {
offset_allowed = false;
None
} else {
let y1 = digit(&mut chars)? as u16;
let y2 = digit(&mut chars)? as u16;
let y3 = digit(&mut chars)? as u16;
let y4 = digit(&mut chars)? as u16;
match chars.next() {
Some('-') => {}
_ => return Err(DatetimeParseError { _private: () }),
}
let m1 = digit(&mut chars)?;
let m2 = digit(&mut chars)?;
match chars.next() {
Some('-') => {}
_ => return Err(DatetimeParseError { _private: () }),
}
let d1 = digit(&mut chars)?;
let d2 = digit(&mut chars)?;
let date = Date {
year: y1 * 1000 + y2 * 100 + y3 * 10 + y4,
month: m1 * 10 + m2,
day: d1 * 10 + d2,
};
if date.month < 1 || date.month > 12 {
return Err(DatetimeParseError { _private: () })
}
if date.day < 1 || date.day > 31 {
return Err(DatetimeParseError { _private: () })
}
Some(date)
};
// Next parse the "partial-time" if available
let partial_time = if full_date.is_some() &&
chars.clone().next() == Some('T') {
chars.next();
true
} else if full_date.is_none() {
true
} else {
false
};
let time = if partial_time {
let h1 = digit(&mut chars)?;
let h2 = digit(&mut chars)?;
match chars.next() {
Some(':') => {}
_ => return Err(DatetimeParseError { _private: () }),
}
let m1 = digit(&mut chars)?;
let m2 = digit(&mut chars)?;
match chars.next() {
Some(':') => {}
_ => return Err(DatetimeParseError { _private: () }),
}
let s1 = digit(&mut chars)?;
let s2 = digit(&mut chars)?;
let secfract = if chars.clone().next() == Some('.') {
chars.next();
let mut first = true;
let whole = chars.as_str();
let mut end = whole.len();
for (i, c) in whole.char_indices() {
match c {
'0' ... '9' => {}
_ => {
end = i;
break
}
}
first = false;
}
if first {
return Err(DatetimeParseError { _private: () })
}
chars = whole[end..].chars();
match format!("0.{}", &whole[..end]).parse() {
Ok(f) => Some(f),
Err(_) => return Err(DatetimeParseError { _private: () }),
}
} else {
None
};
let time = Time {
hour: h1 * 10 + h2,
minute: m1 * 10 + m2,
second: s1 * 10 + s2,
secfract: secfract,
};
if time.hour > 24 {
return Err(DatetimeParseError { _private: () })
}
if time.minute > 59 {
return Err(DatetimeParseError { _private: () })
}
if time.second > 60 {
return Err(DatetimeParseError { _private: () })
}
Some(time)
} else {
offset_allowed = false;
None
};
// And finally, parse the offset
let offset = if offset_allowed {
let next = chars.clone().next();
if next == Some('Z') {
chars.next();
Some(Offset::Z)
} else if next.is_none() {
None
} else {
let sign = match next {
Some('+') => 1,
Some('-') => -1,
_ => return Err(DatetimeParseError { _private: () }),
};
chars.next();
let h1 = digit(&mut chars)? as i8;
let h2 = digit(&mut chars)? as i8;
match chars.next() {
Some(':') => {}
_ => return Err(DatetimeParseError { _private: () }),
}
let m1 = digit(&mut chars)?;
let m2 = digit(&mut chars)?;
Some(Offset::Custom {
hours: sign * (h1 * 10 + h2),
minutes: m1 * 10 + m2,
})
}
} else {
None
};
// Return an error if we didn't hit eof, otherwise return our parsed
// date
if chars.next().is_some() {
return Err(DatetimeParseError { _private: () })
}
Ok(Datetime {
date: full_date,
time: time,
offset: offset,
})
}
}
fn digit(chars: &mut str::Chars) -> Result<u8, DatetimeParseError> {
match chars.next() {
Some(c) if '0' <= c && c <= '9' => Ok(c as u8 - '0' as u8),
_ => Err(DatetimeParseError { _private: () }),
}
}
impl ser::Serialize for Datetime {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: ser::Serializer
{
use serde::ser::SerializeStruct;
let mut s = serializer.serialize_struct(SERDE_STRUCT_NAME, 1)?;
s.serialize_field(SERDE_STRUCT_FIELD_NAME, &self.to_string())?;
s.end()
}
}
impl de::Deserialize for Datetime {
fn deserialize<D>(deserializer: D) -> Result<Datetime, D::Error>
where D: de::Deserializer
{
struct DatetimeVisitor;
impl de::Visitor for DatetimeVisitor {
type Value = Datetime;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a TOML datetime")
}
fn visit_map<V>(self, mut visitor: V) -> Result<Datetime, V::Error>
where V: de::MapVisitor
{
let value = visitor.visit_key::<DatetimeKey>()?;
if value.is_none() {
return Err(de::Error::custom("datetime key not found"))
}
let v: DatetimeFromString = visitor.visit_value()?;
Ok(v.value)
}
}
static FIELDS: [&'static str; 1] = [SERDE_STRUCT_FIELD_NAME];
deserializer.deserialize_struct(SERDE_STRUCT_NAME,
&FIELDS,
DatetimeVisitor)
}
}
struct DatetimeKey;
impl de::Deserialize for DatetimeKey {
fn deserialize<D>(deserializer: D) -> Result<DatetimeKey, D::Error>
where D: de::Deserializer
{
struct FieldVisitor;
impl de::Visitor for FieldVisitor {
type Value = ();
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a valid datetime field")
}
fn visit_str<E>(self, s: &str) -> Result<(), E>
where E: de::Error
{
if s == SERDE_STRUCT_FIELD_NAME {
Ok(())
} else {
Err(de::Error::custom("expected field with custom name"))
}
}
}
deserializer.deserialize_struct_field(FieldVisitor)?;
Ok(DatetimeKey)
}
}
pub struct DatetimeFromString {
pub value: Datetime,
}
impl de::Deserialize for DatetimeFromString {
fn deserialize<D>(deserializer: D) -> Result<DatetimeFromString, D::Error>
where D: de::Deserializer
{
struct Visitor;
impl de::Visitor for Visitor {
type Value = DatetimeFromString;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("string containing a datetime")
}
fn visit_str<E>(self, s: &str) -> Result<DatetimeFromString, E>
where E: de::Error,
{
match s.parse() {
Ok(date) => Ok(DatetimeFromString { value: date }),
Err(e) => Err(de::Error::custom(e)),
}
}
}
deserializer.deserialize_str(Visitor)
}
}
impl fmt::Display for DatetimeParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
"failed to parse datetime".fmt(f)
}
}
impl error::Error for DatetimeParseError {
fn description(&self) -> &str {
"failed to parse datetime"
}
}

1195
src/de.rs Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,240 +0,0 @@
use std::error;
use std::fmt;
use std::collections::{btree_map, BTreeMap};
use std::iter::Peekable;
use Value;
use self::DecodeErrorKind::*;
#[cfg(feature = "rustc-serialize")] mod rustc_serialize;
#[cfg(feature = "serde")] mod serde;
/// A structure to transform TOML values into Rust values.
///
/// 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>,
// These aren't used if serde is in use
#[cfg_attr(feature = "serde", allow(dead_code))]
cur_map: Peekable<btree_map::IntoIter<String, Value>>,
#[cfg_attr(feature = "serde", allow(dead_code))]
leftover_map: ::Table,
}
/// 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 was not an expected one.
UnknownField,
/// 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,
/// A custom error was generated when decoding.
CustomError(String),
/// The end of the TOML input was reached too soon
EndOfStream,
/// Produced by serde ...
InvalidType(&'static str),
}
/// 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
/// returned.
///
/// 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
/// returned.
///
/// 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::new_empty(Some(toml), None)
}
fn sub_decoder(&self, toml: Option<Value>, field: &str) -> Decoder {
let cur_field = if field.is_empty() {
self.cur_field.clone()
} else {
match self.cur_field {
None => Some(field.to_string()),
Some(ref s) => Some(format!("{}.{}", s, field))
}
};
Decoder::new_empty(toml, cur_field)
}
fn new_empty(toml: Option<Value>, cur_field: Option<String>) -> Decoder {
Decoder {
toml: toml,
cur_field: cur_field,
leftover_map: BTreeMap::new(),
cur_map: BTreeMap::new().into_iter().peekable(),
}
}
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"),
}
}
UnknownField => write!(f, "unknown field"),
ExpectedType(expected, found) => {
fn humanize(s: &str) -> String {
if s == "section" {
"a section".to_string()
} 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")
}
InvalidType(s) => {
write!(f, "invalid type: {}", s)
}
CustomError(ref s) => {
write!(f, "custom error: {}", s)
}
});
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",
UnknownField => "found an unknown 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",
InvalidType(..) => "invalid type",
CustomError(..) => "custom error",
}
}
}

View file

@ -1,371 +0,0 @@
use rustc_serialize;
use std::mem;
use std::collections::BTreeMap;
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.is_empty() => {}
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)) => { self.toml.take(); 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>
{
// When decoding enums, this crate takes the strategy of trying to
// decode the current TOML as all of the possible variants, returning
// success on the first one that succeeds.
//
// Note that fidelity of the errors returned here is a little nebulous,
// but we try to return the error that had the relevant field as the
// longest field. This way we hopefully match an error against what was
// most likely being written down without losing too much info.
let mut first_error = None::<DecodeError>;
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 let Some(ref first) = first_error {
let my_len = e.field.as_ref().map(|s| s.len());
let first_len = first.field.as_ref().map(|s| s.len());
if my_len <= first_len {
continue
}
}
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.is_empty() => {}
_ => 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 = f_name.to_string();
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.is_empty() { 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));
if let Some(toml) = d.toml {
if let Some(Value::Array(ref mut arr)) = self.toml {
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 map = match self.toml.take() {
Some(Value::Table(table)) => table,
found => {
self.toml = found;
return Err(self.mismatch("table", &self.toml))
}
};
let amt = map.len();
let prev_iter = mem::replace(&mut self.cur_map,
map.into_iter().peekable());
let prev_map = mem::replace(&mut self.leftover_map, BTreeMap::new());
let ret = try!(f(self, amt));
let leftover = mem::replace(&mut self.leftover_map, prev_map);
self.cur_map = prev_iter;
if !leftover.is_empty() {
self.toml = Some(Value::Table(leftover));
}
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>
{
let key = match self.cur_map.peek().map(|p| p.0.clone()) {
Some(k) => k,
None => return Err(self.err(ExpectedMapKey(idx))),
};
let val = Value::String(key.clone());
f(&mut self.sub_decoder(Some(val), &key))
}
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.cur_map.next() {
Some((key, value)) => {
let mut d = self.sub_decoder(Some(value), &key);
let ret = f(&mut d);
if let Some(toml) = d.toml.take() {
self.leftover_map.insert(key, toml);
}
ret
}
None => Err(self.err(ExpectedMapElement(idx))),
}
}
fn error(&mut self, err: &str) -> DecodeError {
DecodeError {
field: self.cur_field.clone(),
kind: ApplicationError(err.to_string())
}
}
}
#[cfg(test)]
mod tests {
use rustc_serialize::Decodable;
use std::collections::HashMap;
use {Parser, Decoder, Value};
#[test]
fn bad_enum_chooses_longest_error() {
#[derive(RustcDecodable)]
#[allow(dead_code)]
struct Foo {
wut: HashMap<String, Bar>,
}
#[derive(RustcDecodable)]
enum Bar {
Simple(String),
Detailed(Baz),
}
#[derive(RustcDecodable, Debug)]
struct Baz {
features: Vec<String>,
}
let s = r#"
[wut]
a = { features = "" }
"#;
let v = Parser::new(s).parse().unwrap();
let mut d = Decoder::new(Value::Table(v));
let err = match Foo::decode(&mut d) {
Ok(_) => panic!("expected error"),
Err(e) => e,
};
assert_eq!(err.field.as_ref().unwrap(), "wut.a.features");
}
}

View file

@ -1,773 +0,0 @@
use serde::de;
use Value;
use super::{Decoder, DecodeError, DecodeErrorKind};
use std::collections::BTreeMap;
macro_rules! forward_to_deserialize {
($(
$name:ident ( $( $arg:ident : $ty:ty ),* );
)*) => {
$(
forward_to_deserialize!{
func: $name ( $( $arg: $ty ),* );
}
)*
};
(func: deserialize_enum ( $( $arg:ident : $ty:ty ),* );) => {
fn deserialize_enum<V>(
&mut self,
$(_: $ty,)*
_visitor: V,
) -> ::std::result::Result<V::Value, Self::Error>
where V: ::serde::de::EnumVisitor
{
Err(::serde::de::Error::invalid_type(::serde::de::Type::Enum))
}
};
(func: $name:ident ( $( $arg:ident : $ty:ty ),* );) => {
#[inline]
fn $name<V>(
&mut self,
$(_: $ty,)*
visitor: V,
) -> ::std::result::Result<V::Value, Self::Error>
where V: ::serde::de::Visitor
{
self.deserialize(visitor)
}
};
}
impl de::Deserializer for Decoder {
type Error = DecodeError;
fn deserialize<V>(&mut self, mut visitor: V)
-> Result<V::Value, DecodeError>
where V: de::Visitor
{
match self.toml.take() {
Some(Value::String(s)) => visitor.visit_string(s),
Some(Value::Integer(i)) => visitor.visit_i64(i),
Some(Value::Float(f)) => visitor.visit_f64(f),
Some(Value::Boolean(b)) => visitor.visit_bool(b),
Some(Value::Datetime(s)) => visitor.visit_string(s),
Some(Value::Array(a)) => {
let len = a.len();
let iter = a.into_iter();
visitor.visit_seq(SeqDeserializer::new(iter, len, &mut self.toml))
}
Some(Value::Table(t)) => {
visitor.visit_map(MapVisitor {
iter: t.into_iter(),
de: self,
key: None,
value: None,
})
}
None => Err(self.err(DecodeErrorKind::EndOfStream)),
}
}
fn deserialize_bool<V>(&mut self, mut visitor: V)
-> Result<V::Value, DecodeError>
where V: de::Visitor
{
match self.toml.take() {
Some(Value::Boolean(b)) => visitor.visit_bool(b),
ref found => Err(self.mismatch("bool", found)),
}
}
fn deserialize_i8<V>(&mut self, visitor: V)
-> Result<V::Value, DecodeError>
where V: de::Visitor
{
self.deserialize_i64(visitor)
}
fn deserialize_i16<V>(&mut self, visitor: V)
-> Result<V::Value, DecodeError>
where V: de::Visitor
{
self.deserialize_i64(visitor)
}
fn deserialize_i32<V>(&mut self, visitor: V)
-> Result<V::Value, DecodeError>
where V: de::Visitor
{
self.deserialize_i64(visitor)
}
fn deserialize_i64<V>(&mut self, mut visitor: V)
-> Result<V::Value, DecodeError>
where V: de::Visitor
{
match self.toml.take() {
Some(Value::Integer(f)) => visitor.visit_i64(f),
ref found => Err(self.mismatch("integer", found)),
}
}
fn deserialize_isize<V>(&mut self, visitor: V)
-> Result<V::Value, DecodeError>
where V: de::Visitor
{
self.deserialize_i64(visitor)
}
fn deserialize_u8<V>(&mut self, visitor: V)
-> Result<V::Value, DecodeError>
where V: de::Visitor
{
self.deserialize_i64(visitor)
}
fn deserialize_u16<V>(&mut self, visitor: V)
-> Result<V::Value, DecodeError>
where V: de::Visitor
{
self.deserialize_i64(visitor)
}
fn deserialize_u32<V>(&mut self, visitor: V)
-> Result<V::Value, DecodeError>
where V: de::Visitor
{
self.deserialize_i64(visitor)
}
fn deserialize_u64<V>(&mut self, visitor: V)
-> Result<V::Value, DecodeError>
where V: de::Visitor
{
self.deserialize_i64(visitor)
}
fn deserialize_usize<V>(&mut self, visitor: V)
-> Result<V::Value, DecodeError>
where V: de::Visitor
{
self.deserialize_i64(visitor)
}
fn deserialize_f32<V>(&mut self, visitor: V)
-> Result<V::Value, DecodeError>
where V: de::Visitor
{
self.deserialize_f64(visitor)
}
fn deserialize_f64<V>(&mut self, mut visitor: V)
-> Result<V::Value, DecodeError>
where V: de::Visitor
{
match self.toml.take() {
Some(Value::Float(f)) => visitor.visit_f64(f),
ref found => Err(self.mismatch("float", found)),
}
}
fn deserialize_str<V>(&mut self, mut visitor: V)
-> Result<V::Value, Self::Error>
where V: de::Visitor,
{
match self.toml.take() {
Some(Value::String(s)) => visitor.visit_string(s),
ref found => Err(self.mismatch("string", found)),
}
}
fn deserialize_string<V>(&mut self, visitor: V)
-> Result<V::Value, Self::Error>
where V: de::Visitor,
{
self.deserialize_str(visitor)
}
fn deserialize_char<V>(&mut self, mut visitor: V)
-> Result<V::Value, DecodeError>
where V: de::Visitor
{
match self.toml.take() {
Some(Value::String(ref s)) if s.chars().count() == 1 => {
visitor.visit_char(s.chars().next().unwrap())
}
ref found => return Err(self.mismatch("string", found)),
}
}
fn deserialize_option<V>(&mut self, mut visitor: V)
-> Result<V::Value, DecodeError>
where V: de::Visitor
{
if self.toml.is_none() {
visitor.visit_none()
} else {
visitor.visit_some(self)
}
}
fn deserialize_seq<V>(&mut self, mut visitor: V)
-> Result<V::Value, DecodeError>
where V: de::Visitor,
{
if self.toml.is_none() {
let iter = None::<i32>.into_iter();
visitor.visit_seq(de::value::SeqDeserializer::new(iter, 0))
} else {
self.deserialize(visitor)
}
}
fn deserialize_map<V>(&mut self, mut visitor: V)
-> Result<V::Value, DecodeError>
where V: de::Visitor,
{
match self.toml.take() {
Some(Value::Table(t)) => {
visitor.visit_map(MapVisitor {
iter: t.into_iter(),
de: self,
key: None,
value: None,
})
}
ref found => Err(self.mismatch("table", found)),
}
}
fn deserialize_enum<V>(&mut self,
_enum: &str,
variants: &[&str],
mut visitor: V) -> Result<V::Value, DecodeError>
where V: de::EnumVisitor,
{
// When decoding enums, this crate takes the strategy of trying to
// decode the current TOML as all of the possible variants, returning
// success on the first one that succeeds.
//
// Note that fidelity of the errors returned here is a little nebulous,
// but we try to return the error that had the relevant field as the
// longest field. This way we hopefully match an error against what was
// most likely being written down without losing too much info.
let mut first_error = None::<DecodeError>;
for variant in 0..variants.len() {
let mut de = VariantVisitor {
de: self.sub_decoder(self.toml.clone(), ""),
variant: variant,
};
match visitor.visit(&mut de) {
Ok(value) => {
self.toml = de.de.toml;
return Ok(value);
}
Err(e) => {
if let Some(ref first) = first_error {
let my_len = e.field.as_ref().map(|s| s.len());
let first_len = first.field.as_ref().map(|s| s.len());
if my_len <= first_len {
continue
}
}
first_error = Some(e);
}
}
}
Err(first_error.unwrap_or_else(|| self.err(DecodeErrorKind::NoEnumVariants)))
}
// When #[derive(Deserialize)] encounters an unknown struct field it will
// call this method (somehow), and we want to preserve all unknown struct
// fields to return them upwards (to warn about unused keys), so we override
// that here to not tamper with our own internal state.
fn deserialize_ignored_any<V>(&mut self, visitor: V)
-> Result<V::Value, Self::Error>
where V: de::Visitor
{
use serde::de::value::ValueDeserializer;
let mut d = <() as ValueDeserializer<Self::Error>>::into_deserializer(());
d.deserialize(visitor)
}
fn deserialize_bytes<V>(&mut self, visitor: V)
-> Result<V::Value, Self::Error>
where V: de::Visitor
{
self.deserialize_seq(visitor)
}
fn deserialize_seq_fixed_size<V>(&mut self, _len: usize, visitor: V)
-> Result<V::Value, Self::Error>
where V: de::Visitor
{
self.deserialize_seq(visitor)
}
fn deserialize_newtype_struct<V>(&mut self, _name: &'static str, visitor: V)
-> Result<V::Value, Self::Error>
where V: de::Visitor
{
self.deserialize_seq(visitor)
}
fn deserialize_tuple_struct<V>(&mut self,
_name: &'static str,
_len: usize,
visitor: V)
-> Result<V::Value, Self::Error>
where V: de::Visitor
{
self.deserialize_seq(visitor)
}
fn deserialize_struct<V>(&mut self,
_name: &'static str,
_fields: &'static [&'static str],
visitor: V)
-> Result<V::Value, Self::Error>
where V: de::Visitor
{
self.deserialize_map(visitor)
}
fn deserialize_tuple<V>(&mut self,
_len: usize,
visitor: V)
-> Result<V::Value, Self::Error>
where V: de::Visitor
{
self.deserialize_seq(visitor)
}
forward_to_deserialize!{
deserialize_unit();
deserialize_unit_struct(name: &'static str);
deserialize_struct_field();
}
}
struct VariantVisitor {
de: Decoder,
variant: usize,
}
impl de::VariantVisitor for VariantVisitor {
type Error = DecodeError;
fn visit_variant<V>(&mut self) -> Result<V, DecodeError>
where V: de::Deserialize
{
use serde::de::value::ValueDeserializer;
let mut de = self.variant.into_deserializer();
de::Deserialize::deserialize(&mut de)
}
fn visit_unit(&mut self) -> Result<(), DecodeError> {
de::Deserialize::deserialize(&mut self.de)
}
fn visit_newtype<T>(&mut self) -> Result<T, DecodeError>
where T: de::Deserialize,
{
de::Deserialize::deserialize(&mut self.de)
}
fn visit_tuple<V>(&mut self,
_len: usize,
visitor: V) -> Result<V::Value, DecodeError>
where V: de::Visitor,
{
de::Deserializer::deserialize(&mut self.de, visitor)
}
fn visit_struct<V>(&mut self,
_fields: &'static [&'static str],
visitor: V) -> Result<V::Value, DecodeError>
where V: de::Visitor,
{
de::Deserializer::deserialize(&mut self.de, visitor)
}
}
struct SeqDeserializer<'a, I> {
iter: I,
len: usize,
toml: &'a mut Option<Value>,
}
impl<'a, I> SeqDeserializer<'a, I> where I: Iterator<Item=Value> {
fn new(iter: I, len: usize, toml: &'a mut Option<Value>) -> Self {
SeqDeserializer {
iter: iter,
len: len,
toml: toml,
}
}
fn put_value_back(&mut self, v: Value) {
*self.toml = self.toml.take().or(Some(Value::Array(Vec::new())));
match self.toml.as_mut().unwrap() {
&mut Value::Array(ref mut a) => {
a.push(v);
},
_ => unreachable!(),
}
}
}
impl<'a, I> de::Deserializer for SeqDeserializer<'a, I>
where I: Iterator<Item=Value>,
{
type Error = DecodeError;
fn deserialize<V>(&mut self, mut visitor: V)
-> Result<V::Value, DecodeError>
where V: de::Visitor,
{
visitor.visit_seq(self)
}
forward_to_deserialize!{
deserialize_bool();
deserialize_usize();
deserialize_u8();
deserialize_u16();
deserialize_u32();
deserialize_u64();
deserialize_isize();
deserialize_i8();
deserialize_i16();
deserialize_i32();
deserialize_i64();
deserialize_f32();
deserialize_f64();
deserialize_char();
deserialize_str();
deserialize_string();
deserialize_unit();
deserialize_option();
deserialize_seq();
deserialize_seq_fixed_size(len: usize);
deserialize_bytes();
deserialize_map();
deserialize_unit_struct(name: &'static str);
deserialize_newtype_struct(name: &'static str);
deserialize_tuple_struct(name: &'static str, len: usize);
deserialize_struct(name: &'static str, fields: &'static [&'static str]);
deserialize_struct_field();
deserialize_tuple(len: usize);
deserialize_enum(name: &'static str, variants: &'static [&'static str]);
deserialize_ignored_any();
}
}
impl<'a, I> de::SeqVisitor for SeqDeserializer<'a, I>
where I: Iterator<Item=Value>
{
type Error = DecodeError;
fn visit<V>(&mut self) -> Result<Option<V>, DecodeError>
where V: de::Deserialize
{
match self.iter.next() {
Some(value) => {
self.len -= 1;
let mut de = Decoder::new(value);
let v = try!(de::Deserialize::deserialize(&mut de));
if let Some(t) = de.toml {
self.put_value_back(t);
}
Ok(Some(v))
}
None => Ok(None),
}
}
fn end(&mut self) -> Result<(), DecodeError> {
if self.len == 0 {
Ok(())
} else {
Err(de::Error::end_of_stream())
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len, Some(self.len))
}
}
impl de::Error for DecodeError {
fn custom<T: Into<String>>(msg: T) -> DecodeError {
DecodeError {
field: None,
kind: DecodeErrorKind::CustomError(msg.into()),
}
}
fn end_of_stream() -> DecodeError {
DecodeError { field: None, kind: DecodeErrorKind::EndOfStream }
}
fn missing_field(name: &'static str) -> DecodeError {
DecodeError {
field: Some(name.to_string()),
kind: DecodeErrorKind::ExpectedField(None),
}
}
fn unknown_field(name: &str) -> DecodeError {
DecodeError {
field: Some(name.to_string()),
kind: DecodeErrorKind::UnknownField,
}
}
fn invalid_type(ty: de::Type) -> Self {
DecodeError {
field: None,
kind: DecodeErrorKind::InvalidType(match ty {
de::Type::Bool => "bool",
de::Type::Usize |
de::Type::U8 |
de::Type::U16 |
de::Type::U32 |
de::Type::U64 |
de::Type::Isize |
de::Type::I8 |
de::Type::I16 |
de::Type::I32 |
de::Type::I64 => "integer",
de::Type::F32 |
de::Type::F64 => "float",
de::Type::Char |
de::Type::Str |
de::Type::String => "string",
de::Type::Seq => "array",
de::Type::Struct |
de::Type::Map => "table",
de::Type::Unit => "Unit",
de::Type::Option => "Option",
de::Type::UnitStruct => "UnitStruct",
de::Type::NewtypeStruct => "NewtypeStruct",
de::Type::TupleStruct => "TupleStruct",
de::Type::FieldName => "FieldName",
de::Type::Tuple => "Tuple",
de::Type::Enum => "Enum",
de::Type::VariantName => "VariantName",
de::Type::StructVariant => "StructVariant",
de::Type::TupleVariant => "TupleVariant",
de::Type::UnitVariant => "UnitVariant",
de::Type::Bytes => "Bytes",
})
}
}
}
struct MapVisitor<'a, I> {
iter: I,
de: &'a mut Decoder,
key: Option<String>,
value: Option<Value>,
}
impl<'a, I> MapVisitor<'a, I> {
fn put_value_back(&mut self, v: Value) {
self.de.toml = self.de.toml.take().or_else(|| {
Some(Value::Table(BTreeMap::new()))
});
match self.de.toml.as_mut().unwrap() {
&mut Value::Table(ref mut t) => {
t.insert(self.key.take().unwrap(), v);
},
_ => unreachable!(),
}
}
}
impl<'a, I> de::MapVisitor for MapVisitor<'a, I>
where I: Iterator<Item=(String, Value)>
{
type Error = DecodeError;
fn visit_key<K>(&mut self) -> Result<Option<K>, DecodeError>
where K: de::Deserialize
{
while let Some((k, v)) = self.iter.next() {
let mut dec = self.de.sub_decoder(Some(Value::String(k.clone())), &k);
self.key = Some(k);
match de::Deserialize::deserialize(&mut dec) {
Ok(val) => {
self.value = Some(v);
return Ok(Some(val))
}
// If this was an unknown field, then we put the toml value
// back into the map and keep going.
Err(DecodeError {kind: DecodeErrorKind::UnknownField, ..}) => {
self.put_value_back(v);
}
Err(e) => return Err(e),
}
}
Ok(None)
}
fn visit_value<V>(&mut self) -> Result<V, DecodeError>
where V: de::Deserialize
{
match self.value.take() {
Some(t) => {
let mut dec = {
// Borrowing the key here because Rust doesn't have
// non-lexical borrows yet.
let key = match self.key {
Some(ref key) => &**key,
None => ""
};
self.de.sub_decoder(Some(t), key)
};
let v = try!(de::Deserialize::deserialize(&mut dec));
if let Some(t) = dec.toml {
self.put_value_back(t);
}
Ok(v)
},
None => Err(de::Error::end_of_stream())
}
}
fn end(&mut self) -> Result<(), DecodeError> {
if let Some(v) = self.value.take() {
self.put_value_back(v);
}
while let Some((k, v)) = self.iter.next() {
self.key = Some(k);
self.put_value_back(v);
}
Ok(())
}
fn missing_field<V>(&mut self, field_name: &'static str)
-> Result<V, DecodeError> where V: de::Deserialize {
// See if the type can deserialize from a unit.
match de::Deserialize::deserialize(&mut UnitDeserializer) {
Err(DecodeError {
kind: DecodeErrorKind::InvalidType(..),
field,
}) => Err(DecodeError {
field: field.or(Some(field_name.to_string())),
kind: DecodeErrorKind::ExpectedField(None),
}),
v => v,
}
}
}
struct UnitDeserializer;
impl de::Deserializer for UnitDeserializer {
type Error = DecodeError;
fn deserialize<V>(&mut self, mut visitor: V)
-> Result<V::Value, DecodeError>
where V: de::Visitor,
{
visitor.visit_unit()
}
fn deserialize_option<V>(&mut self, mut visitor: V)
-> Result<V::Value, DecodeError>
where V: de::Visitor,
{
visitor.visit_none()
}
forward_to_deserialize!{
deserialize_bool();
deserialize_usize();
deserialize_u8();
deserialize_u16();
deserialize_u32();
deserialize_u64();
deserialize_isize();
deserialize_i8();
deserialize_i16();
deserialize_i32();
deserialize_i64();
deserialize_f32();
deserialize_f64();
deserialize_char();
deserialize_str();
deserialize_string();
deserialize_unit();
deserialize_seq();
deserialize_seq_fixed_size(len: usize);
deserialize_bytes();
deserialize_map();
deserialize_unit_struct(name: &'static str);
deserialize_newtype_struct(name: &'static str);
deserialize_tuple_struct(name: &'static str, len: usize);
deserialize_struct(name: &'static str, fields: &'static [&'static str]);
deserialize_struct_field();
deserialize_tuple(len: usize);
deserialize_enum(name: &'static str, variants: &'static [&'static str]);
deserialize_ignored_any();
}
}
impl de::Deserialize for Value {
fn deserialize<D>(deserializer: &mut D) -> Result<Value, D::Error>
where D: de::Deserializer
{
struct ValueVisitor;
impl de::Visitor for ValueVisitor {
type Value = Value;
fn visit_bool<E>(&mut self, value: bool) -> Result<Value, E> {
Ok(Value::Boolean(value))
}
fn visit_i64<E>(&mut self, value: i64) -> Result<Value, E> {
Ok(Value::Integer(value))
}
fn visit_f64<E>(&mut self, value: f64) -> Result<Value, E> {
Ok(Value::Float(value))
}
fn visit_str<E>(&mut self, value: &str) -> Result<Value, E> {
Ok(Value::String(value.into()))
}
fn visit_string<E>(&mut self, value: String) -> Result<Value, E> {
Ok(Value::String(value))
}
fn visit_seq<V>(&mut self, visitor: V) -> Result<Value, V::Error>
where V: de::SeqVisitor
{
let values = try!(de::impls::VecVisitor::new().visit_seq(visitor));
Ok(Value::Array(values))
}
fn visit_map<V>(&mut self, visitor: V) -> Result<Value, V::Error>
where V: de::MapVisitor
{
let mut v = de::impls::BTreeMapVisitor::new();
let values = try!(v.visit_map(visitor));
Ok(Value::Table(values))
}
}
deserializer.deserialize(ValueVisitor)
}
}

View file

@ -1,222 +0,0 @@
use std::collections::BTreeMap;
use std::error;
use std::fmt;
use std::mem;
use {Value, Table};
#[cfg(feature = "rustc-serialize")] mod rustc_serialize;
#[cfg(feature = "serde")] mod serde;
/// A structure to transform Rust values into TOML values.
///
/// 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)))
/// # }
/// ```
#[derive(Default, Debug)]
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 no 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,
/// An error returned whenever a `NaN` value for a float is attempted to be
/// encoded
NanEncoded,
/// An error returned whenever an infinity value for a float is attempted to
/// be encoded
InfinityEncoded,
/// A custom error type was generated
Custom(String),
}
/// Internal state of the encoder when encoding transitions
#[derive(Debug)]
pub struct EncoderState {
inner: State,
}
#[derive(PartialEq, Debug)]
enum State {
Start,
NextKey(String),
NextArray(Vec<Value>),
NextMapKey,
}
impl Default for State {
fn default() -> State { State::Start }
}
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 v {
Value::Float(f) => {
if f.is_nan() {
return Err(Error::NanEncoded)
}
if f.is_infinite() {
return Err(Error::InfinityEncoded)
}
}
_ => {}
}
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_begin(&mut self) -> Result<State, Error> {
Ok(mem::replace(&mut self.state, State::NextArray(Vec::new())))
}
fn seq_end(&mut self, old: State) -> Result<(), Error> {
match mem::replace(&mut self.state, old) {
State::NextArray(v) => self.emit_value(Value::Array(v)),
_ => unreachable!(),
}
}
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.serialize(&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, "no 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"),
Error::NanEncoded => write!(f, "cannot encode NaN"),
Error::InfinityEncoded => write!(f, "cannot encode infinity"),
Error::Custom(ref s) => write!(f, "custom error: {}", s),
}
}
}
impl error::Error for Error {
fn description(&self) -> &str { "TOML encoding error" }
}

View file

@ -1,748 +0,0 @@
use std::mem;
use rustc_serialize;
use Value;
use super::{Encoder, Error, State};
use super::Error::*;
impl Encoder {
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 seq<F>(&mut self, f: F) -> Result<(), Error>
where F: FnOnce(&mut Encoder) -> Result<(), Error>
{
let old = try!(self.seq_begin());
try!(f(self));
self.seq_end(old)
}
}
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(&v.to_string())
}
fn emit_str(&mut self, v: &str) -> Result<(), Error> {
self.emit_value(Value::String(v.to_string()))
}
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(f_name.to_string()));
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!(e.to_string(),
"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!(e.to_string(),
"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 unused_fields8() {
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
struct Foo { a: BTreeMap<String, Bar> }
#[derive(RustcEncodable, RustcDecodable, PartialEq, Debug)]
struct Bar { a: isize }
let v = Foo { a: map! { a, Bar { a: 2 } } };
let mut d = Decoder::new(Table(map! {
a, Table(map! {
a, Table(map! {
a, Integer(2),
b, Integer(2)
})
})
}));
assert_eq!(v, Decodable::decode(&mut d).unwrap());
assert_eq!(d.toml, Some(Table(map! {
a, Table(map! {
a, 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);
}
}

View file

@ -1,339 +0,0 @@
use std::mem;
use serde::ser;
use Value;
use super::{Encoder, Error, EncoderState, State};
impl Encoder {
fn table_begin(&mut self) -> Result<Self, Error> {
match self.state {
State::NextMapKey => Err(Error::InvalidMapKeyLocation),
_ => Ok(mem::replace(self, Encoder::new()))
}
}
fn table_end(&mut self, mut state: Self) -> Result<(), Error> {
match state.state {
State::NextKey(key) => {
mem::swap(&mut self.toml, &mut state.toml);
self.toml.insert(key, Value::Table(state.toml));
},
State::NextArray(mut arr) => {
mem::swap(&mut self.toml, &mut state.toml);
arr.push(Value::Table(state.toml));
self.state = State::NextArray(arr);
},
State::Start => {},
State::NextMapKey => unreachable!(),
}
Ok(())
}
}
impl ser::Serializer for Encoder {
type Error = Error;
type MapState = Self;
type StructState = Self;
type StructVariantState = Self;
type SeqState = EncoderState;
type TupleState = EncoderState;
type TupleStructState = EncoderState;
type TupleVariantState = EncoderState;
fn serialize_bool(&mut self, v: bool) -> Result<(), Error> {
self.emit_value(Value::Boolean(v))
}
fn serialize_i64(&mut self, v: i64) -> Result<(), Error> {
self.emit_value(Value::Integer(v))
}
// TODO: checked casts
fn serialize_u64(&mut self, v: u64) -> Result<(), Error> {
self.serialize_i64(v as i64)
}
fn serialize_isize(&mut self, v: isize) -> Result<(), Error> {
self.serialize_i64(v as i64)
}
fn serialize_usize(&mut self, v: usize) -> Result<(), Error> {
self.serialize_i64(v as i64)
}
fn serialize_i8(&mut self, v: i8) -> Result<(), Error> {
self.serialize_i64(v as i64)
}
fn serialize_u8(&mut self, v: u8) -> Result<(), Error> {
self.serialize_i64(v as i64)
}
fn serialize_i16(&mut self, v: i16) -> Result<(), Error> {
self.serialize_i64(v as i64)
}
fn serialize_u16(&mut self, v: u16) -> Result<(), Error> {
self.serialize_i64(v as i64)
}
fn serialize_i32(&mut self, v: i32) -> Result<(), Error> {
self.serialize_i64(v as i64)
}
fn serialize_u32(&mut self, v: u32) -> Result<(), Error> {
self.serialize_i64(v as i64)
}
fn serialize_f32(&mut self, v: f32) -> Result<(), Error> {
self.serialize_f64(v as f64)
}
fn serialize_f64(&mut self, v: f64) -> Result<(), Error> {
self.emit_value(Value::Float(v))
}
fn serialize_str(&mut self, value: &str) -> Result<(), Error> {
self.emit_value(Value::String(value.to_string()))
}
fn serialize_unit_struct(&mut self, _name: &'static str) -> Result<(), Error> {
Ok(())
}
fn serialize_unit(&mut self) -> Result<(), Error> {
Ok(())
}
fn serialize_none(&mut self) -> Result<(), Error> {
self.emit_none()
}
fn serialize_char(&mut self, c: char) -> Result<(), Error> {
self.serialize_str(&c.to_string())
}
fn serialize_some<V>(&mut self, value: V) -> Result<(), Error>
where V: ser::Serialize
{
value.serialize(self)
}
fn serialize_bytes(&mut self, v: &[u8]) -> Result<(), Error> {
let mut state = try!(self.serialize_seq(Some(v.len())));
for c in v {
try!(self.serialize_seq_elt(&mut state, c));
}
self.serialize_seq_end(state)
}
fn serialize_seq_fixed_size(&mut self, len: usize)
-> Result<EncoderState, Error> {
self.serialize_seq(Some(len))
}
fn serialize_seq(&mut self, _len: Option<usize>)
-> Result<EncoderState, Error> {
self.seq_begin().map(|s| EncoderState { inner: s })
}
fn serialize_seq_elt<T>(&mut self,
_state: &mut EncoderState,
value: T) -> Result<(), Error>
where T: ser::Serialize
{
value.serialize(self)
}
fn serialize_seq_end(&mut self, state: EncoderState) -> Result<(), Error> {
self.seq_end(state.inner)
}
fn serialize_tuple(&mut self, len: usize)
-> Result<EncoderState, Error> {
self.serialize_seq(Some(len))
}
fn serialize_tuple_elt<T>(&mut self,
state: &mut EncoderState,
value: T) -> Result<(), Error>
where T: ser::Serialize
{
self.serialize_seq_elt(state, value)
}
fn serialize_tuple_end(&mut self, state: EncoderState) -> Result<(), Error> {
self.serialize_seq_end(state)
}
fn serialize_tuple_struct(&mut self,
_name: &'static str,
len: usize) -> Result<EncoderState, Error> {
self.serialize_seq(Some(len))
}
fn serialize_tuple_struct_elt<T>(&mut self,
state: &mut EncoderState,
value: T) -> Result<(), Error>
where T: ser::Serialize
{
self.serialize_seq_elt(state, value)
}
fn serialize_tuple_struct_end(&mut self, state: EncoderState)
-> Result<(), Error> {
self.serialize_seq_end(state)
}
fn serialize_tuple_variant(&mut self,
_name: &'static str,
_id: usize,
_variant: &'static str,
len: usize) -> Result<EncoderState, Error> {
self.serialize_seq(Some(len))
}
fn serialize_tuple_variant_elt<T>(&mut self,
state: &mut EncoderState,
value: T) -> Result<(), Error>
where T: ser::Serialize
{
self.serialize_seq_elt(state, value)
}
fn serialize_tuple_variant_end(&mut self, state: EncoderState)
-> Result<(), Error> {
self.serialize_seq_end(state)
}
fn serialize_map(&mut self, _len: Option<usize>) -> Result<Self, Error> {
self.table_begin()
}
fn serialize_map_key<K>(&mut self,
_state: &mut Encoder,
key: K) -> Result<(), Error>
where K: ser::Serialize
{
self.table_key(|me| key.serialize(me))
}
fn serialize_map_value<V>(&mut self,
_state: &mut Encoder,
value: V) -> Result<(), Error>
where V: ser::Serialize
{
value.serialize(self)
}
fn serialize_map_end(&mut self, state: Self) -> Result<(), Error> {
self.table_end(state)
}
fn serialize_struct(&mut self,
_name: &'static str,
len: usize) -> Result<Self, Error> {
self.serialize_map(Some(len))
}
fn serialize_struct_elt<V>(&mut self,
state: &mut Encoder,
key: &'static str,
value: V) -> Result<(), Error>
where V: ser::Serialize
{
try!(self.serialize_map_key(state, key));
self.serialize_map_value(state, value)
}
fn serialize_struct_end(&mut self, state: Self) -> Result<(), Error> {
self.serialize_map_end(state)
}
fn serialize_struct_variant(&mut self,
_name: &'static str,
_id: usize,
_variant: &'static str,
len: usize) -> Result<Self, Error> {
self.serialize_map(Some(len))
}
fn serialize_struct_variant_elt<V>(&mut self,
state: &mut Encoder,
key: &'static str,
value: V) -> Result<(), Error>
where V: ser::Serialize
{
try!(self.serialize_map_key(state, key));
self.serialize_map_value(state, value)
}
fn serialize_struct_variant_end(&mut self, state: Self) -> Result<(), Error> {
self.serialize_map_end(state)
}
fn serialize_newtype_struct<T>(&mut self,
_name: &'static str,
value: T) -> Result<(), Self::Error>
where T: ser::Serialize,
{
// Don't serialize the newtype struct in a tuple.
value.serialize(self)
}
fn serialize_newtype_variant<T>(&mut self,
_name: &'static str,
_variant_index: usize,
_variant: &'static str,
value: T) -> Result<(), Self::Error>
where T: ser::Serialize,
{
// Don't serialize the newtype struct variant in a tuple.
value.serialize(self)
}
fn serialize_unit_variant(&mut self,
_name: &'static str,
_variant_index: usize,
_variant: &'static str,
) -> Result<(), Self::Error>
{
Ok(())
}
}
impl ser::Serialize for Value {
fn serialize<E>(&self, e: &mut E) -> Result<(), E::Error>
where E: ser::Serializer
{
match *self {
Value::String(ref s) => e.serialize_str(s),
Value::Integer(i) => e.serialize_i64(i),
Value::Float(f) => e.serialize_f64(f),
Value::Boolean(b) => e.serialize_bool(b),
Value::Datetime(ref s) => e.serialize_str(s),
Value::Array(ref a) => {
let mut state = try!(e.serialize_seq(Some(a.len())));
for el in a.iter() {
try!(e.serialize_seq_elt(&mut state, el));
}
e.serialize_seq_end(state)
}
Value::Table(ref t) => {
let mut state = try!(e.serialize_map(Some(t.len())));
for (k, v) in t.iter() {
try!(e.serialize_map_key(&mut state, k));
try!(e.serialize_map_value(&mut state, v));
}
e.serialize_map_end(state)
}
}
}
}
impl ser::Error for Error {
fn custom<T: Into<String>>(msg: T) -> Error {
Error::Custom(msg.into())
}
}

View file

@ -1,507 +1,169 @@
//! A TOML-parsing library //! A [TOML]-parsing library
//! //!
//! This library is an implementation in Rust of a parser for TOML configuration //! [TOML]: https://github.com/toml-lang/toml
//! files [1]. It is focused around high quality errors including specific spans
//! and detailed error messages when things go wrong.
//! //!
//! This implementation currently passes the language agnostic [test suite][2]. //! This library implements a [TOML] v0.4.0 compatible parser. This crate also
//! primarily supports the [`serde`] library for encoding/decoding support to
//! various types in Rust.
//! //!
//! # Example //! TOML itself is a simple, ergonomic, and readable configuration format:
//! //!
//! ``` //! ```toml
//! let toml = r#" //! [package]
//! [test] //! name = "toml"
//! foo = "bar" //! version = "0.2.1"
//! "#; //! authors = ["Alex Crichton <alex@alexcrichton.com>"]
//! //!
//! let value = toml::Parser::new(toml).parse().unwrap(); //! [dependencies]
//! println!("{:?}", value); //! serde = "0.9"
//! ``` //! ```
//! //!
//! # Conversions //! The TOML format tends to be relatively common throughout the Rust community
//! for configuration, notably being used by [Cargo], Rust's package manager.
//! //!
//! This library also supports using the standard `Encodable` and `Decodable` //! ## TOML values
//! traits with TOML values. This library provides the following conversion
//! capabilities:
//! //!
//! * `String` => `toml::Value` - via `Parser` //! A value in TOML is represented with the `Value` enum in this crate:
//! * `toml::Value` => `String` - via `Display`
//! * `toml::Value` => rust object - via `Decoder`
//! * rust object => `toml::Value` - via `Encoder`
//! //!
//! Convenience functions for performing multiple conversions at a time are also //! ```rust,ignore
//! provided. //! pub enum Value {
//! String(String),
//! Integer(i64),
//! Float(f64),
//! Boolean(bool),
//! Datetime(Datetime),
//! Array(Array),
//! Table(Table),
//! }
//! ```
//! //!
//! [1]: https://github.com/mojombo/toml //! You'll note that TOML is very similar to JSON with the notable addition of a
//! [2]: https://github.com/BurntSushi/toml-test //! `Datetime` type. In general TOML and JSON are interchangeable in terms of
//! formats.
//! //!
//! # Encoding support //! ## Parsing TOML
//! //!
//! This crate optionally supports the [`rustc-serialize`] crate and also the //! The easiest way to parse a TOML document is via the `Value` type:
//! [`serde`] crate through respective feature names. The `rustc-serialize`
//! feature is enabled by default.
//! //!
//! [`rustc-serialize`]: http://github.com/rust-lang/rustc-serialize //! ```rust
//! [`serde`]: http://github.com/serde-rs/serde //! use toml::Value;
//!
//! let value = "foo = 'bar'".parse::<Value>().unwrap();
//!
//! assert_eq!(value["foo"].as_str(), Some("bar"));
//! ```
//!
//! The `Value` type implements a number of convenience methods and traits,
//! where the example above is using `FromStr` to parse a `str` into a `Value`.
//!
//! ## Deserialization and Serialization
//!
//! This crate currently supports [`serde`] 0.9 with a number of
//! implementations of the `Deserialize`, `Serialize`, `Deserializer`, and
//! `Serializer` traits. Namely, you'll find in this crate:
//!
//! * `Deserialize for Value`
//! * `Serialize for Value`
//! * `Deserialize for Datetime`
//! * `Serialize for Datetime`
//!
//! * `Deserializer for de::Deserializer`
//! * `Serializer for ser::Serializer`
//! * `Deserializer for Value`
//!
//! This notably means that you can use Serde to deserialize/serialize the
//! `Value` type as well as the `Datetime` type in this crate. Similarly you can
//! use the `Deserializer`, `Serializer`, or `Value` type itself to act as
//! a deserializer/serializer for arbitrary types.
//!
//! An example of deserializing with TOML is:
//!
//! ```rust
//! #[macro_use]
//! extern crate serde_derive;
//! extern crate toml;
//!
//! #[derive(Deserialize)]
//! struct Config {
//! ip: String,
//! port: Option<u16>,
//! keys: Keys,
//! }
//!
//! #[derive(Deserialize)]
//! struct Keys {
//! github: String,
//! travis: Option<String>,
//! }
//!
//! fn main() {
//! let config: Config = toml::from_str(r#"
//! ip = '127.0.0.1'
//!
//! [keys]
//! github = 'xxxxxxxxxxxxxxxxx'
//! travis = 'yyyyyyyyyyyyyyyyy'
//! "#).unwrap();
//!
//! assert_eq!(config.ip, "127.0.0.1");
//! assert_eq!(config.port, None);
//! assert_eq!(config.keys.github, "xxxxxxxxxxxxxxxxx");
//! assert_eq!(config.keys.travis.as_ref().unwrap(), "yyyyyyyyyyyyyyyyy");
//! }
//! ```
//!
//! Similarly you can serialize types in a similar fashion:
//!
//! ```rust
//! #[macro_use]
//! extern crate serde_derive;
//! extern crate toml;
//!
//! #[derive(Serialize)]
//! struct Config {
//! ip: String,
//! port: Option<u16>,
//! keys: Keys,
//! }
//!
//! #[derive(Serialize)]
//! struct Keys {
//! github: String,
//! travis: Option<String>,
//! }
//!
//! fn main() {
//! let config = Config {
//! ip: "127.0.0.1".to_string(),
//! port: None,
//! keys: Keys {
//! github: "xxxxxxxxxxxxxxxxx".to_string(),
//! travis: Some("yyyyyyyyyyyyyyyyy".to_string()),
//! },
//! };
//!
//! let toml = toml::to_string(&config).unwrap();
//! }
//! ```
//!
//! [Cargo]: https://crates.io/
//! [`serde`]: https://serde.rs/
#![doc(html_root_url = "http://alexcrichton.com/toml-rs")] #![doc(html_root_url = "https://docs.rs/toml/0.3")]
#![deny(missing_docs)] #![deny(missing_docs)]
#![cfg_attr(test, deny(warnings))]
#[cfg(feature = "rustc-serialize")] extern crate rustc_serialize; #[macro_use]
#[cfg(feature = "serde")] extern crate serde; extern crate serde;
use std::collections::BTreeMap; pub mod value;
use std::str::FromStr; mod datetime;
#[doc(no_inline)]
pub use parser::{Parser, ParserError}; pub use value::Value;
#[cfg(any(feature = "rustc-serialize", feature = "serde"))] pub mod ser;
pub use self::encoder::{Encoder, Error, EncoderState, encode, encode_str}; #[doc(no_inline)]
#[cfg(any(feature = "rustc-serialize", feature = "serde"))] pub use ser::{to_string, to_vec, Serializer};
pub use self::decoder::{Decoder, DecodeError, DecodeErrorKind, decode, decode_str}; pub mod de;
#[doc(no_inline)]
mod parser; pub use de::{from_slice, from_str, Deserializer};
mod display; mod tokens;
#[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)]
#[allow(missing_docs)]
pub enum Value {
String(String),
Integer(i64),
Float(f64),
Boolean(bool),
Datetime(String),
Array(Array),
Table(Table),
}
/// Type representing a TOML array, payload of the `Value::Array` variant
pub type Array = Vec<Value>;
/// Type representing a TOML table, payload of the `Value::Table` variant
pub type Table = BTreeMap<String, Value>;
impl Value {
/// Tests whether this and another value have the same type.
pub fn same_type(&self, other: &Value) -> bool {
match (self, other) {
(&Value::String(..), &Value::String(..)) |
(&Value::Integer(..), &Value::Integer(..)) |
(&Value::Float(..), &Value::Float(..)) |
(&Value::Boolean(..), &Value::Boolean(..)) |
(&Value::Datetime(..), &Value::Datetime(..)) |
(&Value::Array(..), &Value::Array(..)) |
(&Value::Table(..), &Value::Table(..)) => true,
_ => false,
}
}
/// Returns a human-readable representation of the type of this value.
pub fn type_str(&self) -> &'static str {
match *self {
Value::String(..) => "string",
Value::Integer(..) => "integer",
Value::Float(..) => "float",
Value::Boolean(..) => "boolean",
Value::Datetime(..) => "datetime",
Value::Array(..) => "array",
Value::Table(..) => "table",
}
}
/// Extracts the string of this value if it is a string.
pub fn as_str(&self) -> Option<&str> {
match *self { Value::String(ref s) => Some(&**s), _ => None }
}
/// Extracts the integer value if it is an integer.
pub fn as_integer(&self) -> Option<i64> {
match *self { Value::Integer(i) => Some(i), _ => None }
}
/// Extracts the float value if it is a float.
pub fn as_float(&self) -> Option<f64> {
match *self { Value::Float(f) => Some(f), _ => None }
}
/// Extracts the boolean value if it is a boolean.
pub fn as_bool(&self) -> Option<bool> {
match *self { Value::Boolean(b) => Some(b), _ => None }
}
/// Extracts the datetime value if it is a datetime.
///
/// Note that a parsed TOML value will only contain ISO 8601 dates. An
/// example date is:
///
/// ```notrust
/// 1979-05-27T07:32:00Z
/// ```
pub fn as_datetime(&self) -> Option<&str> {
match *self { Value::Datetime(ref s) => Some(&**s), _ => None }
}
/// Extracts the array value if it is an array.
pub fn as_slice(&self) -> Option<&[Value]> {
match *self { Value::Array(ref s) => Some(&**s), _ => None }
}
/// Extracts the table value if it is a table.
pub fn as_table(&self) -> Option<&Table> {
match *self { Value::Table(ref s) => Some(s), _ => None }
}
/// Lookups for value at specified path.
///
/// Uses '.' as a path separator.
///
/// Note: arrays have zero-based indexes.
///
/// Note: empty path returns self.
///
/// ```
/// # #![allow(unstable)]
/// let toml = r#"
/// [test]
/// foo = "bar"
///
/// [[values]]
/// foo = "baz"
///
/// [[values]]
/// foo = "qux"
/// "#;
/// let value: toml::Value = toml.parse().unwrap();
///
/// let foo = value.lookup("test.foo").unwrap();
/// assert_eq!(foo.as_str().unwrap(), "bar");
///
/// let foo = value.lookup("values.1.foo").unwrap();
/// assert_eq!(foo.as_str().unwrap(), "qux");
///
/// let no_bar = value.lookup("test.bar");
/// assert_eq!(no_bar.is_none(), true);
/// ```
pub fn lookup(&self, path: &str) -> Option<&Value> {
let ref path = match Parser::new(path).lookup() {
Some(path) => path,
None => return None,
};
let mut cur_value = self;
if path.is_empty() {
return Some(cur_value)
}
for key in path {
match *cur_value {
Value::Table(ref hm) => {
match hm.get(key) {
Some(v) => cur_value = v,
None => return None
}
},
Value::Array(ref v) => {
match key.parse::<usize>().ok() {
Some(idx) if idx < v.len() => cur_value = &v[idx],
_ => return None
}
},
_ => return None
}
};
Some(cur_value)
}
/// Lookups for mutable value at specified path.
///
/// Uses '.' as a path separator.
///
/// Note: arrays have zero-based indexes.
///
/// Note: empty path returns self.
///
/// ```
/// # #![allow(unstable)]
/// let toml = r#"
/// [test]
/// foo = "bar"
///
/// [[values]]
/// foo = "baz"
///
/// [[values]]
/// foo = "qux"
/// "#;
/// let mut value: toml::Value = toml.parse().unwrap();
/// {
/// let string = value.lookup_mut("test.foo").unwrap();
/// assert_eq!(string, &mut toml::Value::String(String::from("bar")));
/// *string = toml::Value::String(String::from("foo"));
/// }
/// let result = value.lookup_mut("test.foo").unwrap();
/// assert_eq!(result.as_str().unwrap(), "foo");
/// ```
pub fn lookup_mut(&mut self, path: &str) -> Option<&mut Value> {
let ref path = match Parser::new(path).lookup() {
Some(path) => path,
None => return None,
};
let mut cur = self;
if path.is_empty() {
return Some(cur)
}
for key in path {
let tmp = cur;
match *tmp {
Value::Table(ref mut hm) => {
match hm.get_mut(key) {
Some(v) => cur = v,
None => return None
}
}
Value::Array(ref mut v) => {
match key.parse::<usize>().ok() {
Some(idx) if idx < v.len() => cur = &mut v[idx],
_ => return None
}
}
_ => return None
}
}
Some(cur)
}
}
impl FromStr for Value {
type Err = Vec<ParserError>;
fn from_str(s: &str) -> Result<Value, Vec<ParserError>> {
let mut p = Parser::new(s);
match p.parse().map(Value::Table) {
Some(n) => Ok(n),
None => Err(p.errors),
}
}
}
#[cfg(test)]
mod tests {
use super::Value;
#[test]
fn lookup_mut_change() {
let toml = r#"
[test]
foo = "bar"
[[values]]
foo = "baz"
[[values]]
foo = "qux"
"#;
let mut value: Value = toml.parse().unwrap();
{
let foo = value.lookup_mut("values.0.foo").unwrap();
*foo = Value::String(String::from("bar"));
}
let foo = value.lookup("values.0.foo").unwrap();
assert_eq!(foo.as_str().unwrap(), "bar");
}
#[test]
fn lookup_mut_valid() {
let toml = r#"
[test]
foo = "bar"
[[values]]
foo = "baz"
[[values]]
foo = "qux"
"#;
let mut value: Value = toml.parse().unwrap();
{
let test_foo = value.lookup_mut("test.foo").unwrap();
assert_eq!(test_foo.as_str().unwrap(), "bar");
}
{
let foo1 = value.lookup_mut("values.1.foo").unwrap();
assert_eq!(foo1.as_str().unwrap(), "qux");
}
assert!(value.lookup_mut("test.bar").is_none());
assert!(value.lookup_mut("test.foo.bar").is_none());
}
#[test]
fn lookup_mut_invalid_index() {
let toml = r#"
[[values]]
foo = "baz"
"#;
let mut value: Value = toml.parse().unwrap();
{
let foo = value.lookup_mut("test.foo");
assert!(foo.is_none());
}
{
let foo = value.lookup_mut("values.100.foo");
assert!(foo.is_none());
}
{
let foo = value.lookup_mut("values.str.foo");
assert!(foo.is_none());
}
}
#[test]
fn lookup_mut_self() {
let mut value: Value = r#"foo = "bar""#.parse().unwrap();
{
let foo = value.lookup_mut("foo").unwrap();
assert_eq!(foo.as_str().unwrap(), "bar");
}
let foo = value.lookup_mut("").unwrap();
assert!(foo.as_table().is_some());
let baz = foo.lookup_mut("foo").unwrap();
assert_eq!(baz.as_str().unwrap(), "bar");
}
#[test]
fn lookup_valid() {
let toml = r#"
[test]
foo = "bar"
[[values]]
foo = "baz"
[[values]]
foo = "qux"
"#;
let value: Value = toml.parse().unwrap();
let test_foo = value.lookup("test.foo").unwrap();
assert_eq!(test_foo.as_str().unwrap(), "bar");
let foo1 = value.lookup("values.1.foo").unwrap();
assert_eq!(foo1.as_str().unwrap(), "qux");
assert!(value.lookup("test.bar").is_none());
assert!(value.lookup("test.foo.bar").is_none());
}
#[test]
fn lookup_invalid_index() {
let toml = r#"
[[values]]
foo = "baz"
"#;
let value: Value = toml.parse().unwrap();
let foo = value.lookup("test.foo");
assert!(foo.is_none());
let foo = value.lookup("values.100.foo");
assert!(foo.is_none());
let foo = value.lookup("values.str.foo");
assert!(foo.is_none());
}
#[test]
fn lookup_self() {
let value: Value = r#"foo = "bar""#.parse().unwrap();
let foo = value.lookup("foo").unwrap();
assert_eq!(foo.as_str().unwrap(), "bar");
let foo = value.lookup("").unwrap();
assert!(foo.as_table().is_some());
let baz = foo.lookup("foo").unwrap();
assert_eq!(baz.as_str().unwrap(), "bar");
}
#[test]
fn lookup_advanced() {
let value: Value = "[table]\n\"value\" = 0".parse().unwrap();
let looked = value.lookup("table.\"value\"").unwrap();
assert_eq!(*looked, Value::Integer(0));
}
#[test]
fn lookup_advanced_table() {
let value: Value = "[table.\"name.other\"]\nvalue = \"my value\"".parse().unwrap();
let looked = value.lookup(r#"table."name.other".value"#).unwrap();
assert_eq!(*looked, Value::String(String::from("my value")));
}
#[test]
fn lookup_mut_advanced() {
let mut value: Value = "[table]\n\"value\" = [0, 1, 2]".parse().unwrap();
let looked = value.lookup_mut("table.\"value\".1").unwrap();
assert_eq!(*looked, Value::Integer(1));
}
#[test]
fn single_dot() {
let value: Value = "[table]\n\"value\" = [0, 1, 2]".parse().unwrap();
assert_eq!(None, value.lookup("."));
}
#[test]
fn array_dot() {
let value: Value = "[table]\n\"value\" = [0, 1, 2]".parse().unwrap();
assert_eq!(None, value.lookup("0."));
}
#[test]
fn dot_inside() {
let value: Value = "[table]\n\"value\" = [0, 1, 2]".parse().unwrap();
assert_eq!(None, value.lookup("table.\"value.0\""));
}
#[test]
fn table_with_quotes() {
let value: Value = "[table.\"element\"]\n\"value\" = [0, 1, 2]".parse().unwrap();
assert_eq!(None, value.lookup("\"table.element\".\"value\".0"));
}
#[test]
fn table_with_quotes_2() {
let value: Value = "[table.\"element\"]\n\"value\" = [0, 1, 2]".parse().unwrap();
assert_eq!(Value::Integer(0), *value.lookup("table.\"element\".\"value\".0").unwrap());
}
#[test]
fn control_characters() {
let value = Value::String("\x05".to_string());
assert_eq!(value.to_string(), r#""\u0005""#);
}
}

File diff suppressed because it is too large Load diff

1006
src/ser.rs Normal file

File diff suppressed because it is too large Load diff

620
src/tokens.rs Normal file
View file

@ -0,0 +1,620 @@
use std::borrow::Cow;
use std::char;
use std::str;
use std::string;
use self::Token::*;
#[derive(Eq, PartialEq, Debug)]
pub enum Token<'a> {
Whitespace(&'a str),
Newline,
Comment(&'a str),
Equals,
Period,
Comma,
Colon,
Plus,
LeftBrace,
RightBrace,
LeftBracket,
RightBracket,
Keylike(&'a str),
String { src: &'a str, val: Cow<'a, str> },
}
#[derive(Eq, PartialEq, Debug)]
pub enum Error {
InvalidCharInString(usize, char),
InvalidEscape(usize, char),
InvalidHexEscape(usize, char),
InvalidEscapeValue(usize, u32),
NewlineInString(usize),
Unexpected(usize, char),
UnterminatedString(usize),
NewlineInTableKey(usize),
EmptyTableKey(usize),
Wanted { at: usize, expected: &'static str, found: &'static str },
}
#[derive(Clone)]
pub struct Tokenizer<'a> {
input: &'a str,
chars: CrlfFold<'a>,
}
#[derive(Clone)]
struct CrlfFold<'a> {
chars: str::CharIndices<'a>,
}
#[derive(Debug)]
enum MaybeString {
NotEscaped(usize),
Owned(string::String),
}
impl<'a> Tokenizer<'a> {
pub fn new(input: &'a str) -> Tokenizer<'a> {
let mut t = Tokenizer {
input: input,
chars: CrlfFold {
chars: input.char_indices(),
},
};
// Eat utf-8 BOM
t.eatc('\u{feff}');
return t
}
pub fn next(&mut self) -> Result<Option<Token<'a>>, Error> {
let token = match self.chars.next() {
Some((_, '\n')) => Newline,
Some((start, ' ')) => self.whitespace_token(start),
Some((start, '\t')) => self.whitespace_token(start),
Some((start, '#')) => self.comment_token(start),
Some((_, '=')) => Equals,
Some((_, '.')) => Period,
Some((_, ',')) => Comma,
Some((_, ':')) => Colon,
Some((_, '+')) => Plus,
Some((_, '{')) => LeftBrace,
Some((_, '}')) => RightBrace,
Some((_, '[')) => LeftBracket,
Some((_, ']')) => RightBracket,
Some((start, '\'')) => return self.literal_string(start).map(Some),
Some((start, '"')) => return self.basic_string(start).map(Some),
Some((start, ch)) if is_keylike(ch) => self.keylike(start),
Some((start, ch)) => return Err(Error::Unexpected(start, ch)),
None => return Ok(None),
};
Ok(Some(token))
}
pub fn peek(&mut self) -> Result<Option<Token<'a>>, Error> {
self.clone().next()
}
pub fn eat(&mut self, expected: Token<'a>) -> Result<bool, Error> {
match self.peek()? {
Some(ref found) if expected == *found => {}
Some(_) => return Ok(false),
None => return Ok(false),
}
drop(self.next());
Ok(true)
}
pub fn expect(&mut self, expected: Token<'a>) -> Result<(), Error> {
let current = self.current();
match self.next()? {
Some(found) => {
if expected == found {
Ok(())
} else {
Err(Error::Wanted {
at: current,
expected: expected.describe(),
found: found.describe(),
})
}
}
None => {
Err(Error::Wanted {
at: self.input.len(),
expected: expected.describe(),
found: "eof",
})
}
}
}
pub fn table_key(&mut self) -> Result<Cow<'a, str>, Error> {
let current = self.current();
match self.next()? {
Some(Token::Keylike(k)) => Ok(k.into()),
Some(Token::String { src, val }) => {
let offset = self.substr_offset(src);
if val == "" {
return Err(Error::EmptyTableKey(offset))
}
match src.find("\n") {
None => Ok(val),
Some(i) => Err(Error::NewlineInTableKey(offset + i)),
}
}
Some(other) => {
Err(Error::Wanted {
at: current,
expected: "a table key",
found: other.describe(),
})
}
None => {
Err(Error::Wanted {
at: self.input.len(),
expected: "a table key",
found: "eof",
})
}
}
}
pub fn eat_whitespace(&mut self) -> Result<(), Error> {
while self.eatc(' ') || self.eatc('\t') {
// ...
}
Ok(())
}
pub fn eat_comment(&mut self) -> Result<bool, Error> {
if !self.eatc('#') {
return Ok(false)
}
drop(self.comment_token(0));
self.eat_newline_or_eof().map(|()| true)
}
pub fn eat_newline_or_eof(&mut self) -> Result<(), Error> {
let current = self.current();
match self.next()? {
None |
Some(Token::Newline) => Ok(()),
Some(other) => {
Err(Error::Wanted {
at: current,
expected: "newline",
found: other.describe(),
})
}
}
}
pub fn skip_to_newline(&mut self) {
loop {
match self.chars.next() {
Some((_, '\n')) |
None => break,
_ => {}
}
}
}
fn eatc(&mut self, ch: char) -> bool {
match self.chars.clone().next() {
Some((_, ch2)) if ch == ch2 => {
self.chars.next();
true
}
_ => false,
}
}
pub fn current(&mut self) -> usize {
self.chars.clone().next().map(|i| i.0).unwrap_or(self.input.len())
}
pub fn input(&self) -> &'a str {
self.input
}
fn whitespace_token(&mut self, start: usize) -> Token<'a> {
while self.eatc(' ') || self.eatc('\t') {
// ...
}
Whitespace(&self.input[start..self.current()])
}
fn comment_token(&mut self, start: usize) -> Token<'a> {
while let Some((_, ch)) = self.chars.clone().next() {
if ch != '\t' && (ch < '\u{20}' || ch > '\u{10ffff}') {
break
}
self.chars.next();
}
Comment(&self.input[start..self.current()])
}
fn read_string(&mut self,
delim: char,
start: usize,
new_ch: &mut FnMut(&mut Tokenizer, &mut MaybeString,
bool, usize, char)
-> Result<(), Error>)
-> Result<Token<'a>, Error> {
let mut multiline = false;
if self.eatc(delim) {
if self.eatc(delim) {
multiline = true;
} else {
return Ok(String {
src: &self.input[start..start+2],
val: Cow::Borrowed(""),
})
}
}
let mut val = MaybeString::NotEscaped(self.current());
let mut n = 0;
'outer: loop {
n += 1;
match self.chars.next() {
Some((i, '\n')) => {
if multiline {
if self.input.as_bytes()[i] == b'\r' {
val.to_owned(&self.input[..i]);
}
if n == 1 {
val = MaybeString::NotEscaped(self.current());
} else {
val.push('\n');
}
continue
} else {
return Err(Error::NewlineInString(i))
}
}
Some((i, ch)) if ch == delim => {
if multiline {
for _ in 0..2 {
if !self.eatc(delim) {
val.push(delim);
continue 'outer
}
}
}
return Ok(String {
src: &self.input[start..self.current()],
val: val.into_cow(&self.input[..i]),
})
}
Some((i, c)) => try!(new_ch(self, &mut val, multiline, i, c)),
None => return Err(Error::UnterminatedString(start))
}
}
}
fn literal_string(&mut self, start: usize) -> Result<Token<'a>, Error> {
self.read_string('\'', start, &mut |_me, val, _multi, i, ch| {
if ch == '\u{09}' || ('\u{20}' <= ch && ch <= '\u{10ffff}') {
val.push(ch);
Ok(())
} else {
Err(Error::InvalidCharInString(i, ch))
}
})
}
fn basic_string(&mut self, start: usize) -> Result<Token<'a>, Error> {
self.read_string('"', start, &mut |me, val, multi, i, ch| {
match ch {
'\\' => {
val.to_owned(&me.input[..i]);
match me.chars.next() {
Some((_, '"')) => val.push('"'),
Some((_, '\\')) => val.push('\\'),
Some((_, 'b')) => val.push('\u{8}'),
Some((_, 'f')) => val.push('\u{c}'),
Some((_, 'n')) => val.push('\n'),
Some((_, 'r')) => val.push('\r'),
Some((_, 't')) => val.push('\t'),
Some((i, c @ 'u')) |
Some((i, c @ 'U')) => {
let len = if c == 'u' {4} else {8};
val.push(try!(me.hex(start, i, len)));
}
Some((_, '\n')) if multi => {
while let Some((_, ch)) = me.chars.clone().next() {
match ch {
' ' | '\t' | '\n' => {
me.chars.next();
}
_ => break,
}
}
}
Some((i, c)) => return Err(Error::InvalidEscape(i, c)),
None => return Err(Error::UnterminatedString(start)),
}
Ok(())
}
ch if '\u{20}' <= ch && ch <= '\u{10ffff}' => {
val.push(ch);
Ok(())
}
_ => Err(Error::InvalidCharInString(i, ch))
}
})
}
fn hex(&mut self, start: usize, i: usize, len: usize) -> Result<char, Error> {
let mut val = 0;
for _ in 0..len {
match self.chars.next() {
Some((_, ch)) if '0' <= ch && ch <= '9' => {
val = val * 16 + (ch as u32 - '0' as u32);
}
Some((_, ch)) if 'A' <= ch && ch <= 'F' => {
val = val * 16 + (ch as u32 - 'A' as u32) + 10;
}
Some((i, ch)) => return Err(Error::InvalidHexEscape(i, ch)),
None => return Err(Error::UnterminatedString(start)),
}
}
match char::from_u32(val) {
Some(ch) => Ok(ch),
None => Err(Error::InvalidEscapeValue(i, val)),
}
}
fn keylike(&mut self, start: usize) -> Token<'a> {
while let Some((_, ch)) = self.chars.clone().next() {
if !is_keylike(ch) {
break
}
self.chars.next();
}
Keylike(&self.input[start..self.current()])
}
pub fn substr_offset(&self, s: &'a str) -> usize {
assert!(s.len() < self.input.len());
let a = self.input.as_ptr() as usize;
let b = s.as_ptr() as usize;
assert!(a <= b);
b - a
}
}
impl<'a> Iterator for CrlfFold<'a> {
type Item = (usize, char);
fn next(&mut self) -> Option<(usize, char)> {
self.chars.next().map(|(i, c)| {
if c == '\r' {
let mut attempt = self.chars.clone();
if let Some((_, '\n')) = attempt.next() {
self.chars = attempt;
return (i, '\n')
}
}
(i, c)
})
}
}
impl MaybeString {
fn push(&mut self, ch: char) {
match *self {
MaybeString::NotEscaped(..) => {}
MaybeString::Owned(ref mut s) => s.push(ch),
}
}
fn to_owned(&mut self, input: &str) {
match *self {
MaybeString::NotEscaped(start) => {
*self = MaybeString::Owned(input[start..].to_owned());
}
MaybeString::Owned(..) => {}
}
}
fn into_cow<'a>(self, input: &'a str) -> Cow<'a, str> {
match self {
MaybeString::NotEscaped(start) => Cow::Borrowed(&input[start..]),
MaybeString::Owned(s) => Cow::Owned(s),
}
}
}
fn is_keylike(ch: char) -> bool {
('A' <= ch && ch <= 'Z') ||
('a' <= ch && ch <= 'z') ||
('0' <= ch && ch <= '9') ||
ch == '-' ||
ch == '_'
}
impl<'a> Token<'a> {
pub fn describe(&self) -> &'static str {
match *self {
Token::Keylike(_) => "an identifier",
Token::Equals => "an equals",
Token::Period => "a period",
Token::Comment(_) => "a comment",
Token::Newline => "a newline",
Token::Whitespace(_) => "whitespace",
Token::Comma => "a comma",
Token::RightBrace => "a right brace",
Token::LeftBrace => "a left brace",
Token::RightBracket => "a right bracket",
Token::LeftBracket => "a left bracket",
Token::String { .. } => "a string",
Token::Colon => "a colon",
Token::Plus => "a plus",
}
}
}
#[cfg(test)]
mod tests {
use std::borrow::Cow;
use super::{Tokenizer, Token, Error};
fn err(input: &str, err: Error) {
let mut t = Tokenizer::new(input);
let token = t.next().unwrap_err();
assert_eq!(token, err);
assert!(t.next().unwrap().is_none());
}
#[test]
fn literal_strings() {
fn t(input: &str, val: &str) {
let mut t = Tokenizer::new(input);
let token = t.next().unwrap().unwrap();
assert_eq!(token, Token::String {
src: input,
val: Cow::Borrowed(val),
});
assert!(t.next().unwrap().is_none());
}
t("''", "");
t("''''''", "");
t("'''\n'''", "");
t("'a'", "a");
t("'\"a'", "\"a");
t("''''a'''", "'a");
t("'''\n'a\n'''", "'a\n");
t("'''a\n'a\r\n'''", "a\n'a\n");
}
#[test]
fn basic_strings() {
fn t(input: &str, val: &str) {
let mut t = Tokenizer::new(input);
let token = t.next().unwrap().unwrap();
assert_eq!(token, Token::String {
src: input,
val: Cow::Borrowed(val),
});
assert!(t.next().unwrap().is_none());
}
t(r#""""#, "");
t(r#""""""""#, "");
t(r#""a""#, "a");
t(r#""""a""""#, "a");
t(r#""\t""#, "\t");
t(r#""\u0000""#, "\0");
t(r#""\U00000000""#, "\0");
t(r#""\U000A0000""#, "\u{A0000}");
t(r#""\\t""#, "\\t");
t("\"\"\"\\\n\"\"\"", "");
t("\"\"\"\\\n \t \t \\\r\n \t \n \t \r\n\"\"\"", "");
t(r#""\r""#, "\r");
t(r#""\n""#, "\n");
t(r#""\b""#, "\u{8}");
t(r#""a\fa""#, "a\u{c}a");
t(r#""\"a""#, "\"a");
t("\"\"\"\na\"\"\"", "a");
t("\"\"\"\n\"\"\"", "");
err(r#""\a"#, Error::InvalidEscape(2, 'a'));
err("\"\\\n", Error::InvalidEscape(2, '\n'));
err("\"\\\r\n", Error::InvalidEscape(2, '\n'));
err("\"\\", Error::UnterminatedString(0));
err("\"\u{0}", Error::InvalidCharInString(1, '\u{0}'));
err(r#""\U00""#, Error::InvalidHexEscape(5, '"'));
err(r#""\U00"#, Error::UnterminatedString(0));
err(r#""\uD800"#, Error::InvalidEscapeValue(2, 0xd800));
err(r#""\UFFFFFFFF"#, Error::InvalidEscapeValue(2, 0xffffffff));
}
#[test]
fn keylike() {
fn t(input: &str) {
let mut t = Tokenizer::new(input);
let token = t.next().unwrap().unwrap();
assert_eq!(token, Token::Keylike(input));
assert!(t.next().unwrap().is_none());
}
t("foo");
t("0bar");
t("bar0");
t("1234");
t("a-b");
t("a_B");
t("-_-");
t("___");
}
#[test]
fn all() {
fn t(input: &str, expected: &[Token]) {
let mut tokens = Tokenizer::new(input);
let mut actual = Vec::new();
while let Some(token) = tokens.next().unwrap() {
actual.push(token);
}
for (a, b) in actual.iter().zip(expected) {
assert_eq!(a, b);
}
assert_eq!(actual.len(), expected.len());
}
t(" a ", &[
Token::Whitespace(" "),
Token::Keylike("a"),
Token::Whitespace(" "),
]);
t(" a\t [[]] \t [] {} , . =\n# foo \r\n#foo \n ", &[
Token::Whitespace(" "),
Token::Keylike("a"),
Token::Whitespace("\t "),
Token::LeftBracket,
Token::LeftBracket,
Token::RightBracket,
Token::RightBracket,
Token::Whitespace(" \t "),
Token::LeftBracket,
Token::RightBracket,
Token::Whitespace(" "),
Token::LeftBrace,
Token::RightBrace,
Token::Whitespace(" "),
Token::Comma,
Token::Whitespace(" "),
Token::Period,
Token::Whitespace(" "),
Token::Equals,
Token::Newline,
Token::Comment("# foo "),
Token::Newline,
Token::Comment("#foo "),
Token::Newline,
Token::Whitespace(" "),
]);
}
#[test]
fn bare_cr_bad() {
err("\r", Error::Unexpected(0, '\r'));
err("'\n", Error::NewlineInString(1));
err("'\u{0}", Error::InvalidCharInString(1, '\u{0}'));
err("'", Error::UnterminatedString(0));
err("\u{0}", Error::Unexpected(0, '\u{0}'));
}
#[test]
fn bad_comment() {
let mut t = Tokenizer::new("#\u{0}");
t.next().unwrap().unwrap();
assert_eq!(t.next(), Err(Error::Unexpected(1, '\u{0}')));
assert!(t.next().unwrap().is_none());
}
}

894
src/value.rs Normal file
View file

@ -0,0 +1,894 @@
//! Definition of a TOML value
use std::collections::BTreeMap;
use std::fmt;
use std::ops;
use std::str::FromStr;
use std::vec;
use serde::ser;
use serde::de;
pub use datetime::{Datetime, DatetimeParseError};
use datetime::{DatetimeFromString, SERDE_STRUCT_FIELD_NAME};
/// Representation of a TOML value.
#[derive(PartialEq, Clone, Debug)]
pub enum Value {
/// Represents a TOML string
String(String),
/// Represents a TOML integer
Integer(i64),
/// Represents a TOML float
Float(f64),
/// Represents a TOML boolean
Boolean(bool),
/// Represents a TOML datetime
Datetime(Datetime),
/// Represents a TOML array
Array(Array),
/// Represents a TOML table
Table(Table),
}
/// Type representing a TOML array, payload of the `Value::Array` variant
pub type Array = Vec<Value>;
/// Type representing a TOML table, payload of the `Value::Table` variant
pub type Table = BTreeMap<String, Value>;
impl Value {
/// Convert a `T` into `toml::Value` which is an enum that can represent
/// any valid TOML data.
///
/// This conversion can fail if `T`'s implementation of `Serialize` decides to
/// fail, or if `T` contains a map with non-string keys.
pub fn try_from<T>(value: T) -> Result<Value, ::ser::Error>
where T: ser::Serialize,
{
value.serialize(Serializer)
}
/// Interpret a `toml::Value` as an instance of type `T`.
///
/// This conversion can fail if the structure of the `Value` does not match the
/// structure expected by `T`, for example if `T` is a struct type but the
/// `Value` contains something other than a TOML table. It can also fail if the
/// structure is correct but `T`'s implementation of `Deserialize` decides that
/// something is wrong with the data, for example required struct fields are
/// missing from the TOML map or some number is too big to fit in the expected
/// primitive type.
pub fn try_into<T>(self) -> Result<T, ::de::Error>
where T: de::Deserialize,
{
de::Deserialize::deserialize(self)
}
/// Index into a TOML array or map. A string index can be used to access a
/// value in a map, and a usize index can be used to access an element of an
/// array.
///
/// Returns `None` if the type of `self` does not match the type of the
/// index, for example if the index is a string and `self` is an array or a
/// number. Also returns `None` if the given key does not exist in the map
/// or the given index is not within the bounds of the array.
pub fn get<I: Index>(&self, index: I) -> Option<&Value> {
index.index(self)
}
/// Mutably index into a TOML array or map. A string index can be used to
/// access a value in a map, and a usize index can be used to access an
/// element of an array.
///
/// Returns `None` if the type of `self` does not match the type of the
/// index, for example if the index is a string and `self` is an array or a
/// number. Also returns `None` if the given key does not exist in the map
/// or the given index is not within the bounds of the array.
pub fn get_mut<I: Index>(&mut self, index: I) -> Option<&mut Value> {
index.index_mut(self)
}
/// Extracts the integer value if it is an integer.
pub fn as_integer(&self) -> Option<i64> {
match *self { Value::Integer(i) => Some(i), _ => None }
}
/// Tests whether this value is an integer
pub fn is_integer(&self) -> bool {
self.as_integer().is_some()
}
/// Extracts the float value if it is a float.
pub fn as_float(&self) -> Option<f64> {
match *self { Value::Float(f) => Some(f), _ => None }
}
/// Tests whether this value is an float
pub fn is_float(&self) -> bool {
self.as_float().is_some()
}
/// Extracts the boolean value if it is a boolean.
pub fn as_bool(&self) -> Option<bool> {
match *self { Value::Boolean(b) => Some(b), _ => None }
}
/// Tests whether this value is an boolg
pub fn is_bool(&self) -> bool {
self.as_bool().is_some()
}
/// Extracts the string of this value if it is a string.
pub fn as_str(&self) -> Option<&str> {
match *self { Value::String(ref s) => Some(&**s), _ => None }
}
/// Tests if this value is a string
pub fn is_str(&self) -> bool {
self.as_str().is_some()
}
/// Extracts the datetime value if it is a datetime.
///
/// Note that a parsed TOML value will only contain ISO 8601 dates. An
/// example date is:
///
/// ```notrust
/// 1979-05-27T07:32:00Z
/// ```
pub fn as_datetime(&self) -> Option<&Datetime> {
match *self { Value::Datetime(ref s) => Some(s), _ => None }
}
/// Tests whether this value is an datetime
pub fn is_datetime(&self) -> bool {
self.as_datetime().is_some()
}
/// Extracts the array value if it is an array.
pub fn as_array(&self) -> Option<&Vec<Value>> {
match *self { Value::Array(ref s) => Some(s), _ => None }
}
/// Extracts the array value if it is an array.
pub fn as_array_mut(&mut self) -> Option<&mut Vec<Value>> {
match *self { Value::Array(ref mut s) => Some(s), _ => None }
}
/// Tests whether this value is an array
pub fn is_array(&self) -> bool {
self.as_array().is_some()
}
/// Extracts the table value if it is a table.
pub fn as_table(&self) -> Option<&Table> {
match *self { Value::Table(ref s) => Some(s), _ => None }
}
/// Extracts the table value if it is a table.
pub fn as_table_mut(&mut self) -> Option<&mut Table> {
match *self { Value::Table(ref mut s) => Some(s), _ => None }
}
/// Extracts the table value if it is a table.
pub fn is_table(&self) -> bool {
self.as_table().is_some()
}
/// Tests whether this and another value have the same type.
pub fn same_type(&self, other: &Value) -> bool {
match (self, other) {
(&Value::String(..), &Value::String(..)) |
(&Value::Integer(..), &Value::Integer(..)) |
(&Value::Float(..), &Value::Float(..)) |
(&Value::Boolean(..), &Value::Boolean(..)) |
(&Value::Datetime(..), &Value::Datetime(..)) |
(&Value::Array(..), &Value::Array(..)) |
(&Value::Table(..), &Value::Table(..)) => true,
_ => false,
}
}
/// Returns a human-readable representation of the type of this value.
pub fn type_str(&self) -> &'static str {
match *self {
Value::String(..) => "string",
Value::Integer(..) => "integer",
Value::Float(..) => "float",
Value::Boolean(..) => "boolean",
Value::Datetime(..) => "datetime",
Value::Array(..) => "array",
Value::Table(..) => "table",
}
}
}
impl<I> ops::Index<I> for Value where I: Index {
type Output = Value;
fn index(&self, index: I) -> &Value {
self.get(index).expect("index not found")
}
}
impl<I> ops::IndexMut<I> for Value where I: Index {
fn index_mut(&mut self, index: I) -> &mut Value {
self.get_mut(index).expect("index not found")
}
}
impl From<String> for Value {
fn from(val: String) -> Value {
Value::String(val)
}
}
impl From<i64> for Value {
fn from(val: i64) -> Value {
Value::Integer(val)
}
}
impl From<f64> for Value {
fn from(val: f64) -> Value {
Value::Float(val)
}
}
impl From<bool> for Value {
fn from(val: bool) -> Value {
Value::Boolean(val)
}
}
impl From<Array> for Value {
fn from(val: Array) -> Value {
Value::Array(val)
}
}
impl From<Table> for Value {
fn from(val: Table) -> Value {
Value::Table(val)
}
}
impl From<Datetime> for Value {
fn from(val: Datetime) -> Value {
Value::Datetime(val)
}
}
/// Types that can be used to index a `toml::Value`
///
/// Currently this is implemented for `usize` to index arrays and `str` to index
/// tables.
///
/// This trait is sealed and not intended for implementation outside of the
/// `toml` crate.
pub trait Index: Sealed {
#[doc(hidden)]
fn index<'a>(&self, val: &'a Value) -> Option<&'a Value>;
#[doc(hidden)]
fn index_mut<'a>(&self, val: &'a mut Value) -> Option<&'a mut Value>;
}
/// An implementation detail that should not be implemented, this will change in
/// the future and break code otherwise.
#[doc(hidden)]
pub trait Sealed {}
impl Sealed for usize {}
impl Sealed for str {}
impl Sealed for String {}
impl<'a, T: Sealed + ?Sized> Sealed for &'a T {}
impl Index for usize {
fn index<'a>(&self, val: &'a Value) -> Option<&'a Value> {
match *val {
Value::Array(ref a) => a.get(*self),
_ => None,
}
}
fn index_mut<'a>(&self, val: &'a mut Value) -> Option<&'a mut Value> {
match *val {
Value::Array(ref mut a) => a.get_mut(*self),
_ => None,
}
}
}
impl Index for str {
fn index<'a>(&self, val: &'a Value) -> Option<&'a Value> {
match *val {
Value::Table(ref a) => a.get(self),
_ => None,
}
}
fn index_mut<'a>(&self, val: &'a mut Value) -> Option<&'a mut Value> {
match *val {
Value::Table(ref mut a) => a.get_mut(self),
_ => None,
}
}
}
impl Index for String {
fn index<'a>(&self, val: &'a Value) -> Option<&'a Value> {
self[..].index(val)
}
fn index_mut<'a>(&self, val: &'a mut Value) -> Option<&'a mut Value> {
self[..].index_mut(val)
}
}
impl<'s, T: ?Sized> Index for &'s T where T: Index {
fn index<'a>(&self, val: &'a Value) -> Option<&'a Value> {
(**self).index(val)
}
fn index_mut<'a>(&self, val: &'a mut Value) -> Option<&'a mut Value> {
(**self).index_mut(val)
}
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
::ser::to_string(self).unwrap().fmt(f)
}
}
impl FromStr for Value {
type Err = ::de::Error;
fn from_str(s: &str) -> Result<Value, Self::Err> {
::from_str(s)
}
}
impl ser::Serialize for Value {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: ser::Serializer
{
use serde::ser::SerializeMap;
match *self {
Value::String(ref s) => serializer.serialize_str(s),
Value::Integer(i) => serializer.serialize_i64(i),
Value::Float(f) => serializer.serialize_f64(f),
Value::Boolean(b) => serializer.serialize_bool(b),
Value::Datetime(ref s) => s.serialize(serializer),
Value::Array(ref a) => a.serialize(serializer),
Value::Table(ref t) => {
let mut map = serializer.serialize_map(Some(t.len()))?;
// Be sure to visit non-tables first (and also non
// array-of-tables) as all keys must be emitted first.
for (k, v) in t {
if !v.is_array() && !v.is_table() {
map.serialize_key(k)?;
map.serialize_value(v)?;
}
}
for (k, v) in t {
if v.is_array() {
map.serialize_key(k)?;
map.serialize_value(v)?;
}
}
for (k, v) in t {
if v.is_table() {
map.serialize_key(k)?;
map.serialize_value(v)?;
}
}
map.end()
}
}
}
}
impl de::Deserialize for Value {
fn deserialize<D>(deserializer: D) -> Result<Value, D::Error>
where D: de::Deserializer
{
struct ValueVisitor;
impl de::Visitor for ValueVisitor {
type Value = Value;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("any valid TOML value")
}
fn visit_bool<E>(self, value: bool) -> Result<Value, E> {
Ok(Value::Boolean(value))
}
fn visit_i64<E>(self, value: i64) -> Result<Value, E> {
Ok(Value::Integer(value))
}
fn visit_f64<E>(self, value: f64) -> Result<Value, E> {
Ok(Value::Float(value))
}
fn visit_str<E>(self, value: &str) -> Result<Value, E> {
Ok(Value::String(value.into()))
}
fn visit_string<E>(self, value: String) -> Result<Value, E> {
Ok(Value::String(value))
}
fn visit_some<D>(self, deserializer: D) -> Result<Value, D::Error>
where D: de::Deserializer,
{
de::Deserialize::deserialize(deserializer)
}
fn visit_seq<V>(self, visitor: V) -> Result<Value, V::Error>
where V: de::SeqVisitor
{
let values = de::impls::VecVisitor::new().visit_seq(visitor)?;
Ok(Value::Array(values))
}
fn visit_map<V>(self, mut visitor: V) -> Result<Value, V::Error>
where V: de::MapVisitor
{
let mut key = String::new();
let datetime = visitor.visit_key_seed(DatetimeOrTable {
key: &mut key,
})?;
match datetime {
Some(true) => {
let date: DatetimeFromString = visitor.visit_value()?;
return Ok(Value::Datetime(date.value))
}
None => return Ok(Value::Table(BTreeMap::new())),
Some(false) => {}
}
let mut map = BTreeMap::new();
map.insert(key, visitor.visit_value()?);
while let Some(key) = visitor.visit_key()? {
if map.contains_key(&key) {
let msg = format!("duplicate key: `{}`", key);
return Err(de::Error::custom(msg))
}
map.insert(key, visitor.visit_value()?);
}
Ok(Value::Table(map))
}
}
deserializer.deserialize(ValueVisitor)
}
}
impl de::Deserializer for Value {
type Error = ::de::Error;
fn deserialize<V>(self, visitor: V) -> Result<V::Value, ::de::Error>
where V: de::Visitor,
{
match self {
Value::Boolean(v) => visitor.visit_bool(v),
Value::Integer(n) => visitor.visit_i64(n),
Value::Float(n) => visitor.visit_f64(n),
Value::String(v) => visitor.visit_string(v),
Value::Datetime(v) => visitor.visit_string(v.to_string()),
Value::Array(v) => {
let len = v.len();
let mut deserializer = SeqDeserializer::new(v);
let seq = visitor.visit_seq(&mut deserializer)?;
let remaining = deserializer.iter.len();
if remaining == 0 {
Ok(seq)
} else {
Err(de::Error::invalid_length(len, &"fewer elements in array"))
}
}
Value::Table(v) => {
let len = v.len();
let mut deserializer = MapDeserializer::new(v);
let map = visitor.visit_map(&mut deserializer)?;
let remaining = deserializer.iter.len();
if remaining == 0 {
Ok(map)
} else {
Err(de::Error::invalid_length(len, &"fewer elements in map"))
}
}
}
}
// `None` is interpreted as a missing field so be sure to implement `Some`
// as a present field.
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, ::de::Error>
where V: de::Visitor
{
visitor.visit_some(self)
}
forward_to_deserialize! {
bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string unit seq
seq_fixed_size bytes byte_buf map unit_struct tuple_struct struct
struct_field tuple ignored_any enum newtype_struct
}
}
struct SeqDeserializer {
iter: vec::IntoIter<Value>,
}
impl SeqDeserializer {
fn new(vec: Vec<Value>) -> Self {
SeqDeserializer {
iter: vec.into_iter(),
}
}
}
impl de::SeqVisitor for SeqDeserializer {
type Error = ::de::Error;
fn visit_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, ::de::Error>
where T: de::DeserializeSeed,
{
match self.iter.next() {
Some(value) => seed.deserialize(value).map(Some),
None => Ok(None),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
struct MapDeserializer {
iter: <BTreeMap<String, Value> as IntoIterator>::IntoIter,
value: Option<(String, Value)>,
}
impl MapDeserializer {
fn new(map: BTreeMap<String, Value>) -> Self {
MapDeserializer {
iter: map.into_iter(),
value: None,
}
}
}
impl de::MapVisitor for MapDeserializer {
type Error = ::de::Error;
fn visit_key_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, ::de::Error>
where T: de::DeserializeSeed,
{
match self.iter.next() {
Some((key, value)) => {
self.value = Some((key.clone(), value));
seed.deserialize(Value::String(key)).map(Some)
}
None => Ok(None),
}
}
fn visit_value_seed<T>(&mut self, seed: T) -> Result<T::Value, ::de::Error>
where T: de::DeserializeSeed,
{
let (key, res) = match self.value.take() {
Some((key, value)) => (key, seed.deserialize(value)),
None => return Err(de::Error::custom("value is missing")),
};
res.map_err(|mut error| {
error.add_key_context(&key);
error
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.iter.size_hint()
}
}
struct Serializer;
impl ser::Serializer for Serializer {
type Ok = Value;
type Error = ::ser::Error;
type SerializeSeq = SerializeVec;
type SerializeTuple = ser::Impossible<Value, ::ser::Error>;
type SerializeTupleStruct = ser::Impossible<Value, ::ser::Error>;
type SerializeTupleVariant = ser::Impossible<Value, ::ser::Error>;
type SerializeMap = SerializeMap;
type SerializeStruct = SerializeMap;
type SerializeStructVariant = ser::Impossible<Value, ::ser::Error>;
fn serialize_bool(self, value: bool) -> Result<Value, ::ser::Error> {
Ok(Value::Boolean(value))
}
fn serialize_i8(self, value: i8) -> Result<Value, ::ser::Error> {
self.serialize_i64(value.into())
}
fn serialize_i16(self, value: i16) -> Result<Value, ::ser::Error> {
self.serialize_i64(value.into())
}
fn serialize_i32(self, value: i32) -> Result<Value, ::ser::Error> {
self.serialize_i64(value.into())
}
fn serialize_i64(self, value: i64) -> Result<Value, ::ser::Error> {
Ok(Value::Integer(value.into()))
}
fn serialize_u8(self, value: u8) -> Result<Value, ::ser::Error> {
self.serialize_i64(value.into())
}
fn serialize_u16(self, value: u16) -> Result<Value, ::ser::Error> {
self.serialize_i64(value.into())
}
fn serialize_u32(self, value: u32) -> Result<Value, ::ser::Error> {
self.serialize_i64(value.into())
}
fn serialize_u64(self, value: u64) -> Result<Value, ::ser::Error> {
if value <= i64::max_value() as u64 {
self.serialize_i64(value as i64)
} else {
Err(ser::Error::custom("u64 value was too large"))
}
}
fn serialize_f32(self, value: f32) -> Result<Value, ::ser::Error> {
self.serialize_f64(value.into())
}
fn serialize_f64(self, value: f64) -> Result<Value, ::ser::Error> {
Ok(Value::Float(value))
}
fn serialize_char(self, value: char) -> Result<Value, ::ser::Error> {
let mut s = String::new();
s.push(value);
self.serialize_str(&s)
}
fn serialize_str(self, value: &str) -> Result<Value, ::ser::Error> {
Ok(Value::String(value.to_owned()))
}
fn serialize_bytes(self, value: &[u8]) -> Result<Value, ::ser::Error> {
let vec = value.iter().map(|&b| Value::Integer(b.into())).collect();
Ok(Value::Array(vec))
}
fn serialize_unit(self) -> Result<Value, ::ser::Error> {
Err(::ser::Error::UnsupportedType)
}
fn serialize_unit_struct(self, _name: &'static str)
-> Result<Value, ::ser::Error> {
Err(::ser::Error::UnsupportedType)
}
fn serialize_unit_variant(self,
_name: &'static str,
_variant_index: usize,
_variant: &'static str)
-> Result<Value, ::ser::Error> {
Err(::ser::Error::UnsupportedType)
}
fn serialize_newtype_struct<T: ?Sized>(self,
_name: &'static str,
value: &T)
-> Result<Value, ::ser::Error>
where T: ser::Serialize,
{
value.serialize(self)
}
fn serialize_newtype_variant<T: ?Sized>(self,
_name: &'static str,
_variant_index: usize,
_variant: &'static str,
_value: &T)
-> Result<Value, ::ser::Error>
where T: ser::Serialize,
{
Err(::ser::Error::UnsupportedType)
}
fn serialize_none(self) -> Result<Value, ::ser::Error> {
Err(::ser::Error::UnsupportedNone)
}
fn serialize_some<T: ?Sized>(self, value: &T) -> Result<Value, ::ser::Error>
where T: ser::Serialize,
{
value.serialize(self)
}
fn serialize_seq(self, len: Option<usize>)
-> Result<Self::SerializeSeq, ::ser::Error>
{
Ok(SerializeVec {
vec: Vec::with_capacity(len.unwrap_or(0))
})
}
fn serialize_seq_fixed_size(self, size: usize)
-> Result<Self::SerializeSeq, ::ser::Error> {
self.serialize_seq(Some(size))
}
fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, ::ser::Error> {
Err(::ser::Error::UnsupportedType)
}
fn serialize_tuple_struct(self, _name: &'static str, _len: usize)
-> Result<Self::SerializeTupleStruct, ::ser::Error> {
Err(::ser::Error::UnsupportedType)
}
fn serialize_tuple_variant(self,
_name: &'static str,
_variant_index: usize,
_variant: &'static str,
_len: usize)
-> Result<Self::SerializeTupleVariant, ::ser::Error>
{
Err(::ser::Error::UnsupportedType)
}
fn serialize_map(self, _len: Option<usize>)
-> Result<Self::SerializeMap, ::ser::Error>
{
Ok(SerializeMap {
map: BTreeMap::new(),
next_key: None,
})
}
fn serialize_struct(self, _name: &'static str, len: usize)
-> Result<Self::SerializeStruct, ::ser::Error> {
self.serialize_map(Some(len))
}
fn serialize_struct_variant(self,
_name: &'static str,
_variant_index: usize,
_variant: &'static str,
_len: usize)
-> Result<Self::SerializeStructVariant, ::ser::Error>
{
Err(::ser::Error::UnsupportedType)
}
}
struct SerializeVec {
vec: Vec<Value>,
}
struct SerializeMap {
map: BTreeMap<String, Value>,
next_key: Option<String>,
}
impl ser::SerializeSeq for SerializeVec {
type Ok = Value;
type Error = ::ser::Error;
fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), ::ser::Error>
where T: ser::Serialize
{
self.vec.push(Value::try_from(value)?);
Ok(())
}
fn end(self) -> Result<Value, ::ser::Error> {
Ok(Value::Array(self.vec))
}
}
impl ser::SerializeMap for SerializeMap {
type Ok = Value;
type Error = ::ser::Error;
fn serialize_key<T: ?Sized>(&mut self, key: &T) -> Result<(), ::ser::Error>
where T: ser::Serialize
{
match Value::try_from(key)? {
Value::String(s) => self.next_key = Some(s),
_ => return Err(::ser::Error::KeyNotString),
};
Ok(())
}
fn serialize_value<T: ?Sized>(&mut self, value: &T) -> Result<(), ::ser::Error>
where T: ser::Serialize
{
let key = self.next_key.take();
let key = key.expect("serialize_value called before serialize_key");
match Value::try_from(value) {
Ok(value) => { self.map.insert(key, value); }
Err(::ser::Error::UnsupportedNone) => {}
Err(e) => return Err(e),
}
Ok(())
}
fn end(self) -> Result<Value, ::ser::Error> {
Ok(Value::Table(self.map))
}
}
impl ser::SerializeStruct for SerializeMap {
type Ok = Value;
type Error = ::ser::Error;
fn serialize_field<T: ?Sized>(&mut self, key: &'static str, value: &T) -> Result<(), ::ser::Error>
where T: ser::Serialize
{
try!(ser::SerializeMap::serialize_key(self, key));
ser::SerializeMap::serialize_value(self, value)
}
fn end(self) -> Result<Value, ::ser::Error> {
ser::SerializeMap::end(self)
}
}
struct DatetimeOrTable<'a> {
key: &'a mut String,
}
impl<'a> de::DeserializeSeed for DatetimeOrTable<'a> {
type Value = bool;
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where D: de::Deserializer
{
deserializer.deserialize(self)
}
}
impl<'a> de::Visitor for DatetimeOrTable<'a> {
type Value = bool;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a string key")
}
fn visit_str<E>(self, s: &str) -> Result<bool, E>
where E: de::Error,
{
if s == SERDE_STRUCT_FIELD_NAME {
Ok(true)
} else {
self.key.push_str(s);
Ok(false)
}
}
fn visit_string<E>(self, s: String) -> Result<bool, E>
where E: de::Error,
{
if s == SERDE_STRUCT_FIELD_NAME {
Ok(true)
} else {
*self.key = s;
Ok(false)
}
}
}

View file

@ -132,81 +132,3 @@ impl<'a> fmt::Display for Key<'a> {
Ok(()) Ok(())
} }
} }
#[cfg(test)]
#[allow(warnings)]
mod tests {
use Value;
use Value::{String, Integer, Float, Boolean, Datetime, Array, Table};
use std::collections::BTreeMap;
macro_rules! map( ($($k:expr => $v:expr),*) => ({
let mut _m = BTreeMap::new();
$(_m.insert($k.to_string(), $v);)*
_m
}) );
#[test]
fn simple_show() {
assert_eq!(String("foo".to_string()).to_string(),
"\"foo\"");
assert_eq!(Integer(10).to_string(),
"10");
assert_eq!(Float(10.0).to_string(),
"10.0");
assert_eq!(Float(2.4).to_string(),
"2.4");
assert_eq!(Boolean(true).to_string(),
"true");
assert_eq!(Datetime("test".to_string()).to_string(),
"test");
assert_eq!(Array(vec![]).to_string(),
"[]");
assert_eq!(Array(vec![Integer(1), Integer(2)]).to_string(),
"[1, 2]");
}
#[test]
fn table() {
assert_eq!(Table(map! { }).to_string(),
"");
assert_eq!(Table(map! { "test" => Integer(2) }).to_string(),
"test = 2\n");
assert_eq!(Table(map! {
"test" => Integer(2),
"test2" => Table(map! {
"test" => String("wut".to_string())
})
}).to_string(),
"test = 2\n\
\n\
[test2]\n\
test = \"wut\"\n");
assert_eq!(Table(map! {
"test" => Integer(2),
"test2" => Table(map! {
"test" => String("wut".to_string())
})
}).to_string(),
"test = 2\n\
\n\
[test2]\n\
test = \"wut\"\n");
assert_eq!(Table(map! {
"test" => Integer(2),
"test2" => Array(vec![Table(map! {
"test" => String("wut".to_string())
})])
}).to_string(),
"test = 2\n\
\n\
[[test2]]\n\
test = \"wut\"\n");
assert_eq!(Table(map! {
"foo.bar" => Integer(2),
"foo\"bar" => Integer(2)
}).to_string(),
"\"foo\\\"bar\" = 2\n\
\"foo.bar\" = 2\n");
}
}

58
tests/datetime.rs Normal file
View file

@ -0,0 +1,58 @@
extern crate toml;
use std::str::FromStr;
use toml::Value;
#[test]
fn times() {
fn good(s: &str) {
let to_parse = format!("foo = {}", s);
let value = Value::from_str(&to_parse).unwrap();
assert_eq!(value["foo"].as_datetime().unwrap().to_string(), s);
}
good("1997-09-09T09:09:09Z");
good("1997-09-09T09:09:09+09:09");
good("1997-09-09T09:09:09-09:09");
good("1997-09-09T09:09:09");
good("1997-09-09");
good("09:09:09");
good("1997-09-09T09:09:09.09Z");
good("1997-09-09T09:09:09.09+09:09");
good("1997-09-09T09:09:09.09-09:09");
good("1997-09-09T09:09:09.09");
good("09:09:09.09");
}
#[test]
fn bad_times() {
fn bad(s: &str) {
let to_parse = format!("foo = {}", s);
assert!(Value::from_str(&to_parse).is_err());
}
bad("199-09-09");
bad("199709-09");
bad("1997-9-09");
bad("1997-09-9");
bad("1997-09-0909:09:09");
bad("1997-09-09T09:09:09.");
bad("T");
bad("T.");
bad("TZ");
bad("1997-09-09T09:09:09.09+");
bad("1997-09-09T09:09:09.09+09");
bad("1997-09-09T09:09:09.09+09:9");
bad("1997-09-09T09:09:09.09+0909");
bad("1997-09-09T09:09:09.09-");
bad("1997-09-09T09:09:09.09-09");
bad("1997-09-09T09:09:09.09-09:9");
bad("1997-09-09T09:09:09.09-0909");
bad("1997-00-09T09:09:09.09Z");
bad("1997-09-00T09:09:09.09Z");
bad("1997-09-09T30:09:09.09Z");
bad("1997-09-09T12:69:09.09Z");
bad("1997-09-09T12:09:69.09Z");
}

97
tests/display.rs Normal file
View file

@ -0,0 +1,97 @@
extern crate toml;
use std::collections::BTreeMap;
use toml::Value::{String, Integer, Float, Boolean, Array, Table};
macro_rules! map( ($($k:expr => $v:expr),*) => ({
let mut _m = BTreeMap::new();
$(_m.insert($k.to_string(), $v);)*
_m
}) );
#[test]
fn simple_show() {
assert_eq!(String("foo".to_string()).to_string(),
"\"foo\"");
assert_eq!(Integer(10).to_string(),
"10");
assert_eq!(Float(10.0).to_string(),
"10.0");
assert_eq!(Float(2.4).to_string(),
"2.4");
assert_eq!(Boolean(true).to_string(),
"true");
assert_eq!(Array(vec![]).to_string(),
"[]");
assert_eq!(Array(vec![Integer(1), Integer(2)]).to_string(),
"[1, 2]");
}
#[test]
fn table() {
assert_eq!(Table(map! { }).to_string(),
"");
assert_eq!(Table(map! {
"test" => Integer(2),
"test2" => Integer(3) }).to_string(),
"test = 2\ntest2 = 3\n");
assert_eq!(Table(map! {
"test" => Integer(2),
"test2" => Table(map! {
"test" => String("wut".to_string())
})
}).to_string(),
"test = 2\n\
\n\
[test2]\n\
test = \"wut\"\n");
assert_eq!(Table(map! {
"test" => Integer(2),
"test2" => Table(map! {
"test" => String("wut".to_string())
})
}).to_string(),
"test = 2\n\
\n\
[test2]\n\
test = \"wut\"\n");
assert_eq!(Table(map! {
"test" => Integer(2),
"test2" => Array(vec![Table(map! {
"test" => String("wut".to_string())
})])
}).to_string(),
"test = 2\n\
\n\
[[test2]]\n\
test = \"wut\"\n");
assert_eq!(Table(map! {
"foo.bar" => Integer(2),
"foo\"bar" => Integer(2)
}).to_string(),
"\"foo\\\"bar\" = 2\n\
\"foo.bar\" = 2\n");
assert_eq!(Table(map! {
"test" => Integer(2),
"test2" => Array(vec![Table(map! {
"test" => Array(vec![Integer(2)])
})])
}).to_string(),
"test = 2\n\
\n\
[[test2]]\n\
test = [2]\n");
let table = Table(map! {
"test" => Integer(2),
"test2" => Array(vec![Table(map! {
"test" => Array(vec![Array(vec![Integer(2), Integer(3)]),
Array(vec![String("foo".to_string()), String("bar".to_string())])])
})])
});
assert_eq!(table.to_string(),
"test = 2\n\
\n\
[[test2]]\n\
test = [[2, 3], [\"foo\", \"bar\"]]\n");
}

View file

@ -1,19 +1,22 @@
extern crate rustc_serialize; extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate toml; extern crate toml;
use toml::encode_str;
#[derive(Debug, Clone, Hash, PartialEq, Eq, RustcEncodable, RustcDecodable)] use toml::to_string;
#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
struct User { struct User {
pub name: String, pub name: String,
pub surname: String, pub surname: String,
} }
#[derive(Debug, Clone, Hash, PartialEq, Eq, RustcEncodable, RustcDecodable)] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
struct Users { struct Users {
pub user: Vec<User>, pub user: Vec<User>,
} }
#[derive(Debug, Clone, Hash, PartialEq, Eq, RustcEncodable, RustcDecodable)] #[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
struct TwoUsers { struct TwoUsers {
pub user0: User, pub user0: User,
pub user1: User, pub user1: User,
@ -21,7 +24,7 @@ struct TwoUsers {
#[test] #[test]
fn no_unnecessary_newlines_array() { fn no_unnecessary_newlines_array() {
assert!(!encode_str(&Users { assert!(!to_string(&Users {
user: vec![ user: vec![
User { User {
name: "John".to_string(), name: "John".to_string(),
@ -32,13 +35,13 @@ fn no_unnecessary_newlines_array() {
surname: "Dough".to_string(), surname: "Dough".to_string(),
}, },
], ],
}) }).unwrap()
.starts_with("\n")); .starts_with("\n"));
} }
#[test] #[test]
fn no_unnecessary_newlines_table() { fn no_unnecessary_newlines_table() {
assert!(!encode_str(&TwoUsers { assert!(!to_string(&TwoUsers {
user0: User { user0: User {
name: "John".to_string(), name: "John".to_string(),
surname: "Doe".to_string(), surname: "Doe".to_string(),
@ -47,6 +50,6 @@ fn no_unnecessary_newlines_table() {
name: "Jane".to_string(), name: "Jane".to_string(),
surname: "Dough".to_string(), surname: "Dough".to_string(),
}, },
}) }).unwrap()
.starts_with("\n")); .starts_with("\n"));
} }

12
tests/invalid-misc.rs Normal file
View file

@ -0,0 +1,12 @@
extern crate toml;
#[test]
fn bad() {
fn bad(s: &str) {
assert!(s.parse::<toml::Value>().is_err());
}
bad("a = 01");
bad("a = 1__1");
bad("a = 1_");
}

View file

@ -1,17 +1,9 @@
extern crate toml; extern crate toml;
use toml::{Parser};
fn run(toml: &str) { fn run(toml: &str) {
let mut p = Parser::new(toml); println!("test if invalid:\n{}", toml);
let table = p.parse(); if let Ok(e) = toml.parse::<toml::Value>() {
assert!(table.is_none()); panic!("parsed to: {:#?}", e);
assert!(p.errors.len() > 0);
// test Parser::to_linecol with the generated error offsets
for error in &p.errors {
p.to_linecol(error.lo);
p.to_linecol(error.hi);
} }
} }
@ -32,8 +24,6 @@ test!(datetime_malformed_no_secs,
include_str!("invalid/datetime-malformed-no-secs.toml")); include_str!("invalid/datetime-malformed-no-secs.toml"));
test!(datetime_malformed_no_t, test!(datetime_malformed_no_t,
include_str!("invalid/datetime-malformed-no-t.toml")); include_str!("invalid/datetime-malformed-no-t.toml"));
test!(datetime_malformed_no_z,
include_str!("invalid/datetime-malformed-no-z.toml"));
test!(datetime_malformed_with_milli, test!(datetime_malformed_with_milli,
include_str!("invalid/datetime-malformed-with-milli.toml")); include_str!("invalid/datetime-malformed-with-milli.toml"));
test!(duplicate_keys, test!(duplicate_keys,

View file

@ -1 +0,0 @@
no-z = 1987-07-05T17:45:00

495
tests/parser.rs Normal file
View file

@ -0,0 +1,495 @@
extern crate toml;
use toml::Value;
macro_rules! bad {
($s:expr, $msg:expr) => ({
match $s.parse::<Value>() {
Ok(s) => panic!("successfully parsed as {}", s),
Err(e) => {
let e = e.to_string();
assert!(e.contains($msg), "error: {}", e);
}
}
})
}
#[test]
fn crlf() {
"\
[project]\r\n\
\r\n\
name = \"splay\"\r\n\
version = \"0.1.0\"\r\n\
authors = [\"alex@crichton.co\"]\r\n\
\r\n\
[[lib]]\r\n\
\r\n\
path = \"lib.rs\"\r\n\
name = \"splay\"\r\n\
description = \"\"\"\
A Rust implementation of a TAR file reader and writer. This library does not\r\n\
currently handle compression, but it is abstract over all I/O readers and\r\n\
writers. Additionally, great lengths are taken to ensure that the entire\r\n\
contents are never required to be entirely resident in memory all at once.\r\n\
\"\"\"\
".parse::<Value>().unwrap();
}
#[test]
fn fun_with_strings() {
let table = r#"
bar = "\U00000000"
key1 = "One\nTwo"
key2 = """One\nTwo"""
key3 = """
One
Two"""
key4 = "The quick brown fox jumps over the lazy dog."
key5 = """
The quick brown \
fox jumps over \
the lazy dog."""
key6 = """\
The quick brown \
fox jumps over \
the lazy dog.\
"""
# What you see is what you get.
winpath = 'C:\Users\nodejs\templates'
winpath2 = '\\ServerX\admin$\system32\'
quoted = 'Tom "Dubs" Preston-Werner'
regex = '<\i\c*\s*>'
regex2 = '''I [dw]on't need \d{2} apples'''
lines = '''
The first newline is
trimmed in raw strings.
All other whitespace
is preserved.
'''
"#.parse::<Value>().unwrap();
assert_eq!(table["bar"].as_str(), Some("\0"));
assert_eq!(table["key1"].as_str(), Some("One\nTwo"));
assert_eq!(table["key2"].as_str(), Some("One\nTwo"));
assert_eq!(table["key3"].as_str(), Some("One\nTwo"));
let msg = "The quick brown fox jumps over the lazy dog.";
assert_eq!(table["key4"].as_str(), Some(msg));
assert_eq!(table["key5"].as_str(), Some(msg));
assert_eq!(table["key6"].as_str(), Some(msg));
assert_eq!(table["winpath"].as_str(), Some(r"C:\Users\nodejs\templates"));
assert_eq!(table["winpath2"].as_str(), Some(r"\\ServerX\admin$\system32\"));
assert_eq!(table["quoted"].as_str(), Some(r#"Tom "Dubs" Preston-Werner"#));
assert_eq!(table["regex"].as_str(), Some(r"<\i\c*\s*>"));
assert_eq!(table["regex2"].as_str(), Some(r"I [dw]on't need \d{2} apples"));
assert_eq!(table["lines"].as_str(),
Some("The first newline is\n\
trimmed in raw strings.\n\
All other whitespace\n\
is preserved.\n"));
}
#[test]
fn tables_in_arrays() {
let table = r#"
[[foo]]
#
[foo.bar]
#
[[foo]] # ...
#
[foo.bar]
#...
"#.parse::<Value>().unwrap();
table["foo"][0]["bar"].as_table().unwrap();
table["foo"][1]["bar"].as_table().unwrap();
}
#[test]
fn empty_table() {
let table = r#"
[foo]"#.parse::<Value>().unwrap();
table["foo"].as_table().unwrap();
}
#[test]
fn fruit() {
let table = r#"
[[fruit]]
name = "apple"
[fruit.physical]
color = "red"
shape = "round"
[[fruit.variety]]
name = "red delicious"
[[fruit.variety]]
name = "granny smith"
[[fruit]]
name = "banana"
[[fruit.variety]]
name = "plantain"
"#.parse::<Value>().unwrap();
assert_eq!(table["fruit"][0]["name"].as_str(), Some("apple"));
assert_eq!(table["fruit"][0]["physical"]["color"].as_str(), Some("red"));
assert_eq!(table["fruit"][0]["physical"]["shape"].as_str(), Some("round"));
assert_eq!(table["fruit"][0]["variety"][0]["name"].as_str(), Some("red delicious"));
assert_eq!(table["fruit"][0]["variety"][1]["name"].as_str(), Some("granny smith"));
assert_eq!(table["fruit"][1]["name"].as_str(), Some("banana"));
assert_eq!(table["fruit"][1]["variety"][0]["name"].as_str(), Some("plantain"));
}
#[test]
fn stray_cr() {
"\r".parse::<Value>().unwrap_err();
"a = [ \r ]".parse::<Value>().unwrap_err();
"a = \"\"\"\r\"\"\"".parse::<Value>().unwrap_err();
"a = \"\"\"\\ \r \"\"\"".parse::<Value>().unwrap_err();
"a = '''\r'''".parse::<Value>().unwrap_err();
"a = '\r'".parse::<Value>().unwrap_err();
"a = \"\r\"".parse::<Value>().unwrap_err();
}
#[test]
fn blank_literal_string() {
let table = "foo = ''".parse::<Value>().unwrap();
assert_eq!(table["foo"].as_str(), Some(""));
}
#[test]
fn many_blank() {
let table = "foo = \"\"\"\n\n\n\"\"\"".parse::<Value>().unwrap();
assert_eq!(table["foo"].as_str(), Some("\n\n"));
}
#[test]
fn literal_eats_crlf() {
let table = "
foo = \"\"\"\\\r\n\"\"\"
bar = \"\"\"\\\r\n \r\n \r\n a\"\"\"
".parse::<Value>().unwrap();
assert_eq!(table["foo"].as_str(), Some(""));
assert_eq!(table["bar"].as_str(), Some("a"));
}
#[test]
fn string_no_newline() {
"a = \"\n\"".parse::<Value>().unwrap_err();
"a = '\n'".parse::<Value>().unwrap_err();
}
#[test]
fn bad_leading_zeros() {
"a = 00".parse::<Value>().unwrap_err();
"a = -00".parse::<Value>().unwrap_err();
"a = +00".parse::<Value>().unwrap_err();
"a = 00.0".parse::<Value>().unwrap_err();
"a = -00.0".parse::<Value>().unwrap_err();
"a = +00.0".parse::<Value>().unwrap_err();
"a = 9223372036854775808".parse::<Value>().unwrap_err();
"a = -9223372036854775809".parse::<Value>().unwrap_err();
}
#[test]
fn bad_floats() {
"a = 0.".parse::<Value>().unwrap_err();
"a = 0.e".parse::<Value>().unwrap_err();
"a = 0.E".parse::<Value>().unwrap_err();
"a = 0.0E".parse::<Value>().unwrap_err();
"a = 0.0e".parse::<Value>().unwrap_err();
"a = 0.0e-".parse::<Value>().unwrap_err();
"a = 0.0e+".parse::<Value>().unwrap_err();
"a = 0.0e+00".parse::<Value>().unwrap_err();
}
#[test]
fn floats() {
macro_rules! t {
($actual:expr, $expected:expr) => ({
let f = format!("foo = {}", $actual);
println!("{}", f);
let a = f.parse::<Value>().unwrap();
assert_eq!(a["foo"].as_float().unwrap(), $expected);
})
}
t!("1.0", 1.0);
t!("1.0e0", 1.0);
t!("1.0e+0", 1.0);
t!("1.0e-0", 1.0);
t!("1.001e-0", 1.001);
t!("2e10", 2e10);
t!("2e+10", 2e10);
t!("2e-10", 2e-10);
t!("2_0.0", 20.0);
t!("2_0.0_0e1_0", 20.0e10);
t!("2_0.1_0e1_0", 20.1e10);
}
#[test]
fn bare_key_names() {
let a = "
foo = 3
foo_3 = 3
foo_-2--3--r23f--4-f2-4 = 3
_ = 3
- = 3
8 = 8
\"a\" = 3
\"!\" = 3
\"a^b\" = 3
\"\\\"\" = 3
\"character encoding\" = \"value\"
'ʎǝʞ' = \"value\"
".parse::<Value>().unwrap();
&a["foo"];
&a["-"];
&a["_"];
&a["8"];
&a["foo_3"];
&a["foo_-2--3--r23f--4-f2-4"];
&a["a"];
&a["!"];
&a["\""];
&a["character encoding"];
&a["ʎǝʞ"];
}
#[test]
fn bad_keys() {
"key\n=3".parse::<Value>().unwrap_err();
"key=\n3".parse::<Value>().unwrap_err();
"key|=3".parse::<Value>().unwrap_err();
"\"\"=3".parse::<Value>().unwrap_err();
"=3".parse::<Value>().unwrap_err();
"\"\"|=3".parse::<Value>().unwrap_err();
"\"\n\"|=3".parse::<Value>().unwrap_err();
"\"\r\"|=3".parse::<Value>().unwrap_err();
}
#[test]
fn bad_table_names() {
"[]".parse::<Value>().unwrap_err();
"[.]".parse::<Value>().unwrap_err();
"[\"\".\"\"]".parse::<Value>().unwrap_err();
"[a.]".parse::<Value>().unwrap_err();
"[\"\"]".parse::<Value>().unwrap_err();
"[!]".parse::<Value>().unwrap_err();
"[\"\n\"]".parse::<Value>().unwrap_err();
"[a.b]\n[a.\"b\"]".parse::<Value>().unwrap_err();
"[']".parse::<Value>().unwrap_err();
"[''']".parse::<Value>().unwrap_err();
"['''''']".parse::<Value>().unwrap_err();
"['\n']".parse::<Value>().unwrap_err();
"['\r\n']".parse::<Value>().unwrap_err();
}
#[test]
fn table_names() {
let a = "
[a.\"b\"]
[\"f f\"]
[\"f.f\"]
[\"\\\"\"]
['a.a']
['\"\"']
".parse::<Value>().unwrap();
println!("{:?}", a);
&a["a"]["b"];
&a["f f"];
&a["f.f"];
&a["\""];
&a["\"\""];
}
#[test]
fn invalid_bare_numeral() {
"4".parse::<Value>().unwrap_err();
}
#[test]
fn inline_tables() {
"a = {}".parse::<Value>().unwrap();
"a = {b=1}".parse::<Value>().unwrap();
"a = { b = 1 }".parse::<Value>().unwrap();
"a = {a=1,b=2}".parse::<Value>().unwrap();
"a = {a=1,b=2,c={}}".parse::<Value>().unwrap();
"a = {a=1,}".parse::<Value>().unwrap_err();
"a = {,}".parse::<Value>().unwrap_err();
"a = {a=1,a=1}".parse::<Value>().unwrap_err();
"a = {\n}".parse::<Value>().unwrap_err();
"a = {".parse::<Value>().unwrap_err();
"a = {a=[\n]}".parse::<Value>().unwrap();
"a = {\"a\"=[\n]}".parse::<Value>().unwrap();
"a = [\n{},\n{},\n]".parse::<Value>().unwrap();
}
#[test]
fn number_underscores() {
macro_rules! t {
($actual:expr, $expected:expr) => ({
let f = format!("foo = {}", $actual);
let table = f.parse::<Value>().unwrap();
assert_eq!(table["foo"].as_integer().unwrap(), $expected);
})
}
t!("1_0", 10);
t!("1_0_0", 100);
t!("1_000", 1000);
t!("+1_000", 1000);
t!("-1_000", -1000);
}
#[test]
fn bad_underscores() {
bad!("foo = 0_", "invalid number");
bad!("foo = 0__0", "invalid number");
bad!("foo = __0", "invalid number");
bad!("foo = 1_0_", "invalid number");
}
#[test]
fn bad_unicode_codepoint() {
bad!("foo = \"\\uD800\"", "invalid escape value");
}
#[test]
fn bad_strings() {
bad!("foo = \"\\uxx\"", "invalid hex escape");
bad!("foo = \"\\u\"", "invalid hex escape");
bad!("foo = \"\\", "unterminated");
bad!("foo = '", "unterminated");
}
#[test]
fn empty_string() {
assert_eq!("foo = \"\"".parse::<Value>()
.unwrap()["foo"]
.as_str()
.unwrap(),
"");
}
#[test]
fn booleans() {
let table = "foo = true".parse::<Value>().unwrap();
assert_eq!(table["foo"].as_bool(), Some(true));
let table = "foo = false".parse::<Value>().unwrap();
assert_eq!(table["foo"].as_bool(), Some(false));
assert!("foo = true2".parse::<Value>().is_err());
assert!("foo = false2".parse::<Value>().is_err());
assert!("foo = t1".parse::<Value>().is_err());
assert!("foo = f2".parse::<Value>().is_err());
}
#[test]
fn bad_nesting() {
bad!("
a = [2]
[[a]]
b = 5
", "duplicate key: `a`");
bad!("
a = 1
[a.b]
", "duplicate key: `a`");
bad!("
a = []
[a.b]
", "duplicate key: `a`");
bad!("
a = []
[[a.b]]
", "duplicate key: `a`");
bad!("
[a]
b = { c = 2, d = {} }
[a.b]
c = 2
", "duplicate key: `b`");
}
#[test]
fn bad_table_redefine() {
bad!("
[a]
foo=\"bar\"
[a.b]
foo=\"bar\"
[a]
", "redefinition of table `a`");
bad!("
[a]
foo=\"bar\"
b = { foo = \"bar\" }
[a]
", "redefinition of table `a`");
bad!("
[a]
b = {}
[a.b]
", "duplicate key: `b`");
bad!("
[a]
b = {}
[a]
", "redefinition of table `a`");
}
#[test]
fn datetimes() {
macro_rules! t {
($actual:expr) => ({
let f = format!("foo = {}", $actual);
let toml = f.parse::<Value>().expect(&format!("failed: {}", f));
assert_eq!(toml["foo"].as_datetime().unwrap().to_string(), $actual);
})
}
t!("2016-09-09T09:09:09Z");
t!("2016-09-09T09:09:09.1Z");
t!("2016-09-09T09:09:09.2+10:00");
t!("2016-09-09T09:09:09.0123456789-02:00");
bad!("foo = 2016-09-09T09:09:09.Z", "failed to parse date");
bad!("foo = 2016-9-09T09:09:09Z", "failed to parse date");
bad!("foo = 2016-09-09T09:09:09+2:00", "failed to parse date");
bad!("foo = 2016-09-09T09:09:09-2:00", "failed to parse date");
bad!("foo = 2016-09-09T09:09:09Z-2:00", "failed to parse date");
}
#[test]
fn require_newline_after_value() {
bad!("0=0r=false", "invalid number at line 1");
bad!(r#"
0=""o=""m=""r=""00="0"q="""0"""e="""0"""
"#, "expected newline");
bad!(r#"
[[0000l0]]
0="0"[[0000l0]]
0="0"[[0000l0]]
0="0"l="0"
"#, "expected newline");
bad!(r#"
0=[0]00=[0,0,0]t=["0","0","0"]s=[1000-00-00T00:00:00Z,2000-00-00T00:00:00Z]
"#, "expected newline");
bad!(r#"
0=0r0=0r=false
"#, "invalid number at line 2");
bad!(r#"
0=0r0=0r=falsefal=false
"#, "invalid number at line 2");
}

496
tests/serde.rs Normal file
View file

@ -0,0 +1,496 @@
extern crate serde;
extern crate toml;
#[macro_use]
extern crate serde_derive;
use std::collections::{BTreeMap, HashSet};
use serde::{Deserialize, Deserializer};
use toml::Value;
use toml::Value::{Table, Integer, Array, Float};
macro_rules! t {
($e:expr) => (match $e {
Ok(t) => t,
Err(e) => panic!("{} failed with {}", stringify!($e), e),
})
}
macro_rules! equivalent {
($literal:expr, $toml:expr,) => ({
let toml = $toml;
let literal = $literal;
// In/out of Value is equivalent
println!("try_from");
assert_eq!(t!(Value::try_from(literal.clone())), toml);
println!("try_into");
assert_eq!(literal, t!(toml.clone().try_into()));
// Through a string equivalent
println!("to_string(literal)");
assert_eq!(t!(toml::to_string(&literal)), toml.to_string());
println!("to_string(toml)");
assert_eq!(t!(toml::to_string(&toml)), toml.to_string());
println!("literal, from_str(toml)");
assert_eq!(literal, t!(toml::from_str(&toml.to_string())));
println!("toml, from_str(toml)");
assert_eq!(toml, t!(toml::from_str(&toml.to_string())));
})
}
macro_rules! error {
($ty:ty, $toml:expr, $error:expr) => ({
println!("attempting parsing");
match toml::from_str::<$ty>(&$toml.to_string()) {
Ok(_) => panic!("successful"),
Err(e) => {
assert!(e.to_string().contains($error),
"bad error: {}", e);
}
}
println!("attempting toml decoding");
match $toml.try_into::<$ty>() {
Ok(_) => panic!("successful"),
Err(e) => {
assert!(e.to_string().contains($error),
"bad error: {}", e);
}
}
})
}
macro_rules! decode( ($t:expr) => ({
t!($t.try_into())
}) );
macro_rules! map( ($($k:ident: $v:expr),*) => ({
let mut _m = BTreeMap::new();
$(_m.insert(stringify!($k).to_string(), $v);)*
_m
}) );
#[test]
fn smoke() {
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Foo { a: isize }
equivalent!(
Foo { a: 2 },
Table(map! { a: Integer(2) }),
);
}
#[test]
fn smoke_hyphen() {
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Foo {
a_b: isize,
}
equivalent! {
Foo { a_b: 2 },
Table(map! { a_b: Integer(2) }),
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Foo2 {
#[serde(rename = "a-b")]
a_b: isize,
}
let mut m = BTreeMap::new();
m.insert("a-b".to_string(), Integer(2));
equivalent! {
Foo2 { a_b: 2 },
Table(m),
}
}
#[test]
fn nested() {
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Foo { a: isize, b: Bar }
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Bar { a: String }
equivalent! {
Foo { a: 2, b: Bar { a: "test".to_string() } },
Table(map! {
a: Integer(2),
b: Table(map! {
a: Value::String("test".to_string())
})
}),
}
}
#[test]
fn application_decode_error() {
#[derive(PartialEq, Debug)]
struct Range10(usize);
impl Deserialize for Range10 {
fn deserialize<D: Deserializer>(d: D) -> Result<Range10, D::Error> {
let x: usize = try!(Deserialize::deserialize(d));
if x > 10 {
Err(serde::de::Error::custom("more than 10"))
} else {
Ok(Range10(x))
}
}
}
let d_good = Integer(5);
let d_bad1 = Value::String("not an isize".to_string());
let d_bad2 = Integer(11);
assert_eq!(Range10(5), d_good.try_into().unwrap());
let err1: Result<Range10, _> = d_bad1.try_into();
assert!(err1.is_err());
let err2: Result<Range10, _> = d_bad2.try_into();
assert!(err2.is_err());
}
#[test]
fn array() {
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Foo { a: Vec<isize> }
equivalent! {
Foo { a: vec![1, 2, 3, 4] },
Table(map! {
a: Array(vec![
Integer(1),
Integer(2),
Integer(3),
Integer(4)
])
}),
};
}
#[test]
fn inner_structs_with_options() {
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Foo {
a: Option<Box<Foo>>,
b: Bar,
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Bar {
a: String,
b: f64,
}
equivalent! {
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 },
},
Table(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)
})
}),
}
}
#[test]
fn hashmap() {
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Foo {
set: HashSet<char>,
map: BTreeMap<String, isize>,
}
equivalent! {
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
},
},
Table(map! {
map: Table(map! {
foo: Integer(10),
bar: Integer(4)
}),
set: Array(vec![Value::String("a".to_string())])
}),
}
}
#[test]
fn table_array() {
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Foo { a: Vec<Bar>, }
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Bar { a: isize }
equivalent! {
Foo { a: vec![Bar { a: 1 }, Bar { a: 2 }] },
Table(map! {
a: Array(vec![
Table(map!{ a: Integer(1) }),
Table(map!{ a: Integer(2) }),
])
}),
}
}
#[test]
fn type_errors() {
#[derive(Deserialize)]
#[allow(dead_code)]
struct Foo { bar: isize }
error! {
Foo,
Table(map! {
bar: Value::String("a".to_string())
}),
"invalid type: string \"a\", expected isize for key `bar`"
}
#[derive(Deserialize)]
#[allow(dead_code)]
struct Bar { foo: Foo }
error! {
Bar,
Table(map! {
foo: Table(map! {
bar: Value::String("a".to_string())
})
}),
"invalid type: string \"a\", expected isize for key `foo.bar`"
}
}
#[test]
fn missing_errors() {
#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Foo { bar: isize }
error! {
Foo,
Table(map! { }),
"missing field `bar`"
}
}
#[test]
fn parse_enum() {
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Foo { a: E }
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
#[serde(untagged)]
enum E {
Bar(isize),
Baz(String),
Last(Foo2),
}
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Foo2 {
test: String,
}
equivalent! {
Foo { a: E::Bar(10) },
Table(map! { a: Integer(10) }),
}
equivalent! {
Foo { a: E::Baz("foo".to_string()) },
Table(map! { a: Value::String("foo".to_string()) }),
}
equivalent! {
Foo { a: E::Last(Foo2 { test: "test".to_string() }) },
Table(map! { a: Table(map! { test: Value::String("test".to_string()) }) }),
}
}
// #[test]
// fn unused_fields() {
// #[derive(Serialize, Deserialize, PartialEq, Debug)]
// struct Foo { a: isize }
//
// let v = Foo { a: 2 };
// let mut d = Decoder::new(Table(map! {
// a, Integer(2),
// b, Integer(5)
// }));
// assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
//
// assert_eq!(d.toml, Some(Table(map! {
// b, Integer(5)
// })));
// }
//
// #[test]
// fn unused_fields2() {
// #[derive(Serialize, Deserialize, PartialEq, Debug)]
// struct Foo { a: Bar }
// #[derive(Serialize, Deserialize, PartialEq, Debug)]
// struct Bar { a: isize }
//
// let v = Foo { a: Bar { a: 2 } };
// let mut d = Decoder::new(Table(map! {
// a, Table(map! {
// a, Integer(2),
// b, Integer(5)
// })
// }));
// assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
//
// assert_eq!(d.toml, Some(Table(map! {
// a, Table(map! {
// b, Integer(5)
// })
// })));
// }
//
// #[test]
// fn unused_fields3() {
// #[derive(Serialize, Deserialize, PartialEq, Debug)]
// struct Foo { a: Bar }
// #[derive(Serialize, Deserialize, PartialEq, Debug)]
// struct Bar { a: isize }
//
// let v = Foo { a: Bar { a: 2 } };
// let mut d = Decoder::new(Table(map! {
// a, Table(map! {
// a, Integer(2)
// })
// }));
// assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
//
// assert_eq!(d.toml, None);
// }
//
// #[test]
// fn unused_fields4() {
// #[derive(Serialize, Deserialize, PartialEq, Debug)]
// struct Foo { a: BTreeMap<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, t!(Deserialize::deserialize(&mut d)));
//
// assert_eq!(d.toml, None);
// }
//
// #[test]
// fn unused_fields5() {
// #[derive(Serialize, Deserialize, 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, t!(Deserialize::deserialize(&mut d)));
//
// assert_eq!(d.toml, None);
// }
//
// #[test]
// fn unused_fields6() {
// #[derive(Serialize, Deserialize, 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, t!(Deserialize::deserialize(&mut d)));
//
// assert_eq!(d.toml, None);
// }
//
// #[test]
// fn unused_fields7() {
// #[derive(Serialize, Deserialize, PartialEq, Debug)]
// struct Foo { a: Vec<Bar> }
// #[derive(Serialize, Deserialize, PartialEq, Debug)]
// struct Bar { a: isize }
//
// let v = Foo { a: vec![Bar { a: 1 }] };
// let mut d = Decoder::new(Table(map! {
// a, Array(vec![Table(map! {
// a, Integer(1),
// b, Integer(2)
// })])
// }));
// assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
//
// assert_eq!(d.toml, Some(Table(map! {
// a, Array(vec![Table(map! {
// b, Integer(2)
// })])
// })));
// }
#[test]
fn empty_arrays() {
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Foo { a: Vec<Bar> }
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Bar;
equivalent! {
Foo { a: vec![] },
Table(map! {a: Array(Vec::new())}),
}
}
#[test]
fn empty_arrays2() {
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Foo { a: Option<Vec<Bar>> }
#[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
struct Bar;
equivalent! {
Foo { a: None },
Table(map! {}),
}
equivalent!{
Foo { a: Some(vec![]) },
Table(map! { a: Array(vec![]) }),
}
}
#[test]
fn extra_keys() {
#[derive(Serialize, Deserialize)]
struct Foo { a: isize }
let toml = Table(map! { a: Integer(2), b: Integer(2) });
assert!(toml.clone().try_into::<Foo>().is_ok());
assert!(toml::from_str::<Foo>(&toml.to_string()).is_ok());
}

View file

@ -1,65 +1,61 @@
extern crate rustc_serialize;
extern crate toml; extern crate toml;
extern crate serde_json;
use std::collections::BTreeMap; use toml::Value as Toml;
use rustc_serialize::json::Json; use serde_json::Value as Json;
use toml::{Parser, Value}; fn to_json(toml: toml::Value) -> Json {
use toml::Value::{Table, Integer, Float, Boolean, Datetime, Array};
fn to_json(toml: Value) -> Json {
fn doit(s: &str, json: Json) -> Json { fn doit(s: &str, json: Json) -> Json {
let mut map = BTreeMap::new(); let mut map = serde_json::Map::new();
map.insert(format!("{}", "type"), Json::String(format!("{}", s))); map.insert("type".to_string(), Json::String(s.to_string()));
map.insert(format!("{}", "value"), json); map.insert("value".to_string(), json);
Json::Object(map) Json::Object(map)
} }
match toml { match toml {
Value::String(s) => doit("string", Json::String(s)), Toml::String(s) => doit("string", Json::String(s)),
Integer(i) => doit("integer", Json::String(format!("{}", i))), Toml::Integer(i) => doit("integer", Json::String(i.to_string())),
Float(f) => doit("float", Json::String({ Toml::Float(f) => doit("float", Json::String({
let s = format!("{:.15}", f); let s = format!("{:.15}", f);
let s = format!("{}", s.trim_right_matches('0')); let s = format!("{}", s.trim_right_matches('0'));
if s.ends_with(".") {format!("{}0", s)} else {s} if s.ends_with(".") {format!("{}0", s)} else {s}
})), })),
Boolean(b) => doit("bool", Json::String(format!("{}", b))), Toml::Boolean(b) => doit("bool", Json::String(format!("{}", b))),
Datetime(s) => doit("datetime", Json::String(s)), Toml::Datetime(s) => doit("datetime", Json::String(s.to_string())),
Array(arr) => { Toml::Array(arr) => {
let is_table = match arr.first() { let is_table = match arr.first() {
Some(&Table(..)) => true, Some(&Toml::Table(..)) => true,
_ => false, _ => false,
}; };
let json = Json::Array(arr.into_iter().map(to_json).collect()); let json = Json::Array(arr.into_iter().map(to_json).collect());
if is_table {json} else {doit("array", json)} if is_table {json} else {doit("array", json)}
} }
Table(table) => Json::Object(table.into_iter().map(|(k, v)| { Toml::Table(table) => {
(k, to_json(v)) let mut map = serde_json::Map::new();
}).collect()), for (k, v) in table {
map.insert(k, to_json(v));
}
Json::Object(map)
}
} }
} }
fn run(toml: &str, json: &str) { fn run(toml: &str, json: &str) {
let mut p = Parser::new(toml); println!("parsing:\n{}", toml);
let table = p.parse(); let toml: Toml = toml.parse().unwrap();
assert!(p.errors.len() == 0, "had_errors: {:?}", let json: Json = json.parse().unwrap();
p.errors.iter().map(|e| {
(e.desc.clone(), &toml[e.lo - 5..e.hi + 5])
}).collect::<Vec<(String, &str)>>());
assert!(table.is_some());
let toml = Table(table.unwrap());
let toml_string = format!("{}", toml);
let json = Json::from_str(json).unwrap(); // Assert toml == json
let toml_json = to_json(toml.clone()); let toml_json = to_json(toml.clone());
assert!(json == toml_json, assert!(json == toml_json,
"expected\n{}\ngot\n{}\n", "expected\n{}\ngot\n{}\n",
json.pretty(), serde_json::to_string_pretty(&json).unwrap(),
toml_json.pretty()); serde_json::to_string_pretty(&toml_json).unwrap());
let table2 = Parser::new(&toml_string).parse().unwrap(); // Assert round trip
// floats are a little lossy println!("round trip parse: {}", toml);
if table2.values().any(|v| v.as_float().is_some()) { return } let toml2 = toml.to_string().parse().unwrap();
assert_eq!(toml, Table(table2)); assert_eq!(toml, toml2);
} }
macro_rules! test( ($name:ident, $toml:expr, $json:expr) => ( macro_rules! test( ($name:ident, $toml:expr, $json:expr) => (