diff options
author | Alex Crichton <alex@alexcrichton.com> | 2017-02-08 21:23:29 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-02-08 21:23:29 -0800 |
commit | beff7f992d738db3565d899a72542baae57f835d (patch) | |
tree | 76498b837fc5f1f6ba0a5f53e1b2d85c6638da4d | |
parent | 473908c9722eeedeec1777237a135f582faa78d8 (diff) | |
parent | f66d8bcf33530c858a502bfa170f2383a8cbc204 (diff) | |
download | milf-rs-beff7f992d738db3565d899a72542baae57f835d.tar.gz milf-rs-beff7f992d738db3565d899a72542baae57f835d.zip |
Merge pull request #137 from alexcrichton/serde-upgrade
Rewrite crate with serde support from ground up
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: @@ -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()) - } -} @@ -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) => ( |