aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2017-02-08 21:23:29 -0800
committerGitHub <noreply@github.com>2017-02-08 21:23:29 -0800
commitbeff7f992d738db3565d899a72542baae57f835d (patch)
tree76498b837fc5f1f6ba0a5f53e1b2d85c6638da4d
parent473908c9722eeedeec1777237a135f582faa78d8 (diff)
parentf66d8bcf33530c858a502bfa170f2383a8cbc204 (diff)
downloadmilf-rs-beff7f992d738db3565d899a72542baae57f835d.tar.gz
milf-rs-beff7f992d738db3565d899a72542baae57f835d.zip
Merge pull request #137 from alexcrichton/serde-upgrade
Rewrite crate with serde support from ground up
-rw-r--r--.travis.yml6
-rw-r--r--Cargo.toml10
-rw-r--r--examples/decode.rs18
-rw-r--r--examples/toml2json.rs52
-rw-r--r--serde-tests/Cargo.toml20
-rw-r--r--serde-tests/build.rs13
-rw-r--r--serde-tests/lib.rs1
-rw-r--r--serde-tests/test.rs1
-rw-r--r--serde-tests/test.rs.in482
-rw-r--r--src/datetime.rs424
-rw-r--r--src/de.rs1195
-rw-r--r--src/decoder/mod.rs240
-rw-r--r--src/decoder/rustc_serialize.rs371
-rw-r--r--src/decoder/serde.rs773
-rw-r--r--src/encoder/mod.rs222
-rw-r--r--src/encoder/rustc_serialize.rs748
-rw-r--r--src/encoder/serde.rs339
-rw-r--r--src/lib.rs640
-rw-r--r--src/parser.rs1627
-rw-r--r--src/ser.rs1006
-rw-r--r--src/tokens.rs620
-rw-r--r--src/value.rs894
-rw-r--r--src/value/display.rs (renamed from src/display.rs)78
-rw-r--r--tests/datetime.rs58
-rw-r--r--tests/display.rs97
-rw-r--r--tests/formatting.rs21
-rw-r--r--tests/invalid-misc.rs12
-rw-r--r--tests/invalid.rs16
-rw-r--r--tests/invalid/datetime-malformed-no-z.toml1
-rw-r--r--tests/parser.rs495
-rw-r--r--tests/serde.rs496
-rw-r--r--tests/valid.rs68
32 files changed, 5529 insertions, 5515 deletions
diff --git a/.travis.yml b/.travis.yml
index dc725d4..a7c4b12 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,11 +7,7 @@ sudo: false
before_script:
- pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH
script:
- - cargo build --verbose
- - 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
+ - cargo test
- rustdoc --test README.md -L target
- cargo doc --no-deps
after_success:
diff --git a/Cargo.toml b/Cargo.toml
index 0e47242..31c60e8 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,5 +1,4 @@
[package]
-
name = "toml"
version = "0.2.1"
authors = ["Alex Crichton <alex@alexcrichton.com>"]
@@ -16,11 +15,8 @@ facilitate deserializing and serializing Rust structures.
"""
[dependencies]
-rustc-serialize = { optional = true, version = "0.3.0" }
-serde = { optional = true, version = "0.8" }
-
-[features]
-default = ["rustc-serialize"]
+serde = "0.9.6"
[dev-dependencies]
-rustc-serialize = "0.3"
+serde_derive = "0.9"
+serde_json = "0.9"
diff --git a/examples/decode.rs b/examples/decode.rs
index 9124596..8390e15 100644
--- a/examples/decode.rs
+++ b/examples/decode.rs
@@ -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`
//!
//! Note that this works similarly with `serde` as well.
@@ -6,11 +6,12 @@
#![deny(warnings)]
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
/// that it doesn't have to be present in TOML.
-#[derive(Debug, RustcDecodable)]
+#[derive(Debug, Deserialize)]
struct Config {
global_string: Option<String>,
global_integer: Option<u64>,
@@ -22,13 +23,13 @@ struct Config {
/// table.
///
/// Again, each field is optional, meaning they don't have to be present.
-#[derive(Debug, RustcDecodable)]
+#[derive(Debug, Deserialize)]
struct ServerConfig {
ip: Option<String>,
port: Option<u64>,
}
-#[derive(Debug, RustcDecodable)]
+#[derive(Debug, Deserialize)]
struct PeerConfig {
ip: Option<String>,
port: Option<u64>,
@@ -51,11 +52,6 @@ fn main() {
ip = "127.0.0.1"
"#;
- // Use the `decode_str` convenience here to decode a TOML string directly
- // 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();
+ let decoded: Config = toml::from_str(toml_str).unwrap();
println!("{:#?}", decoded);
}
diff --git a/examples/toml2json.rs b/examples/toml2json.rs
index 0d40680..1ed441a 100644
--- a/examples/toml2json.rs
+++ b/examples/toml2json.rs
@@ -1,57 +1,51 @@
#![deny(warnings)]
extern crate toml;
-extern crate rustc_serialize;
+extern crate serde_json;
use std::fs::File;
use std::env;
use std::io;
use std::io::prelude::*;
-use toml::Value;
-use rustc_serialize::json::Json;
+use toml::Value as Toml;
+use serde_json::Value as Json;
fn main() {
let mut args = env::args();
let mut input = String::new();
- let filename = if args.len() > 1 {
+ if args.len() > 1 {
let name = args.nth(1).unwrap();
File::open(&name).and_then(|mut f| {
f.read_to_string(&mut input)
}).unwrap();
- name
} else {
io::stdin().read_to_string(&mut input).unwrap();
- "<stdin>".to_string()
- };
+ }
- let mut parser = toml::Parser::new(&input);
- let toml = match parser.parse() {
- Some(toml) => toml,
- None => {
- 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
+ match input.parse() {
+ Ok(toml) => {
+ let json = convert(toml);
+ println!("{}", serde_json::to_string_pretty(&json).unwrap());
}
- };
- let json = convert(Value::Table(toml));
- println!("{}", json.pretty());
+ Err(error) => println!("failed to parse TOML: {}", error),
+ }
}
-fn convert(toml: Value) -> Json {
+fn convert(toml: Toml) -> Json {
match toml {
- Value::String(s) => Json::String(s),
- Value::Integer(i) => Json::I64(i),
- Value::Float(f) => Json::F64(f),
- Value::Boolean(b) => Json::Boolean(b),
- Value::Array(arr) => Json::Array(arr.into_iter().map(convert).collect()),
- Value::Table(table) => Json::Object(table.into_iter().map(|(k, v)| {
+ Toml::String(s) => Json::String(s),
+ Toml::Integer(i) => Json::Number(i.into()),
+ Toml::Float(f) => {
+ let n = serde_json::Number::from_f64(f)
+ .expect("float infinite and nan not allowed");
+ 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))
}).collect()),
- Value::Datetime(dt) => Json::String(dt),
+ Toml::Datetime(dt) => Json::String(dt.to_string()),
}
}
diff --git a/serde-tests/Cargo.toml b/serde-tests/Cargo.toml
deleted file mode 100644
index a41747f..0000000
--- a/serde-tests/Cargo.toml
+++ /dev/null
@@ -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"
diff --git a/serde-tests/build.rs b/serde-tests/build.rs
deleted file mode 100644
index 1cc5062..0000000
--- a/serde-tests/build.rs
+++ /dev/null
@@ -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();
-}
diff --git a/serde-tests/lib.rs b/serde-tests/lib.rs
deleted file mode 100644
index 65e2cc3..0000000
--- a/serde-tests/lib.rs
+++ /dev/null
@@ -1 +0,0 @@
-// intentionally blank
diff --git a/serde-tests/test.rs b/serde-tests/test.rs
deleted file mode 100644
index e8de9fa..0000000
--- a/serde-tests/test.rs
+++ /dev/null
@@ -1 +0,0 @@
-include!(concat!(env!("OUT_DIR"), "/test.rs"));
diff --git a/serde-tests/test.rs.in b/serde-tests/test.rs.in
deleted file mode 100644
index 15bb164..0000000
--- a/serde-tests/test.rs.in
+++ /dev/null
@@ -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)));
-}
diff --git a/src/datetime.rs b/src/datetime.rs
new file mode 100644
index 0000000..7a618dc
--- /dev/null
+++ b/src/datetime.rs
@@ -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"
+ }
+}
diff --git a/src/de.rs b/src/de.rs
new file mode 100644
index 0000000..7cb0410
--- /dev/null
+++ b/src/de.rs
@@ -0,0 +1,1195 @@
+//! Deserializing TOML into Rust structures.
+//!
+//! This module contains all the Serde support for deserializing TOML documents
+//! into Rust structures. Note that some top-level functions here are also
+//! provided at the top of the crate.
+
+use std::borrow::Cow;
+use std::error;
+use std::fmt;
+use std::str;
+use std::vec;
+
+use serde::de;
+
+use tokens::{Tokenizer, Token, Error as TokenError};
+use datetime::{SERDE_STRUCT_FIELD_NAME, SERDE_STRUCT_NAME};
+
+/// Deserializes a byte slice into a type.
+///
+/// This function will attempt to interpret `bytes` as UTF-8 data and then
+/// deserialize `T` from the TOML document provided.
+pub fn from_slice<T>(bytes: &[u8]) -> Result<T, Error>
+ where T: de::Deserialize,
+{
+ match str::from_utf8(bytes) {
+ Ok(s) => from_str(s),
+ Err(e) => Err(Error::custom(e.to_string())),
+ }
+}
+
+/// Deserializes a string into a type.
+///
+/// This function will attempt to interpret `s` as a TOML document and
+/// deserialize `T` from the document.
+pub fn from_str<T>(s: &str) -> Result<T, Error>
+ where T: de::Deserialize,
+{
+ let mut d = Deserializer::new(s);
+ let ret = T::deserialize(&mut d)?;
+ d.end()?;
+ return Ok(ret)
+}
+
+/// Errors that can occur when deserializing a type.
+#[derive(Debug, Clone)]
+pub struct Error {
+ inner: Box<ErrorInner>,
+}
+
+#[derive(Debug, Clone)]
+struct ErrorInner {
+ kind: ErrorKind,
+ line: Option<usize>,
+ col: usize,
+ message: String,
+ key: Vec<String>,
+}
+
+/// Errors that can occur when deserializing a type.
+#[derive(Debug, Clone)]
+enum ErrorKind {
+ /// EOF was reached when looking for a value
+ UnexpectedEof,
+
+ /// An invalid character not allowed in a string was found
+ InvalidCharInString(char),
+
+ /// An invalid character was found as an escape
+ InvalidEscape(char),
+
+ /// An invalid character was found in a hex escape
+ InvalidHexEscape(char),
+
+ /// An invalid escape value was specified in a hex escape in a string.
+ ///
+ /// Valid values are in the plane of unicode codepoints.
+ InvalidEscapeValue(u32),
+
+ /// A newline in a string was encountered when one was not allowed.
+ NewlineInString,
+
+ /// An unexpected character was encountered, typically when looking for a
+ /// value.
+ Unexpected(char),
+
+ /// An unterminated string was found where EOF was found before the ending
+ /// EOF mark.
+ UnterminatedString,
+
+ /// A newline was found in a table key.
+ NewlineInTableKey,
+
+ /// A number failed to parse
+ NumberInvalid,
+
+ /// A date or datetime was invalid
+ DateInvalid,
+
+ /// Wanted one sort of token, but found another.
+ Wanted {
+ /// Expected token type
+ expected: &'static str,
+ /// Actually found token type
+ found: &'static str,
+ },
+
+ /// An array was decoded but the types inside of it were mixed, which is
+ /// disallowed by TOML.
+ MixedArrayType,
+
+ /// A duplicate table definition was found.
+ DuplicateTable(String),
+
+ /// A previously defined table was redefined as an array.
+ RedefineAsArray,
+
+ /// An empty table key was found.
+ EmptyTableKey,
+
+ /// A custom error which could be generated when deserializing a particular
+ /// type.
+ Custom,
+
+ #[doc(hidden)]
+ __Nonexhaustive,
+}
+
+/// Deserialization implementation for TOML.
+pub struct Deserializer<'a> {
+ require_newline_after_table: bool,
+ input: &'a str,
+ tokens: Tokenizer<'a>,
+}
+
+impl<'a, 'b> de::Deserializer for &'b mut Deserializer<'a> {
+ type Error = Error;
+
+ fn deserialize<V>(self, visitor: V) -> Result<V::Value, Error>
+ where V: de::Visitor,
+ {
+ let mut tables = Vec::new();
+ let mut cur_table = Table {
+ at: 0,
+ header: Vec::new(),
+ values: None,
+ array: false,
+ };
+ while let Some(line) = self.line()? {
+ match line {
+ Line::Table { at, mut header, array } => {
+ if cur_table.header.len() > 0 || cur_table.values.is_some() {
+ tables.push(cur_table);
+ }
+ cur_table = Table {
+ at: at,
+ header: Vec::new(),
+ values: Some(Vec::new()),
+ array: array,
+ };
+ loop {
+ let part = header.next().map_err(|e| {
+ self.token_error(e)
+ });
+ match part? {
+ Some(part) => cur_table.header.push(part),
+ None => break,
+ }
+ }
+ }
+ Line::KeyValue(key, value) => {
+ if cur_table.values.is_none() {
+ cur_table.values = Some(Vec::new());
+ }
+ cur_table.values.as_mut().unwrap().push((key, value));
+ }
+ }
+ }
+ if cur_table.header.len() > 0 || cur_table.values.is_some() {
+ tables.push(cur_table);
+ }
+
+ visitor.visit_map(MapVisitor {
+ values: Vec::new().into_iter(),
+ next_value: None,
+ depth: 0,
+ cur: 0,
+ cur_parent: 0,
+ max: tables.len(),
+ tables: &mut tables,
+ array: false,
+ de: self,
+ })
+ }
+
+ forward_to_deserialize! {
+ bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq
+ seq_fixed_size bytes byte_buf map struct unit enum newtype_struct
+ struct_field ignored_any unit_struct tuple_struct tuple option
+ }
+}
+
+struct Table<'a> {
+ at: usize,
+ header: Vec<Cow<'a, str>>,
+ values: Option<Vec<(Cow<'a, str>, Value<'a>)>>,
+ array: bool,
+}
+
+#[doc(hidden)]
+pub struct MapVisitor<'a: 'b, 'b> {
+ values: vec::IntoIter<(Cow<'a, str>, Value<'a>)>,
+ next_value: Option<(Cow<'a, str>, Value<'a>)>,
+ depth: usize,
+ cur: usize,
+ cur_parent: usize,
+ max: usize,
+ tables: &'b mut [Table<'a>],
+ array: bool,
+ de: &'b mut Deserializer<'a>,
+}
+
+impl<'a, 'b> de::MapVisitor for MapVisitor<'a, 'b> {
+ type Error = Error;
+
+ fn visit_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Error>
+ where K: de::DeserializeSeed,
+ {
+ if self.cur_parent == self.max || self.cur == self.max {
+ return Ok(None)
+ }
+
+ loop {
+ assert!(self.next_value.is_none());
+ if let Some((key, value)) = self.values.next() {
+ let ret = seed.deserialize(StrDeserializer::new(key.clone()))?;
+ self.next_value = Some((key, value));
+ return Ok(Some(ret))
+ }
+
+ let next_table = {
+ let prefix = &self.tables[self.cur_parent].header[..self.depth];
+ self.tables[self.cur..self.max].iter().enumerate().find(|&(_, t)| {
+ if t.values.is_none() {
+ return false
+ }
+ match t.header.get(..self.depth) {
+ Some(header) => header == prefix,
+ None => false,
+ }
+ }).map(|(i, _)| i + self.cur)
+ };
+
+ let pos = match next_table {
+ Some(pos) => pos,
+ None => return Ok(None),
+ };
+ self.cur = pos;
+
+ // Test to see if we're duplicating our parent's table, and if so
+ // then this is an error in the toml format
+ if self.cur_parent != pos &&
+ self.tables[self.cur_parent].header == self.tables[pos].header {
+ let at = self.tables[pos].at;
+ let name = self.tables[pos].header.join(".");
+ return Err(self.de.error(at, ErrorKind::DuplicateTable(name)))
+ }
+
+ let table = &mut self.tables[pos];
+
+ // If we're not yet at the appropriate depth for this table then we
+ // just visit the next portion of its header and then continue
+ // decoding.
+ if self.depth != table.header.len() {
+ let key = &table.header[self.depth];
+ let key = seed.deserialize(StrDeserializer::new(key[..].into()))?;
+ return Ok(Some(key))
+ }
+
+ // Rule out cases like:
+ //
+ // [[foo.bar]]
+ // [[foo]]
+ if table.array {
+ let kind = ErrorKind::RedefineAsArray;
+ return Err(self.de.error(table.at, kind))
+ }
+
+ self.values = table.values.take().unwrap().into_iter();
+ }
+ }
+
+ fn visit_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Error>
+ where V: de::DeserializeSeed,
+ {
+ if let Some((k, v)) = self.next_value.take() {
+ match seed.deserialize(ValueDeserializer::new(v)) {
+ Ok(v) => return Ok(v),
+ Err(mut e) => {
+ e.add_key_context(&k);
+ return Err(e)
+ }
+ }
+ }
+
+ let array = self.tables[self.cur].array &&
+ self.depth == self.tables[self.cur].header.len() - 1;
+ self.cur += 1;
+ let res = seed.deserialize(MapVisitor {
+ values: Vec::new().into_iter(),
+ next_value: None,
+ depth: self.depth + if array {0} else {1},
+ cur_parent: self.cur - 1,
+ cur: 0,
+ max: self.max,
+ array: array,
+ tables: &mut *self.tables,
+ de: &mut *self.de,
+ });
+ res.map_err(|mut e| {
+ e.add_key_context(&self.tables[self.cur - 1].header[self.depth]);
+ e
+ })
+ }
+}
+
+impl<'a, 'b> de::SeqVisitor for MapVisitor<'a, 'b> {
+ type Error = Error;
+
+ fn visit_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Error>
+ where K: de::DeserializeSeed,
+ {
+ assert!(self.next_value.is_none());
+ assert!(self.values.next().is_none());
+
+ if self.cur_parent == self.max {
+ return Ok(None)
+ }
+
+ let next = self.tables[..self.max]
+ .iter()
+ .enumerate()
+ .skip(self.cur_parent + 1)
+ .find(|&(_, table)| {
+ table.array && table.header == self.tables[self.cur_parent].header
+ }).map(|p| p.0)
+ .unwrap_or(self.max);
+
+ let ret = seed.deserialize(MapVisitor {
+ values: self.tables[self.cur_parent].values.take().unwrap().into_iter(),
+ next_value: None,
+ depth: self.depth + 1,
+ cur_parent: self.cur_parent,
+ max: next,
+ cur: 0,
+ array: false,
+ tables: &mut self.tables,
+ de: &mut self.de,
+ })?;
+ self.cur_parent = next;
+ return Ok(Some(ret))
+ }
+}
+
+impl<'a, 'b> de::Deserializer for MapVisitor<'a, 'b> {
+ type Error = Error;
+
+ fn deserialize<V>(self, visitor: V) -> Result<V::Value, Error>
+ where V: de::Visitor,
+ {
+ if self.array {
+ visitor.visit_seq(self)
+ } else {
+ visitor.visit_map(self)
+ }
+ }
+
+ // `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, 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 seq
+ seq_fixed_size bytes byte_buf map struct unit newtype_struct
+ struct_field ignored_any unit_struct tuple_struct tuple enum
+ }
+}
+
+struct StrDeserializer<'a> {
+ key: Cow<'a, str>,
+}
+
+impl<'a> StrDeserializer<'a> {
+ fn new(key: Cow<'a, str>) -> StrDeserializer<'a> {
+ StrDeserializer {
+ key: key,
+ }
+ }
+}
+
+impl<'a> de::Deserializer for StrDeserializer<'a> {
+ type Error = Error;
+
+ fn deserialize<V>(self, visitor: V) -> Result<V::Value, Error>
+ where V: de::Visitor,
+ {
+ match self.key {
+ Cow::Borrowed(s) => visitor.visit_str(s),
+ Cow::Owned(s) => visitor.visit_string(s),
+ }
+ }
+
+ forward_to_deserialize! {
+ bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq
+ seq_fixed_size bytes byte_buf map struct option unit newtype_struct
+ struct_field ignored_any unit_struct tuple_struct tuple enum
+ }
+}
+
+struct ValueDeserializer<'a> {
+ value: Value<'a>,
+}
+
+impl<'a> ValueDeserializer<'a> {
+ fn new(value: Value<'a>) -> ValueDeserializer<'a> {
+ ValueDeserializer {
+ value: value,
+ }
+ }
+}
+
+impl<'a> de::Deserializer for ValueDeserializer<'a> {
+ type Error = Error;
+
+ fn deserialize<V>(self, visitor: V) -> Result<V::Value, Error>
+ where V: de::Visitor,
+ {
+ match self.value {
+ Value::Integer(i) => visitor.visit_i64(i),
+ Value::Boolean(b) => visitor.visit_bool(b),
+ Value::Float(f) => visitor.visit_f64(f),
+ Value::String(Cow::Borrowed(s)) => visitor.visit_str(s),
+ Value::String(Cow::Owned(s)) => visitor.visit_string(s),
+ Value::Datetime(s) => visitor.visit_map(DatetimeDeserializer {
+ date: s,
+ visited: false,
+ }),
+ Value::Array(values) => {
+ let mut s = de::value::SeqDeserializer::new(values.into_iter());
+ let ret = visitor.visit_seq(&mut s)?;
+ s.end()?;
+ Ok(ret)
+ }
+ Value::InlineTable(values) => {
+ visitor.visit_map(InlineTableDeserializer {
+ values: values.into_iter(),
+ next_value: None,
+ })
+ }
+ }
+ }
+
+ fn deserialize_struct<V>(self,
+ name: &'static str,
+ fields: &'static [&'static str],
+ visitor: V) -> Result<V::Value, Error>
+ where V: de::Visitor,
+ {
+ if name == SERDE_STRUCT_NAME && fields == &[SERDE_STRUCT_FIELD_NAME] {
+ if let Value::Datetime(ref s) = self.value {
+ return visitor.visit_map(DatetimeDeserializer {
+ date: s,
+ visited: false,
+ })
+ }
+ }
+
+ self.deserialize(visitor)
+ }
+
+ // `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, 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 seq
+ seq_fixed_size bytes byte_buf map unit newtype_struct
+ struct_field ignored_any unit_struct tuple_struct tuple enum
+ }
+}
+
+impl<'a> de::value::ValueDeserializer<Error> for Value<'a> {
+ type Deserializer = ValueDeserializer<'a>;
+
+ fn into_deserializer(self) -> Self::Deserializer {
+ ValueDeserializer::new(self)
+ }
+}
+
+struct DatetimeDeserializer<'a> {
+ visited: bool,
+ date: &'a str,
+}
+
+impl<'a> de::MapVisitor for DatetimeDeserializer<'a> {
+ type Error = Error;
+
+ fn visit_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Error>
+ where K: de::DeserializeSeed,
+ {
+ if self.visited {
+ return Ok(None)
+ }
+ self.visited = true;
+ seed.deserialize(DatetimeFieldDeserializer).map(Some)
+ }
+
+ fn visit_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Error>
+ where V: de::DeserializeSeed,
+ {
+ seed.deserialize(StrDeserializer::new(self.date.into()))
+ }
+}
+
+struct DatetimeFieldDeserializer;
+
+impl de::Deserializer for DatetimeFieldDeserializer {
+ type Error = Error;
+
+ fn deserialize<V>(self, visitor: V) -> Result<V::Value, Error>
+ where V: de::Visitor,
+ {
+ visitor.visit_str(SERDE_STRUCT_FIELD_NAME)
+ }
+
+ forward_to_deserialize! {
+ bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq
+ seq_fixed_size bytes byte_buf map struct option unit newtype_struct
+ struct_field ignored_any unit_struct tuple_struct tuple enum
+ }
+}
+
+struct InlineTableDeserializer<'a> {
+ values: vec::IntoIter<(Cow<'a, str>, Value<'a>)>,
+ next_value: Option<Value<'a>>,
+}
+
+impl<'a> de::MapVisitor for InlineTableDeserializer<'a> {
+ type Error = Error;
+
+ fn visit_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Error>
+ where K: de::DeserializeSeed,
+ {
+ let (key, value) = match self.values.next() {
+ Some(pair) => pair,
+ None => return Ok(None),
+ };
+ self.next_value = Some(value);
+ seed.deserialize(StrDeserializer::new(key)).map(Some)
+ }
+
+ fn visit_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Error>
+ where V: de::DeserializeSeed,
+ {
+ let value = self.next_value.take().unwrap();
+ seed.deserialize(ValueDeserializer::new(value))
+ }
+}
+
+impl<'a> Deserializer<'a> {
+ /// Creates a new deserializer which will be deserializing the string
+ /// provided.
+ pub fn new(input: &'a str) -> Deserializer<'a> {
+ Deserializer {
+ tokens: Tokenizer::new(input),
+ input: input,
+ require_newline_after_table: false,
+ }
+ }
+
+ /// The `Deserializer::end` method should be called after a value has been
+ /// fully deserialized. This allows the `Deserializer` to validate that the
+ /// input stream is at the end or that it only has trailing
+ /// whitespace/comments.
+ pub fn end(&mut self) -> Result<(), Error> {
+ Ok(())
+ }
+
+ /// Historical versions of toml-rs accidentally allowed a newline after a
+ /// table definition, but the TOML spec requires a newline after a table
+ /// definition header.
+ ///
+ /// This option can be set to `false` (the default is `true`) to emulate
+ /// this behavior for backwards compatibility with older toml-rs versions.
+ pub fn set_require_newline_after_table(&mut self, require: bool) {
+ self.require_newline_after_table = require;
+ }
+
+ fn line(&mut self) -> Result<Option<Line<'a>>, Error> {
+ loop {
+ self.eat_whitespace()?;
+ if self.eat_comment()? {
+ continue
+ }
+ if self.eat(Token::Newline)? {
+ continue
+ }
+ break
+ }
+
+ match self.peek()? {
+ Some(Token::LeftBracket) => self.table_header().map(Some),
+ Some(_) => self.key_value().map(Some),
+ None => Ok(None),
+ }
+ }
+
+ fn table_header(&mut self) -> Result<Line<'a>, Error> {
+ let start = self.tokens.current();
+ self.expect(Token::LeftBracket)?;
+ let array = self.eat(Token::LeftBracket)?;
+ let ret = Header::new(self.tokens.clone(), array);
+ self.tokens.skip_to_newline();
+ Ok(Line::Table { at: start, header: ret, array: array })
+ }
+
+ fn key_value(&mut self) -> Result<Line<'a>, Error> {
+ let key = self.table_key()?;
+ self.eat_whitespace()?;
+ self.expect(Token::Equals)?;
+ self.eat_whitespace()?;
+
+ let value = self.value()?;
+ self.eat_whitespace()?;
+ if !self.eat_comment()? {
+ self.eat_newline_or_eof()?;
+ }
+
+ Ok(Line::KeyValue(key, value))
+ }
+
+ fn value(&mut self) -> Result<Value<'a>, Error> {
+ let at = self.tokens.current();
+ let value = match self.next()? {
+ Some(Token::String { val, .. }) => Value::String(val),
+ Some(Token::Keylike("true")) => Value::Boolean(true),
+ Some(Token::Keylike("false")) => Value::Boolean(false),
+ Some(Token::Keylike(key)) => self.number_or_date(key)?,
+ Some(Token::Plus) => self.number_leading_plus()?,
+ Some(Token::LeftBrace) => self.inline_table().map(Value::InlineTable)?,
+ Some(Token::LeftBracket) => self.array().map(Value::Array)?,
+ Some(token) => {
+ return Err(self.error(at, ErrorKind::Wanted {
+ expected: "a value",
+ found: token.describe(),
+ }))
+ }
+ None => return Err(self.eof()),
+ };
+ Ok(value)
+ }
+
+ fn number_or_date(&mut self, s: &'a str) -> Result<Value<'a>, Error> {
+ if s.contains("T") || (s.len() > 1 && s[1..].contains("-")) &&
+ !s.contains("e-") {
+ self.datetime(s, false).map(Value::Datetime)
+ } else if self.eat(Token::Colon)? {
+ self.datetime(s, true).map(Value::Datetime)
+ } else {
+ self.number(s)
+ }
+ }
+
+ fn number(&mut self, s: &'a str) -> Result<Value<'a>, Error> {
+ if s.contains("e") || s.contains("E") {
+ self.float(s, None).map(Value::Float)
+ } else if self.eat(Token::Period)? {
+ let at = self.tokens.current();
+ match self.next()? {
+ Some(Token::Keylike(after)) => {
+ self.float(s, Some(after)).map(Value::Float)
+ }
+ _ => Err(self.error(at, ErrorKind::NumberInvalid)),
+ }
+ } else {
+ self.integer(s).map(Value::Integer)
+ }
+ }
+
+ fn number_leading_plus(&mut self) -> Result<Value<'a>, Error> {
+ let start = self.tokens.current();
+ match self.next()? {
+ Some(Token::Keylike(s)) => self.number(s),
+ _ => Err(self.error(start, ErrorKind::NumberInvalid)),
+ }
+ }
+
+ fn integer(&self, s: &'a str) -> Result<i64, Error> {
+ let (prefix, suffix) = self.parse_integer(s, true, false)?;
+ let start = self.tokens.substr_offset(s);
+ if suffix != "" {
+ return Err(self.error(start, ErrorKind::NumberInvalid))
+ }
+ prefix.replace("_", "").trim_left_matches("+").parse().map_err(|_e| {
+ self.error(start, ErrorKind::NumberInvalid)
+ })
+ }
+
+ fn parse_integer(&self,
+ s: &'a str,
+ allow_sign: bool,
+ allow_leading_zeros: bool)
+ -> Result<(&'a str, &'a str), Error> {
+ let start = self.tokens.substr_offset(s);
+
+ let mut first = true;
+ let mut first_zero = false;
+ let mut underscore = false;
+ let mut end = s.len();
+ for (i, c) in s.char_indices() {
+ let at = i + start;
+ if i == 0 && (c == '+' || c == '-') && allow_sign {
+ continue
+ }
+
+ match c {
+ '0' if first => first_zero = true,
+ '0' ... '9' if !first && first_zero && !allow_leading_zeros => {
+ return Err(self.error(at, ErrorKind::NumberInvalid))
+ }
+ '0' ... '9' => underscore = false,
+ '_' if first => {
+ return Err(self.error(at, ErrorKind::NumberInvalid))
+ }
+ '_' if !underscore => underscore = true,
+ _ => {
+ end = i;
+ break
+ }
+
+ }
+ first = false;
+ }
+ if first || underscore {
+ return Err(self.error(start, ErrorKind::NumberInvalid))
+ }
+ Ok((&s[..end], &s[end..]))
+ }
+
+ fn float(&mut self, s: &'a str, after_decimal: Option<&'a str>)
+ -> Result<f64, Error> {
+ let (integral, mut suffix) = self.parse_integer(s, true, false)?;
+ let start = self.tokens.substr_offset(integral);
+
+ let mut fraction = None;
+ if let Some(after) = after_decimal {
+ if suffix != "" {
+ return Err(self.error(start, ErrorKind::NumberInvalid))
+ }
+ let (a, b) = self.parse_integer(&after, false, true)?;
+ fraction = Some(a);
+ suffix = b;
+ }
+
+ let mut exponent = None;
+ if suffix.starts_with("e") || suffix.starts_with("E") {
+ let (a, b) = if suffix.len() == 1 {
+ self.eat(Token::Plus)?;
+ match self.next()? {
+ Some(Token::Keylike(s)) => {
+ self.parse_integer(s, false, false)?
+ }
+ _ => return Err(self.error(start, ErrorKind::NumberInvalid)),
+ }
+ } else {
+ self.parse_integer(&suffix[1..], true, false)?
+ };
+ if b != "" {
+ return Err(self.error(start, ErrorKind::NumberInvalid))
+ }
+ exponent = Some(a);
+ }
+
+ let mut number = integral.trim_left_matches("+")
+ .chars()
+ .filter(|c| *c != '_')
+ .collect::<String>();
+ if let Some(fraction) = fraction {
+ number.push_str(".");
+ number.extend(fraction.chars().filter(|c| *c != '_'));
+ }
+ if let Some(exponent) = exponent {
+ number.push_str("E");
+ number.extend(exponent.chars().filter(|c| *c != '_'));
+ }
+ number.parse().map_err(|_e| {
+ self.error(start, ErrorKind::NumberInvalid)
+ })
+ }
+
+ fn datetime(&mut self, date: &'a str, colon_eaten: bool)
+ -> Result<&'a str, Error> {
+ let start = self.tokens.substr_offset(date);
+ if colon_eaten || self.eat(Token::Colon)? {
+ // minutes
+ match self.next()? {
+ Some(Token::Keylike(_)) => {}
+ _ => return Err(self.error(start, ErrorKind::DateInvalid)),
+ }
+ // Seconds
+ self.expect(Token::Colon)?;
+ match self.next()? {
+ Some(Token::Keylike(_)) => {}
+ _ => return Err(self.error(start, ErrorKind::DateInvalid)),
+ }
+ // Fractional seconds
+ if self.eat(Token::Period)? {
+ match self.next()? {
+ Some(Token::Keylike(_)) => {}
+ _ => return Err(self.error(start, ErrorKind::DateInvalid)),
+ }
+ }
+
+ // offset
+ if self.eat(Token::Plus)? {
+ match self.next()? {
+ Some(Token::Keylike(_)) => {}
+ _ => return Err(self.error(start, ErrorKind::DateInvalid)),
+ }
+ }
+ if self.eat(Token::Colon)? {
+ match self.next()? {
+ Some(Token::Keylike(_)) => {}
+ _ => return Err(self.error(start, ErrorKind::DateInvalid)),
+ }
+ }
+ }
+ let end = self.tokens.current();
+ Ok(&self.tokens.input()[start..end])
+ }
+
+ // TODO(#140): shouldn't buffer up this entire table in memory, it'd be
+ // great to defer parsing everything until later.
+ fn inline_table(&mut self) -> Result<Vec<(Cow<'a, str>, Value<'a>)>, Error> {
+ let mut ret = Vec::new();
+ self.eat_whitespace()?;
+ if self.eat(Token::RightBrace)? {
+ return Ok(ret)
+ }
+ loop {
+ let key = self.table_key()?;
+ self.eat_whitespace()?;
+ self.expect(Token::Equals)?;
+ self.eat_whitespace()?;
+ ret.push((key, self.value()?));
+
+ self.eat_whitespace()?;
+ if self.eat(Token::RightBrace)? {
+ return Ok(ret)
+ }
+ self.expect(Token::Comma)?;
+ self.eat_whitespace()?;
+ }
+ }
+
+ // TODO(#140): shouldn't buffer up this entire array in memory, it'd be
+ // great to defer parsing everything until later.
+ fn array(&mut self) -> Result<Vec<Value<'a>>, Error> {
+ let mut ret = Vec::new();
+
+ let intermediate = |me: &mut Deserializer| {
+ loop {
+ me.eat_whitespace()?;
+ if !me.eat(Token::Newline)? && !me.eat_comment()? {
+ break
+ }
+ }
+ Ok(())
+ };
+
+ loop {
+ intermediate(self)?;
+ if self.eat(Token::RightBracket)? {
+ return Ok(ret)
+ }
+ let at = self.tokens.current();
+ let value = self.value()?;
+ if let Some(last) = ret.last() {
+ if !value.same_type(last) {
+ return Err(self.error(at, ErrorKind::MixedArrayType))
+ }
+ }
+ ret.push(value);
+ intermediate(self)?;
+ if !self.eat(Token::Comma)? {
+ break
+ }
+ }
+ intermediate(self)?;
+ self.expect(Token::RightBracket)?;
+ Ok(ret)
+ }
+
+ fn table_key(&mut self) -> Result<Cow<'a, str>, Error> {
+ self.tokens.table_key().map_err(|e| self.token_error(e))
+ }
+
+ fn eat_whitespace(&mut self) -> Result<(), Error> {
+ self.tokens.eat_whitespace().map_err(|e| self.token_error(e))
+ }
+
+ fn eat_comment(&mut self) -> Result<bool, Error> {
+ self.tokens.eat_comment().map_err(|e| self.token_error(e))
+ }
+
+ fn eat_newline_or_eof(&mut self) -> Result<(), Error> {
+ self.tokens.eat_newline_or_eof().map_err(|e| self.token_error(e))
+ }
+
+ fn eat(&mut self, expected: Token<'a>) -> Result<bool, Error> {
+ self.tokens.eat(expected).map_err(|e| self.token_error(e))
+ }
+
+ fn expect(&mut self, expected: Token<'a>) -> Result<(), Error> {
+ self.tokens.expect(expected).map_err(|e| self.token_error(e))
+ }
+
+ fn next(&mut self) -> Result<Option<Token<'a>>, Error> {
+ self.tokens.next().map_err(|e| self.token_error(e))
+ }
+
+ fn peek(&mut self) -> Result<Option<Token<'a>>, Error> {
+ self.tokens.peek().map_err(|e| self.token_error(e))
+ }
+
+ fn eof(&self) -> Error {
+ self.error(self.input.len(), ErrorKind::UnexpectedEof)
+ }
+
+ fn token_error(&self, error: TokenError) -> Error {
+ match error {
+ TokenError::InvalidCharInString(at, ch) => {
+ self.error(at, ErrorKind::InvalidCharInString(ch))
+ }
+ TokenError::InvalidEscape(at, ch) => {
+ self.error(at, ErrorKind::InvalidEscape(ch))
+ }
+ TokenError::InvalidEscapeValue(at, v) => {
+ self.error(at, ErrorKind::InvalidEscapeValue(v))
+ }
+ TokenError::InvalidHexEscape(at, ch) => {
+ self.error(at, ErrorKind::InvalidHexEscape(ch))
+ }
+ TokenError::NewlineInString(at) => {
+ self.error(at, ErrorKind::NewlineInString)
+ }
+ TokenError::Unexpected(at, ch) => {
+ self.error(at, ErrorKind::Unexpected(ch))
+ }
+ TokenError::UnterminatedString(at) => {
+ self.error(at, ErrorKind::UnterminatedString)
+ }
+ TokenError::NewlineInTableKey(at) => {
+ self.error(at, ErrorKind::NewlineInTableKey)
+ }
+ TokenError::Wanted { at, expected, found } => {
+ self.error(at, ErrorKind::Wanted { expected: expected, found: found })
+ }
+ TokenError::EmptyTableKey(at) => {
+ self.error(at, ErrorKind::EmptyTableKey)
+ }
+ }
+ }
+
+ fn error(&self, at: usize, kind: ErrorKind) -> Error {
+ let mut err = Error::from_kind(kind);
+ let (line, col) = self.to_linecol(at);
+ err.inner.line = Some(line);
+ err.inner.col = col;
+ return err
+ }
+
+ /// Converts a byte offset from an error message to a (line, column) pair
+ ///
+ /// All indexes are 0-based.
+ fn to_linecol(&self, offset: usize) -> (usize, usize) {
+ let mut cur = 0;
+ for (i, line) in self.input.lines().enumerate() {
+ if cur + line.len() + 1 > offset {
+ return (i, offset - cur)
+ }
+ cur += line.len() + 1;
+ }
+ (self.input.lines().count(), 0)
+ }
+}
+
+impl Error {
+ fn from_kind(kind: ErrorKind) -> Error {
+ Error {
+ inner: Box::new(ErrorInner {
+ kind: kind,
+ line: None,
+ col: 0,
+ message: String::new(),
+ key: Vec::new(),
+ }),
+ }
+ }
+
+ fn custom(s: String) -> Error {
+ Error {
+ inner: Box::new(ErrorInner {
+ kind: ErrorKind::Custom,
+ line: None,
+ col: 0,
+ message: s,
+ key: Vec::new(),
+ }),
+ }
+ }
+
+ /// Do not call this method, it may be removed at any time, it's just an
+ /// internal implementation detail.
+ #[doc(hidden)]
+ pub fn add_key_context(&mut self, key: &str) {
+ self.inner.key.insert(0, key.to_string());
+ }
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self.inner.kind {
+ ErrorKind::UnexpectedEof => "unexpected eof encountered".fmt(f)?,
+ ErrorKind::InvalidCharInString(c) => {
+ write!(f, "invalid character in string: `{}`",
+ c.escape_default().collect::<String>())?
+ }
+ ErrorKind::InvalidEscape(c) => {
+ write!(f, "invalid escape character in string: `{}`",
+ c.escape_default().collect::<String>())?
+ }
+ ErrorKind::InvalidHexEscape(c) => {
+ write!(f, "invalid hex escape character in string: `{}`",
+ c.escape_default().collect::<String>())?
+ }
+ ErrorKind::InvalidEscapeValue(c) => {
+ write!(f, "invalid escape value: `{}`", c)?
+ }
+ ErrorKind::NewlineInString => "newline in string found".fmt(f)?,
+ ErrorKind::Unexpected(ch) => {
+ write!(f, "unexpected character found: `{}`",
+ ch.escape_default().collect::<String>())?
+ }
+ ErrorKind::UnterminatedString => "unterminated string".fmt(f)?,
+ ErrorKind::NewlineInTableKey => "found newline in table key".fmt(f)?,
+ ErrorKind::Wanted { expected, found } => {
+ write!(f, "expected {}, found {}", expected, found)?
+ }
+ ErrorKind::NumberInvalid => "invalid number".fmt(f)?,
+ ErrorKind::DateInvalid => "invalid date".fmt(f)?,
+ ErrorKind::MixedArrayType => "mixed types in an array".fmt(f)?,
+ ErrorKind::DuplicateTable(ref s) => {
+ write!(f, "redefinition of table `{}`", s)?;
+ }
+ ErrorKind::RedefineAsArray => "table redefined as array".fmt(f)?,
+ ErrorKind::EmptyTableKey => "empty table key found".fmt(f)?,
+ ErrorKind::Custom => self.inner.message.fmt(f)?,
+ ErrorKind::__Nonexhaustive => panic!(),
+ }
+
+ if self.inner.key.len() > 0 {
+ write!(f, " for key `")?;
+ for (i, k) in self.inner.key.iter().enumerate() {
+ if i > 0 {
+ write!(f, ".")?;
+ }
+ write!(f, "{}", k)?;
+ }
+ write!(f, "`")?;
+ }
+
+ if let Some(line) = self.inner.line {
+ write!(f, " at line {}", line + 1)?;
+ }
+
+ Ok(())
+ }
+}
+
+impl error::Error for Error {
+ fn description(&self) -> &str {
+ match self.inner.kind {
+ ErrorKind::UnexpectedEof => "unexpected eof encountered",
+ ErrorKind::InvalidCharInString(_) => "invalid char in string",
+ ErrorKind::InvalidEscape(_) => "invalid escape in string",
+ ErrorKind::InvalidHexEscape(_) => "invalid hex escape in string",
+ ErrorKind::InvalidEscapeValue(_) => "invalid escape value in string",
+ ErrorKind::NewlineInString => "newline in string found",
+ ErrorKind::Unexpected(_) => "unexpected or invalid character",
+ ErrorKind::UnterminatedString => "unterminated string",
+ ErrorKind::NewlineInTableKey => "found newline in table key",
+ ErrorKind::Wanted { .. } => "expected a token but found another",
+ ErrorKind::NumberInvalid => "invalid number",
+ ErrorKind::DateInvalid => "invalid date",
+ ErrorKind::MixedArrayType => "mixed types in an array",
+ ErrorKind::DuplicateTable(_) => "duplicate table",
+ ErrorKind::RedefineAsArray => "table redefined as array",
+ ErrorKind::EmptyTableKey => "empty table key found",
+ ErrorKind::Custom => "a custom error",
+ ErrorKind::__Nonexhaustive => panic!(),
+ }
+ }
+}
+
+impl de::Error for Error {
+ fn custom<T: fmt::Display>(msg: T) -> Error {
+ Error::custom(msg.to_string())
+ }
+}
+
+enum Line<'a> {
+ Table { at: usize, header: Header<'a>, array: bool },
+ KeyValue(Cow<'a, str>, Value<'a>),
+}
+
+struct Header<'a> {
+ first: bool,
+ array: bool,
+ tokens: Tokenizer<'a>,
+}
+
+impl<'a> Header<'a> {
+ fn new(tokens: Tokenizer<'a>, array: bool) -> Header<'a> {
+ Header {
+ first: true,
+ array: array,
+ tokens: tokens,
+ }
+ }
+
+ fn next(&mut self) -> Result<Option<Cow<'a, str>>, TokenError> {
+ self.tokens.eat_whitespace()?;
+
+ if self.first || self.tokens.eat(Token::Period)? {
+ self.first = false;
+ self.tokens.eat_whitespace()?;
+ self.tokens.table_key().map(Some)
+ } else {
+ self.tokens.expect(Token::RightBracket)?;
+ if self.array {
+ self.tokens.expect(Token::RightBracket)?;
+ }
+
+ self.tokens.eat_whitespace()?;
+ if !self.tokens.eat_comment()? {
+ self.tokens.eat_newline_or_eof()?;
+ }
+ Ok(None)
+ }
+ }
+}
+
+enum Value<'a> {
+ Integer(i64),
+ Float(f64),
+ Boolean(bool),
+ String(Cow<'a, str>),
+ Datetime(&'a str),
+ Array(Vec<Value<'a>>),
+ InlineTable(Vec<(Cow<'a, str>, Value<'a>)>),
+}
+
+impl<'a> Value<'a> {
+ fn same_type(&self, other: &Value<'a>) -> 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::InlineTable(..), &Value::InlineTable(..)) => true,
+
+ _ => false,
+ }
+ }
+}
diff --git a/src/decoder/mod.rs b/src/decoder/mod.rs
deleted file mode 100644
index 51a9ea2..0000000
--- a/src/decoder/mod.rs
+++ /dev/null
@@ -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",
- }
- }
-}
diff --git a/src/decoder/rustc_serialize.rs b/src/decoder/rustc_serialize.rs
deleted file mode 100644
index f850663..0000000
--- a/src/decoder/rustc_serialize.rs
+++ /dev/null
@@ -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");
-
- }
-}
diff --git a/src/decoder/serde.rs b/src/decoder/serde.rs
deleted file mode 100644
index 81a2bae..0000000
--- a/src/decoder/serde.rs
+++ /dev/null
@@ -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)
- }
-}
diff --git a/src/encoder/mod.rs b/src/encoder/mod.rs
deleted file mode 100644
index 910c970..0000000
--- a/src/encoder/mod.rs
+++ /dev/null
@@ -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" }
-}
diff --git a/src/encoder/rustc_serialize.rs b/src/encoder/rustc_serialize.rs
deleted file mode 100644
index 0eda740..0000000
--- a/src/encoder/rustc_serialize.rs
+++ /dev/null
@@ -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);
- }
-}
diff --git a/src/encoder/serde.rs b/src/encoder/serde.rs
deleted file mode 100644
index 997bc37..0000000
--- a/src/encoder/serde.rs
+++ /dev/null
@@ -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())
- }
-}
diff --git a/src/lib.rs b/src/lib.rs
index 51d85b4..d2034e7 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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
-//! files [1]. It is focused around high quality errors including specific spans
-//! and detailed error messages when things go wrong.
+//! [TOML]: https://github.com/toml-lang/toml
//!
-//! 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
+//! [package]
+//! name = "toml"
+//! version = "0.2.1"
+//! authors = ["Alex Crichton <alex@alexcrichton.com>"]
+//!
+//! [dependencies]
+//! serde = "0.9"
+//! ```
+//!
+//! The TOML format tends to be relatively common throughout the Rust community
+//! for configuration, notably being used by [Cargo], Rust's package manager.
+//!
+//! ## TOML values
+//!
+//! A value in TOML is represented with the `Value` enum in this crate:
+//!
+//! ```rust,ignore
+//! pub enum Value {
+//! String(String),
+//! Integer(i64),
+//! Float(f64),
+//! Boolean(bool),
+//! Datetime(Datetime),
+//! Array(Array),
+//! Table(Table),
+//! }
//! ```
-//! let toml = r#"
-//! [test]
-//! foo = "bar"
-//! "#;
//!
-//! let value = toml::Parser::new(toml).parse().unwrap();
-//! println!("{:?}", value);
+//! You'll note that TOML is very similar to JSON with the notable addition of a
+//! `Datetime` type. In general TOML and JSON are interchangeable in terms of
+//! formats.
+//!
+//! ## Parsing TOML
+//!
+//! The easiest way to parse a TOML document is via the `Value` type:
+//!
+//! ```rust
+//! use toml::Value;
+//!
+//! let value = "foo = 'bar'".parse::<Value>().unwrap();
+//!
+//! assert_eq!(value["foo"].as_str(), Some("bar"));
//! ```
//!
-//! # Conversions
+//! 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");
+//! }
+//! ```
//!
-//! This library also supports using the standard `Encodable` and `Decodable`
-//! traits with TOML values. This library provides the following conversion
-//! capabilities:
+//! Similarly you can serialize types in a similar fashion:
//!
-//! * `String` => `toml::Value` - via `Parser`
-//! * `toml::Value` => `String` - via `Display`
-//! * `toml::Value` => rust object - via `Decoder`
-//! * rust object => `toml::Value` - via `Encoder`
+//! ```rust
+//! #[macro_use]
+//! extern crate serde_derive;
+//! extern crate toml;
//!
-//! Convenience functions for performing multiple conversions at a time are also
-//! provided.
+//! #[derive(Serialize)]
+//! struct Config {
+//! ip: String,
+//! port: Option<u16>,
+//! keys: Keys,
+//! }
//!
-//! [1]: https://github.com/mojombo/toml
-//! [2]: https://github.com/BurntSushi/toml-test
+//! #[derive(Serialize)]
+//! struct Keys {
+//! github: String,
+//! travis: Option<String>,
+//! }
//!
-//! # Encoding support
+//! 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()),
+//! },
+//! };
//!
-//! This crate optionally supports the [`rustc-serialize`] crate and also the
-//! [`serde`] crate through respective feature names. The `rustc-serialize`
-//! feature is enabled by default.
+//! let toml = toml::to_string(&config).unwrap();
+//! }
+//! ```
//!
-//! [`rustc-serialize`]: http://github.com/rust-lang/rustc-serialize
-//! [`serde`]: http://github.com/serde-rs/serde
+//! [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)]
-#![cfg_attr(test, deny(warnings))]
-
-#[cfg(feature = "rustc-serialize")] extern crate rustc_serialize;
-#[cfg(feature = "serde")] extern crate serde;
-
-use std::collections::BTreeMap;
-use std::str::FromStr;
-
-pub use parser::{Parser, ParserError};
-
-#[cfg(any(feature = "rustc-serialize", feature = "serde"))]
-pub use self::encoder::{Encoder, Error, EncoderState, encode, encode_str};
-#[cfg(any(feature = "rustc-serialize", feature = "serde"))]
-pub use self::decoder::{Decoder, DecodeError, DecodeErrorKind, decode, decode_str};
-
-mod parser;
-mod display;
-#[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""#);
- }
-}
+#[macro_use]
+extern crate serde;
+
+pub mod value;
+mod datetime;
+#[doc(no_inline)]
+pub use value::Value;
+
+pub mod ser;
+#[doc(no_inline)]
+pub use ser::{to_string, to_vec, Serializer};
+pub mod de;
+#[doc(no_inline)]
+pub use de::{from_slice, from_str, Deserializer};
+mod tokens;
diff --git a/src/parser.rs b/src/parser.rs
deleted file mode 100644
index eb86d62..0000000
--- a/src/parser.rs
+++ /dev/null
@@ -1,1627 +0,0 @@
-use std::char;
-use std::collections::btree_map::{BTreeMap, Entry};
-use std::error::Error;
-use std::fmt;
-use std::str;
-
-macro_rules! try {
- ($e:expr) => (match $e { Some(s) => s, None => return None })
-}
-
-// We redefine Value because we need to keep track of encountered table
-// definitions, eg when parsing:
-//
-// [a]
-// [a.b]
-// [a]
-//
-// we have to error out on redefinition of [a]. This bit of data is difficult to
-// track in a side table so we just have a "stripped down" AST to work with
-// which has the relevant metadata fields in it.
-struct TomlTable {
- values: BTreeMap<String, Value>,
- defined: bool,
-}
-
-impl TomlTable {
- fn convert(self) -> super::Table {
- self.values.into_iter().map(|(k,v)| (k, v.convert())).collect()
- }
-}
-
-enum Value {
- String(String),
- Integer(i64),
- Float(f64),
- Boolean(bool),
- Datetime(String),
- Array(Vec<Value>),
- Table(TomlTable),
-}
-
-impl Value {
- 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",
- }
- }
-
- 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,
- }
- }
-
- fn convert(self) -> super::Value {
- match self {
- Value::String(x) => super::Value::String(x),
- Value::Integer(x) => super::Value::Integer(x),
- Value::Float(x) => super::Value::Float(x),
- Value::Boolean(x) => super::Value::Boolean(x),
- Value::Datetime(x) => super::Value::Datetime(x),
- Value::Array(v) =>
- super::Value::Array(
- v.into_iter().map(|x| x.convert()).collect()
- ),
- Value::Table(t) => super::Value::Table(t.convert())
- }
- }
-}
-
-/// Parser for converting a string to a TOML `Value` instance.
-///
-/// This parser contains the string slice that is being parsed, and exports the
-/// list of errors which have occurred during parsing.
-pub struct Parser<'a> {
- input: &'a str,
- cur: str::CharIndices<'a>,
- require_newline_after_table: bool,
-
- /// A list of all errors which have occurred during parsing.
- ///
- /// Not all parse errors are fatal, so this list is added to as much as
- /// possible without aborting parsing. If `None` is returned by `parse`, it
- /// is guaranteed that this list is not empty.
- pub errors: Vec<ParserError>,
-}
-
-/// A structure representing a parse error.
-///
-/// The data in this structure can be used to trace back to the original cause
-/// of the error in order to provide diagnostics about parse errors.
-#[derive(Debug, Clone)]
-pub struct ParserError {
- /// The low byte at which this error is pointing at.
- pub lo: usize,
- /// One byte beyond the last character at which this error is pointing at.
- pub hi: usize,
- /// A human-readable description explaining what the error is.
- pub desc: String,
-}
-
-impl<'a> Parser<'a> {
- /// Creates a new parser for a string.
- ///
- /// The parser can be executed by invoking the `parse` method.
- ///
- /// # Example
- ///
- /// ```
- /// let toml = r#"
- /// [test]
- /// foo = "bar"
- /// "#;
- ///
- /// let mut parser = toml::Parser::new(toml);
- /// match parser.parse() {
- /// Some(value) => println!("found toml: {:?}", value),
- /// None => {
- /// println!("parse errors: {:?}", parser.errors);
- /// }
- /// }
- /// ```
- pub fn new(s: &'a str) -> Parser<'a> {
- Parser {
- input: s,
- cur: s.char_indices(),
- errors: Vec::new(),
- require_newline_after_table: true,
- }
- }
-
- /// Converts a byte offset from an error message to a (line, column) pair
- ///
- /// All indexes are 0-based.
- pub fn to_linecol(&self, offset: usize) -> (usize, usize) {
- let mut cur = 0;
- for (i, line) in self.input.lines().enumerate() {
- if cur + line.len() + 1 > offset {
- return (i, offset - cur)
- }
- cur += line.len() + 1;
- }
- (self.input.lines().count(), 0)
- }
-
- /// Historical versions of toml-rs accidentally allowed a newline after a
- /// table definition, but the TOML spec requires a newline after a table
- /// definition header.
- ///
- /// This option can be set to `false` (the default is `true`) to emulate
- /// this behavior for backwards compatibility with older toml-rs versions.
- pub fn set_require_newline_after_table(&mut self, require: bool) {
- self.require_newline_after_table = require;
- }
-
- fn next_pos(&self) -> usize {
- self.cur.clone().next().map(|p| p.0).unwrap_or(self.input.len())
- }
-
- // Returns true and consumes the next character if it matches `ch`,
- // otherwise do nothing and return false
- fn eat(&mut self, ch: char) -> bool {
- match self.peek(0) {
- Some((_, c)) if c == ch => { self.cur.next(); true }
- Some(_) | None => false,
- }
- }
-
- // Peeks ahead `n` characters
- fn peek(&self, n: usize) -> Option<(usize, char)> {
- self.cur.clone().skip(n).next()
- }
-
- fn expect(&mut self, ch: char) -> bool {
- if self.eat(ch) { return true }
- let mut it = self.cur.clone();
- let lo = it.next().map(|p| p.0).unwrap_or(self.input.len());
- let hi = it.next().map(|p| p.0).unwrap_or(self.input.len());
- self.errors.push(ParserError {
- lo: lo,
- hi: hi,
- desc: match self.cur.clone().next() {
- Some((_, c)) => format!("expected `{}`, but found `{}`", ch, c),
- None => format!("expected `{}`, but found eof", ch)
- }
- });
- false
- }
-
- // Consumes a BOM (Byte Order Mark) if one is next
- fn bom(&mut self) -> bool {
- match self.peek(0) {
- Some((_, '\u{feff}')) => { self.cur.next(); true }
- _ => false
- }
- }
-
- // Consumes whitespace ('\t' and ' ') until another character (or EOF) is
- // reached. Returns if any whitespace was consumed
- fn ws(&mut self) -> bool {
- let mut ret = false;
- loop {
- match self.peek(0) {
- Some((_, '\t')) |
- Some((_, ' ')) => { self.cur.next(); ret = true; }
- _ => break,
- }
- }
- ret
- }
-
- // Consumes the rest of the line after a comment character
- fn comment(&mut self) -> bool {
- if !self.eat('#') { return false }
- for (_, ch) in self.cur.by_ref() {
- if ch == '\n' { break }
- }
- true
- }
-
- // Consumes a newline if one is next
- fn newline(&mut self) -> bool {
- match self.peek(0) {
- Some((_, '\n')) => { self.cur.next(); true }
- Some((_, '\r')) if self.peek(1).map(|c| c.1) == Some('\n') => {
- self.cur.next(); self.cur.next(); true
- }
- _ => false
- }
- }
-
- // Match EOF
- fn eof(&self) -> bool {
- self.peek(0).is_none()
- }
-
- /// Executes the parser, parsing the string contained within.
- ///
- /// This function will return the `TomlTable` instance if parsing is
- /// successful, or it will return `None` if any parse error or invalid TOML
- /// error occurs.
- ///
- /// If an error occurs, the `errors` field of this parser can be consulted
- /// to determine the cause of the parse failure.
- pub fn parse(&mut self) -> Option<super::Table> {
- let mut ret = TomlTable { values: BTreeMap::new(), defined: false };
- self.bom();
- while self.peek(0).is_some() {
- self.ws();
- if self.newline() { continue }
- if self.comment() { continue }
- if self.eat('[') {
- let array = self.eat('[');
- let start = self.next_pos();
-
- // Parse the name of the section
- let mut keys = Vec::new();
- loop {
- self.ws();
- if let Some(s) = self.key_name() {
- keys.push(s);
- }
- self.ws();
- if self.eat(']') {
- if array && !self.expect(']') { return None }
- break
- }
- if !self.expect('.') { return None }
- }
- if keys.is_empty() { return None }
-
- // Build the section table
- let mut table = TomlTable {
- values: BTreeMap::new(),
- defined: true,
- };
- if self.require_newline_after_table {
- self.ws();
- if !self.comment() && !self.newline() && !self.eof() {
- self.errors.push(ParserError {
- lo: start,
- hi: start,
- desc: "expected a newline after table definition".to_string(),
- });
- return None
- }
- }
- if !self.values(&mut table) { return None }
- if array {
- self.insert_array(&mut ret, &keys, Value::Table(table),
- start)
- } else {
- self.insert_table(&mut ret, &keys, table, start)
- }
- } else if !self.values(&mut ret) {
- return None
- }
- }
- if !self.errors.is_empty() {
- None
- } else {
- Some(ret.convert())
- }
- }
-
- // Parse an array index as a natural number
- fn array_index(&mut self) -> Option<String> {
- self.integer(0, false, false)
- }
-
- /// Parse a path into a vector of paths
- pub fn lookup(&mut self) -> Option<Vec<String>> {
- if self.input.is_empty() {
- return Some(vec![]);
- }
- let mut keys = Vec::new();
- loop {
- self.ws();
- if let Some(s) = self.key_name() {
- keys.push(s);
- } else if let Some(s) = self.array_index() {
- keys.push(s);
- } else {
- return None
- }
- self.ws();
- if !self.expect('.') { return Some(keys) }
- }
- }
-
- // Parse a single key name starting at `start`
- fn key_name(&mut self) -> Option<String> {
- let start = self.next_pos();
- let key = if self.eat('"') {
- self.finish_basic_string(start, false)
- } else if self.eat('\'') {
- self.finish_literal_string(start, false)
- } else {
- let mut ret = String::new();
- while let Some((_, ch)) = self.cur.clone().next() {
- match ch {
- 'a' ... 'z' |
- 'A' ... 'Z' |
- '0' ... '9' |
- '_' | '-' => { self.cur.next(); ret.push(ch) }
- _ => break,
- }
- }
- Some(ret)
- };
- match key {
- Some(ref name) if name.is_empty() => {
- self.errors.push(ParserError {
- lo: start,
- hi: start,
- desc: "expected a key but found an empty string".to_string(),
- });
- None
- }
- Some(name) => Some(name),
- None => None,
- }
- }
-
- // Parses the values into the given TomlTable. Returns true in case of success
- // and false in case of error.
- fn values(&mut self, into: &mut TomlTable) -> bool {
- loop {
- self.ws();
- if self.newline() { continue }
- if self.comment() { continue }
- match self.peek(0) {
- Some((_, '[')) => break,
- Some(..) => {}
- None => break,
- }
- let key_lo = self.next_pos();
- let key = match self.key_name() {
- Some(s) => s,
- None => return false
- };
- if !self.keyval_sep() { return false }
- let value = match self.value() {
- Some(value) => value,
- None => return false,
- };
- let end = self.next_pos();
- self.insert(into, key, value, key_lo);
- self.ws();
- if !self.comment() && !self.newline() {
- if self.peek(0).is_none() {
- return true
- }
- self.errors.push(ParserError {
- lo: key_lo,
- hi: end,
- desc: "expected a newline after a key".to_string(),
- });
- return false
- }
- }
- true
- }
-
- fn keyval_sep(&mut self) -> bool {
- self.ws();
- if !self.expect('=') { return false }
- self.ws();
- true
- }
-
- // Parses a value
- fn value(&mut self) -> Option<Value> {
- self.ws();
- match self.cur.clone().next() {
- Some((pos, '"')) => self.basic_string(pos),
- Some((pos, '\'')) => self.literal_string(pos),
- Some((pos, 't')) |
- Some((pos, 'f')) => self.boolean(pos),
- Some((pos, '[')) => self.array(pos),
- Some((pos, '{')) => self.inline_table(pos),
- Some((pos, '-')) |
- Some((pos, '+')) => self.number_or_datetime(pos),
- Some((pos, ch)) if is_digit(ch) => self.number_or_datetime(pos),
- _ => {
- let mut it = self.cur.clone();
- let lo = it.next().map(|p| p.0).unwrap_or(self.input.len());
- let hi = it.next().map(|p| p.0).unwrap_or(self.input.len());
- self.errors.push(ParserError {
- lo: lo,
- hi: hi,
- desc: "expected a value".to_string(),
- });
- None
- }
- }
- }
-
- // Parses a single or multi-line string
- fn basic_string(&mut self, start: usize) -> Option<Value> {
- if !self.expect('"') { return None }
- let mut multiline = false;
-
- // detect multiline literals, but be careful about empty ""
- // strings
- if self.eat('"') {
- if self.eat('"') {
- multiline = true;
- self.newline();
- } else {
- // empty
- return Some(Value::String(String::new()))
- }
- }
-
- self.finish_basic_string(start, multiline).map(Value::String)
- }
-
- // Finish parsing a basic string after the opening quote has been seen
- fn finish_basic_string(&mut self,
- start: usize,
- multiline: bool) -> Option<String> {
- let mut ret = String::new();
- loop {
- while multiline && self.newline() { ret.push('\n') }
- match self.cur.next() {
- Some((_, '"')) => {
- if multiline {
- if !self.eat('"') { ret.push_str("\""); continue }
- if !self.eat('"') { ret.push_str("\"\""); continue }
- }
- return Some(ret)
- }
- Some((pos, '\\')) => {
- if let Some(c) = escape(self, pos, multiline) {
- ret.push(c);
- }
- }
- Some((pos, ch)) if ch < '\u{1f}' => {
- self.errors.push(ParserError {
- lo: pos,
- hi: pos + 1,
- desc: format!("control character `{}` must be escaped",
- ch.escape_default().collect::<String>())
- });
- }
- Some((_, ch)) => ret.push(ch),
- None => {
- self.errors.push(ParserError {
- lo: start,
- hi: self.input.len(),
- desc: "unterminated string literal".to_string(),
- });
- return None
- }
- }
- }
-
- fn escape(me: &mut Parser, pos: usize, multiline: bool) -> Option<char> {
- if multiline && me.newline() {
- while me.ws() || me.newline() { /* ... */ }
- return None
- }
- match me.cur.next() {
- Some((_, 'b')) => Some('\u{8}'),
- Some((_, 't')) => Some('\u{9}'),
- Some((_, 'n')) => Some('\u{a}'),
- Some((_, 'f')) => Some('\u{c}'),
- Some((_, 'r')) => Some('\u{d}'),
- Some((_, '"')) => Some('\u{22}'),
- Some((_, '\\')) => Some('\u{5c}'),
- Some((pos, c @ 'u')) |
- Some((pos, c @ 'U')) => {
- let len = if c == 'u' {4} else {8};
- let num = &me.input[pos+1..];
- let num = if num.char_indices().nth(len).map(|(i, _)| i).unwrap_or(0) == len {
- &num[..len]
- } else {
- "invalid"
- };
- if let Some(n) = u32::from_str_radix(num, 16).ok() {
- if let Some(c) = char::from_u32(n) {
- me.cur.by_ref().skip(len - 1).next();
- return Some(c)
- } else {
- me.errors.push(ParserError {
- lo: pos + 1,
- hi: pos + 5,
- desc: format!("codepoint `{:x}` is \
- not a valid unicode \
- codepoint", n),
- })
- }
- } else {
- me.errors.push(ParserError {
- lo: pos,
- hi: pos + 1,
- desc: format!("expected {} hex digits \
- after a `{}` escape", len, c),
- })
- }
- None
- }
- Some((pos, ch)) => {
- let next_pos = me.next_pos();
- me.errors.push(ParserError {
- lo: pos,
- hi: next_pos,
- desc: format!("unknown string escape: `{}`",
- ch.escape_default().collect::<String>()),
- });
- None
- }
- None => {
- me.errors.push(ParserError {
- lo: pos,
- hi: pos + 1,
- desc: "unterminated escape sequence".to_string(),
- });
- None
- }
- }
- }
- }
-
- fn literal_string(&mut self, start: usize) -> Option<Value> {
- if !self.expect('\'') { return None }
- let mut multiline = false;
-
- // detect multiline literals
- if self.eat('\'') {
- if self.eat('\'') {
- multiline = true;
- self.newline();
- } else {
- return Some(Value::String(String::new())) // empty
- }
- }
-
- self.finish_literal_string(start, multiline).map(Value::String)
- }
-
- fn finish_literal_string(&mut self, start: usize, multiline: bool)
- -> Option<String> {
- let mut ret = String::new();
- loop {
- if !multiline && self.newline() {
- let next = self.next_pos();
- self.errors.push(ParserError {
- lo: start,
- hi: next,
- desc: "literal strings cannot contain newlines".to_string(),
- });
- return None
- }
- match self.cur.next() {
- Some((_, '\'')) => {
- if multiline {
- if !self.eat('\'') { ret.push_str("'"); continue }
- if !self.eat('\'') { ret.push_str("''"); continue }
- }
- return Some(ret)
- }
- Some((_, ch)) => ret.push(ch),
- None => {
- self.errors.push(ParserError {
- lo: start,
- hi: self.input.len(),
- desc: "unterminated string literal".to_string(),
- });
- return None
- }
- }
- }
- }
-
- fn number_or_datetime(&mut self, start: usize) -> Option<Value> {
- let mut is_float = false;
- let prefix = try!(self.integer(start, false, true));
- let decimal = if self.eat('.') {
- is_float = true;
- Some(try!(self.integer(start, true, false)))
- } else {
- None
- };
- let exponent = if self.eat('e') || self.eat('E') {
- is_float = true;
- Some(try!(self.integer(start, false, true)))
- } else {
- None
- };
- let end = self.next_pos();
- let input = &self.input[start..end];
- let ret = if decimal.is_none() &&
- exponent.is_none() &&
- !input.starts_with('+') &&
- !input.starts_with('-') &&
- start + 4 == end &&
- self.eat('-') {
- self.datetime(start)
- } else {
- let input = match (decimal, exponent) {
- (None, None) => prefix,
- (Some(ref d), None) => prefix + "." + d,
- (None, Some(ref e)) => prefix + "E" + e,
- (Some(ref d), Some(ref e)) => prefix + "." + d + "E" + e,
- };
- let input = input.trim_left_matches('+');
- if is_float {
- input.parse().ok().map(Value::Float)
- } else {
- input.parse().ok().map(Value::Integer)
- }
- };
- if ret.is_none() {
- self.errors.push(ParserError {
- lo: start,
- hi: end,
- desc: "invalid numeric literal".to_string(),
- });
- }
- ret
- }
-
- fn integer(&mut self,
- start: usize,
- allow_leading_zeros: bool,
- allow_sign: bool) -> Option<String> {
- let mut s = String::new();
- if allow_sign {
- if self.eat('-') { s.push('-'); }
- else if self.eat('+') { s.push('+'); }
- }
- match self.cur.next() {
- Some((_, '0')) if !allow_leading_zeros => {
- s.push('0');
- match self.peek(0) {
- Some((pos, c)) if '0' <= c && c <= '9' => {
- self.errors.push(ParserError {
- lo: start,
- hi: pos,
- desc: "leading zeroes are not allowed".to_string(),
- });
- return None
- }
- _ => {}
- }
- }
- Some((_, ch)) if '0' <= ch && ch <= '9' => {
- s.push(ch);
- }
- _ => {
- let pos = self.next_pos();
- self.errors.push(ParserError {
- lo: pos,
- hi: pos,
- desc: "expected start of a numeric literal".to_string(),
- });
- return None;
- }
- }
- let mut underscore = false;
- loop {
- match self.cur.clone().next() {
- Some((_, ch)) if '0' <= ch && ch <= '9' => {
- s.push(ch);
- self.cur.next();
- underscore = false;
- }
- Some((_, '_')) if !underscore => {
- self.cur.next();
- underscore = true;
- }
- Some(_) | None => break,
- }
- }
- if underscore {
- let pos = self.next_pos();
- self.errors.push(ParserError {
- lo: pos,
- hi: pos,
- desc: "numeral cannot end with an underscore".to_string(),
- });
- None
- } else {
- Some(s)
- }
- }
-
- fn boolean(&mut self, start: usize) -> Option<Value> {
- let rest = &self.input[start..];
- if rest.starts_with("true") {
- for _ in 0..4 {
- self.cur.next();
- }
- Some(Value::Boolean(true))
- } else if rest.starts_with("false") {
- for _ in 0..5 {
- self.cur.next();
- }
- Some(Value::Boolean(false))
- } else {
- let next = self.next_pos();
- self.errors.push(ParserError {
- lo: start,
- hi: next,
- desc: format!("unexpected character: `{}`",
- rest.chars().next().unwrap()),
- });
- None
- }
- }
-
- fn datetime(&mut self, start: usize) -> Option<Value> {
- // Up to `start` already contains the year, and we've eaten the next
- // `-`, so we just resume parsing from there.
-
- let mut valid = true;
-
- // month
- valid = valid && digit(self.cur.next());
- valid = valid && digit(self.cur.next());
-
- // day
- valid = valid && self.cur.next().map(|c| c.1) == Some('-');
- valid = valid && digit(self.cur.next());
- valid = valid && digit(self.cur.next());
-
- valid = valid && self.cur.next().map(|c| c.1) == Some('T');
-
- // hour
- valid = valid && digit(self.cur.next());
- valid = valid && digit(self.cur.next());
-
- // minute
- valid = valid && self.cur.next().map(|c| c.1) == Some(':');
- valid = valid && digit(self.cur.next());
- valid = valid && digit(self.cur.next());
-
- // second
- valid = valid && self.cur.next().map(|c| c.1) == Some(':');
- valid = valid && digit(self.cur.next());
- valid = valid && digit(self.cur.next());
-
- // fractional seconds
- if self.eat('.') {
- valid = valid && digit(self.cur.next());
- loop {
- match self.cur.clone().next() {
- Some((_, c)) if is_digit(c) => {
- self.cur.next();
- }
- _ => break,
- }
- }
- }
-
- // time zone
- if !self.eat('Z') {
- valid = valid && (self.eat('+') || self.eat('-'));
-
- // hour
- valid = valid && digit(self.cur.next());
- valid = valid && digit(self.cur.next());
-
- // minute
- valid = valid && self.cur.next().map(|c| c.1) == Some(':');
- valid = valid && digit(self.cur.next());
- valid = valid && digit(self.cur.next());
- }
-
- return if valid {
- Some(Value::Datetime(self.input[start..self.next_pos()].to_string()))
- } else {
- let next = self.next_pos();
- self.errors.push(ParserError {
- lo: start,
- hi: start + next,
- desc: "malformed date literal".to_string(),
- });
- None
- };
-
- fn digit(val: Option<(usize, char)>) -> bool {
- match val {
- Some((_, c)) => is_digit(c),
- None => false,
- }
- }
- }
-
- fn array(&mut self, _start: usize) -> Option<Value> {
- if !self.expect('[') { return None }
- let mut ret = Vec::new();
- fn consume(me: &mut Parser) {
- loop {
- me.ws();
- if !me.newline() && !me.comment() { break }
- }
- }
- let mut type_str = None;
- loop {
- // Break out early if we see the closing bracket
- consume(self);
- if self.eat(']') { return Some(Value::Array(ret)) }
-
- // Attempt to parse a value, triggering an error if it's the wrong
- // type.
- let start = self.next_pos();
- let value = try!(self.value());
- let end = self.next_pos();
- let expected = type_str.unwrap_or(value.type_str());
- if value.type_str() != expected {
- self.errors.push(ParserError {
- lo: start,
- hi: end,
- desc: format!("expected type `{}`, found type `{}`",
- expected, value.type_str()),
- });
- } else {
- type_str = Some(expected);
- ret.push(value);
- }
-
- // Look for a comma. If we don't find one we're done
- consume(self);
- if !self.eat(',') { break }
- }
- consume(self);
- if !self.expect(']') { return None }
- Some(Value::Array(ret))
- }
-
- fn inline_table(&mut self, _start: usize) -> Option<Value> {
- if !self.expect('{') { return None }
- self.ws();
- let mut ret = TomlTable { values: BTreeMap::new(), defined: true };
- if self.eat('}') { return Some(Value::Table(ret)) }
- loop {
- let lo = self.next_pos();
- let key = try!(self.key_name());
- if !self.keyval_sep() { return None }
- let value = try!(self.value());
- self.insert(&mut ret, key, value, lo);
-
- self.ws();
- if self.eat('}') { break }
- if !self.expect(',') { return None }
- self.ws();
- }
- Some(Value::Table(ret))
- }
-
- fn insert(&mut self, into: &mut TomlTable, key: String, value: Value,
- key_lo: usize) {
- if let Entry::Vacant(entry) = into.values.entry(key.clone()) {
- entry.insert(value);
- } else {
- self.errors.push(ParserError {
- lo: key_lo,
- hi: key_lo + key.len(),
- desc: format!("duplicate key: `{}`", key),
- });
- }
- }
-
- fn recurse<'b>(&mut self, mut cur: &'b mut TomlTable, keys: &'b [String],
- key_lo: usize) -> Option<(&'b mut TomlTable, &'b str)> {
- let key_hi = keys.iter().fold(0, |a, b| a + b.len());
- for part in keys[..keys.len() - 1].iter() {
- let tmp = cur;
-
- if tmp.values.contains_key(part) {
- match *tmp.values.get_mut(part).unwrap() {
- Value::Table(ref mut table) => cur = table,
- Value::Array(ref mut array) => {
- match array.last_mut() {
- Some(&mut Value::Table(ref mut table)) => cur = table,
- _ => {
- self.errors.push(ParserError {
- lo: key_lo,
- hi: key_hi,
- desc: format!("array `{}` does not contain \
- tables", part)
- });
- return None
- }
- }
- }
- _ => {
- self.errors.push(ParserError {
- lo: key_lo,
- hi: key_hi,
- desc: format!("key `{}` was not previously a table",
- part)
- });
- return None
- }
- }
- continue
- }
-
- // Initialize an empty table as part of this sub-key
- tmp.values.insert(part.clone(), Value::Table(TomlTable {
- values: BTreeMap::new(),
- defined: false,
- }));
- match *tmp.values.get_mut(part).unwrap() {
- Value::Table(ref mut inner) => cur = inner,
- _ => unreachable!(),
- }
- }
- Some((cur, &**keys.last().unwrap()))
- }
-
- fn insert_table(&mut self, into: &mut TomlTable, keys: &[String],
- table: TomlTable, key_lo: usize) {
- let (into, key) = match self.recurse(into, keys, key_lo) {
- Some(pair) => pair,
- None => return,
- };
- if !into.values.contains_key(key) {
- into.values.insert(key.to_owned(), Value::Table(table));
- return
- }
- if let Value::Table(ref mut into) = *into.values.get_mut(key).unwrap() {
- if into.defined {
- self.errors.push(ParserError {
- lo: key_lo,
- hi: key_lo + key.len(),
- desc: format!("redefinition of table `{}`", key),
- });
- }
- for (k, v) in table.values {
- if into.values.insert(k.clone(), v).is_some() {
- self.errors.push(ParserError {
- lo: key_lo,
- hi: key_lo + key.len(),
- desc: format!("duplicate key `{}` in table", k),
- });
- }
- }
- } else {
- self.errors.push(ParserError {
- lo: key_lo,
- hi: key_lo + key.len(),
- desc: format!("duplicate key `{}` in table", key),
- });
- }
- }
-
- fn insert_array(&mut self, into: &mut TomlTable,
- keys: &[String], value: Value, key_lo: usize) {
- let (into, key) = match self.recurse(into, keys, key_lo) {
- Some(pair) => pair,
- None => return,
- };
- if !into.values.contains_key(key) {
- into.values.insert(key.to_owned(), Value::Array(Vec::new()));
- }
- match *into.values.get_mut(key).unwrap() {
- Value::Array(ref mut vec) => {
- match vec.first() {
- Some(ref v) if !v.same_type(&value) => {
- self.errors.push(ParserError {
- lo: key_lo,
- hi: key_lo + key.len(),
- desc: format!("expected type `{}`, found type `{}`",
- v.type_str(), value.type_str()),
- })
- }
- Some(..) | None => {}
- }
- vec.push(value);
- }
- _ => {
- self.errors.push(ParserError {
- lo: key_lo,
- hi: key_lo + key.len(),
- desc: format!("key `{}` was previously not an array", key),
- });
- }
- }
- }
-}
-
-impl Error for ParserError {
- fn description(&self) -> &str { "TOML parse error" }
-}
-
-impl fmt::Display for ParserError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- self.desc.fmt(f)
- }
-}
-
-fn is_digit(c: char) -> bool {
- match c { '0' ... '9' => true, _ => false }
-}
-
-#[cfg(test)]
-mod tests {
- use Value::Table;
- use Parser;
-
- macro_rules! bad {
- ($s:expr, $msg:expr) => ({
- let mut p = Parser::new($s);
- assert!(p.parse().is_none());
- assert!(p.errors.iter().any(|e| e.desc.contains($msg)),
- "errors: {:?}", p.errors);
- })
- }
-
- #[test]
- fn lookup_internal() {
- let mut parser = Parser::new(r#"hello."world\t".a.0.'escaped'.value"#);
- let result = vec![
- String::from("hello"),
- String::from("world\t"),
- String::from("a"),
- String::from("0"),
- String::from("escaped"),
- String::from("value")
- ];
-
- assert_eq!(parser.lookup().unwrap(), result);
- }
-
- #[test]
- fn lookup_internal_void() {
- let mut parser = Parser::new("");
- assert_eq!(parser.lookup().unwrap(), Vec::<String>::new());
- }
-
- #[test]
- fn lookup_internal_simple() {
- let mut parser = Parser::new("value");
- assert_eq!(parser.lookup().unwrap(), vec![String::from("value")]);
- }
-
- // This is due to key_name not parsing an empty "" correctly. Disabled for now.
- #[test]
- #[ignore]
- fn lookup_internal_quoted_void() {
- let mut parser = Parser::new("\"\"");
- assert_eq!(parser.lookup().unwrap(), vec![String::from("")]);
- }
-
-
- #[test]
- fn crlf() {
- let mut p = Parser::new("\
-[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\
-\"\"\"\
-");
- assert!(p.parse().is_some());
- }
-
- #[test]
- fn linecol() {
- let p = Parser::new("ab\ncde\nf");
- assert_eq!(p.to_linecol(0), (0, 0));
- assert_eq!(p.to_linecol(1), (0, 1));
- assert_eq!(p.to_linecol(3), (1, 0));
- assert_eq!(p.to_linecol(4), (1, 1));
- assert_eq!(p.to_linecol(7), (2, 0));
- }
-
- #[test]
- fn fun_with_strings() {
- let mut p = Parser::new(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.
-'''
-"#);
- let table = Table(p.parse().unwrap());
- assert_eq!(table.lookup("bar").and_then(|k| k.as_str()), Some("\0"));
- assert_eq!(table.lookup("key1").and_then(|k| k.as_str()),
- Some("One\nTwo"));
- assert_eq!(table.lookup("key2").and_then(|k| k.as_str()),
- Some("One\nTwo"));
- assert_eq!(table.lookup("key3").and_then(|k| k.as_str()),
- Some("One\nTwo"));
-
- let msg = "The quick brown fox jumps over the lazy dog.";
- assert_eq!(table.lookup("key4").and_then(|k| k.as_str()), Some(msg));
- assert_eq!(table.lookup("key5").and_then(|k| k.as_str()), Some(msg));
- assert_eq!(table.lookup("key6").and_then(|k| k.as_str()), Some(msg));
-
- assert_eq!(table.lookup("winpath").and_then(|k| k.as_str()),
- Some(r"C:\Users\nodejs\templates"));
- assert_eq!(table.lookup("winpath2").and_then(|k| k.as_str()),
- Some(r"\\ServerX\admin$\system32\"));
- assert_eq!(table.lookup("quoted").and_then(|k| k.as_str()),
- Some(r#"Tom "Dubs" Preston-Werner"#));
- assert_eq!(table.lookup("regex").and_then(|k| k.as_str()),
- Some(r"<\i\c*\s*>"));
- assert_eq!(table.lookup("regex2").and_then(|k| k.as_str()),
- Some(r"I [dw]on't need \d{2} apples"));
- assert_eq!(table.lookup("lines").and_then(|k| k.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 mut p = Parser::new(r#"
-[[foo]]
- #…
- [foo.bar]
- #…
-
-[[foo]] # ...
- #…
- [foo.bar]
- #...
-"#);
- let table = Table(p.parse().unwrap());
- table.lookup("foo.0.bar").unwrap().as_table().unwrap();
- table.lookup("foo.1.bar").unwrap().as_table().unwrap();
- }
-
- #[test]
- fn empty_table() {
- let mut p = Parser::new(r#"
-[foo]"#);
- let table = Table(p.parse().unwrap());
- table.lookup("foo").unwrap().as_table().unwrap();
- }
-
- #[test]
- fn fruit() {
- let mut p = Parser::new(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"
-"#);
- let table = Table(p.parse().unwrap());
- assert_eq!(table.lookup("fruit.0.name").and_then(|k| k.as_str()),
- Some("apple"));
- assert_eq!(table.lookup("fruit.0.physical.color").and_then(|k| k.as_str()),
- Some("red"));
- assert_eq!(table.lookup("fruit.0.physical.shape").and_then(|k| k.as_str()),
- Some("round"));
- assert_eq!(table.lookup("fruit.0.variety.0.name").and_then(|k| k.as_str()),
- Some("red delicious"));
- assert_eq!(table.lookup("fruit.0.variety.1.name").and_then(|k| k.as_str()),
- Some("granny smith"));
- assert_eq!(table.lookup("fruit.1.name").and_then(|k| k.as_str()),
- Some("banana"));
- assert_eq!(table.lookup("fruit.1.variety.0.name").and_then(|k| k.as_str()),
- Some("plantain"));
- }
-
- #[test]
- fn stray_cr() {
- assert!(Parser::new("\r").parse().is_none());
- assert!(Parser::new("a = [ \r ]").parse().is_none());
- assert!(Parser::new("a = \"\"\"\r\"\"\"").parse().is_none());
- assert!(Parser::new("a = \"\"\"\\ \r \"\"\"").parse().is_none());
-
- let mut p = Parser::new("foo = '''\r'''");
- let table = Table(p.parse().unwrap());
- assert_eq!(table.lookup("foo").and_then(|k| k.as_str()), Some("\r"));
-
- let mut p = Parser::new("foo = '\r'");
- let table = Table(p.parse().unwrap());
- assert_eq!(table.lookup("foo").and_then(|k| k.as_str()), Some("\r"));
- }
-
- #[test]
- fn blank_literal_string() {
- let mut p = Parser::new("foo = ''");
- let table = Table(p.parse().unwrap());
- assert_eq!(table.lookup("foo").and_then(|k| k.as_str()), Some(""));
- }
-
- #[test]
- fn many_blank() {
- let mut p = Parser::new("foo = \"\"\"\n\n\n\"\"\"");
- let table = Table(p.parse().unwrap());
- assert_eq!(table.lookup("foo").and_then(|k| k.as_str()), Some("\n\n"));
- }
-
- #[test]
- fn literal_eats_crlf() {
- let mut p = Parser::new("
- foo = \"\"\"\\\r\n\"\"\"
- bar = \"\"\"\\\r\n \r\n \r\n a\"\"\"
- ");
- let table = Table(p.parse().unwrap());
- assert_eq!(table.lookup("foo").and_then(|k| k.as_str()), Some(""));
- assert_eq!(table.lookup("bar").and_then(|k| k.as_str()), Some("a"));
- }
-
- #[test]
- fn string_no_newline() {
- assert!(Parser::new("a = \"\n\"").parse().is_none());
- assert!(Parser::new("a = '\n'").parse().is_none());
- }
-
- #[test]
- fn bad_leading_zeros() {
- assert!(Parser::new("a = 00").parse().is_none());
- assert!(Parser::new("a = -00").parse().is_none());
- assert!(Parser::new("a = +00").parse().is_none());
- assert!(Parser::new("a = 00.0").parse().is_none());
- assert!(Parser::new("a = -00.0").parse().is_none());
- assert!(Parser::new("a = +00.0").parse().is_none());
- assert!(Parser::new("a = 9223372036854775808").parse().is_none());
- assert!(Parser::new("a = -9223372036854775809").parse().is_none());
- }
-
- #[test]
- fn bad_floats() {
- assert!(Parser::new("a = 0.").parse().is_none());
- assert!(Parser::new("a = 0.e").parse().is_none());
- assert!(Parser::new("a = 0.E").parse().is_none());
- assert!(Parser::new("a = 0.0E").parse().is_none());
- assert!(Parser::new("a = 0.0e").parse().is_none());
- assert!(Parser::new("a = 0.0e-").parse().is_none());
- assert!(Parser::new("a = 0.0e+").parse().is_none());
- assert!(Parser::new("a = 0.0e+00").parse().is_none());
- }
-
- #[test]
- fn floats() {
- macro_rules! t {
- ($actual:expr, $expected:expr) => ({
- let f = format!("foo = {}", $actual);
- let mut p = Parser::new(&f);
- let table = Table(p.parse().unwrap());
- assert_eq!(table.lookup("foo").and_then(|k| k.as_float()),
- Some($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_0e0_0", 20.0);
- t!("2_0.1_0e1_0", 20.1e10);
- }
-
- #[test]
- fn bare_key_names() {
- let mut p = Parser::new("
- 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\"
- ");
- let table = Table(p.parse().unwrap());
- assert!(table.lookup("foo").is_some());
- assert!(table.lookup("-").is_some());
- assert!(table.lookup("_").is_some());
- assert!(table.lookup("8").is_some());
- assert!(table.lookup("foo_3").is_some());
- assert!(table.lookup("foo_-2--3--r23f--4-f2-4").is_some());
- assert!(table.lookup("a").is_some());
- assert!(table.lookup("\"!\"").is_some());
- assert!(table.lookup("\"\\\"\"").is_some());
- assert!(table.lookup("\"character encoding\"").is_some());
- assert!(table.lookup("'ʎǝʞ'").is_some());
- }
-
- #[test]
- fn bad_keys() {
- assert!(Parser::new("key\n=3").parse().is_none());
- assert!(Parser::new("key=\n3").parse().is_none());
- assert!(Parser::new("key|=3").parse().is_none());
- assert!(Parser::new("\"\"=3").parse().is_none());
- assert!(Parser::new("=3").parse().is_none());
- assert!(Parser::new("\"\"|=3").parse().is_none());
- assert!(Parser::new("\"\n\"|=3").parse().is_none());
- assert!(Parser::new("\"\r\"|=3").parse().is_none());
- }
-
- #[test]
- fn bad_table_names() {
- assert!(Parser::new("[]").parse().is_none());
- assert!(Parser::new("[.]").parse().is_none());
- assert!(Parser::new("[\"\".\"\"]").parse().is_none());
- assert!(Parser::new("[a.]").parse().is_none());
- assert!(Parser::new("[\"\"]").parse().is_none());
- assert!(Parser::new("[!]").parse().is_none());
- assert!(Parser::new("[\"\n\"]").parse().is_none());
- assert!(Parser::new("[a.b]\n[a.\"b\"]").parse().is_none());
- assert!(Parser::new("[']").parse().is_none());
- assert!(Parser::new("[''']").parse().is_none());
- assert!(Parser::new("['''''']").parse().is_none());
- assert!(Parser::new("['\n']").parse().is_none());
- assert!(Parser::new("['\r\n']").parse().is_none());
- }
-
- #[test]
- fn table_names() {
- let mut p = Parser::new("
- [a.\"b\"]
- [\"f f\"]
- [\"f.f\"]
- [\"\\\"\"]
- ['a.a']
- ['\"\"']
- ");
- let table = Table(p.parse().unwrap());
- assert!(table.lookup("a.b").is_some());
- assert!(table.lookup("\"f f\"").is_some());
- assert!(table.lookup("\"\\\"\"").is_some());
- assert!(table.lookup("'\"\"'").is_some());
- }
-
- #[test]
- fn invalid_bare_numeral() {
- assert!(Parser::new("4").parse().is_none());
- }
-
- #[test]
- fn inline_tables() {
- assert!(Parser::new("a = {}").parse().is_some());
- assert!(Parser::new("a = {b=1}").parse().is_some());
- assert!(Parser::new("a = { b = 1 }").parse().is_some());
- assert!(Parser::new("a = {a=1,b=2}").parse().is_some());
- assert!(Parser::new("a = {a=1,b=2,c={}}").parse().is_some());
- assert!(Parser::new("a = {a=1,}").parse().is_none());
- assert!(Parser::new("a = {,}").parse().is_none());
- assert!(Parser::new("a = {a=1,a=1}").parse().is_none());
- assert!(Parser::new("a = {\n}").parse().is_none());
- assert!(Parser::new("a = {").parse().is_none());
- assert!(Parser::new("a = {a=[\n]}").parse().is_some());
- assert!(Parser::new("a = {\"a\"=[\n]}").parse().is_some());
- assert!(Parser::new("a = [\n{},\n{},\n]").parse().is_some());
- }
-
- #[test]
- fn number_underscores() {
- macro_rules! t {
- ($actual:expr, $expected:expr) => ({
- let f = format!("foo = {}", $actual);
- let mut p = Parser::new(&f);
- let table = Table(p.parse().unwrap());
- assert_eq!(table.lookup("foo").and_then(|k| k.as_integer()),
- Some($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() {
- assert!(Parser::new("foo = 0_").parse().is_none());
- assert!(Parser::new("foo = 0__0").parse().is_none());
- assert!(Parser::new("foo = __0").parse().is_none());
- assert!(Parser::new("foo = 1_0_").parse().is_none());
- }
-
- #[test]
- fn bad_unicode_codepoint() {
- bad!("foo = \"\\uD800\"", "not a valid unicode codepoint");
- }
-
- #[test]
- fn bad_strings() {
- bad!("foo = \"\\uxx\"", "expected 4 hex digits");
- bad!("foo = \"\\u\"", "expected 4 hex digits");
- bad!("foo = \"\\", "unterminated");
- bad!("foo = '", "unterminated");
- }
-
- #[test]
- fn empty_string() {
- let mut p = Parser::new("foo = \"\"");
- let table = Table(p.parse().unwrap());
- assert_eq!(table.lookup("foo").unwrap().as_str(), Some(""));
- }
-
- #[test]
- fn booleans() {
- let mut p = Parser::new("foo = true");
- let table = Table(p.parse().unwrap());
- assert_eq!(table.lookup("foo").unwrap().as_bool(), Some(true));
-
- let mut p = Parser::new("foo = false");
- let table = Table(p.parse().unwrap());
- assert_eq!(table.lookup("foo").unwrap().as_bool(), Some(false));
-
- assert!(Parser::new("foo = true2").parse().is_none());
- assert!(Parser::new("foo = false2").parse().is_none());
- assert!(Parser::new("foo = t1").parse().is_none());
- assert!(Parser::new("foo = f2").parse().is_none());
- }
-
- #[test]
- fn bad_nesting() {
- bad!("
- a = [2]
- [[a]]
- b = 5
- ", "expected type `integer`, found type `table`");
- bad!("
- a = 1
- [a.b]
- ", "key `a` was not previously a table");
- bad!("
- a = []
- [a.b]
- ", "array `a` does not contain tables");
- bad!("
- a = []
- [[a.b]]
- ", "array `a` does not contain tables");
- bad!("
- [a]
- b = { c = 2, d = {} }
- [a.b]
- c = 2
- ", "duplicate key `c` in table");
- }
-
- #[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]
- ", "redefinition of table `b`");
-
- bad!("
- [a]
- b = {}
- [a]
- ", "redefinition of table `a`");
- }
-
- #[test]
- fn datetimes() {
- macro_rules! t {
- ($actual:expr) => ({
- let f = format!("foo = {}", $actual);
- let mut p = Parser::new(&f);
- let table = Table(p.parse().unwrap());
- assert_eq!(table.lookup("foo").and_then(|k| k.as_datetime()),
- Some($actual));
- })
- }
-
- t!("2016-09-09T09:09:09Z");
- t!("2016-09-09T09:09:09.0Z");
- t!("2016-09-09T09:09:09.0+10:00");
- t!("2016-09-09T09:09:09.01234567890-02:00");
- bad!("foo = 2016-09-09T09:09:09.Z", "malformed date literal");
- bad!("foo = 2016-9-09T09:09:09Z", "malformed date literal");
- bad!("foo = 2016-09-09T09:09:09+2:00", "malformed date literal");
- bad!("foo = 2016-09-09T09:09:09-2:00", "malformed date literal");
- bad!("foo = 2016-09-09T09:09:09Z-2:00", "expected");
- }
-
- #[test]
- fn require_newline_after_value() {
- bad!("0=0r=false", "expected a newline");
- bad!(r#"
-0=""o=""m=""r=""00="0"q="""0"""e="""0"""
-"#, "expected a newline");
- bad!(r#"
-[[0000l0]]
-0="0"[[0000l0]]
-0="0"[[0000l0]]
-0="0"l="0"
-"#, "expected a 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 a newline");
- bad!(r#"
-0=0r0=0r=false
-"#, "expected a newline");
- bad!(r#"
-0=0r0=0r=falsefal=false
-"#, "expected a newline");
- }
-}
diff --git a/src/ser.rs b/src/ser.rs
new file mode 100644
index 0000000..0a557c2
--- /dev/null
+++ b/src/ser.rs
@@ -0,0 +1,1006 @@
+//! Serializing Rust structures into TOML.
+//!
+//! This module contains all the Serde support for serializing Rust structures
+//! into TOML documents (as strings). Note that some top-level functions here
+//! are also provided at the top of the crate.
+
+use std::fmt::{self, Write};
+use std::error;
+use std::cell::Cell;
+
+use serde::ser;
+use datetime::{SERDE_STRUCT_FIELD_NAME, SERDE_STRUCT_NAME};
+
+/// Serialize the given data structure as a TOML byte vector.
+///
+/// Serialization can fail if `T`'s implementation of `Serialize` decides to
+/// fail, if `T` contains a map with non-string keys, or if `T` attempts to
+/// serialize an unsupported datatype such as an enum, tuple, or tuple struct.
+pub fn to_vec<T: ?Sized>(value: &T) -> Result<Vec<u8>, Error>
+ where T: ser::Serialize,
+{
+ to_string(value).map(|e| e.into_bytes())
+}
+
+/// Serialize the given data structure as a String of TOML.
+///
+/// Serialization can fail if `T`'s implementation of `Serialize` decides to
+/// fail, if `T` contains a map with non-string keys, or if `T` attempts to
+/// serialize an unsupported datatype such as an enum, tuple, or tuple struct.
+pub fn to_string<T: ?Sized>(value: &T) -> Result<String, Error>
+ where T: ser::Serialize,
+{
+ let mut dst = String::with_capacity(128);
+ value.serialize(&mut Serializer::new(&mut dst))?;
+ Ok(dst)
+}
+
+/// Errors that can occur when serializing a type.
+#[derive(Debug, Clone)]
+pub enum Error {
+ /// Indicates that a Rust type was requested to be serialized but it was not
+ /// supported.
+ ///
+ /// Currently the TOML format does not support serializing types such as
+ /// enums, tuples and tuple structs.
+ UnsupportedType,
+
+ /// The key of all TOML maps must be strings, but serialization was
+ /// attempted where the key of a map was not a string.
+ KeyNotString,
+
+ /// Keys in maps are not allowed to have newlines.
+ KeyNewline,
+
+ /// Arrays in TOML must have a homogenous type, but a heterogeneous array
+ /// was emitted.
+ ArrayMixedType,
+
+ /// All values in a TOML table must be emitted before further tables are
+ /// emitted. If a value is emitted *after* a table then this error is
+ /// generated.
+ ValueAfterTable,
+
+ /// A serialized date was invalid.
+ DateInvalid,
+
+ /// None was attempted to be serialized, but it's not supported.
+ UnsupportedNone,
+
+ /// A custom error which could be generated when serializing a particular
+ /// type.
+ Custom(String),
+
+ #[doc(hidden)]
+ __Nonexhaustive,
+}
+
+/// Serialization implementation for TOML.
+///
+/// This structure implements serialization support for TOML to serialize an
+/// arbitrary type to TOML. Note that the TOML format does not support all
+/// datatypes in Rust, such as enums, tuples, and tuple structs. These types
+/// will generate an error when serialized.
+///
+/// Currently a serializer always writes its output to an in-memory `String`,
+/// which is passed in when creating the serializer itself.
+pub struct Serializer<'a> {
+ dst: &'a mut String,
+ state: State<'a>,
+}
+
+#[derive(Debug, Clone)]
+enum State<'a> {
+ Table {
+ key: &'a str,
+ parent: &'a State<'a>,
+ first: &'a Cell<bool>,
+ table_emitted: &'a Cell<bool>,
+ },
+ Array {
+ parent: &'a State<'a>,
+ first: &'a Cell<bool>,
+ type_: &'a Cell<Option<&'static str>>,
+ },
+ End,
+}
+
+#[doc(hidden)]
+pub struct SerializeSeq<'a: 'b, 'b> {
+ ser: &'b mut Serializer<'a>,
+ first: Cell<bool>,
+ type_: Cell<Option<&'static str>>,
+}
+
+#[doc(hidden)]
+pub enum SerializeTable<'a: 'b, 'b> {
+ Datetime(&'b mut Serializer<'a>),
+ Table {
+ ser: &'b mut Serializer<'a>,
+ key: String,
+ first: Cell<bool>,
+ table_emitted: Cell<bool>,
+ }
+}
+
+impl<'a> Serializer<'a> {
+ /// Creates a new serializer which will emit TOML into the buffer provided.
+ ///
+ /// The serializer can then be used to serialize a type after which the data
+ /// will be present in `dst`.
+ pub fn new(dst: &'a mut String) -> Serializer<'a> {
+ Serializer {
+ dst: dst,
+ state: State::End,
+ }
+ }
+
+ fn display<T: fmt::Display>(&mut self,
+ t: T,
+ type_: &'static str) -> Result<(), Error> {
+ self.emit_key(type_)?;
+ drop(write!(self.dst, "{}", t));
+ if let State::Table { .. } = self.state {
+ self.dst.push_str("\n");
+ }
+ Ok(())
+ }
+
+ fn emit_key(&mut self, type_: &'static str) -> Result<(), Error> {
+ self.array_type(type_)?;
+ let state = self.state.clone();
+ self._emit_key(&state)
+ }
+
+ // recursive implementation of `emit_key` above
+ fn _emit_key(&mut self, state: &State) -> Result<(), Error> {
+ match *state {
+ State::End => Ok(()),
+ State::Array { parent, first, type_ } => {
+ assert!(type_.get().is_some());
+ if first.get() {
+ self._emit_key(parent)?;
+ }
+ self.emit_array(first)
+ }
+ State::Table { parent, first, table_emitted, key } => {
+ if table_emitted.get() {
+ return Err(Error::ValueAfterTable)
+ }
+ if first.get() {
+ self.emit_table_header(parent)?;
+ first.set(false);
+ }
+ self.escape_key(key)?;
+ self.dst.push_str(" = ");
+ Ok(())
+ }
+ }
+ }
+
+ fn emit_array(&mut self, first: &Cell<bool>) -> Result<(), Error> {
+ if first.get() {
+ self.dst.push_str("[");
+ } else {
+ self.dst.push_str(", ");
+ }
+ Ok(())
+ }
+
+ fn array_type(&mut self, type_: &'static str) -> Result<(), Error> {
+ let prev = match self.state {
+ State::Array { type_, .. } => type_,
+ _ => return Ok(()),
+ };
+ if let Some(prev) = prev.get() {
+ if prev != type_ {
+ return Err(Error::ArrayMixedType)
+ }
+ } else {
+ prev.set(Some(type_));
+ }
+ Ok(())
+ }
+
+ fn escape_key(&mut self, key: &str) -> Result<(), Error> {
+ let ok = key.chars().all(|c| {
+ match c {
+ 'a' ... 'z' |
+ 'A' ... 'Z' |
+ '0' ... '9' |
+ '-' | '_' => true,
+ _ => false,
+ }
+ });
+ if ok {
+ drop(write!(self.dst, "{}", key));
+ } else {
+ self.emit_str(key)?;
+ }
+ Ok(())
+ }
+
+ fn emit_str(&mut self, value: &str) -> Result<(), Error> {
+ drop(write!(self.dst, "\""));
+ for ch in value.chars() {
+ match ch {
+ '\u{8}' => drop(write!(self.dst, "\\b")),
+ '\u{9}' => drop(write!(self.dst, "\\t")),
+ '\u{a}' => drop(write!(self.dst, "\\n")),
+ '\u{c}' => drop(write!(self.dst, "\\f")),
+ '\u{d}' => drop(write!(self.dst, "\\r")),
+ '\u{22}' => drop(write!(self.dst, "\\\"")),
+ '\u{5c}' => drop(write!(self.dst, "\\\\")),
+ c if c < '\u{1f}' => {
+ drop(write!(self.dst, "\\u{:04}", ch as u32))
+ }
+ ch => drop(write!(self.dst, "{}", ch)),
+ }
+ }
+ drop(write!(self.dst, "\""));
+ Ok(())
+ }
+
+ fn emit_table_header(&mut self, state: &State) -> Result<(), Error> {
+ let array_of_tables = match *state {
+ State::End => return Ok(()),
+ State::Array { .. } => true,
+ _ => false,
+ };
+ match *state {
+ State::Table { first , .. } |
+ State::Array { parent: &State::Table { first, .. }, .. } => {
+ if !first.get() {
+ self.dst.push_str("\n");
+ }
+ }
+ _ => {}
+ }
+ self.dst.push_str("[");
+ if array_of_tables {
+ self.dst.push_str("[");
+ }
+ self.emit_key_part(&state)?;
+ if array_of_tables {
+ self.dst.push_str("]");
+ }
+ self.dst.push_str("]\n");
+ Ok(())
+ }
+
+ fn emit_key_part(&mut self, key: &State) -> Result<bool, Error> {
+ match *key {
+ State::Array { parent, .. } => self.emit_key_part(parent),
+ State::End => Ok(true),
+ State::Table { key, parent, table_emitted, .. } => {
+ table_emitted.set(true);
+ let first = self.emit_key_part(parent)?;
+ if !first {
+ self.dst.push_str(".");
+ }
+ self.escape_key(key)?;
+ Ok(false)
+ }
+ }
+ }
+}
+
+impl<'a, 'b> ser::Serializer for &'b mut Serializer<'a> {
+ type Ok = ();
+ type Error = Error;
+ type SerializeSeq = SerializeSeq<'a, 'b>;
+ type SerializeTuple = ser::Impossible<(), Error>;
+ type SerializeTupleStruct = ser::Impossible<(), Error>;
+ type SerializeTupleVariant = ser::Impossible<(), Error>;
+ type SerializeMap = SerializeTable<'a, 'b>;
+ type SerializeStruct = SerializeTable<'a, 'b>;
+ type SerializeStructVariant = ser::Impossible<(), Error>;
+
+ fn serialize_bool(self, v: bool) -> Result<(), Self::Error> {
+ self.display(v, "bool")
+ }
+
+ fn serialize_i8(self, v: i8) -> Result<(), Self::Error> {
+ self.display(v, "integer")
+ }
+
+ fn serialize_i16(self, v: i16) -> Result<(), Self::Error> {
+ self.display(v, "integer")
+ }
+
+ fn serialize_i32(self, v: i32) -> Result<(), Self::Error> {
+ self.display(v, "integer")
+ }
+
+ fn serialize_i64(self, v: i64) -> Result<(), Self::Error> {
+ self.display(v, "integer")
+ }
+
+ fn serialize_u8(self, v: u8) -> Result<(), Self::Error> {
+ self.display(v, "integer")
+ }
+
+ fn serialize_u16(self, v: u16) -> Result<(), Self::Error> {
+ self.display(v, "integer")
+ }
+
+ fn serialize_u32(self, v: u32) -> Result<(), Self::Error> {
+ self.display(v, "integer")
+ }
+
+ fn serialize_u64(self, v: u64) -> Result<(), Self::Error> {
+ self.display(v, "integer")
+ }
+
+ fn serialize_f32(mut self, v: f32) -> Result<(), Self::Error> {
+ self.emit_key("float")?;
+ drop(write!(self.dst, "{}", v));
+ if v % 1.0 == 0.0 {
+ drop(write!(self.dst, ".0"));
+ }
+ if let State::Table { .. } = self.state {
+ self.dst.push_str("\n");
+ }
+ Ok(())
+ }
+
+ fn serialize_f64(mut self, v: f64) -> Result<(), Self::Error> {
+ self.emit_key("float")?;
+ drop(write!(self.dst, "{}", v));
+ if v % 1.0 == 0.0 {
+ drop(write!(self.dst, ".0"));
+ }
+ if let State::Table { .. } = self.state {
+ self.dst.push_str("\n");
+ }
+ Ok(())
+ }
+
+ fn serialize_char(self, v: char) -> Result<(), Self::Error> {
+ let mut buf = [0; 4];
+ self.serialize_str(v.encode_utf8(&mut buf))
+ }
+
+ fn serialize_str(mut self, value: &str) -> Result<(), Self::Error> {
+ self.emit_key("string")?;
+ self.emit_str(value)?;
+ if let State::Table { .. } = self.state {
+ self.dst.push_str("\n");
+ }
+ Ok(())
+ }
+
+ fn serialize_bytes(self, _value: &[u8]) -> Result<(), Self::Error> {
+ Err(Error::UnsupportedType)
+ }
+
+ fn serialize_none(self) -> Result<(), Self::Error> {
+ Err(Error::UnsupportedNone)
+ }
+
+ fn serialize_some<T: ?Sized>(self, value: &T) -> Result<(), Self::Error>
+ where T: ser::Serialize
+ {
+ value.serialize(self)
+ }
+
+ fn serialize_unit(self) -> Result<(), Self::Error> {
+ Err(Error::UnsupportedType)
+ }
+
+ fn serialize_unit_struct(self,
+ _name: &'static str)
+ -> Result<(), Self::Error> {
+ Err(Error::UnsupportedType)
+ }
+
+ fn serialize_unit_variant(self,
+ _name: &'static str,
+ _variant_index: usize,
+ _variant: &'static str)
+ -> Result<(), Self::Error> {
+ Err(Error::UnsupportedType)
+ }
+
+ fn serialize_newtype_struct<T: ?Sized>(self, _name: &'static str, value: &T)
+ -> Result<(), Self::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<(), Self::Error>
+ where T: ser::Serialize,
+ {
+ Err(Error::UnsupportedType)
+ }
+
+ fn serialize_seq(mut self, _len: Option<usize>)
+ -> Result<Self::SerializeSeq, Self::Error> {
+ self.array_type("array")?;
+ Ok(SerializeSeq {
+ ser: self,
+ first: Cell::new(true),
+ type_: Cell::new(None),
+ })
+ }
+
+ fn serialize_seq_fixed_size(self, size: usize)
+ -> Result<Self::SerializeSeq, Self::Error> {
+ self.serialize_seq(Some(size))
+ }
+
+ fn serialize_tuple(self, _len: usize)
+ -> Result<Self::SerializeTuple, Self::Error> {
+ Err(Error::UnsupportedType)
+ }
+
+ fn serialize_tuple_struct(self, _name: &'static str, _len: usize)
+ -> Result<Self::SerializeTupleStruct, Self::Error> {
+ Err(Error::UnsupportedType)
+ }
+
+ fn serialize_tuple_variant(self,
+ _name: &'static str,
+ _variant_index: usize,
+ _variant: &'static str,
+ _len: usize)
+ -> Result<Self::SerializeTupleVariant, Self::Error> {
+ Err(Error::UnsupportedType)
+ }
+
+ fn serialize_map(mut self, _len: Option<usize>)
+ -> Result<Self::SerializeMap, Self::Error> {
+ self.array_type("table")?;
+ Ok(SerializeTable::Table {
+ ser: self,
+ key: String::new(),
+ first: Cell::new(true),
+ table_emitted: Cell::new(false),
+ })
+ }
+
+ fn serialize_struct(mut self, name: &'static str, _len: usize)
+ -> Result<Self::SerializeStruct, Self::Error> {
+ if name == SERDE_STRUCT_NAME {
+ self.array_type("datetime")?;
+ Ok(SerializeTable::Datetime(self))
+ } else {
+ self.array_type("table")?;
+ Ok(SerializeTable::Table {
+ ser: self,
+ key: String::new(),
+ first: Cell::new(true),
+ table_emitted: Cell::new(false),
+ })
+ }
+ }
+
+ fn serialize_struct_variant(self,
+ _name: &'static str,
+ _variant_index: usize,
+ _variant: &'static str,
+ _len: usize)
+ -> Result<Self::SerializeStructVariant, Self::Error> {
+ Err(Error::UnsupportedType)
+ }
+}
+
+impl<'a, 'b> ser::SerializeSeq for SerializeSeq<'a, 'b> {
+ type Ok = ();
+ type Error = Error;
+
+ fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), Error>
+ where T: ser::Serialize,
+ {
+ value.serialize(&mut Serializer {
+ dst: &mut *self.ser.dst,
+ state: State::Array {
+ parent: &self.ser.state,
+ first: &self.first,
+ type_: &self.type_,
+ },
+ })?;
+ self.first.set(false);
+ Ok(())
+ }
+
+ fn end(self) -> Result<(), Error> {
+ match self.type_.get() {
+ Some("table") => return Ok(()),
+ Some(_) => self.ser.dst.push_str("]"),
+ None => {
+ assert!(self.first.get());
+ self.ser.emit_key("array")?;
+ self.ser.dst.push_str("[]")
+ }
+ }
+ if let State::Table { .. } = self.ser.state {
+ self.ser.dst.push_str("\n");
+ }
+ Ok(())
+ }
+}
+
+impl<'a, 'b> ser::SerializeMap for SerializeTable<'a, 'b> {
+ type Ok = ();
+ type Error = Error;
+
+ fn serialize_key<T: ?Sized>(&mut self, input: &T) -> Result<(), Error>
+ where T: ser::Serialize,
+ {
+ match *self {
+ SerializeTable::Datetime(_) => panic!(), // shouldn't be possible
+ SerializeTable::Table { ref mut key, .. } => {
+ key.truncate(0);
+ *key = input.serialize(StringExtractor)?;
+ if key.contains("\n") {
+ return Err(Error::KeyNewline)
+ }
+ }
+ }
+ Ok(())
+ }
+
+ fn serialize_value<T: ?Sized>(&mut self, value: &T) -> Result<(), Error>
+ where T: ser::Serialize,
+ {
+ match *self {
+ SerializeTable::Datetime(_) => panic!(), // shouldn't be possible
+ SerializeTable::Table {
+ ref mut ser,
+ ref key,
+ ref first,
+ ref table_emitted,
+ ..
+ } => {
+ let res = value.serialize(&mut Serializer {
+ dst: &mut *ser.dst,
+ state: State::Table {
+ key: &key,
+ parent: &ser.state,
+ first: &first,
+ table_emitted: &table_emitted,
+ },
+ });
+ match res {
+ Ok(()) => first.set(false),
+ Err(Error::UnsupportedNone) => {},
+ Err(e) => return Err(e),
+ }
+ }
+ }
+ Ok(())
+ }
+
+ fn end(self) -> Result<(), Error> {
+ match self {
+ SerializeTable::Datetime(_) => panic!(), // shouldn't be possible
+ SerializeTable::Table { mut ser, first, .. } => {
+ if first.get() {
+ let state = ser.state.clone();
+ ser.emit_table_header(&state)?;
+ }
+ }
+ }
+ Ok(())
+ }
+}
+
+impl<'a, 'b> ser::SerializeStruct for SerializeTable<'a, 'b> {
+ type Ok = ();
+ type Error = Error;
+
+ fn serialize_field<T: ?Sized>(&mut self, key: &'static str, value: &T)
+ -> Result<(), Error>
+ where T: ser::Serialize,
+ {
+ match *self {
+ SerializeTable::Datetime(ref mut ser) => {
+ if key == SERDE_STRUCT_FIELD_NAME {
+ value.serialize(DateStrEmitter(&mut *ser))?;
+ } else {
+ return Err(Error::DateInvalid)
+ }
+ }
+ SerializeTable::Table {
+ ref mut ser,
+ ref first,
+ ref table_emitted,
+ ..
+ } => {
+ let res = value.serialize(&mut Serializer {
+ dst: &mut *ser.dst,
+ state: State::Table {
+ key: key,
+ parent: &ser.state,
+ first: first,
+ table_emitted: table_emitted,
+ },
+ });
+ match res {
+ Ok(()) => first.set(false),
+ Err(Error::UnsupportedNone) => {},
+ Err(e) => return Err(e),
+ }
+ }
+ }
+ Ok(())
+ }
+
+ fn end(self) -> Result<(), Error> {
+ Ok(())
+ }
+}
+
+struct DateStrEmitter<'a: 'b, 'b>(&'b mut Serializer<'a>);
+
+impl<'a, 'b> ser::Serializer for DateStrEmitter<'a, 'b> {
+ type Ok = ();
+ type Error = Error;
+ type SerializeSeq = ser::Impossible<(), Error>;
+ type SerializeTuple = ser::Impossible<(), Error>;
+ type SerializeTupleStruct = ser::Impossible<(), Error>;
+ type SerializeTupleVariant = ser::Impossible<(), Error>;
+ type SerializeMap = ser::Impossible<(), Error>;
+ type SerializeStruct = ser::Impossible<(), Error>;
+ type SerializeStructVariant = ser::Impossible<(), Error>;
+
+ fn serialize_bool(self, _v: bool) -> Result<(), Self::Error> {
+ Err(Error::DateInvalid)
+ }
+
+ fn serialize_i8(self, _v: i8) -> Result<(), Self::Error> {
+ Err(Error::DateInvalid)
+ }
+
+ fn serialize_i16(self, _v: i16) -> Result<(), Self::Error> {
+ Err(Error::DateInvalid)
+ }
+
+ fn serialize_i32(self, _v: i32) -> Result<(), Self::Error> {
+ Err(Error::DateInvalid)
+ }
+
+ fn serialize_i64(self, _v: i64) -> Result<(), Self::Error> {
+ Err(Error::DateInvalid)
+ }
+
+ fn serialize_u8(self, _v: u8) -> Result<(), Self::Error> {
+ Err(Error::DateInvalid)
+ }
+
+ fn serialize_u16(self, _v: u16) -> Result<(), Self::Error> {
+ Err(Error::DateInvalid)
+ }
+
+ fn serialize_u32(self, _v: u32) -> Result<(), Self::Error> {
+ Err(Error::DateInvalid)
+ }
+
+ fn serialize_u64(self, _v: u64) -> Result<(), Self::Error> {
+ Err(Error::DateInvalid)
+ }
+
+ fn serialize_f32(self, _v: f32) -> Result<(), Self::Error> {
+ Err(Error::DateInvalid)
+ }
+
+ fn serialize_f64(self, _v: f64) -> Result<(), Self::Error> {
+ Err(Error::DateInvalid)
+ }
+
+ fn serialize_char(self, _v: char) -> Result<(), Self::Error> {
+ Err(Error::DateInvalid)
+ }
+
+ fn serialize_str(self, value: &str) -> Result<(), Self::Error> {
+ self.0.display(value, "datetime")?;
+ Ok(())
+ }
+
+ fn serialize_bytes(self, _value: &[u8]) -> Result<(), Self::Error> {
+ Err(Error::DateInvalid)
+ }
+
+ fn serialize_none(self) -> Result<(), Self::Error> {
+ Err(Error::DateInvalid)
+ }
+
+ fn serialize_some<T: ?Sized>(self, _value: &T) -> Result<(), Self::Error>
+ where T: ser::Serialize
+ {
+ Err(Error::KeyNotString)
+ }
+
+ fn serialize_unit(self) -> Result<(), Self::Error> {
+ Err(Error::KeyNotString)
+ }
+
+ fn serialize_unit_struct(self,
+ _name: &'static str)
+ -> Result<(), Self::Error> {
+ Err(Error::DateInvalid)
+ }
+
+ fn serialize_unit_variant(self,
+ _name: &'static str,
+ _variant_index: usize,
+ _variant: &'static str)
+ -> Result<(), Self::Error> {
+ Err(Error::DateInvalid)
+ }
+
+ fn serialize_newtype_struct<T: ?Sized>(self, _name: &'static str, _value: &T)
+ -> Result<(), Self::Error>
+ where T: ser::Serialize,
+ {
+ Err(Error::DateInvalid)
+ }
+
+ fn serialize_newtype_variant<T: ?Sized>(self,
+ _name: &'static str,
+ _variant_index: usize,
+ _variant: &'static str,
+ _value: &T)
+ -> Result<(), Self::Error>
+ where T: ser::Serialize,
+ {
+ Err(Error::DateInvalid)
+ }
+
+ fn serialize_seq(self, _len: Option<usize>)
+ -> Result<Self::SerializeSeq, Self::Error> {
+ Err(Error::DateInvalid)
+ }
+
+ fn serialize_seq_fixed_size(self, _size: usize)
+ -> Result<Self::SerializeSeq, Self::Error> {
+ Err(Error::DateInvalid)
+ }
+
+ fn serialize_tuple(self, _len: usize)
+ -> Result<Self::SerializeTuple, Self::Error> {
+ Err(Error::DateInvalid)
+ }
+
+ fn serialize_tuple_struct(self, _name: &'static str, _len: usize)
+ -> Result<Self::SerializeTupleStruct, Self::Error> {
+ Err(Error::DateInvalid)
+ }
+
+ fn serialize_tuple_variant(self,
+ _name: &'static str,
+ _variant_index: usize,
+ _variant: &'static str,
+ _len: usize)
+ -> Result<Self::SerializeTupleVariant, Self::Error> {
+ Err(Error::DateInvalid)
+ }
+
+ fn serialize_map(self, _len: Option<usize>)
+ -> Result<Self::SerializeMap, Self::Error> {
+ Err(Error::DateInvalid)
+ }
+
+ fn serialize_struct(self, _name: &'static str, _len: usize)
+ -> Result<Self::SerializeStruct, Self::Error> {
+ Err(Error::DateInvalid)
+ }
+
+ fn serialize_struct_variant(self,
+ _name: &'static str,
+ _variant_index: usize,
+ _variant: &'static str,
+ _len: usize)
+ -> Result<Self::SerializeStructVariant, Self::Error> {
+ Err(Error::DateInvalid)
+ }
+}
+
+struct StringExtractor;
+
+impl ser::Serializer for StringExtractor {
+ type Ok = String;
+ type Error = Error;
+ type SerializeSeq = ser::Impossible<String, Error>;
+ type SerializeTuple = ser::Impossible<String, Error>;
+ type SerializeTupleStruct = ser::Impossible<String, Error>;
+ type SerializeTupleVariant = ser::Impossible<String, Error>;
+ type SerializeMap = ser::Impossible<String, Error>;
+ type SerializeStruct = ser::Impossible<String, Error>;
+ type SerializeStructVariant = ser::Impossible<String, Error>;
+
+ fn serialize_bool(self, _v: bool) -> Result<String, Self::Error> {
+ Err(Error::KeyNotString)
+ }
+
+ fn serialize_i8(self, _v: i8) -> Result<String, Self::Error> {
+ Err(Error::KeyNotString)
+ }
+
+ fn serialize_i16(self, _v: i16) -> Result<String, Self::Error> {
+ Err(Error::KeyNotString)
+ }
+
+ fn serialize_i32(self, _v: i32) -> Result<String, Self::Error> {
+ Err(Error::KeyNotString)
+ }
+
+ fn serialize_i64(self, _v: i64) -> Result<String, Self::Error> {
+ Err(Error::KeyNotString)
+ }
+
+ fn serialize_u8(self, _v: u8) -> Result<String, Self::Error> {
+ Err(Error::KeyNotString)
+ }
+
+ fn serialize_u16(self, _v: u16) -> Result<String, Self::Error> {
+ Err(Error::KeyNotString)
+ }
+
+ fn serialize_u32(self, _v: u32) -> Result<String, Self::Error> {
+ Err(Error::KeyNotString)
+ }
+
+ fn serialize_u64(self, _v: u64) -> Result<String, Self::Error> {
+ Err(Error::KeyNotString)
+ }
+
+ fn serialize_f32(self, _v: f32) -> Result<String, Self::Error> {
+ Err(Error::KeyNotString)
+ }
+
+ fn serialize_f64(self, _v: f64) -> Result<String, Self::Error> {
+ Err(Error::KeyNotString)
+ }
+
+ fn serialize_char(self, _v: char) -> Result<String, Self::Error> {
+ Err(Error::KeyNotString)
+ }
+
+ fn serialize_str(self, value: &str) -> Result<String, Self::Error> {
+ Ok(value.to_string())
+ }
+
+ fn serialize_bytes(self, _value: &[u8]) -> Result<String, Self::Error> {
+ Err(Error::KeyNotString)
+ }
+
+ fn serialize_none(self) -> Result<String, Self::Error> {
+ Err(Error::KeyNotString)
+ }
+
+ fn serialize_some<T: ?Sized>(self, _value: &T) -> Result<String, Self::Error>
+ where T: ser::Serialize
+ {
+ Err(Error::KeyNotString)
+ }
+
+ fn serialize_unit(self) -> Result<String, Self::Error> {
+ Err(Error::KeyNotString)
+ }
+
+ fn serialize_unit_struct(self,
+ _name: &'static str)
+ -> Result<String, Self::Error> {
+ Err(Error::KeyNotString)
+ }
+
+ fn serialize_unit_variant(self,
+ _name: &'static str,
+ _variant_index: usize,
+ _variant: &'static str)
+ -> Result<String, Self::Error> {
+ Err(Error::KeyNotString)
+ }
+
+ fn serialize_newtype_struct<T: ?Sized>(self, _name: &'static str, _value: &T)
+ -> Result<String, Self::Error>
+ where T: ser::Serialize,
+ {
+ Err(Error::KeyNotString)
+ }
+
+ fn serialize_newtype_variant<T: ?Sized>(self,
+ _name: &'static str,
+ _variant_index: usize,
+ _variant: &'static str,
+ _value: &T)
+ -> Result<String, Self::Error>
+ where T: ser::Serialize,
+ {
+ Err(Error::KeyNotString)
+ }
+
+ fn serialize_seq(self, _len: Option<usize>)
+ -> Result<Self::SerializeSeq, Self::Error> {
+ Err(Error::KeyNotString)
+ }
+
+ fn serialize_seq_fixed_size(self, _size: usize)
+ -> Result<Self::SerializeSeq, Self::Error> {
+ Err(Error::KeyNotString)
+ }
+
+ fn serialize_tuple(self, _len: usize)
+ -> Result<Self::SerializeTuple, Self::Error> {
+ Err(Error::KeyNotString)
+ }
+
+ fn serialize_tuple_struct(self, _name: &'static str, _len: usize)
+ -> Result<Self::SerializeTupleStruct, Self::Error> {
+ Err(Error::KeyNotString)
+ }
+
+ fn serialize_tuple_variant(self,
+ _name: &'static str,
+ _variant_index: usize,
+ _variant: &'static str,
+ _len: usize)
+ -> Result<Self::SerializeTupleVariant, Self::Error> {
+ Err(Error::KeyNotString)
+ }
+
+ fn serialize_map(self, _len: Option<usize>)
+ -> Result<Self::SerializeMap, Self::Error> {
+ Err(Error::KeyNotString)
+ }
+
+ fn serialize_struct(self, _name: &'static str, _len: usize)
+ -> Result<Self::SerializeStruct, Self::Error> {
+ Err(Error::KeyNotString)
+ }
+
+ fn serialize_struct_variant(self,
+ _name: &'static str,
+ _variant_index: usize,
+ _variant: &'static str,
+ _len: usize)
+ -> Result<Self::SerializeStructVariant, Self::Error> {
+ Err(Error::KeyNotString)
+ }
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ Error::UnsupportedType => "unsupported Rust type".fmt(f),
+ Error::KeyNotString => "map key was not a string".fmt(f),
+ Error::KeyNewline => "map keys cannot contain newlines".fmt(f),
+ Error::ArrayMixedType => "arrays cannot have mixed types".fmt(f),
+ Error::ValueAfterTable => "values must be emitted before tables".fmt(f),
+ Error::DateInvalid => "a serialize date was invalid".fmt(f),
+ Error::UnsupportedNone => "unsupported None value".fmt(f),
+ Error::Custom(ref s) => s.fmt(f),
+ Error::__Nonexhaustive => panic!(),
+ }
+ }
+}
+
+impl error::Error for Error {
+ fn description(&self) -> &str {
+ match *self {
+ Error::UnsupportedType => "unsupported Rust type",
+ Error::KeyNotString => "map key was not a string",
+ Error::KeyNewline => "map keys cannot contain newlines",
+ Error::ArrayMixedType => "arrays cannot have mixed types",
+ Error::ValueAfterTable => "values must be emitted before tables",
+ Error::DateInvalid => "a serialized date was invalid",
+ Error::UnsupportedNone => "unsupported None value",
+ Error::Custom(_) => "custom error",
+ Error::__Nonexhaustive => panic!(),
+ }
+ }
+}
+
+impl ser::Error for Error {
+ fn custom<T: fmt::Display>(msg: T) -> Error {
+ Error::Custom(msg.to_string())
+ }
+}
diff --git a/src/tokens.rs b/src/tokens.rs
new file mode 100644
index 0000000..26c8277
--- /dev/null
+++ b/src/tokens.rs
@@ -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());
+ }
+}
diff --git a/src/value.rs b/src/value.rs
new file mode 100644
index 0000000..4769ef1
--- /dev/null
+++ b/src/value.rs
@@ -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)
+ }
+ }
+}
diff --git a/src/display.rs b/src/value/display.rs
index 0a41e2a..0a6365d 100644
--- a/src/display.rs
+++ b/src/value/display.rs
@@ -132,81 +132,3 @@ impl<'a> fmt::Display for Key<'a> {
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");
- }
-}
diff --git a/tests/datetime.rs b/tests/datetime.rs
new file mode 100644
index 0000000..948e863
--- /dev/null
+++ b/tests/datetime.rs
@@ -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");
+}
diff --git a/tests/display.rs b/tests/display.rs
new file mode 100644
index 0000000..c38355a
--- /dev/null
+++ b/tests/display.rs
@@ -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");
+}
diff --git a/tests/formatting.rs b/tests/formatting.rs
index b8f4082..10fb165 100644
--- a/tests/formatting.rs
+++ b/tests/formatting.rs
@@ -1,19 +1,22 @@
-extern crate rustc_serialize;
+extern crate serde;
+#[macro_use]
+extern crate serde_derive;
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 {
pub name: String,
pub surname: String,
}
-#[derive(Debug, Clone, Hash, PartialEq, Eq, RustcEncodable, RustcDecodable)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
struct Users {
pub user: Vec<User>,
}
-#[derive(Debug, Clone, Hash, PartialEq, Eq, RustcEncodable, RustcDecodable)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
struct TwoUsers {
pub user0: User,
pub user1: User,
@@ -21,7 +24,7 @@ struct TwoUsers {
#[test]
fn no_unnecessary_newlines_array() {
- assert!(!encode_str(&Users {
+ assert!(!to_string(&Users {
user: vec![
User {
name: "John".to_string(),
@@ -32,13 +35,13 @@ fn no_unnecessary_newlines_array() {
surname: "Dough".to_string(),
},
],
- })
+ }).unwrap()
.starts_with("\n"));
}
#[test]
fn no_unnecessary_newlines_table() {
- assert!(!encode_str(&TwoUsers {
+ assert!(!to_string(&TwoUsers {
user0: User {
name: "John".to_string(),
surname: "Doe".to_string(),
@@ -47,6 +50,6 @@ fn no_unnecessary_newlines_table() {
name: "Jane".to_string(),
surname: "Dough".to_string(),
},
- })
+ }).unwrap()
.starts_with("\n"));
}
diff --git a/tests/invalid-misc.rs b/tests/invalid-misc.rs
new file mode 100644
index 0000000..e8360fd
--- /dev/null
+++ b/tests/invalid-misc.rs
@@ -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_");
+}
diff --git a/tests/invalid.rs b/tests/invalid.rs
index 63e4de8..4679684 100644
--- a/tests/invalid.rs
+++ b/tests/invalid.rs
@@ -1,17 +1,9 @@
extern crate toml;
-use toml::{Parser};
-
fn run(toml: &str) {
- let mut p = Parser::new(toml);
- let table = p.parse();
- assert!(table.is_none());
- 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);
+ println!("test if invalid:\n{}", toml);
+ if let Ok(e) = toml.parse::<toml::Value>() {
+ panic!("parsed to: {:#?}", e);
}
}
@@ -32,8 +24,6 @@ test!(datetime_malformed_no_secs,
include_str!("invalid/datetime-malformed-no-secs.toml"));
test!(datetime_malformed_no_t,
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,
include_str!("invalid/datetime-malformed-with-milli.toml"));
test!(duplicate_keys,
diff --git a/tests/invalid/datetime-malformed-no-z.toml b/tests/invalid/datetime-malformed-no-z.toml
deleted file mode 100644
index cf66b1e..0000000
--- a/tests/invalid/datetime-malformed-no-z.toml
+++ /dev/null
@@ -1 +0,0 @@
-no-z = 1987-07-05T17:45:00
diff --git a/tests/parser.rs b/tests/parser.rs
new file mode 100644
index 0000000..2db2cfb
--- /dev/null
+++ b/tests/parser.rs
@@ -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");
+}
diff --git a/tests/serde.rs b/tests/serde.rs
new file mode 100644
index 0000000..e4d88fc
--- /dev/null
+++ b/tests/serde.rs
@@ -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());
+}
diff --git a/tests/valid.rs b/tests/valid.rs
index 09589c9..4229f1c 100644
--- a/tests/valid.rs
+++ b/tests/valid.rs
@@ -1,65 +1,61 @@
-extern crate rustc_serialize;
extern crate toml;
+extern crate serde_json;
-use std::collections::BTreeMap;
-use rustc_serialize::json::Json;
+use toml::Value as Toml;
+use serde_json::Value as Json;
-use toml::{Parser, Value};
-use toml::Value::{Table, Integer, Float, Boolean, Datetime, Array};
-
-fn to_json(toml: Value) -> Json {
+fn to_json(toml: toml::Value) -> Json {
fn doit(s: &str, json: Json) -> Json {
- let mut map = BTreeMap::new();
- map.insert(format!("{}", "type"), Json::String(format!("{}", s)));
- map.insert(format!("{}", "value"), json);
+ let mut map = serde_json::Map::new();
+ map.insert("type".to_string(), Json::String(s.to_string()));
+ map.insert("value".to_string(), json);
Json::Object(map)
}
+
match toml {
- Value::String(s) => doit("string", Json::String(s)),
- Integer(i) => doit("integer", Json::String(format!("{}", i))),
- Float(f) => doit("float", Json::String({
+ Toml::String(s) => doit("string", Json::String(s)),
+ Toml::Integer(i) => doit("integer", Json::String(i.to_string())),
+ Toml::Float(f) => doit("float", Json::String({
let s = format!("{:.15}", f);
let s = format!("{}", s.trim_right_matches('0'));
if s.ends_with(".") {format!("{}0", s)} else {s}
})),
- Boolean(b) => doit("bool", Json::String(format!("{}", b))),
- Datetime(s) => doit("datetime", Json::String(s)),
- Array(arr) => {
+ Toml::Boolean(b) => doit("bool", Json::String(format!("{}", b))),
+ Toml::Datetime(s) => doit("datetime", Json::String(s.to_string())),
+ Toml::Array(arr) => {
let is_table = match arr.first() {
- Some(&Table(..)) => true,
+ Some(&Toml::Table(..)) => true,
_ => false,
};
let json = Json::Array(arr.into_iter().map(to_json).collect());
if is_table {json} else {doit("array", json)}
}
- Table(table) => Json::Object(table.into_iter().map(|(k, v)| {
- (k, to_json(v))
- }).collect()),
+ Toml::Table(table) => {
+ let mut map = serde_json::Map::new();
+ for (k, v) in table {
+ map.insert(k, to_json(v));
+ }
+ Json::Object(map)
+ }
}
}
fn run(toml: &str, json: &str) {
- let mut p = Parser::new(toml);
- let table = p.parse();
- assert!(p.errors.len() == 0, "had_errors: {:?}",
- 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);
+ println!("parsing:\n{}", toml);
+ let toml: Toml = toml.parse().unwrap();
+ let json: Json = json.parse().unwrap();
- let json = Json::from_str(json).unwrap();
+ // Assert toml == json
let toml_json = to_json(toml.clone());
assert!(json == toml_json,
"expected\n{}\ngot\n{}\n",
- json.pretty(),
- toml_json.pretty());
+ serde_json::to_string_pretty(&json).unwrap(),
+ serde_json::to_string_pretty(&toml_json).unwrap());
- let table2 = Parser::new(&toml_string).parse().unwrap();
- // floats are a little lossy
- if table2.values().any(|v| v.as_float().is_some()) { return }
- assert_eq!(toml, Table(table2));
+ // Assert round trip
+ println!("round trip parse: {}", toml);
+ let toml2 = toml.to_string().parse().unwrap();
+ assert_eq!(toml, toml2);
}
macro_rules! test( ($name:ident, $toml:expr, $json:expr) => (