aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Tolnay <dtolnay@gmail.com>2017-11-12 15:17:52 -0800
committerDavid Tolnay <dtolnay@gmail.com>2017-11-12 23:46:34 -0800
commitd53db5c5335fb73389e5a78c190bf2a1903f3b6a (patch)
tree38d92a55f2bde9b02fe4cfb78dd9c8f6f29ee9e3
parent3ad6e71f53a87215fb5286bcf87de15524699561 (diff)
downloadmilf-rs-d53db5c5335fb73389e5a78c190bf2a1903f3b6a.tar.gz
milf-rs-d53db5c5335fb73389e5a78c190bf2a1903f3b6a.zip
A toml macro
-rw-r--r--src/lib.rs3
-rw-r--r--src/macros.rs373
-rw-r--r--test-suite/Cargo.toml1
-rw-r--r--test-suite/build.rs8
-rw-r--r--test-suite/tests/macros.rs286
5 files changed, 671 insertions, 0 deletions
diff --git a/src/lib.rs b/src/lib.rs
index c4a7e9d..1c4842b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -166,3 +166,6 @@ pub mod de;
#[doc(no_inline)]
pub use de::{from_slice, from_str, Deserializer};
mod tokens;
+
+#[doc(hidden)]
+pub mod macros;
diff --git a/src/macros.rs b/src/macros.rs
new file mode 100644
index 0000000..7a2bcd3
--- /dev/null
+++ b/src/macros.rs
@@ -0,0 +1,373 @@
+pub use serde::de::{Deserialize, IntoDeserializer};
+
+use value::{Value, Table, Array};
+
+/// Construct a [`toml::Value`] from TOML syntax.
+///
+/// [`toml::Value`]: value/enum.Value.html
+///
+/// ```rust
+/// #[macro_use]
+/// extern crate toml;
+///
+/// fn main() {
+/// let cargo_toml = toml! {
+/// [package]
+/// name = "toml"
+/// version = "0.4.5"
+/// authors = ["Alex Crichton <alex@alexcrichton.com>"]
+///
+/// [badges]
+/// travis-ci = { repository = "alexcrichton/toml-rs" }
+///
+/// [dependencies]
+/// serde = "1.0"
+///
+/// [dev-dependencies]
+/// serde_derive = "1.0"
+/// serde_json = "1.0"
+/// };
+///
+/// println!("{:#?}", cargo_toml);
+/// }
+/// ```
+#[macro_export]
+macro_rules! toml {
+ ($($toml:tt)+) => {{
+ let table = $crate::value::Table::new();
+ let mut root = $crate::Value::Table(table);
+ toml_internal!(@toplevel root [] $($toml)+);
+ root
+ }};
+}
+
+// TT-muncher to parse TOML syntax into a toml::Value.
+//
+// @toplevel -- Parse tokens outside of an inline table or inline array. In
+// this state, `[table headers]` and `[[array headers]]` are
+// allowed and `key = value` pairs are not separated by commas.
+//
+// @topleveldatetime -- Helper to parse a Datetime from string and insert it
+// into a table, continuing in the @toplevel state.
+//
+// @path -- Turn a path segment into a string. Segments that look like idents
+// are stringified, while quoted segments like `"cfg(windows)"`
+// are not.
+//
+// @value -- Parse the value part of a `key = value` pair, which may be a
+// primitive or inline table or inline array.
+//
+// @table -- Parse the contents of an inline table, returning them as a
+// toml::Value::Table.
+//
+// @tabledatetime -- Helper to parse a Datetime from string and insert it
+// into a table, continuing in the @table state.
+//
+// @array -- Parse the contents of an inline array, returning them as a
+// toml::Value::Array.
+//
+// @arraydatetime -- Helper to parse a Datetime from string and push it into
+// an array, continuing in the @array state.
+//
+// @trailingcomma -- Helper to append a comma to a sequence of tokens if the
+// sequence is non-empty and does not already end in a trailing
+// comma.
+//
+#[macro_export]
+#[doc(hidden)]
+macro_rules! toml_internal {
+ // Base case, no elements remaining.
+ (@toplevel $root:ident [$($path:tt)*]) => {};
+
+ // Parse negative number `key = -value`.
+ (@toplevel $root:ident [$($path:tt)*] $($k:tt)-+ = - $v:tt $($rest:tt)*) => {
+ toml_internal!(@toplevel $root [$($path)*] $($k)-+ = (-$v) $($rest)*);
+ };
+
+ // Parse offset datetime `key = 1979-05-27T00:32:00.999999-07:00`.
+ (@toplevel $root:ident [$($path:tt)*] $($k:tt)-+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt . $frac:tt - $tzh:tt : $tzm:tt $($rest:tt)*) => {
+ toml_internal!(@topleveldatetime $root [$($path)*] $($k)-+ = ($yr - $mo - $dhr : $min : $sec . $frac - $tzh : $tzm) $($rest)*);
+ };
+
+ // Parse offset datetime `key = 1979-05-27T00:32:00-07:00`.
+ (@toplevel $root:ident [$($path:tt)*] $($k:tt)-+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt - $tzh:tt : $tzm:tt $($rest:tt)*) => {
+ toml_internal!(@topleveldatetime $root [$($path)*] $($k)-+ = ($yr - $mo - $dhr : $min : $sec - $tzh : $tzm) $($rest)*);
+ };
+
+ // Parse local datetime `key = 1979-05-27T00:32:00.999999`.
+ (@toplevel $root:ident [$($path:tt)*] $($k:tt)-+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt . $frac:tt $($rest:tt)*) => {
+ toml_internal!(@topleveldatetime $root [$($path)*] $($k)-+ = ($yr - $mo - $dhr : $min : $sec . $frac) $($rest)*);
+ };
+
+ // Parse offset datetime `key = 1979-05-27T07:32:00Z` and local datetime `key = 1979-05-27T07:32:00`.
+ (@toplevel $root:ident [$($path:tt)*] $($k:tt)-+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt $($rest:tt)*) => {
+ toml_internal!(@topleveldatetime $root [$($path)*] $($k)-+ = ($yr - $mo - $dhr : $min : $sec) $($rest)*);
+ };
+
+ // Parse local date `key = 1979-05-27`.
+ (@toplevel $root:ident [$($path:tt)*] $($k:tt)-+ = $yr:tt - $mo:tt - $day:tt $($rest:tt)*) => {
+ toml_internal!(@topleveldatetime $root [$($path)*] $($k)-+ = ($yr - $mo - $day) $($rest)*);
+ };
+
+ // Parse local time `key = 00:32:00.999999`.
+ (@toplevel $root:ident [$($path:tt)*] $($k:tt)-+ = $hr:tt : $min:tt : $sec:tt . $frac:tt $($rest:tt)*) => {
+ toml_internal!(@topleveldatetime $root [$($path)*] $($k)-+ = ($hr : $min : $sec . $frac) $($rest)*);
+ };
+
+ // Parse local time `key = 07:32:00`.
+ (@toplevel $root:ident [$($path:tt)*] $($k:tt)-+ = $hr:tt : $min:tt : $sec:tt $($rest:tt)*) => {
+ toml_internal!(@topleveldatetime $root [$($path)*] $($k)-+ = ($hr : $min : $sec) $($rest)*);
+ };
+
+ // Parse any other `key = value` including string, inline array, inline
+ // table, number, and boolean.
+ (@toplevel $root:ident [$($path:tt)*] $($k:tt)-+ = $v:tt $($rest:tt)*) => {
+ $crate::macros::insert_toml(
+ &mut $root,
+ &[$($path)* &concat!($("-", toml_internal!(@path $k),)+)[1..]],
+ toml_internal!(@value $v));
+ toml_internal!(@toplevel $root [$($path)*] $($rest)*);
+ };
+
+ // Parse array header `[[bin]]`.
+ (@toplevel $root:ident $oldpath:tt [[$($($path:tt)-+).+]] $($rest:tt)*) => {
+ $crate::macros::push_toml(
+ &mut $root,
+ &[$(&concat!($("-", toml_internal!(@path $path),)+)[1..],)+]);
+ toml_internal!(@toplevel $root [$(&concat!($("-", toml_internal!(@path $path),)+)[1..],)+] $($rest)*);
+ };
+
+ // Parse table header `[patch.crates-io]`.
+ (@toplevel $root:ident $oldpath:tt [$($($path:tt)-+).+] $($rest:tt)*) => {
+ $crate::macros::insert_toml(
+ &mut $root,
+ &[$(&concat!($("-", toml_internal!(@path $path),)+)[1..],)+],
+ $crate::Value::Table($crate::value::Table::new()));
+ toml_internal!(@toplevel $root [$(&concat!($("-", toml_internal!(@path $path),)+)[1..],)+] $($rest)*);
+ };
+
+ // Parse datetime from string and insert into table.
+ (@topleveldatetime $root:ident [$($path:tt)*] $($k:tt)-+ = ($($datetime:tt)+) $($rest:tt)*) => {
+ $crate::macros::insert_toml(
+ &mut $root,
+ &[$($path)* &concat!($("-", toml_internal!(@path $k),)+)[1..]],
+ $crate::Value::Datetime(concat!($(stringify!($datetime)),+).parse().unwrap()));
+ toml_internal!(@toplevel $root [$($path)*] $($rest)*);
+ };
+
+ // Turn a path segment into a string.
+ (@path $ident:ident) => {
+ stringify!($ident)
+ };
+
+ // For a path segment that is not an ident, expect that it is already a
+ // quoted string, like in `[target."cfg(windows)".dependencies]`.
+ (@path $quoted:tt) => {
+ $quoted
+ };
+
+ // Construct a Value from an inline table.
+ (@value { $($inline:tt)* }) => {{
+ let mut table = $crate::value::Table::new();
+ toml_internal!(@trailingcomma (@table table) $($inline)*);
+ $crate::Value::Table(table)
+ }};
+
+ // Construct a Value from an inline array.
+ (@value [ $($inline:tt)* ]) => {{
+ let mut array = $crate::value::Array::new();
+ toml_internal!(@trailingcomma (@array array) $($inline)*);
+ $crate::Value::Array(array)
+ }};
+
+ // Construct a Value from any other type, probably string or boolean or number.
+ (@value $v:tt) => {{
+ // TODO: Implement this with something like serde_json::to_value instead.
+ let de = $crate::macros::IntoDeserializer::<$crate::de::Error>::into_deserializer($v);
+ <$crate::Value as $crate::macros::Deserialize>::deserialize(de).unwrap()
+ }};
+
+ // Base case of inline table.
+ (@table $root:ident) => {};
+
+ // Parse negative number `key = -value`.
+ (@table $root:ident $($k:tt)-+ = - $v:tt , $($rest:tt)*) => {
+ toml_internal!(@table $root $($k)-+ = (-$v) , $($rest)*);
+ };
+
+ // Parse offset datetime `key = 1979-05-27T00:32:00.999999-07:00`.
+ (@table $root:ident $($k:tt)-+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt . $frac:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
+ toml_internal!(@tabledatetime $root $($k)-+ = ($yr - $mo - $dhr : $min : $sec . $frac - $tzh : $tzm) $($rest)*);
+ };
+
+ // Parse offset datetime `key = 1979-05-27T00:32:00-07:00`.
+ (@table $root:ident $($k:tt)-+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
+ toml_internal!(@tabledatetime $root $($k)-+ = ($yr - $mo - $dhr : $min : $sec - $tzh : $tzm) $($rest)*);
+ };
+
+ // Parse local datetime `key = 1979-05-27T00:32:00.999999`.
+ (@table $root:ident $($k:tt)-+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt . $frac:tt , $($rest:tt)*) => {
+ toml_internal!(@tabledatetime $root $($k)-+ = ($yr - $mo - $dhr : $min : $sec . $frac) $($rest)*);
+ };
+
+ // Parse offset datetime `key = 1979-05-27T07:32:00Z` and local datetime `key = 1979-05-27T07:32:00`.
+ (@table $root:ident $($k:tt)-+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt , $($rest:tt)*) => {
+ toml_internal!(@tabledatetime $root $($k)-+ = ($yr - $mo - $dhr : $min : $sec) $($rest)*);
+ };
+
+ // Parse local date `key = 1979-05-27`.
+ (@table $root:ident $($k:tt)-+ = $yr:tt - $mo:tt - $day:tt , $($rest:tt)*) => {
+ toml_internal!(@tabledatetime $root $($k)-+ = ($yr - $mo - $day) $($rest)*);
+ };
+
+ // Parse local time `key = 00:32:00.999999`.
+ (@table $root:ident $($k:tt)-+ = $hr:tt : $min:tt : $sec:tt . $frac:tt , $($rest:tt)*) => {
+ toml_internal!(@tabledatetime $root $($k)-+ = ($hr : $min : $sec . $frac) $($rest)*);
+ };
+
+ // Parse local time `key = 07:32:00`.
+ (@table $root:ident $($k:tt)-+ = $hr:tt : $min:tt : $sec:tt , $($rest:tt)*) => {
+ toml_internal!(@tabledatetime $root $($k)-+ = ($hr : $min : $sec) $($rest)*);
+ };
+
+ // Parse any other type, probably string or boolean or number.
+ (@table $root:ident $($k:tt)-+ = $v:tt , $($rest:tt)*) => {
+ $root.insert(
+ concat!($("-", toml_internal!(@path $k),)+)[1..].to_owned(),
+ toml_internal!(@value $v));
+ toml_internal!(@table $root $($rest)*);
+ };
+
+ // Parse a Datetime from string and continue in @table state.
+ (@tabledatetime $root:ident $($k:tt)-+ = ($($datetime:tt)*) $($rest:tt)*) => {
+ $root.insert(
+ concat!($("-", toml_internal!(@path $k),)+)[1..].to_owned(),
+ $crate::Value::Datetime(concat!($(stringify!($datetime)),+).parse().unwrap()));
+ toml_internal!(@table $root $($rest)*);
+ };
+
+ // Base case of inline array.
+ (@array $root:ident) => {};
+
+ // Parse negative number `-value`.
+ (@array $root:ident - $v:tt , $($rest:tt)*) => {
+ toml_internal!(@array $root (-$v) , $($rest)*);
+ };
+
+ // Parse offset datetime `1979-05-27T00:32:00.999999-07:00`.
+ (@array $root:ident $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt . $frac:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
+ toml_internal!(@arraydatetime $root ($yr - $mo - $dhr : $min : $sec . $frac - $tzh : $tzm) $($rest)*);
+ };
+
+ // Parse offset datetime `1979-05-27T00:32:00-07:00`.
+ (@array $root:ident $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
+ toml_internal!(@arraydatetime $root ($yr - $mo - $dhr : $min : $sec - $tzh : $tzm) $($rest)*);
+ };
+
+ // Parse local datetime `1979-05-27T00:32:00.999999`.
+ (@array $root:ident $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt . $frac:tt , $($rest:tt)*) => {
+ toml_internal!(@arraydatetime $root ($yr - $mo - $dhr : $min : $sec . $frac) $($rest)*);
+ };
+
+ // Parse offset datetime `1979-05-27T07:32:00Z` and local datetime `1979-05-27T07:32:00`.
+ (@array $root:ident $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt , $($rest:tt)*) => {
+ toml_internal!(@arraydatetime $root ($yr - $mo - $dhr : $min : $sec) $($rest)*);
+ };
+
+ // Parse local date `1979-05-27`.
+ (@array $root:ident $yr:tt - $mo:tt - $day:tt , $($rest:tt)*) => {
+ toml_internal!(@arraydatetime $root ($yr - $mo - $day) $($rest)*);
+ };
+
+ // Parse local time `00:32:00.999999`.
+ (@array $root:ident $hr:tt : $min:tt : $sec:tt . $frac:tt , $($rest:tt)*) => {
+ toml_internal!(@arraydatetime $root ($hr : $min : $sec . $frac) $($rest)*);
+ };
+
+ // Parse local time `07:32:00`.
+ (@array $root:ident $hr:tt : $min:tt : $sec:tt , $($rest:tt)*) => {
+ toml_internal!(@arraydatetime $root ($hr : $min : $sec) $($rest)*);
+ };
+
+ // Parse any other type, probably string or boolean or number.
+ (@array $root:ident $v:tt , $($rest:tt)*) => {
+ $root.push(toml_internal!(@value $v));
+ toml_internal!(@array $root $($rest)*);
+ };
+
+ // Parse a Datetime from string and continue in @array state.
+ (@arraydatetime $root:ident ($($datetime:tt)*) $($rest:tt)*) => {
+ $root.push($crate::Value::Datetime(concat!($(stringify!($datetime)),+).parse().unwrap()));
+ toml_internal!(@array $root $($rest)*);
+ };
+
+ // No trailing comma required if the tokens are empty.
+ (@trailingcomma ($($args:tt)*)) => {
+ toml_internal!($($args)*);
+ };
+
+ // Tokens end with a trailing comma, do not append another one.
+ (@trailingcomma ($($args:tt)*) ,) => {
+ toml_internal!($($args)* ,);
+ };
+
+ // Tokens end with something other than comma, append a trailing comma.
+ (@trailingcomma ($($args:tt)*) $last:tt) => {
+ toml_internal!($($args)* $last ,);
+ };
+
+ // Not yet at the last token.
+ (@trailingcomma ($($args:tt)*) $first:tt $($rest:tt)+) => {
+ toml_internal!(@trailingcomma ($($args)* $first) $($rest)+);
+ };
+}
+
+// Called when parsing a `key = value` pair.
+// Inserts an entry into the table at the given path.
+pub fn insert_toml(root: &mut Value, path: &[&str], value: Value) {
+ *traverse(root, path) = value;
+}
+
+// Called when parsing an `[[array header]]`.
+// Pushes an empty table onto the array at the given path.
+pub fn push_toml(root: &mut Value, path: &[&str]) {
+ let target = traverse(root, path);
+ if !target.is_array() {
+ *target = Value::Array(Array::new());
+ }
+ target.as_array_mut().unwrap().push(Value::Table(Table::new()));
+}
+
+fn traverse<'a>(root: &'a mut Value, path: &[&str]) -> &'a mut Value {
+ let mut cur = root;
+ for &key in path {
+ // Lexical lifetimes :D
+ let cur1 = cur;
+ let cur2;
+
+ // From the TOML spec:
+ //
+ // > Each double-bracketed sub-table will belong to the most recently
+ // > defined table element above it.
+ if cur1.is_array() {
+ cur2 = cur1.as_array_mut().unwrap().last_mut().unwrap();
+ } else {
+ cur2 = cur1;
+ };
+
+ // We are about to index into this value, so it better be a table.
+ if !cur2.is_table() {
+ *cur2 = Value::Table(Table::new());
+ }
+
+ if !cur2.as_table().unwrap().contains_key(key) {
+ // Insert an empty table for the next loop iteration to point to.
+ let empty = Value::Table(Table::new());
+ cur2.as_table_mut().unwrap().insert(key.to_owned(), empty);
+ }
+
+ // Step into the current table.
+ cur = cur2.as_table_mut().unwrap().get_mut(key).unwrap();
+ }
+ cur
+}
diff --git a/test-suite/Cargo.toml b/test-suite/Cargo.toml
index 7c91787..10ffbcb 100644
--- a/test-suite/Cargo.toml
+++ b/test-suite/Cargo.toml
@@ -2,6 +2,7 @@
name = "toml_test_suite"
version = "0.0.0"
authors = ["Alex Crichton <alex@alexcrichton.com>"]
+build = "build.rs"
publish = false
[build-dependencies]
diff --git a/test-suite/build.rs b/test-suite/build.rs
new file mode 100644
index 0000000..ca63946
--- /dev/null
+++ b/test-suite/build.rs
@@ -0,0 +1,8 @@
+extern crate rustc_version;
+use rustc_version::{version, Version};
+
+fn main() {
+ if version().unwrap() >= Version::parse("1.20.0").unwrap() {
+ println!(r#"cargo:rustc-cfg=feature="test-quoted-keys-in-macro""#);
+ }
+}
diff --git a/test-suite/tests/macros.rs b/test-suite/tests/macros.rs
new file mode 100644
index 0000000..439420d
--- /dev/null
+++ b/test-suite/tests/macros.rs
@@ -0,0 +1,286 @@
+#![recursion_limit = "128"]
+
+#[macro_use]
+extern crate toml;
+
+macro_rules! table {
+ ($($key:expr => $value:expr,)*) => {{
+ let mut table = toml::value::Table::new();
+ $(
+ table.insert($key.to_string(), $value.into());
+ )*
+ toml::Value::Table(table)
+ }};
+}
+
+macro_rules! array {
+ ($($element:expr,)*) => {{
+ let mut array = toml::value::Array::new();
+ $(
+ array.push($element.into());
+ )*
+ toml::Value::Array(array)
+ }};
+}
+
+macro_rules! datetime {
+ ($s:tt) => {
+ $s.parse::<toml::value::Datetime>().unwrap()
+ };
+}
+
+#[test]
+fn test_cargo_toml() {
+ // Simple sanity check of:
+ //
+ // - Ordinary tables
+ // - Inline tables
+ // - Inline arrays
+ // - String values
+ // - Table keys containing hyphen
+ // - Table headers containing hyphen
+ let actual = toml! {
+ [package]
+ name = "toml"
+ version = "0.4.5"
+ authors = ["Alex Crichton <alex@alexcrichton.com>"]
+
+ [badges]
+ travis-ci = { repository = "alexcrichton/toml-rs" }
+
+ [dependencies]
+ serde = "1.0"
+
+ [dev-dependencies]
+ serde_derive = "1.0"
+ serde_json = "1.0"
+ };
+
+ let expected = table! {
+ "package" => table! {
+ "name" => "toml".to_owned(),
+ "version" => "0.4.5".to_owned(),
+ "authors" => array! {
+ "Alex Crichton <alex@alexcrichton.com>".to_owned(),
+ },
+ },
+ "badges" => table! {
+ "travis-ci" => table! {
+ "repository" => "alexcrichton/toml-rs".to_owned(),
+ },
+ },
+ "dependencies" => table! {
+ "serde" => "1.0".to_owned(),
+ },
+ "dev-dependencies" => table! {
+ "serde_derive" => "1.0".to_owned(),
+ "serde_json" => "1.0".to_owned(),
+ },
+ };
+
+ assert_eq!(actual, expected);
+}
+
+#[test]
+fn test_array() {
+ // Copied from the TOML spec.
+ let actual = toml! {
+ [[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 expected = table! {
+ "fruit" => array! {
+ table! {
+ "name" => "apple",
+ "physical" => table! {
+ "color" => "red",
+ "shape" => "round",
+ },
+ "variety" => array! {
+ table! {
+ "name" => "red delicious",
+ },
+ table! {
+ "name" => "granny smith",
+ },
+ },
+ },
+ table! {
+ "name" => "banana",
+ "variety" => array! {
+ table! {
+ "name" => "plantain",
+ },
+ },
+ },
+ },
+ };
+
+ assert_eq!(actual, expected);
+}
+
+#[test]
+fn test_number() {
+ let actual = toml! {
+ positive = 1
+ negative = -1
+ table = { positive = 1, negative = -1 }
+ array = [ 1, -1 ]
+ };
+
+ let expected = table! {
+ "positive" => 1,
+ "negative" => -1,
+ "table" => table! {
+ "positive" => 1,
+ "negative" => -1,
+ },
+ "array" => array! {
+ 1,
+ -1,
+ },
+ };
+
+ assert_eq!(actual, expected);
+}
+
+#[test]
+fn test_datetime() {
+ let actual = toml! {
+ // Copied from the TOML spec.
+ odt1 = 1979-05-27T07:32:00Z
+ odt2 = 1979-05-27T00:32:00-07:00
+ odt3 = 1979-05-27T00:32:00.999999-07:00
+ ldt1 = 1979-05-27T07:32:00
+ ldt2 = 1979-05-27T00:32:00.999999
+ ld1 = 1979-05-27
+ lt1 = 07:32:00
+ lt2 = 00:32:00.999999
+
+ table = {
+ odt1 = 1979-05-27T07:32:00Z,
+ odt2 = 1979-05-27T00:32:00-07:00,
+ odt3 = 1979-05-27T00:32:00.999999-07:00,
+ ldt1 = 1979-05-27T07:32:00,
+ ldt2 = 1979-05-27T00:32:00.999999,
+ ld1 = 1979-05-27,
+ lt1 = 07:32:00,
+ lt2 = 00:32:00.999999,
+ }
+
+ array = [
+ 1979-05-27T07:32:00Z,
+ 1979-05-27T00:32:00-07:00,
+ 1979-05-27T00:32:00.999999-07:00,
+ 1979-05-27T07:32:00,
+ 1979-05-27T00:32:00.999999,
+ 1979-05-27,
+ 07:32:00,
+ 00:32:00.999999,
+ ]
+ };
+
+ let expected = table! {
+ "odt1" => datetime!("1979-05-27T07:32:00Z"),
+ "odt2" => datetime!("1979-05-27T00:32:00-07:00"),
+ "odt3" => datetime!("1979-05-27T00:32:00.999999-07:00"),
+ "ldt1" => datetime!("1979-05-27T07:32:00"),
+ "ldt2" => datetime!("1979-05-27T00:32:00.999999"),
+ "ld1" => datetime!("1979-05-27"),
+ "lt1" => datetime!("07:32:00"),
+ "lt2" => datetime!("00:32:00.999999"),
+
+ "table" => table! {
+ "odt1" => datetime!("1979-05-27T07:32:00Z"),
+ "odt2" => datetime!("1979-05-27T00:32:00-07:00"),
+ "odt3" => datetime!("1979-05-27T00:32:00.999999-07:00"),
+ "ldt1" => datetime!("1979-05-27T07:32:00"),
+ "ldt2" => datetime!("1979-05-27T00:32:00.999999"),
+ "ld1" => datetime!("1979-05-27"),
+ "lt1" => datetime!("07:32:00"),
+ "lt2" => datetime!("00:32:00.999999"),
+ },
+
+ "array" => array! {
+ datetime!("1979-05-27T07:32:00Z"),
+ datetime!("1979-05-27T00:32:00-07:00"),
+ datetime!("1979-05-27T00:32:00.999999-07:00"),
+ datetime!("1979-05-27T07:32:00"),
+ datetime!("1979-05-27T00:32:00.999999"),
+ datetime!("1979-05-27"),
+ datetime!("07:32:00"),
+ datetime!("00:32:00.999999"),
+ },
+ };
+
+ assert_eq!(actual, expected);
+}
+
+// This test requires rustc >= 1.20.
+#[test]
+#[cfg(feature = "test-quoted-keys-in-macro")]
+fn test_quoted_key() {
+ let actual = toml! {
+ "quoted" = true
+ table = { "quoted" = true }
+
+ [target."cfg(windows)".dependencies]
+ winapi = "0.2.8"
+ };
+
+ let expected = table! {
+ "quoted" => true,
+ "table" => table! {
+ "quoted" => true,
+ },
+ "target" => table! {
+ "cfg(windows)" => table! {
+ "dependencies" => table! {
+ "winapi" => "0.2.8",
+ },
+ },
+ },
+ };
+
+ assert_eq!(actual, expected);
+}
+
+#[test]
+fn test_empty() {
+ let actual = toml! {
+ empty_inline_table = {}
+ empty_inline_array = []
+
+ [empty_table]
+
+ [[empty_array]]
+ };
+
+ let expected = table! {
+ "empty_inline_table" => table! {},
+ "empty_inline_array" => array! {},
+ "empty_table" => table! {},
+ "empty_array" => array! {
+ table! {},
+ },
+ };
+
+ assert_eq!(actual, expected);
+}