summaryrefslogtreecommitdiffstats
path: root/third_party/rust/toml
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/toml
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/toml')
-rw-r--r--third_party/rust/toml/.cargo-checksum.json1
-rw-r--r--third_party/rust/toml/Cargo.toml33
-rw-r--r--third_party/rust/toml/LICENSE-APACHE201
-rw-r--r--third_party/rust/toml/LICENSE-MIT25
-rw-r--r--third_party/rust/toml/README.md31
-rw-r--r--third_party/rust/toml/examples/decode.rs56
-rw-r--r--third_party/rust/toml/examples/toml2json.rs51
-rw-r--r--third_party/rust/toml/src/datetime.rs425
-rw-r--r--third_party/rust/toml/src/de.rs1327
-rw-r--r--third_party/rust/toml/src/lib.rs168
-rw-r--r--third_party/rust/toml/src/ser.rs1714
-rw-r--r--third_party/rust/toml/src/tokens.rs620
-rw-r--r--third_party/rust/toml/src/value.rs946
-rw-r--r--third_party/rust/toml/tests/README.md1
-rw-r--r--third_party/rust/toml/tests/backcompat.rs19
-rw-r--r--third_party/rust/toml/tests/datetime.rs58
-rw-r--r--third_party/rust/toml/tests/display-tricky.rs49
-rw-r--r--third_party/rust/toml/tests/display.rs103
-rw-r--r--third_party/rust/toml/tests/formatting.rs55
-rw-r--r--third_party/rust/toml/tests/invalid-encoder-misc.rs14
-rw-r--r--third_party/rust/toml/tests/invalid-encoder/array-mixed-types-ints-and-floats.json15
-rw-r--r--third_party/rust/toml/tests/invalid-misc.rs17
-rw-r--r--third_party/rust/toml/tests/invalid.rs98
-rw-r--r--third_party/rust/toml/tests/invalid/array-mixed-types-arrays-and-ints.toml1
-rw-r--r--third_party/rust/toml/tests/invalid/array-mixed-types-ints-and-floats.toml1
-rw-r--r--third_party/rust/toml/tests/invalid/array-mixed-types-strings-and-ints.toml1
-rw-r--r--third_party/rust/toml/tests/invalid/datetime-malformed-no-leads.toml1
-rw-r--r--third_party/rust/toml/tests/invalid/datetime-malformed-no-secs.toml1
-rw-r--r--third_party/rust/toml/tests/invalid/datetime-malformed-no-t.toml1
-rw-r--r--third_party/rust/toml/tests/invalid/datetime-malformed-with-milli.toml1
-rw-r--r--third_party/rust/toml/tests/invalid/duplicate-key-table.toml5
-rw-r--r--third_party/rust/toml/tests/invalid/duplicate-keys.toml2
-rw-r--r--third_party/rust/toml/tests/invalid/duplicate-tables.toml2
-rw-r--r--third_party/rust/toml/tests/invalid/empty-implicit-table.toml1
-rw-r--r--third_party/rust/toml/tests/invalid/empty-table.toml1
-rw-r--r--third_party/rust/toml/tests/invalid/float-no-leading-zero.toml2
-rw-r--r--third_party/rust/toml/tests/invalid/float-no-trailing-digits.toml2
-rw-r--r--third_party/rust/toml/tests/invalid/key-after-array.toml1
-rw-r--r--third_party/rust/toml/tests/invalid/key-after-table.toml1
-rw-r--r--third_party/rust/toml/tests/invalid/key-empty.toml1
-rw-r--r--third_party/rust/toml/tests/invalid/key-hash.toml1
-rw-r--r--third_party/rust/toml/tests/invalid/key-newline.toml2
-rw-r--r--third_party/rust/toml/tests/invalid/key-open-bracket.toml1
-rw-r--r--third_party/rust/toml/tests/invalid/key-single-open-bracket.toml1
-rw-r--r--third_party/rust/toml/tests/invalid/key-space.toml1
-rw-r--r--third_party/rust/toml/tests/invalid/key-start-bracket.toml3
-rw-r--r--third_party/rust/toml/tests/invalid/key-two-equals.toml1
-rw-r--r--third_party/rust/toml/tests/invalid/string-bad-byte-escape.toml1
-rw-r--r--third_party/rust/toml/tests/invalid/string-bad-escape.toml1
-rw-r--r--third_party/rust/toml/tests/invalid/string-byte-escapes.toml1
-rw-r--r--third_party/rust/toml/tests/invalid/string-no-close.toml1
-rw-r--r--third_party/rust/toml/tests/invalid/table-array-implicit.toml14
-rw-r--r--third_party/rust/toml/tests/invalid/table-array-malformed-bracket.toml2
-rw-r--r--third_party/rust/toml/tests/invalid/table-array-malformed-empty.toml2
-rw-r--r--third_party/rust/toml/tests/invalid/table-empty.toml1
-rw-r--r--third_party/rust/toml/tests/invalid/table-nested-brackets-close.toml2
-rw-r--r--third_party/rust/toml/tests/invalid/table-nested-brackets-open.toml2
-rw-r--r--third_party/rust/toml/tests/invalid/table-whitespace.toml1
-rw-r--r--third_party/rust/toml/tests/invalid/table-with-pound.toml2
-rw-r--r--third_party/rust/toml/tests/invalid/text-after-array-entries.toml4
-rw-r--r--third_party/rust/toml/tests/invalid/text-after-integer.toml1
-rw-r--r--third_party/rust/toml/tests/invalid/text-after-string.toml1
-rw-r--r--third_party/rust/toml/tests/invalid/text-after-table.toml1
-rw-r--r--third_party/rust/toml/tests/invalid/text-before-array-separator.toml4
-rw-r--r--third_party/rust/toml/tests/invalid/text-in-array.toml5
-rw-r--r--third_party/rust/toml/tests/parser.rs495
-rw-r--r--third_party/rust/toml/tests/pretty.rs308
-rw-r--r--third_party/rust/toml/tests/serde.rs578
-rw-r--r--third_party/rust/toml/tests/tables-last.rs30
-rw-r--r--third_party/rust/toml/tests/valid.rs249
-rw-r--r--third_party/rust/toml/tests/valid/array-empty.json11
-rw-r--r--third_party/rust/toml/tests/valid/array-empty.toml1
-rw-r--r--third_party/rust/toml/tests/valid/array-nospaces.json10
-rw-r--r--third_party/rust/toml/tests/valid/array-nospaces.toml1
-rw-r--r--third_party/rust/toml/tests/valid/arrays-hetergeneous.json19
-rw-r--r--third_party/rust/toml/tests/valid/arrays-hetergeneous.toml1
-rw-r--r--third_party/rust/toml/tests/valid/arrays-nested.json13
-rw-r--r--third_party/rust/toml/tests/valid/arrays-nested.toml1
-rw-r--r--third_party/rust/toml/tests/valid/arrays.json34
-rw-r--r--third_party/rust/toml/tests/valid/arrays.toml8
-rw-r--r--third_party/rust/toml/tests/valid/bool.json4
-rw-r--r--third_party/rust/toml/tests/valid/bool.toml2
-rw-r--r--third_party/rust/toml/tests/valid/comments-everywhere.json12
-rw-r--r--third_party/rust/toml/tests/valid/comments-everywhere.toml24
-rw-r--r--third_party/rust/toml/tests/valid/datetime-truncate.json6
-rw-r--r--third_party/rust/toml/tests/valid/datetime-truncate.toml1
-rw-r--r--third_party/rust/toml/tests/valid/datetime.json3
-rw-r--r--third_party/rust/toml/tests/valid/datetime.toml1
-rw-r--r--third_party/rust/toml/tests/valid/empty.json1
-rw-r--r--third_party/rust/toml/tests/valid/empty.toml0
-rw-r--r--third_party/rust/toml/tests/valid/example-bom.toml5
-rw-r--r--third_party/rust/toml/tests/valid/example-v0.3.0.json1
-rw-r--r--third_party/rust/toml/tests/valid/example-v0.3.0.toml182
-rw-r--r--third_party/rust/toml/tests/valid/example-v0.4.0.json1
-rw-r--r--third_party/rust/toml/tests/valid/example-v0.4.0.toml235
-rw-r--r--third_party/rust/toml/tests/valid/example.json14
-rw-r--r--third_party/rust/toml/tests/valid/example.toml5
-rw-r--r--third_party/rust/toml/tests/valid/example2.json1
-rw-r--r--third_party/rust/toml/tests/valid/example2.toml47
-rw-r--r--third_party/rust/toml/tests/valid/float.json4
-rw-r--r--third_party/rust/toml/tests/valid/float.toml2
-rw-r--r--third_party/rust/toml/tests/valid/hard_example.json1
-rw-r--r--third_party/rust/toml/tests/valid/hard_example.toml33
-rw-r--r--third_party/rust/toml/tests/valid/implicit-and-explicit-after.json10
-rw-r--r--third_party/rust/toml/tests/valid/implicit-and-explicit-after.toml5
-rw-r--r--third_party/rust/toml/tests/valid/implicit-and-explicit-before.json10
-rw-r--r--third_party/rust/toml/tests/valid/implicit-and-explicit-before.toml5
-rw-r--r--third_party/rust/toml/tests/valid/implicit-groups.json9
-rw-r--r--third_party/rust/toml/tests/valid/implicit-groups.toml2
-rw-r--r--third_party/rust/toml/tests/valid/integer.json4
-rw-r--r--third_party/rust/toml/tests/valid/integer.toml2
-rw-r--r--third_party/rust/toml/tests/valid/key-equals-nospace.json3
-rw-r--r--third_party/rust/toml/tests/valid/key-equals-nospace.toml1
-rw-r--r--third_party/rust/toml/tests/valid/key-quote-newline.json3
-rw-r--r--third_party/rust/toml/tests/valid/key-quote-newline.toml1
-rw-r--r--third_party/rust/toml/tests/valid/key-space.json3
-rw-r--r--third_party/rust/toml/tests/valid/key-space.toml1
-rw-r--r--third_party/rust/toml/tests/valid/key-special-chars.json5
-rw-r--r--third_party/rust/toml/tests/valid/key-special-chars.toml1
-rw-r--r--third_party/rust/toml/tests/valid/key-with-pound.json3
-rw-r--r--third_party/rust/toml/tests/valid/key-with-pound.toml1
-rw-r--r--third_party/rust/toml/tests/valid/long-float.json4
-rw-r--r--third_party/rust/toml/tests/valid/long-float.toml2
-rw-r--r--third_party/rust/toml/tests/valid/long-integer.json4
-rw-r--r--third_party/rust/toml/tests/valid/long-integer.toml2
-rw-r--r--third_party/rust/toml/tests/valid/multiline-string.json30
-rw-r--r--third_party/rust/toml/tests/valid/multiline-string.toml23
-rw-r--r--third_party/rust/toml/tests/valid/raw-multiline-string.json14
-rw-r--r--third_party/rust/toml/tests/valid/raw-multiline-string.toml9
-rw-r--r--third_party/rust/toml/tests/valid/raw-string.json30
-rw-r--r--third_party/rust/toml/tests/valid/raw-string.toml7
-rw-r--r--third_party/rust/toml/tests/valid/string-empty.json6
-rw-r--r--third_party/rust/toml/tests/valid/string-empty.toml1
-rw-r--r--third_party/rust/toml/tests/valid/string-escapes.json50
-rw-r--r--third_party/rust/toml/tests/valid/string-escapes.toml12
-rw-r--r--third_party/rust/toml/tests/valid/string-simple.json6
-rw-r--r--third_party/rust/toml/tests/valid/string-simple.toml1
-rw-r--r--third_party/rust/toml/tests/valid/string-with-pound.json7
-rw-r--r--third_party/rust/toml/tests/valid/string-with-pound.toml2
-rw-r--r--third_party/rust/toml/tests/valid/table-array-implicit.json7
-rw-r--r--third_party/rust/toml/tests/valid/table-array-implicit.toml2
-rw-r--r--third_party/rust/toml/tests/valid/table-array-many.json16
-rw-r--r--third_party/rust/toml/tests/valid/table-array-many.toml11
-rw-r--r--third_party/rust/toml/tests/valid/table-array-nest-no-keys.json14
-rw-r--r--third_party/rust/toml/tests/valid/table-array-nest-no-keys.toml6
-rw-r--r--third_party/rust/toml/tests/valid/table-array-nest.json18
-rw-r--r--third_party/rust/toml/tests/valid/table-array-nest.toml17
-rw-r--r--third_party/rust/toml/tests/valid/table-array-one.json8
-rw-r--r--third_party/rust/toml/tests/valid/table-array-one.toml3
-rw-r--r--third_party/rust/toml/tests/valid/table-empty.json3
-rw-r--r--third_party/rust/toml/tests/valid/table-empty.toml1
-rw-r--r--third_party/rust/toml/tests/valid/table-multi-empty.json5
-rw-r--r--third_party/rust/toml/tests/valid/table-multi-empty.toml5
-rw-r--r--third_party/rust/toml/tests/valid/table-sub-empty.json3
-rw-r--r--third_party/rust/toml/tests/valid/table-sub-empty.toml2
-rw-r--r--third_party/rust/toml/tests/valid/table-whitespace.json3
-rw-r--r--third_party/rust/toml/tests/valid/table-whitespace.toml1
-rw-r--r--third_party/rust/toml/tests/valid/table-with-pound.json5
-rw-r--r--third_party/rust/toml/tests/valid/table-with-pound.toml2
-rw-r--r--third_party/rust/toml/tests/valid/unicode-escape.json5
-rw-r--r--third_party/rust/toml/tests/valid/unicode-escape.toml3
-rw-r--r--third_party/rust/toml/tests/valid/unicode-literal.json3
-rw-r--r--third_party/rust/toml/tests/valid/unicode-literal.toml1
163 files changed, 8875 insertions, 0 deletions
diff --git a/third_party/rust/toml/.cargo-checksum.json b/third_party/rust/toml/.cargo-checksum.json
new file mode 100644
index 0000000000..abdb9689e8
--- /dev/null
+++ b/third_party/rust/toml/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"Cargo.toml":"e3606897c2303508e3091468afb2afd22828bb2fa971d91b522c9ddf4832f100","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"cdb1e357027407997a4666c3cb7d4db6676ef151289dbc49b1ab6df744e06047","examples/decode.rs":"91f5c35b94257f184d85183036a6d6d7aafbc22672b99446105b3cffdf3e7ff4","examples/toml2json.rs":"eaa9ad85ec19714f6437048ae28b844966225a17055d1a37878f41bcbc2c5e47","src/datetime.rs":"770bf5947748c8d563174dd8b4e4dee35fbd3171ea81c445c554d642c524f302","src/de.rs":"47a8e1ecee22068dc9d381f8ec05a47c644af49dbbb160fd3cb7a7b86d664adf","src/lib.rs":"d618368aac8d3ee5873eab36a3672cd88d72a121e26b5fd537cd3548293a1ff6","src/ser.rs":"11d8b67e9f84583d48c1a561c3c8e080b71be0ff479243ddbae28d5da797d261","src/tokens.rs":"ccc58d254a4a5666573fe42cf21b88621292c59858067169f102f2533cea9372","src/value.rs":"87a745d1eeba0ce4dc79451d0640d9a6145dec50dded1748e9a167719217be2a","tests/README.md":"3e7d07d64c34e117c3d862ee12e4947af8c133fb0b53f6f0de43cc3bfa5057b8","tests/backcompat.rs":"1d40b13ed379832d31c5d1ae35b9a3c9d9f66ab0ec40e7c16f46139d2fb4c35e","tests/datetime.rs":"c76e7f60514a748c56e09c369d888ea22d8e600b0200275da62dc8ad47ce9761","tests/display-tricky.rs":"e0ffa4f992ac1b3f6c88cf2dc299b605c6352ca3dcf18a4253fdb9b98ef410fd","tests/display.rs":"4472cf88c0ccc58d47b109570f8b19a218ac274ab5293df9e67db2db4f6e5945","tests/formatting.rs":"8941f2fe843d5294ef3dc7ff8aaa3db325d4ec368f1d138f8c28ab4e18c8261b","tests/invalid-encoder-misc.rs":"7a29c89dd664276cae8869d2394b0e7cf29f7a6d298cd8566fa1fd087dd635f4","tests/invalid-encoder/array-mixed-types-ints-and-floats.json":"206d2a353153efbcee74e7ee7b0f852e7a948cfb88417f74607b3ad6f020e373","tests/invalid-misc.rs":"70e14c859e67ed7e03ec426e5cb25a306a893cf85042e0be4fdbbc423b076887","tests/invalid.rs":"f6798f22652c2c09c23b066af33b402319399f7106ef5c9fda1a5222af522856","tests/invalid/array-mixed-types-arrays-and-ints.toml":"c1547b686357c7a865e333a7ce7eed1e30743ebc4daac8ea6148bdd84b7e4dc7","tests/invalid/array-mixed-types-ints-and-floats.toml":"baa235fc168869716680955fd5bdafab30b6fa9b3d09185086261a56ef12960e","tests/invalid/array-mixed-types-strings-and-ints.toml":"4440057ed90d4461db0be55cfd71299cad96b601d5faec2140666242f6fde147","tests/invalid/datetime-malformed-no-leads.toml":"1fa6bf72ea0b96be835ac031408f7acfd0b8d18d5f54b4c0fce8136aad27d22e","tests/invalid/datetime-malformed-no-secs.toml":"b9c30b7fa1847c80e381d68cc79834eefbb8813cd0eff465fb7cbe0733df429b","tests/invalid/datetime-malformed-no-t.toml":"490a46d2e707ef4f87987ec12a22afa5a71c97c5eaa0f4201a174c47846c6a4a","tests/invalid/datetime-malformed-with-milli.toml":"62bfd0a6645bcb3f78211523e5673a3d1fa726d9e942c1643df243d6fba474c8","tests/invalid/duplicate-key-table.toml":"a896ea2180d16fcdd4f6c30aa27529b5b29e7e195a4e7a9944343028783602e9","tests/invalid/duplicate-keys.toml":"4bb0a65c22e7704893a2a51334eb2518af702850ef59aeecd5226e7b26bc07ec","tests/invalid/duplicate-tables.toml":"23b16ce26e1155ee6bf1fff559701dce86475c6a2878536b61f6b7e68be340de","tests/invalid/empty-implicit-table.toml":"d6026110dc0dee7babd69592218819328caa425f48cc879e895b29e48550c06c","tests/invalid/empty-table.toml":"37517e5f3dc66819f61f5a7bb8ace1921282415f10551d2defa5c3eb0985b570","tests/invalid/float-no-leading-zero.toml":"159654461094c938574ba2d2d09baa3d3c387dd6ed024fd411530c0573a1ec42","tests/invalid/float-no-trailing-digits.toml":"64e4f336186cd096be2804532dbd694dd50ea551d292a9cdbf0bef2abf227101","tests/invalid/key-after-array.toml":"314af33770170b53bf2ec3be43ea1609d981c81d62c968052499b85ed54ccce8","tests/invalid/key-after-table.toml":"ed0dcf38f003d184dd18d1518702da0115cbfb05a5a28cbcf42de2f9bdee05fa","tests/invalid/key-empty.toml":"4303477abf4c0b920b42e96edd61caecf9c1f2d5d97f56af876854cd725aff3c","tests/invalid/key-hash.toml":"cd2a2eba6032d32c829633d3cd2778aeba81f5ff554e69f62af6557d1dc712f6","tests/invalid/key-newline.toml":"06a313a6bd70c2db6a1f5bda479d854d8f87e037e3cabf18fb5db822466ffcac","tests/invalid/key-open-bracket.toml":"52dea939022949290e3a19f1291d15605429344dce3cd1aa1f1568ecad8ca365","tests/invalid/key-single-open-bracket.toml":"245843abef9e72e7efac30138a994bf6301e7e1d7d7042a33d42e863d2638811","tests/invalid/key-space.toml":"b4e336d07c27fb3d0f0a6e50b733e1546202dfd58aaf2f7956f56fd6f075b0a1","tests/invalid/key-start-bracket.toml":"3bd3748a9df1d25ab2661330a3da187bd4da3958292bbf0e8b59d7963634dd87","tests/invalid/key-two-equals.toml":"3ac0c4e339d47c86e57564e43147b772ae83933b78083dc78d0ef77e231df3f1","tests/invalid/string-bad-byte-escape.toml":"c665dcec7c02f442c4fdc80423698eed2376ce65779cf855371772293bec2927","tests/invalid/string-bad-escape.toml":"eeca691fbba3d270f58ae2953d2d1369a773e619e39d8c11f38d6bf6f8472e82","tests/invalid/string-byte-escapes.toml":"4a4604b32971de3a252cd01d2997b450972c3ec9030cf22a070d49c57f050da4","tests/invalid/string-no-close.toml":"bb2eaf96eb9f83a52bd0772abb313060a06b94f650efeb45edce774622686882","tests/invalid/table-array-implicit.toml":"9b841ea64d68be4deb54f67fc807b05fd235452ee563ffa7de69dbca64b2f7dd","tests/invalid/table-array-malformed-bracket.toml":"164f641b2628bf04f8202d9746a360a4a243faca1408dc2ecd0c0fdd2d1c2c27","tests/invalid/table-array-malformed-empty.toml":"56ca2a15019cf5c3555041a191f983dc72b1678f0de0afd1a7b8f46ed7970420","tests/invalid/table-empty.toml":"37517e5f3dc66819f61f5a7bb8ace1921282415f10551d2defa5c3eb0985b570","tests/invalid/table-nested-brackets-close.toml":"991e1210f81e24abcd735988f0d960d4ee94d2ec3b133c6fea6e24932d91c507","tests/invalid/table-nested-brackets-open.toml":"8fb569fc90fa606ae94708ee2bf205bff8db8a023624b3e52ef6b2c1a98ba1c6","tests/invalid/table-whitespace.toml":"2c2db1259adc641df0459e896d349d3db60965d5368d5c8ed50aedd3bc88f040","tests/invalid/table-with-pound.toml":"d8070437f07bd115ac8006c61e286401bd3be88728a62264796e757121750ecd","tests/invalid/text-after-array-entries.toml":"2530cace13292313983b90b01d63e4b8ac484809e7ef0ac79904923573eda7ec","tests/invalid/text-after-integer.toml":"6168ed823a135b8914956b04307aeec2347b10eb1aa79008406d7b547cbde682","tests/invalid/text-after-string.toml":"1771987dd679e1cc181cf53406ba313fdc3413a081d17a93da6041bf6ccccf5e","tests/invalid/text-after-table.toml":"f27ae56bb0b42d3af4c813392857afdfeb4bf8ab77ff896cd93ba32cf1a21b26","tests/invalid/text-before-array-separator.toml":"192d28699573abbdc521797576d4885adf756336c3e76971f10270603c957464","tests/invalid/text-in-array.toml":"50d7b16d7a03d470f1a907eebfeb156d0c696e6f9a8c734a5e4caa2365f54654","tests/parser.rs":"6efa6a073adc041e1347fbaf30225359253a8f878ce328cdb74af3e83f60163e","tests/pretty.rs":"82b69142f0b438b0d76753d1a70ecb5c9d78df9f7c05a75e1be36eef11679960","tests/serde.rs":"94a119191110c4e57ef44f75022c0473fa1ffb9ed4242cd6320cbf96a7a3234a","tests/tables-last.rs":"593c60ea4727f08168a4a016ef876283d50f78888c5eab7d407092681712f912","tests/valid.rs":"ac398cac034eb7b6d320eb97b383a562e4924a22ef8d29fe43417ad95b17877d","tests/valid/array-empty.json":"4ed46e8aa3586a7ddd9097cda38919699860052f188880de18e12d4acbee5307","tests/valid/array-empty.toml":"769faa42a690b02ad1b573277f0d793a4a38a7ecf30b9db67bf9836fe2b7778c","tests/valid/array-nospaces.json":"7c82b474797871488c2b522e9b852772a78c681a86900f780f7a0be4f901e1ec","tests/valid/array-nospaces.toml":"01fba63551b1051f7e117c9551c488b293bd3cd4745cbeec6b3709c32f2a3a64","tests/valid/arrays-hetergeneous.json":"dac4702412c90d5ddd6b898c3657c71d782d0aca46e5b9ca19cc694d3e226c0f","tests/valid/arrays-hetergeneous.toml":"361ae2e8470b47b12b336eb61eedd09abb20e216fbeb582b46d16a2831adda4d","tests/valid/arrays-nested.json":"34d6f8fd770f0728f38dbf0d686bed2c218bc16da978290c0208d3bf3704bdec","tests/valid/arrays-nested.toml":"a5941a01a2ba2fa179a3885039a5f81ca6c9876b2e8bea7b880563238be9f004","tests/valid/arrays.json":"315fff195a7d4c80e867e1f14c12a23e36dcc666e8de36138249b15e99bdd4dd","tests/valid/arrays.toml":"2d3c91b77d4f6a65a6a5a2c5ad521dbe86cc2f0ff389dfe58e8c34275cdc35c7","tests/valid/bool.json":"bb608b912fe4d89cb2186da957c10951780575bb34b2f43305335c745eff049c","tests/valid/bool.toml":"3c06ad4dce7430326f5f867105b2e9382def90cccb3ecb69f657c0b88974ab04","tests/valid/comments-everywhere.json":"800f8efd86d1bab4f91f0e367da52a2465e1480387df892561ed8948fd1a38c3","tests/valid/comments-everywhere.toml":"8636108c34f50d45244b142d342234b7535e01fba2db2d2ffed3430223009cea","tests/valid/datetime-truncate.json":"5ca5dbb56bb00c4cfc4c10aeb5607160284593bad3bf6dc988425a4f1f53dfbc","tests/valid/datetime-truncate.toml":"55ee01485f3e537fb6b2d3977f656c11543e53adb4e79c6fa8f2a953183c0f7f","tests/valid/datetime.json":"94f130c3b2a5f30c625a3a3168b9dfe52aa109b470c4e077f352b3dd79382a69","tests/valid/datetime.toml":"4e1b71ba31a1feef80a1e436225aa9c5d291bf780f558e7cfa76998fe2a29e08","tests/valid/empty.json":"ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356","tests/valid/empty.toml":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","tests/valid/example-bom.toml":"50f3607be97de2f894ccd41a29d3a8a1b1f430982e5ab5bf43047874837f6a42","tests/valid/example-v0.3.0.json":"ec02332eb59dab93a50560771e4b01b972a29d93b6dca8954728c0631e1799a0","tests/valid/example-v0.3.0.toml":"aba9349588c1ba6af2a3ad0c1db2c3d311dca66b64f654480340a0823dfd4546","tests/valid/example-v0.4.0.json":"8b967b246ca2383172eaaecf790b2115a18020f63c70d0848d1dc25fc1fed5a9","tests/valid/example-v0.4.0.toml":"47ee4c4cd83637cd09aca6518f88c8ed56acc2b3022e5ea53d9121e5d35335ed","tests/valid/example.json":"c57fabb1be0ae7ed140fe7ae5082051571c85180204b8ae8d8da1e86133278c9","tests/valid/example.toml":"6f02b2a52ea63c70b629e41e06a8eb1eb4aab359ab966a7a397b248e13849c9c","tests/valid/example2.json":"de89432e78f0a074aae54650fedf151ceca3b0ccb148b8a66e18e2ed68024ba2","tests/valid/example2.toml":"c389eeb485fb7cb9445d617a9a0f8ba70049f08d66cf6b6f2a9a986574295de1","tests/valid/float.json":"9676c13fef00a01bc729456bfe27f1b24a1bd059c9a5913bb0b0401e976a0aab","tests/valid/float.toml":"b6784f554aa38bb210f0905c3bafdfae6db723a4f53288fb07febc66451bbc2d","tests/valid/hard_example.json":"8d170e73e156b8b6be559246880e9cb6a79b36f63d14bc97e3bdf2f2091e7a17","tests/valid/hard_example.toml":"cd3b89f8917a44f944b12fe47f69f86bb39f17db85d1a081bf0c134831eb90f9","tests/valid/implicit-and-explicit-after.json":"6dcaeaf8ee3479bf2cd5c14eb58970757175aaefab014bce9acb0b85e7bf9dd0","tests/valid/implicit-and-explicit-after.toml":"0599b16822764cdb1c3d3cf53f71186b97afc9f60f8d58358a4a89fe4d9477c3","tests/valid/implicit-and-explicit-before.json":"6dcaeaf8ee3479bf2cd5c14eb58970757175aaefab014bce9acb0b85e7bf9dd0","tests/valid/implicit-and-explicit-before.toml":"33435dddb68144b652ca5f5e0c4c53e4f7b3ca0166f9b944eda55f8d76ed2871","tests/valid/implicit-groups.json":"fc2bb43ec20c8c9148c8a70490b3a054506932c41687222ea11faae47eafb723","tests/valid/implicit-groups.toml":"248b3e8272ec43ce4af872981acde10628eeae73537ed6763a1f4245f5a9610c","tests/valid/integer.json":"0fc2d0cb1fb46d7805c1007b1fa4c46a65b273e56ae9d751df686e81d3a3354f","tests/valid/integer.toml":"74b964eb9561be6aa7266f6034cee1cd0657bdab8043a5ec9da33d9b184345da","tests/valid/key-equals-nospace.json":"b9878ee3585d1b48710a4bc09f2163b910ea71a2bfdaa8f1da68e599e8b30f47","tests/valid/key-equals-nospace.toml":"24cab0d01b67b184d0a737de3a5b5d47b8b69b36203273296d5ef763f7fdcf68","tests/valid/key-quote-newline.json":"dc077cc852761ffbab2cb23486c023dae2e07410c76fcb507a40d96ed8922e06","tests/valid/key-quote-newline.toml":"a224611bfce786f7d04a3a6abda62fdff79bc6fd2cb94263334d135d46e0143b","tests/valid/key-space.json":"30be539b01420be5cedc9078b88c3279bbef7c0bdde36ba8249ed8906112d5c7","tests/valid/key-space.toml":"9e9459b8cfebc404cf93d77c2d4082cadcd57165a2f9ce9cb35d1d12dc94a8c0","tests/valid/key-special-chars.json":"8bbebb20660d93efa73707bdb09e87a43c2b31c18f13df6388c701a1bc7cab8c","tests/valid/key-special-chars.toml":"c6cb0ba12d32f03cda4206097a1edb27cd154215d72e1c5791cc4f8dff2270b3","tests/valid/key-with-pound.json":"ea4dd79d0ad2a824bcce5c4c194d7fae82a7584a2ab7c0d83d6ddaae6130973e","tests/valid/key-with-pound.toml":"c334f676e19c01b96111277248654358cc8222fd0639aecaf429407e63b6a2dc","tests/valid/long-float.json":"7e103f56e490aa1b1fe5a762000ceb1f8c599f7d81aa215c90f5da41ab4ba6db","tests/valid/long-float.toml":"4d23f706f2a0d241840f6ea78657820c9c7b904c0c3c16828f8cc2574d7c8761","tests/valid/long-integer.json":"9ed7976639f0c2cd7f112584e2f5d272e92569be7135ea5bb9ba597abaff0767","tests/valid/long-integer.toml":"309f94be7ff5fd6f6dedbd257a1e6c171cb71aa74409ff3f8babda951f89d687","tests/valid/multiline-string.json":"3d67a8b992b85e9a2e58b77a1b76dc29745a9c2b4a362ad517786fed541948d7","tests/valid/multiline-string.toml":"7d6650009eb31a03d5b40b20712ef0157e9b787d9c966e66c38873a34e3b861c","tests/valid/raw-multiline-string.json":"4c95e34497433168cac25eb4132485c3bd13c35cad9d13d7becf7f90469dacca","tests/valid/raw-multiline-string.toml":"c724151024ccde432e0ec0d4ba60a5320d77841008116324c39516b8cbb94f4d","tests/valid/raw-string.json":"19268797aff8dfa28437d6ed8f9d813035f6eee50aade5fc774ba12b3290216b","tests/valid/raw-string.toml":"16510e05d477a0856ebaf38cacd0e9e18f02ab63ac7bd1a2eabbaa47a54d0e49","tests/valid/string-empty.json":"ece7d01326742a46e37d6404175118c0a91a2494f7ba2290bbc1d6f990ddb65b","tests/valid/string-empty.toml":"251e9e4052ede79f6b2462e71f73e0b7c9f5927484f6f77f0cd8b3c839b0c13b","tests/valid/string-escapes.json":"3d516f03cf94d5b5ee6b0887b8d37fdf21152752f049f5922a24adaacb5b8c35","tests/valid/string-escapes.toml":"86b1569e10fec91301709ad747012f0f42395050a2343b42aca450e001120f7a","tests/valid/string-simple.json":"622676e347676cce9f9733210acbd8056ce77a0588772ffd6efb05bb4e81b571","tests/valid/string-simple.toml":"ae74db09acea3be2ccae7f854f7b6f7c874ace9d4d87bf2f437b059a7d38a464","tests/valid/string-with-pound.json":"458a0add71536c1df5e1ed3ee5483c6eb48578abce0b0ebcdf75ea20d41ed6f4","tests/valid/string-with-pound.toml":"1aee397830d9ad2a93d41ee9c435acdbfef3758d1bb7c48bca7424fbbec89466","tests/valid/table-array-implicit.json":"3f7d3cdb468de67bc183162805d9c753ef5772f6f363ac2a26598387a5d991ea","tests/valid/table-array-implicit.toml":"66bcb030899a95e9a25ec44b7c9291b02f80ecbc324061cf1cd93223a2919f21","tests/valid/table-array-many.json":"3f21243eeb71ca3e5657a43559c806e12e3833e9f74c43c0c12aad9b0c853e4c","tests/valid/table-array-many.toml":"8d8ea546f954a81ca149a02147ae5f4bf075151cfcd530e62dcf05a04d843ffb","tests/valid/table-array-nest-no-keys.json":"a93ed1e96a8c52848e65a2f79c0e5c0627fbc62bd5f3e2557fdd0d42c303e7da","tests/valid/table-array-nest-no-keys.toml":"9cfcd72b7d166b4ae77a7be456017f749f375ad562475802e22dc1a57b503dc7","tests/valid/table-array-nest.json":"0a987d2bf1d5bc85f5c9433f23d389063600682a68538b6e57938a3c572959e4","tests/valid/table-array-nest.toml":"71b9c753bf773f232ac71cb2469a54ee0110ff137829045421edd7c5a64d6b6a","tests/valid/table-array-one.json":"7dc0ea3f7f843f7dc7443e68af43a1e5130a5fbae8a27fb02d8d92fa2487888e","tests/valid/table-array-one.toml":"4c478aea2dd7dfcfda682503b49e610f0fa4ce85a3b3cd0bc9041d4959e3626a","tests/valid/table-empty.json":"11e43e212d87b3b2547a5f2541f4091a3d2f6ba00b2a2004b07e02734e927ea7","tests/valid/table-empty.toml":"24d4941e67d5965d270eaebdb9816b994311e0f2f0e79ef6bb626f362c52842e","tests/valid/table-multi-empty.json":"3a2c82201a0447304afb23fb48ee961b4bd812fca3e1b061cc033e7e2bfb976c","tests/valid/table-multi-empty.toml":"886c9c4cc2d691816ed3fa404cb9d362b0511eb8c389a71419a858abb26f83df","tests/valid/table-sub-empty.json":"85cca6d48a5993c4f207c21ed96652af4f50b6936b0807659c75317c1763b6db","tests/valid/table-sub-empty.toml":"ae92e90a806ffefcbf8cda83cb82acf7448f75efa50dcfb5e2384632d36471b3","tests/valid/table-whitespace.json":"ad84ac49a6d13f7c4a8af0e1e71fd7ff2a446aa16a34c21a809a0850dfa76e73","tests/valid/table-whitespace.toml":"2f15dafb263d2771671db299f6202b4b78d293aec1ded7641ec7eb1cb024b52c","tests/valid/table-with-pound.json":"151e76606efe77500cbb0aa8fcf8ccfadb124d533bb79a9caa62e937b826e676","tests/valid/table-with-pound.toml":"a1f86c2e3789cc89500ec1d5eac2ec0bdb94bf445fddc3cab558b5228f3aba56","tests/valid/unicode-escape.json":"ddfc662f25712f1ebfc7b93b839405dfca56fc43dcde3276ad8b1ea9fdcdcc4d","tests/valid/unicode-escape.toml":"d12be96b9316b2092026be060e0008cb67a81472dccd9eab7914c7252a840608","tests/valid/unicode-literal.json":"1dd42756384b954955815dc3e906db64b4cd2c0c094f9b3c86633d1652d6d79d","tests/valid/unicode-literal.toml":"bffc6c3d4757de31d0cbfd7b8dc591edd2910fe8a4e1c46bbee422dddc841003"},"package":"a7540f4ffc193e0d3c94121edb19b055670d369f77d5804db11ae053a45b6e7e"} \ No newline at end of file
diff --git a/third_party/rust/toml/Cargo.toml b/third_party/rust/toml/Cargo.toml
new file mode 100644
index 0000000000..da921ce456
--- /dev/null
+++ b/third_party/rust/toml/Cargo.toml
@@ -0,0 +1,33 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g. crates.io) dependencies
+#
+# If you believe there's an error in this file please file an
+# issue against the rust-lang/cargo repository. If you're
+# editing this file be aware that the upstream Cargo.toml
+# will likely look very different (and much more reasonable)
+
+[package]
+name = "toml"
+version = "0.4.5"
+authors = ["Alex Crichton <alex@alexcrichton.com>"]
+description = "A native Rust encoder and decoder of TOML-formatted files and streams. Provides\nimplementations of the standard Serialize/Deserialize traits for TOML data to\nfacilitate deserializing and serializing Rust structures.\n"
+homepage = "https://github.com/alexcrichton/toml-rs"
+documentation = "https://docs.rs/toml"
+readme = "README.md"
+keywords = ["encoding"]
+categories = ["config", "encoding", "parser-implementations"]
+license = "MIT/Apache-2.0"
+repository = "https://github.com/alexcrichton/toml-rs"
+[dependencies.serde]
+version = "1.0"
+[dev-dependencies.serde_derive]
+version = "1.0"
+
+[dev-dependencies.serde_json]
+version = "1.0"
+[badges.travis-ci]
+repository = "alexcrichton/toml-rs"
diff --git a/third_party/rust/toml/LICENSE-APACHE b/third_party/rust/toml/LICENSE-APACHE
new file mode 100644
index 0000000000..16fe87b06e
--- /dev/null
+++ b/third_party/rust/toml/LICENSE-APACHE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/third_party/rust/toml/LICENSE-MIT b/third_party/rust/toml/LICENSE-MIT
new file mode 100644
index 0000000000..39e0ed6602
--- /dev/null
+++ b/third_party/rust/toml/LICENSE-MIT
@@ -0,0 +1,25 @@
+Copyright (c) 2014 Alex Crichton
+
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/third_party/rust/toml/README.md b/third_party/rust/toml/README.md
new file mode 100644
index 0000000000..47bb8bb5ac
--- /dev/null
+++ b/third_party/rust/toml/README.md
@@ -0,0 +1,31 @@
+# toml-rs
+
+[![Build Status](https://travis-ci.org/alexcrichton/toml-rs.svg?branch=master)](https://travis-ci.org/alexcrichton/toml-rs)
+[![Coverage Status](https://coveralls.io/repos/alexcrichton/toml-rs/badge.svg?branch=master&service=github)](https://coveralls.io/github/alexcrichton/toml-rs?branch=master)
+[![Latest Version](https://img.shields.io/crates/v/toml.svg)](https://crates.io/crates/toml)
+[![Documentation](https://docs.rs/toml/badge.svg)](https://docs.rs/toml)
+
+A [TOML][toml] decoder and encoder for Rust. This library is currently compliant
+with the v0.4.0 version of TOML. This library will also likely continue to stay
+up to date with the TOML specification as changes happen.
+
+[toml]: https://github.com/toml-lang/toml
+
+```toml
+# Cargo.toml
+[dependencies]
+toml = "0.4"
+```
+
+This crate also supports serialization/deserialization through the
+[serde](https://serde.rs) crate on crates.io. Currently the older `rustc-serialize`
+crate is not supported in the 0.3+ series of the `toml` crate, but 0.2 can be
+used for that support.
+
+# License
+
+`toml-rs` is primarily distributed under the terms of both the MIT license and
+the Apache License (Version 2.0), with portions covered by various BSD-like
+licenses.
+
+See LICENSE-APACHE, and LICENSE-MIT for details.
diff --git a/third_party/rust/toml/examples/decode.rs b/third_party/rust/toml/examples/decode.rs
new file mode 100644
index 0000000000..e15da79a68
--- /dev/null
+++ b/third_party/rust/toml/examples/decode.rs
@@ -0,0 +1,56 @@
+//! An example showing off the usage of `Deserialize` to automatically decode
+//! TOML into a Rust `struct`
+
+#![deny(warnings)]
+
+extern crate toml;
+extern crate serde;
+#[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, Deserialize)]
+struct Config {
+ global_string: Option<String>,
+ global_integer: Option<u64>,
+ server: Option<ServerConfig>,
+ peers: Option<Vec<PeerConfig>>,
+}
+
+/// Sub-structs are decoded from tables, so this will decode from the `[server]`
+/// table.
+///
+/// Again, each field is optional, meaning they don't have to be present.
+#[derive(Debug, Deserialize)]
+struct ServerConfig {
+ ip: Option<String>,
+ port: Option<u64>,
+}
+
+#[derive(Debug, Deserialize)]
+struct PeerConfig {
+ ip: Option<String>,
+ port: Option<u64>,
+}
+
+fn main() {
+ let toml_str = r#"
+ global_string = "test"
+ global_integer = 5
+
+ [server]
+ ip = "127.0.0.1"
+ port = 80
+
+ [[peers]]
+ ip = "127.0.0.1"
+ port = 8080
+
+ [[peers]]
+ ip = "127.0.0.1"
+ "#;
+
+ let decoded: Config = toml::from_str(toml_str).unwrap();
+ println!("{:#?}", decoded);
+}
diff --git a/third_party/rust/toml/examples/toml2json.rs b/third_party/rust/toml/examples/toml2json.rs
new file mode 100644
index 0000000000..1ed441a93a
--- /dev/null
+++ b/third_party/rust/toml/examples/toml2json.rs
@@ -0,0 +1,51 @@
+#![deny(warnings)]
+
+extern crate toml;
+extern crate serde_json;
+
+use std::fs::File;
+use std::env;
+use std::io;
+use std::io::prelude::*;
+
+use toml::Value as Toml;
+use serde_json::Value as Json;
+
+fn main() {
+ let mut args = env::args();
+ let mut input = String::new();
+ if args.len() > 1 {
+ let name = args.nth(1).unwrap();
+ File::open(&name).and_then(|mut f| {
+ f.read_to_string(&mut input)
+ }).unwrap();
+ } else {
+ io::stdin().read_to_string(&mut input).unwrap();
+ }
+
+ match input.parse() {
+ Ok(toml) => {
+ let json = convert(toml);
+ println!("{}", serde_json::to_string_pretty(&json).unwrap());
+ }
+ Err(error) => println!("failed to parse TOML: {}", error),
+ }
+}
+
+fn convert(toml: Toml) -> Json {
+ match toml {
+ 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()),
+ Toml::Datetime(dt) => Json::String(dt.to_string()),
+ }
+}
diff --git a/third_party/rust/toml/src/datetime.rs b/third_party/rust/toml/src/datetime.rs
new file mode 100644
index 0000000000..83b5c0bb9c
--- /dev/null
+++ b/third_party/rust/toml/src/datetime.rs
@@ -0,0 +1,425 @@
+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,
+ nanosecond: u32,
+}
+
+#[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 self.nanosecond != 0 {
+ let s = format!("{:09}", self.nanosecond);
+ write!(f, ".{}", s.trim_right_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 {
+ full_date.is_none()
+ };
+
+ 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 mut nanosecond = 0;
+ if chars.clone().next() == Some('.') {
+ chars.next();
+ let whole = chars.as_str();
+
+ let mut end = whole.len();
+ for (i, byte) in whole.bytes().enumerate() {
+ match byte {
+ b'0' ... b'9' => {
+ if i < 9 {
+ let p = 10_u32.pow(8 - i as u32);
+ nanosecond += p * (byte - b'0') as u32;
+ }
+ }
+ _ => {
+ end = i;
+ break;
+ }
+ }
+ }
+ if end == 0 {
+ return Err(DatetimeParseError { _private: () })
+ }
+ chars = whole[end..].chars();
+ }
+
+ let time = Time {
+ hour: h1 * 10 + h2,
+ minute: m1 * 10 + m2,
+ second: s1 * 10 + s2,
+ nanosecond: nanosecond,
+ };
+
+ if time.hour > 24 {
+ return Err(DatetimeParseError { _private: () })
+ }
+ if time.minute > 59 {
+ return Err(DatetimeParseError { _private: () })
+ }
+ if time.second > 59 {
+ return Err(DatetimeParseError { _private: () })
+ }
+ if time.nanosecond > 999_999_999 {
+ 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 - b'0'),
+ _ => 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> de::Deserialize<'de> for Datetime {
+ fn deserialize<D>(deserializer: D) -> Result<Datetime, D::Error>
+ where D: de::Deserializer<'de>
+ {
+ struct DatetimeVisitor;
+
+ impl<'de> de::Visitor<'de> 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::MapAccess<'de>
+ {
+ let value = visitor.next_key::<DatetimeKey>()?;
+ if value.is_none() {
+ return Err(de::Error::custom("datetime key not found"))
+ }
+ let v: DatetimeFromString = visitor.next_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> de::Deserialize<'de> for DatetimeKey {
+ fn deserialize<D>(deserializer: D) -> Result<DatetimeKey, D::Error>
+ where D: de::Deserializer<'de>
+ {
+ struct FieldVisitor;
+
+ impl<'de> de::Visitor<'de> 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_identifier(FieldVisitor)?;
+ Ok(DatetimeKey)
+ }
+}
+
+pub struct DatetimeFromString {
+ pub value: Datetime,
+}
+
+impl<'de> de::Deserialize<'de> for DatetimeFromString {
+ fn deserialize<D>(deserializer: D) -> Result<DatetimeFromString, D::Error>
+ where D: de::Deserializer<'de>
+ {
+ struct Visitor;
+
+ impl<'de> de::Visitor<'de> 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/third_party/rust/toml/src/de.rs b/third_party/rust/toml/src/de.rs
new file mode 100644
index 0000000000..9ff09f7b51
--- /dev/null
+++ b/third_party/rust/toml/src/de.rs
@@ -0,0 +1,1327 @@
+//! 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 serde::de::IntoDeserializer;
+
+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<'de, T>(bytes: &'de [u8]) -> Result<T, Error>
+ where T: de::Deserialize<'de>,
+{
+ 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.
+///
+/// # Examples
+///
+/// ```
+/// #[macro_use]
+/// extern crate serde_derive;
+/// extern crate toml;
+///
+/// #[derive(Deserialize)]
+/// struct Config {
+/// title: String,
+/// owner: Owner,
+/// }
+///
+/// #[derive(Deserialize)]
+/// struct Owner {
+/// name: String,
+/// }
+///
+/// fn main() {
+/// let config: Config = toml::from_str(r#"
+/// title = 'TOML Example'
+///
+/// [owner]
+/// name = 'Lisa'
+/// "#).unwrap();
+///
+/// assert_eq!(config.title, "TOML Example");
+/// assert_eq!(config.owner.name, "Lisa");
+/// }
+/// ```
+pub fn from_str<'de, T>(s: &'de str) -> Result<T, Error>
+ where T: de::Deserialize<'de>,
+{
+ let mut d = Deserializer::new(s);
+ let ret = T::deserialize(&mut d)?;
+ d.end()?;
+ 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,
+
+ /// A struct was expected but something else was found
+ ExpectedString,
+
+ #[doc(hidden)]
+ __Nonexhaustive,
+}
+
+/// Deserialization implementation for TOML.
+pub struct Deserializer<'a> {
+ require_newline_after_table: bool,
+ input: &'a str,
+ tokens: Tokenizer<'a>,
+}
+
+impl<'de, 'b> de::Deserializer<'de> for &'b mut Deserializer<'de> {
+ type Error = Error;
+
+ fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Error>
+ where V: de::Visitor<'de>,
+ {
+ 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.is_empty() || 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.is_empty() || 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,
+ })
+ }
+
+ fn deserialize_enum<V>(
+ self,
+ _name: &'static str,
+ _variants: &'static [&'static str],
+ visitor: V
+ ) -> Result<V::Value, Error>
+ where V: de::Visitor<'de>
+ {
+ if let Some(next) = self.next()? {
+ match next {
+ Token::String { val, .. } => {
+ visitor.visit_enum(val.into_deserializer())
+ },
+ _ => Err(Error::from_kind(ErrorKind::ExpectedString))
+ }
+ } else {
+ Err(Error::from_kind(ErrorKind::UnexpectedEof))
+ }
+ }
+
+ forward_to_deserialize_any! {
+ bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq
+ bytes byte_buf map struct unit newtype_struct
+ ignored_any unit_struct tuple_struct tuple option identifier
+ }
+}
+
+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<'de: 'b, 'b> {
+ values: vec::IntoIter<(Cow<'de, str>, Value<'de>)>,
+ next_value: Option<(Cow<'de, str>, Value<'de>)>,
+ depth: usize,
+ cur: usize,
+ cur_parent: usize,
+ max: usize,
+ tables: &'b mut [Table<'de>],
+ array: bool,
+ de: &'b mut Deserializer<'de>,
+}
+
+impl<'de, 'b> de::MapAccess<'de> for MapVisitor<'de, 'b> {
+ type Error = Error;
+
+ fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Error>
+ where K: de::DeserializeSeed<'de>,
+ {
+ 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 next 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.clone()))?;
+ 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().expect("Unable to read table values").into_iter();
+ }
+ }
+
+ fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Error>
+ where V: de::DeserializeSeed<'de>,
+ {
+ 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<'de, 'b> de::SeqAccess<'de> for MapVisitor<'de, 'b> {
+ type Error = Error;
+
+ fn next_element_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Error>
+ where K: de::DeserializeSeed<'de>,
+ {
+ 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().expect("Unable to read table values").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;
+ Ok(Some(ret))
+ }
+}
+
+impl<'de, 'b> de::Deserializer<'de> for MapVisitor<'de, 'b> {
+ type Error = Error;
+
+ fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Error>
+ where V: de::Visitor<'de>,
+ {
+ 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<'de>,
+ {
+ visitor.visit_some(self)
+ }
+
+ fn deserialize_newtype_struct<V>(
+ self,
+ _name: &'static str,
+ visitor: V
+ ) -> Result<V::Value, Error>
+ where V: de::Visitor<'de>
+ {
+ visitor.visit_newtype_struct(self)
+ }
+
+ forward_to_deserialize_any! {
+ bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq
+ bytes byte_buf map struct unit identifier
+ 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<'de> de::Deserializer<'de> for StrDeserializer<'de> {
+ type Error = Error;
+
+ fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Error>
+ where V: de::Visitor<'de>,
+ {
+ match self.key {
+ Cow::Borrowed(s) => visitor.visit_borrowed_str(s),
+ Cow::Owned(s) => visitor.visit_string(s),
+ }
+ }
+
+ forward_to_deserialize_any! {
+ bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq
+ bytes byte_buf map struct option unit newtype_struct
+ ignored_any unit_struct tuple_struct tuple enum identifier
+ }
+}
+
+struct ValueDeserializer<'a> {
+ value: Value<'a>,
+}
+
+impl<'a> ValueDeserializer<'a> {
+ fn new(value: Value<'a>) -> ValueDeserializer<'a> {
+ ValueDeserializer {
+ value: value,
+ }
+ }
+}
+
+impl<'de> de::Deserializer<'de> for ValueDeserializer<'de> {
+ type Error = Error;
+
+ fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Error>
+ where V: de::Visitor<'de>,
+ {
+ 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_borrowed_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<'de>,
+ {
+ if name == SERDE_STRUCT_NAME && fields == &[SERDE_STRUCT_FIELD_NAME] {
+ if let Value::Datetime(s) = self.value {
+ return visitor.visit_map(DatetimeDeserializer {
+ date: s,
+ visited: false,
+ })
+ }
+ }
+
+ self.deserialize_any(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<'de>,
+ {
+ visitor.visit_some(self)
+ }
+
+ fn deserialize_enum<V>(
+ self,
+ _name: &'static str,
+ _variants: &'static [&'static str],
+ visitor: V
+ ) -> Result<V::Value, Error>
+ where V: de::Visitor<'de>
+ {
+ match self.value {
+ Value::String(val) => visitor.visit_enum(val.into_deserializer()),
+ _ => Err(Error::from_kind(ErrorKind::ExpectedString))
+ }
+ }
+
+ fn deserialize_newtype_struct<V>(
+ self,
+ _name: &'static str,
+ visitor: V
+ ) -> Result<V::Value, Error>
+ where V: de::Visitor<'de>
+ {
+ visitor.visit_newtype_struct(self)
+ }
+
+ forward_to_deserialize_any! {
+ bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq
+ bytes byte_buf map unit identifier
+ ignored_any unit_struct tuple_struct tuple
+ }
+}
+
+impl<'de> de::IntoDeserializer<'de, Error> for Value<'de> {
+ type Deserializer = ValueDeserializer<'de>;
+
+ fn into_deserializer(self) -> Self::Deserializer {
+ ValueDeserializer::new(self)
+ }
+}
+
+struct DatetimeDeserializer<'a> {
+ visited: bool,
+ date: &'a str,
+}
+
+impl<'de> de::MapAccess<'de> for DatetimeDeserializer<'de> {
+ type Error = Error;
+
+ fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Error>
+ where K: de::DeserializeSeed<'de>,
+ {
+ if self.visited {
+ return Ok(None)
+ }
+ self.visited = true;
+ seed.deserialize(DatetimeFieldDeserializer).map(Some)
+ }
+
+ fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Error>
+ where V: de::DeserializeSeed<'de>,
+ {
+ seed.deserialize(StrDeserializer::new(self.date.into()))
+ }
+}
+
+struct DatetimeFieldDeserializer;
+
+impl<'de> de::Deserializer<'de> for DatetimeFieldDeserializer {
+ type Error = Error;
+
+ fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Error>
+ where V: de::Visitor<'de>,
+ {
+ visitor.visit_borrowed_str(SERDE_STRUCT_FIELD_NAME)
+ }
+
+ forward_to_deserialize_any! {
+ bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq
+ bytes byte_buf map struct option unit newtype_struct
+ ignored_any unit_struct tuple_struct tuple enum identifier
+ }
+}
+
+struct InlineTableDeserializer<'a> {
+ values: vec::IntoIter<(Cow<'a, str>, Value<'a>)>,
+ next_value: Option<Value<'a>>,
+}
+
+impl<'de> de::MapAccess<'de> for InlineTableDeserializer<'de> {
+ type Error = Error;
+
+ fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Error>
+ where K: de::DeserializeSeed<'de>,
+ {
+ 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 next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Error>
+ where V: de::DeserializeSeed<'de>,
+ {
+ let value = self.next_value.take().expect("Unable to read table values");
+ 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: true,
+ }
+ }
+
+ /// 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.require_newline_after_table);
+ if self.require_newline_after_table {
+ self.tokens.skip_to_newline();
+ } else {
+ loop {
+ match self.next()? {
+ Some(Token::RightBracket) => {
+ if array {
+ self.eat(Token::RightBracket)?;
+ }
+ break
+ }
+ Some(Token::Newline) |
+ None => break,
+ _ => {}
+ }
+ }
+ self.eat_whitespace()?;
+ }
+ 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)
+ }).and_then(|n: f64| {
+ if n.is_finite() {
+ Ok(n)
+ } else {
+ Err(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;
+ 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 {
+ /// Produces a (line, column) pair of the position of the error if available
+ ///
+ /// All indexes are 0-based.
+ pub fn line_col(&self) -> Option<(usize, usize)> {
+ self.inner.line.map(|line| (line, self.inner.col))
+ }
+
+ 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::ExpectedString => "expected string".fmt(f)?,
+ ErrorKind::__Nonexhaustive => panic!(),
+ }
+
+ if !self.inner.key.is_empty() {
+ 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::ExpectedString => "expected string",
+ 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,
+ require_newline_after_table: bool,
+ tokens: Tokenizer<'a>,
+}
+
+impl<'a> Header<'a> {
+ fn new(tokens: Tokenizer<'a>,
+ array: bool,
+ require_newline_after_table: bool) -> Header<'a> {
+ Header {
+ first: true,
+ array: array,
+ tokens: tokens,
+ require_newline_after_table: require_newline_after_table,
+ }
+ }
+
+ 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.require_newline_after_table {
+ if !self.tokens.eat_comment()? {
+ self.tokens.eat_newline_or_eof()?;
+ }
+ }
+ Ok(None)
+ }
+ }
+}
+
+#[derive(Debug)]
+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/third_party/rust/toml/src/lib.rs b/third_party/rust/toml/src/lib.rs
new file mode 100644
index 0000000000..c4a7e9dfa6
--- /dev/null
+++ b/third_party/rust/toml/src/lib.rs
@@ -0,0 +1,168 @@
+//! A [TOML]-parsing library
+//!
+//! This library implements a [TOML] v0.4.0 compatible parser,
+//! primarily supporting the [`serde`] library for encoding/decoding
+//! various types in Rust.
+//!
+//! TOML itself is a simple, ergonomic, and readable configuration format:
+//!
+//! ```toml
+//! [package]
+//! name = "toml"
+//! version = "0.4.2"
+//! authors = ["Alex Crichton <alex@alexcrichton.com>"]
+//!
+//! [dependencies]
+//! serde = "1.0"
+//! ```
+//!
+//! 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),
+//! }
+//! ```
+//!
+//! TOML is 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"));
+//! ```
+//!
+//! The `Value` type implements a number of convenience methods and
+//! traits; the example above uses `FromStr` to parse a `str` into a
+//! `Value`.
+//!
+//! ## Deserialization and Serialization
+//!
+//! This crate supports [`serde`] 1.0 with a number of
+//! implementations of the `Deserialize`, `Serialize`, `Deserializer`, and
+//! `Serializer` traits. Namely, you'll find:
+//!
+//! * `Deserialize for Value`
+//! * `Serialize for Value`
+//! * `Deserialize for Datetime`
+//! * `Serialize for Datetime`
+//! * `Deserializer for de::Deserializer`
+//! * `Serializer for ser::Serializer`
+//! * `Deserializer for Value`
+//!
+//! This means that you can use Serde to deserialize/serialize the
+//! `Value` type as well as the `Datetime` type in this crate. You can also
+//! 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");
+//! }
+//! ```
+//!
+//! You can serialize types in a similar fashion:
+//!
+//! ```rust
+//! #[macro_use]
+//! extern crate serde_derive;
+//! extern crate toml;
+//!
+//! #[derive(Serialize)]
+//! struct Config {
+//! ip: String,
+//! port: Option<u16>,
+//! keys: Keys,
+//! }
+//!
+//! #[derive(Serialize)]
+//! struct Keys {
+//! github: String,
+//! travis: Option<String>,
+//! }
+//!
+//! fn main() {
+//! let config = Config {
+//! ip: "127.0.0.1".to_string(),
+//! port: None,
+//! keys: Keys {
+//! github: "xxxxxxxxxxxxxxxxx".to_string(),
+//! travis: Some("yyyyyyyyyyyyyyyyy".to_string()),
+//! },
+//! };
+//!
+//! let toml = toml::to_string(&config).unwrap();
+//! }
+//! ```
+//!
+//! [TOML]: https://github.com/toml-lang/toml
+//! [Cargo]: https://crates.io/
+//! [`serde`]: https://serde.rs/
+
+#![doc(html_root_url = "https://docs.rs/toml/0.4")]
+#![deny(missing_docs)]
+
+#[macro_use]
+extern crate serde;
+
+pub mod value;
+#[doc(no_inline)]
+pub use value::Value;
+mod datetime;
+
+pub mod ser;
+#[doc(no_inline)]
+pub use ser::{to_string, to_string_pretty, to_vec, Serializer};
+pub mod de;
+#[doc(no_inline)]
+pub use de::{from_slice, from_str, Deserializer};
+mod tokens;
diff --git a/third_party/rust/toml/src/ser.rs b/third_party/rust/toml/src/ser.rs
new file mode 100644
index 0000000000..ef037ed177
--- /dev/null
+++ b/third_party/rust/toml/src/ser.rs
@@ -0,0 +1,1714 @@
+//! 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.
+//!
+//! Note that the TOML format has a restriction that if a table itself contains
+//! tables, all keys with non-table values must be emitted first. This is
+//! typically easy to ensure happens when you're defining a `struct` as you can
+//! reorder the fields manually, but when working with maps (such as `BTreeMap`
+//! or `HashMap`) this can lead to serialization errors. In those situations you
+//! may use the `tables_last` function in this module like so:
+//!
+//! ```rust
+//! # #[macro_use] extern crate serde_derive;
+//! # extern crate toml;
+//! # use std::collections::HashMap;
+//! #[derive(Serialize)]
+//! struct Manifest {
+//! package: Package,
+//! #[serde(serialize_with = "toml::ser::tables_last")]
+//! dependencies: HashMap<String, Dependency>,
+//! }
+//! # type Package = String;
+//! # type Dependency = String;
+//! # fn main() {}
+//! ```
+
+use std::cell::Cell;
+use std::error;
+use std::fmt::{self, Write};
+use std::marker;
+use std::rc::Rc;
+
+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.
+///
+/// # Examples
+///
+/// ```
+/// #[macro_use]
+/// extern crate serde_derive;
+/// extern crate toml;
+///
+/// #[derive(Serialize)]
+/// struct Config {
+/// database: Database,
+/// }
+///
+/// #[derive(Serialize)]
+/// struct Database {
+/// ip: String,
+/// port: Vec<u16>,
+/// connection_max: u32,
+/// enabled: bool,
+/// }
+///
+/// fn main() {
+/// let config = Config {
+/// database: Database {
+/// ip: "192.168.1.1".to_string(),
+/// port: vec![8001, 8002, 8003],
+/// connection_max: 5000,
+/// enabled: false,
+/// },
+/// };
+///
+/// let toml = toml::to_string(&config).unwrap();
+/// println!("{}", toml)
+/// }
+/// ```
+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)
+}
+
+/// Serialize the given data structure as a "pretty" String of TOML.
+///
+/// This is identical to `to_string` except the output string has a more
+/// "pretty" output. See `Serializer::pretty` for more details.
+pub fn to_string_pretty<T: ?Sized>(value: &T) -> Result<String, Error>
+ where T: ser::Serialize,
+{
+ let mut dst = String::with_capacity(128);
+ value.serialize(&mut Serializer::pretty(&mut dst))?;
+ Ok(dst)
+}
+
+/// Errors that can occur when serializing a type.
+#[derive(Debug, PartialEq, Eq, 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,
+
+ /// An error that we never omit but keep for backwards compatibility
+ #[doc(hidden)]
+ 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,
+
+ /// A serialized number was invalid.
+ NumberInvalid,
+
+ /// 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,
+}
+
+#[derive(Debug, Default, Clone)]
+#[doc(hidden)]
+/// Internal place for holding array setings
+struct ArraySettings {
+ indent: usize,
+ trailing_comma: bool,
+}
+
+impl ArraySettings {
+ fn pretty() -> ArraySettings {
+ ArraySettings {
+ indent: 4,
+ trailing_comma: true,
+ }
+ }
+}
+
+#[doc(hidden)]
+#[derive(Debug, Default, Clone)]
+/// String settings
+struct StringSettings {
+ /// Whether to use literal strings when possible
+ literal: bool,
+}
+
+impl StringSettings {
+ fn pretty() -> StringSettings {
+ StringSettings {
+ literal: true,
+ }
+ }
+}
+
+#[derive(Debug, Default, Clone)]
+#[doc(hidden)]
+/// Internal struct for holding serialization settings
+struct Settings {
+ array: Option<ArraySettings>,
+ string: Option<StringSettings>,
+}
+
+/// 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>,
+ settings: Rc<Settings>,
+}
+
+#[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>>,
+ len: Option<usize>,
+ },
+ End,
+}
+
+#[doc(hidden)]
+pub struct SerializeSeq<'a: 'b, 'b> {
+ ser: &'b mut Serializer<'a>,
+ first: Cell<bool>,
+ type_: Cell<Option<&'static str>>,
+ len: Option<usize>,
+}
+
+#[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,
+ settings: Rc::new(Settings::default()),
+ }
+ }
+
+ /// Instantiate a "pretty" formatter
+ ///
+ /// By default this will use:
+ ///
+ /// - pretty strings: strings with newlines will use the `'''` syntax. See
+ /// `Serializer::pretty_string`
+ /// - pretty arrays: each item in arrays will be on a newline, have an indentation of 4 and
+ /// have a trailing comma. See `Serializer::pretty_array`
+ pub fn pretty(dst: &'a mut String) -> Serializer<'a> {
+ Serializer {
+ dst: dst,
+ state: State::End,
+ settings: Rc::new(Settings {
+ array: Some(ArraySettings::pretty()),
+ string: Some(StringSettings::pretty()),
+ }),
+ }
+ }
+
+ /// Enable or Disable pretty strings
+ ///
+ /// If enabled, literal strings will be used when possible and strings with
+ /// one or more newlines will use triple quotes (i.e.: `'''` or `"""`)
+ ///
+ /// # Examples
+ ///
+ /// Instead of:
+ ///
+ /// ```toml,ignore
+ /// single = "no newlines"
+ /// text = "\nfoo\nbar\n"
+ /// ```
+ ///
+ /// You will have:
+ ///
+ /// ```toml,ignore
+ /// single = 'no newlines'
+ /// text = '''
+ /// foo
+ /// bar
+ /// '''
+ /// ```
+ pub fn pretty_string(&mut self, value: bool) -> &mut Self {
+ Rc::get_mut(&mut self.settings).unwrap().string = if value {
+ Some(StringSettings::pretty())
+ } else {
+ None
+ };
+ self
+ }
+
+ /// Enable or Disable Literal strings for pretty strings
+ ///
+ /// If enabled, literal strings will be used when possible and strings with
+ /// one or more newlines will use triple quotes (i.e.: `'''` or `"""`)
+ ///
+ /// If disabled, literal strings will NEVER be used and strings with one or
+ /// more newlines will use `"""`
+ ///
+ /// # Examples
+ ///
+ /// Instead of:
+ ///
+ /// ```toml,ignore
+ /// single = "no newlines"
+ /// text = "\nfoo\nbar\n"
+ /// ```
+ ///
+ /// You will have:
+ ///
+ /// ```toml,ignore
+ /// single = "no newlines"
+ /// text = """
+ /// foo
+ /// bar
+ /// """
+ /// ```
+ pub fn pretty_string_literal(&mut self, value: bool) -> &mut Self {
+ let use_default = if let &mut Some(ref mut s) = &mut Rc::get_mut(&mut self.settings)
+ .unwrap().string {
+ s.literal = value;
+ false
+ } else {
+ true
+ };
+
+ if use_default {
+ let mut string = StringSettings::pretty();
+ string.literal = value;
+ Rc::get_mut(&mut self.settings).unwrap().string = Some(string);
+ }
+ self
+ }
+
+ /// Enable or Disable pretty arrays
+ ///
+ /// If enabled, arrays will always have each item on their own line.
+ ///
+ /// Some specific features can be controlled via other builder methods:
+ ///
+ /// - `Serializer::pretty_array_indent`: set the indent to a value other
+ /// than 4.
+ /// - `Serializer::pretty_array_trailing_comma`: enable/disable the trailing
+ /// comma on the last item.
+ ///
+ /// # Examples
+ ///
+ /// Instead of:
+ ///
+ /// ```toml,ignore
+ /// array = ["foo", "bar"]
+ /// ```
+ ///
+ /// You will have:
+ ///
+ /// ```toml,ignore
+ /// array = [
+ /// "foo",
+ /// "bar",
+ /// ]
+ /// ```
+ pub fn pretty_array(&mut self, value: bool) -> &mut Self {
+ Rc::get_mut(&mut self.settings).unwrap().array = if value {
+ Some(ArraySettings::pretty())
+ } else {
+ None
+ };
+ self
+ }
+
+ /// Set the indent for pretty arrays
+ ///
+ /// See `Serializer::pretty_array` for more details.
+ pub fn pretty_array_indent(&mut self, value: usize) -> &mut Self {
+ let use_default = if let &mut Some(ref mut a) = &mut Rc::get_mut(&mut self.settings)
+ .unwrap().array {
+ a.indent = value;
+ false
+ } else {
+ true
+ };
+
+ if use_default {
+ let mut array = ArraySettings::pretty();
+ array.indent = value;
+ Rc::get_mut(&mut self.settings).unwrap().array = Some(array);
+ }
+ self
+ }
+
+ /// Specify whether to use a trailing comma when serializing pretty arrays
+ ///
+ /// See `Serializer::pretty_array` for more details.
+ pub fn pretty_array_trailing_comma(&mut self, value: bool) -> &mut Self {
+ let use_default = if let &mut Some(ref mut a) = &mut Rc::get_mut(&mut self.settings)
+ .unwrap().array {
+ a.trailing_comma = value;
+ false
+ } else {
+ true
+ };
+
+ if use_default {
+ let mut array = ArraySettings::pretty();
+ array.trailing_comma = value;
+ Rc::get_mut(&mut self.settings).unwrap().array = Some(array);
+ }
+ self
+ }
+
+ 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_, len } => {
+ assert!(type_.get().is_some());
+ if first.get() {
+ self._emit_key(parent)?;
+ }
+ self.emit_array(first, len)
+ }
+ 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>, len: Option<usize>) -> Result<(), Error> {
+ match (len, &self.settings.array) {
+ (Some(0...1), _) | (_, &None) => {
+ if first.get() {
+ self.dst.push_str("[")
+ } else {
+ self.dst.push_str(", ")
+ }
+ },
+ (_, &Some(ref a)) => {
+ if first.get() {
+ self.dst.push_str("[\n")
+ } else {
+ self.dst.push_str(",\n")
+ }
+ for _ in 0..a.indent {
+ 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, true)?;
+ }
+ Ok(())
+ }
+
+ fn emit_str(&mut self, value: &str, is_key: bool) -> Result<(), Error> {
+ #[derive(PartialEq)]
+ enum Type {
+ NewlineTripple,
+ OnelineTripple,
+ OnelineSingle,
+ }
+
+ enum Repr {
+ /// represent as a literal string (using '')
+ Literal(String, Type),
+ /// represent the std way (using "")
+ Std(Type),
+ }
+
+ fn do_pretty(value: &str) -> Repr {
+ // For doing pretty prints we store in a new String
+ // because there are too many cases where pretty cannot
+ // work. We need to determine:
+ // - if we are a "multi-line" pretty (if there are \n)
+ // - if ['''] appears if multi or ['] if single
+ // - if there are any invalid control characters
+ //
+ // Doing it any other way would require multiple passes
+ // to determine if a pretty string works or not.
+ let mut out = String::with_capacity(value.len() * 2);
+ let mut ty = Type::OnelineSingle;
+ // found consecutive single quotes
+ let mut max_found_singles = 0;
+ let mut found_singles = 0;
+ let mut can_be_pretty = true;
+
+ for ch in value.chars() {
+ if can_be_pretty {
+ if ch == '\'' {
+ found_singles += 1;
+ if found_singles >= 3 {
+ can_be_pretty = false;
+ }
+ } else {
+ if found_singles > max_found_singles {
+ max_found_singles = found_singles;
+ }
+ found_singles = 0
+ }
+ match ch {
+ '\t' => {},
+ '\n' => ty = Type::NewlineTripple,
+ // note that the following are invalid: \b \f \r
+ c if c < '\u{1f}' => can_be_pretty = false, // Invalid control character
+ _ => {}
+ }
+ out.push(ch);
+ } else {
+ // the string cannot be represented as pretty,
+ // still check if it should be multiline
+ if ch == '\n' {
+ ty = Type::NewlineTripple;
+ }
+ }
+ }
+ if !can_be_pretty {
+ debug_assert!(ty != Type::OnelineTripple);
+ return Repr::Std(ty);
+ }
+ if found_singles > max_found_singles {
+ max_found_singles = found_singles;
+ }
+ debug_assert!(max_found_singles < 3);
+ if ty == Type::OnelineSingle && max_found_singles >= 1 {
+ // no newlines, but must use ''' because it has ' in it
+ ty = Type::OnelineTripple;
+ }
+ Repr::Literal(out, ty)
+ }
+
+ let repr = if !is_key && self.settings.string.is_some() {
+ match (&self.settings.string, do_pretty(value)) {
+ (&Some(StringSettings { literal: false, .. }), Repr::Literal(_, ty)) =>
+ Repr::Std(ty),
+ (_, r @ _) => r,
+ }
+ } else {
+ Repr::Std(Type::OnelineSingle)
+ };
+ match repr {
+ Repr::Literal(literal, ty) => {
+ // A pretty string
+ match ty {
+ Type::NewlineTripple => self.dst.push_str("'''\n"),
+ Type::OnelineTripple => self.dst.push_str("'''"),
+ Type::OnelineSingle => self.dst.push('\''),
+ }
+ self.dst.push_str(&literal);
+ match ty {
+ Type::OnelineSingle => self.dst.push('\''),
+ _ => self.dst.push_str("'''"),
+ }
+ },
+ Repr::Std(ty) => {
+ match ty {
+ Type::NewlineTripple => self.dst.push_str("\"\"\"\n"),
+ // note: OnelineTripple can happen if do_pretty wants to do
+ // '''it's one line'''
+ // but settings.string.literal == false
+ Type::OnelineSingle |
+ Type::OnelineTripple => self.dst.push('"'),
+ }
+ for ch in value.chars() {
+ match ch {
+ '\u{8}' => self.dst.push_str("\\b"),
+ '\u{9}' => self.dst.push_str("\\t"),
+ '\u{a}' => {
+ match ty {
+ Type::NewlineTripple => self.dst.push('\n'),
+ Type::OnelineSingle => self.dst.push_str("\\n"),
+ _ => unreachable!(),
+ }
+ },
+ '\u{c}' => self.dst.push_str("\\f"),
+ '\u{d}' => self.dst.push_str("\\r"),
+ '\u{22}' => self.dst.push_str("\\\""),
+ '\u{5c}' => self.dst.push_str("\\\\"),
+ c if c < '\u{1f}' => drop(write!(self.dst, "\\u{:04X}", ch as u32)),
+ ch => self.dst.push(ch),
+ }
+ }
+ match ty {
+ Type::NewlineTripple => self.dst.push_str("\"\"\""),
+ Type::OnelineSingle | Type::OnelineTripple => self.dst.push('"'),
+ }
+ },
+ }
+ 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,
+ };
+
+ // Unlike [..]s, we can't omit [[..]] ancestors, so be sure to emit table
+ // headers for them.
+ let mut p = state;
+ if let State::Array { first, parent, .. } = *state {
+ if first.get() {
+ p = parent;
+ }
+ }
+ while let State::Table { first, parent, .. } = *p {
+ p = parent;
+ if !first.get() {
+ break;
+ }
+ if let State::Array { parent: &State::Table {..}, ..} = *parent {
+ self.emit_table_header(parent)?;
+ break;
+ }
+ }
+
+ match *state {
+ State::Table { first, .. } => {
+ if !first.get() {
+ // Newline if we are a table that is not the first
+ // table in the document.
+ self.dst.push('\n');
+ }
+ },
+ State::Array { parent, first, .. } => {
+ if !first.get() {
+ // Always newline if we are not the first item in the
+ // table-array
+ self.dst.push('\n');
+ } else if let State::Table { first, .. } = *parent {
+ if !first.get() {
+ // Newline if we are not the first item in the document
+ self.dst.push('\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> {
+ if !v.is_finite() {
+ return Err(Error::NumberInvalid);
+ }
+
+ 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> {
+ if !v.is_finite() {
+ return Err(Error::NumberInvalid);
+ }
+
+ 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, false)?;
+ if let State::Table { .. } = self.state {
+ self.dst.push_str("\n");
+ }
+ Ok(())
+ }
+
+ fn serialize_bytes(self, value: &[u8]) -> Result<(), Self::Error> {
+ use serde::ser::Serialize;
+ value.serialize(self)
+ }
+
+ 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: u32,
+ variant: &'static str)
+ -> Result<(), Self::Error> {
+ self.serialize_str(variant)
+ }
+
+ 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: u32,
+ _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),
+ len: len,
+ })
+ }
+
+ 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: u32,
+ _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: u32,
+ _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_,
+ len: self.len,
+ },
+ settings: self.ser.settings.clone(),
+ })?;
+ self.first.set(false);
+ Ok(())
+ }
+
+ fn end(self) -> Result<(), Error> {
+ match self.type_.get() {
+ Some("table") => return Ok(()),
+ Some(_) => {
+ match (self.len, &self.ser.settings.array) {
+ (Some(0...1), _) | (_, &None) => {
+ self.ser.dst.push_str("]");
+ },
+ (_, &Some(ref a)) => {
+ if a.trailing_comma {
+ self.ser.dst.push_str(",");
+ }
+ self.ser.dst.push_str("\n]");
+ },
+ }
+ }
+ 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)?;
+ }
+ }
+ 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,
+ },
+ settings: ser.settings.clone(),
+ });
+ 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,
+ },
+ settings: ser.settings.clone(),
+ });
+ match res {
+ Ok(()) => first.set(false),
+ Err(Error::UnsupportedNone) => {},
+ Err(e) => return Err(e),
+ }
+ }
+ }
+ Ok(())
+ }
+
+ fn end(self) -> Result<(), Error> {
+ match self {
+ SerializeTable::Datetime(_) => {},
+ SerializeTable::Table { mut ser, first, .. } => {
+ if first.get() {
+ let state = ser.state.clone();
+ ser.emit_table_header(&state)?;
+ }
+ }
+ }
+ 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: u32,
+ _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: u32,
+ _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_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: u32,
+ _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: u32,
+ _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: u32,
+ _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: u32,
+ _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_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: u32,
+ _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: u32,
+ _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::ArrayMixedType => "arrays cannot have mixed types".fmt(f),
+ Error::ValueAfterTable => "values must be emitted before tables".fmt(f),
+ Error::DateInvalid => "a serialized date was invalid".fmt(f),
+ Error::NumberInvalid => "a serialized number was invalid".fmt(f),
+ Error::UnsupportedNone => "unsupported None value".fmt(f),
+ Error::Custom(ref s) => s.fmt(f),
+ Error::KeyNewline => unreachable!(),
+ 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::ArrayMixedType => "arrays cannot have mixed types",
+ Error::ValueAfterTable => "values must be emitted before tables",
+ Error::DateInvalid => "a serialized date was invalid",
+ Error::NumberInvalid => "a serialized number was invalid",
+ Error::UnsupportedNone => "unsupported None value",
+ Error::Custom(_) => "custom error",
+ Error::KeyNewline => unreachable!(),
+ Error::__Nonexhaustive => panic!(),
+ }
+ }
+}
+
+impl ser::Error for Error {
+ fn custom<T: fmt::Display>(msg: T) -> Error {
+ Error::Custom(msg.to_string())
+ }
+}
+
+enum Category {
+ Primitive,
+ Array,
+ Table,
+}
+
+/// Convenience function to serialize items in a map in an order valid with
+/// TOML.
+///
+/// TOML carries the restriction that keys in a table must be serialized last if
+/// their value is a table itself. This isn't always easy to guarantee, so this
+/// helper can be used like so:
+///
+/// ```rust
+/// # #[macro_use] extern crate serde_derive;
+/// # extern crate toml;
+/// # use std::collections::HashMap;
+/// #[derive(Serialize)]
+/// struct Manifest {
+/// package: Package,
+/// #[serde(serialize_with = "toml::ser::tables_last")]
+/// dependencies: HashMap<String, Dependency>,
+/// }
+/// # type Package = String;
+/// # type Dependency = String;
+/// # fn main() {}
+/// ```
+pub fn tables_last<'a, I, K, V, S>(data: &'a I, serializer: S)
+ -> Result<S::Ok, S::Error>
+ where &'a I: IntoIterator<Item = (K, V)>,
+ K: ser::Serialize,
+ V: ser::Serialize,
+ S: ser::Serializer
+{
+ use serde::ser::SerializeMap;
+
+ let mut map = serializer.serialize_map(None)?;
+ for (k, v) in data {
+ if let Category::Primitive = v.serialize(Categorize::new())? {
+ map.serialize_entry(&k, &v)?;
+ }
+ }
+ for (k, v) in data {
+ if let Category::Array = v.serialize(Categorize::new())? {
+ map.serialize_entry(&k, &v)?;
+ }
+ }
+ for (k, v) in data {
+ if let Category::Table = v.serialize(Categorize::new())? {
+ map.serialize_entry(&k, &v)?;
+ }
+ }
+ map.end()
+}
+
+struct Categorize<E>(marker::PhantomData<E>);
+
+impl<E> Categorize<E> {
+ fn new() -> Self {
+ Categorize(marker::PhantomData)
+ }
+}
+
+impl<E: ser::Error> ser::Serializer for Categorize<E> {
+ type Ok = Category;
+ type Error = E;
+ type SerializeSeq = Self;
+ type SerializeTuple = ser::Impossible<Category, E>;
+ type SerializeTupleStruct = ser::Impossible<Category, E>;
+ type SerializeTupleVariant = ser::Impossible<Category, E>;
+ type SerializeMap = Self;
+ type SerializeStruct = Self;
+ type SerializeStructVariant = ser::Impossible<Category, E>;
+
+ fn serialize_bool(self, _: bool) -> Result<Self::Ok, Self::Error> {
+ Ok(Category::Primitive)
+ }
+
+ fn serialize_i8(self, _: i8) -> Result<Self::Ok, Self::Error> {
+ Ok(Category::Primitive)
+ }
+
+ fn serialize_i16(self, _: i16) -> Result<Self::Ok, Self::Error> {
+ Ok(Category::Primitive)
+ }
+
+ fn serialize_i32(self, _: i32) -> Result<Self::Ok, Self::Error> {
+ Ok(Category::Primitive)
+ }
+
+ fn serialize_i64(self, _: i64) -> Result<Self::Ok, Self::Error> {
+ Ok(Category::Primitive)
+ }
+
+ fn serialize_u8(self, _: u8) -> Result<Self::Ok, Self::Error> {
+ Ok(Category::Primitive)
+ }
+
+ fn serialize_u16(self, _: u16) -> Result<Self::Ok, Self::Error> {
+ Ok(Category::Primitive)
+ }
+
+ fn serialize_u32(self, _: u32) -> Result<Self::Ok, Self::Error> {
+ Ok(Category::Primitive)
+ }
+
+ fn serialize_u64(self, _: u64) -> Result<Self::Ok, Self::Error> {
+ Ok(Category::Primitive)
+ }
+
+ fn serialize_f32(self, _: f32) -> Result<Self::Ok, Self::Error> {
+ Ok(Category::Primitive)
+ }
+
+ fn serialize_f64(self, _: f64) -> Result<Self::Ok, Self::Error> {
+ Ok(Category::Primitive)
+ }
+
+ fn serialize_char(self, _: char) -> Result<Self::Ok, Self::Error> {
+ Ok(Category::Primitive)
+ }
+
+ fn serialize_str(self, _: &str) -> Result<Self::Ok, Self::Error> {
+ Ok(Category::Primitive)
+ }
+
+ fn serialize_bytes(self, _: &[u8]) -> Result<Self::Ok, Self::Error> {
+ Ok(Category::Array)
+ }
+
+ fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
+ Err(ser::Error::custom("unsupported"))
+ }
+
+ fn serialize_some<T: ?Sized + ser::Serialize>(self, v: &T) -> Result<Self::Ok, Self::Error> {
+ v.serialize(self)
+ }
+
+ fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
+ Err(ser::Error::custom("unsupported"))
+ }
+
+ fn serialize_unit_struct(self, _: &'static str) -> Result<Self::Ok, Self::Error> {
+ Err(ser::Error::custom("unsupported"))
+ }
+
+ fn serialize_unit_variant(self, _: &'static str, _: u32, _: &'static str) -> Result<Self::Ok, Self::Error> {
+ Err(ser::Error::custom("unsupported"))
+ }
+
+ fn serialize_newtype_struct<T: ?Sized + ser::Serialize>(self, _: &'static str, v: &T) -> Result<Self::Ok, Self::Error> {
+ v.serialize(self)
+ }
+
+ fn serialize_newtype_variant<T: ?Sized + ser::Serialize>(self, _: &'static str, _: u32, _: &'static str, _: &T) -> Result<Self::Ok, Self::Error> {
+ Err(ser::Error::custom("unsupported"))
+ }
+
+ fn serialize_seq(self, _: Option<usize>) -> Result<Self, Self::Error> {
+ Ok(self)
+ }
+
+ fn serialize_tuple(self, _: usize) -> Result<Self::SerializeTuple, Self::Error> {
+ Err(ser::Error::custom("unsupported"))
+ }
+
+ fn serialize_tuple_struct(self, _: &'static str, _: usize) -> Result<Self::SerializeTupleStruct, Self::Error> {
+ Err(ser::Error::custom("unsupported"))
+ }
+
+ fn serialize_tuple_variant(self, _: &'static str, _: u32, _: &'static str, _: usize) -> Result<Self::SerializeTupleVariant, Self::Error> {
+ Err(ser::Error::custom("unsupported"))
+ }
+
+ fn serialize_map(self, _: Option<usize>) -> Result<Self, Self::Error> {
+ Ok(self)
+ }
+
+ fn serialize_struct(self, _: &'static str, _: usize) -> Result<Self, Self::Error> {
+ Ok(self)
+ }
+
+ fn serialize_struct_variant(self, _: &'static str, _: u32, _: &'static str, _: usize) -> Result<Self::SerializeStructVariant, Self::Error> {
+ Err(ser::Error::custom("unsupported"))
+ }
+}
+
+impl<E: ser::Error> ser::SerializeSeq for Categorize<E> {
+ type Ok = Category;
+ type Error = E;
+
+ fn serialize_element<T: ?Sized + ser::Serialize>(&mut self, _: &T)
+ -> Result<(), Self::Error> {
+ Ok(())
+ }
+
+ fn end(self) -> Result<Self::Ok, Self::Error> {
+ Ok(Category::Array)
+ }
+}
+
+impl<E: ser::Error> ser::SerializeMap for Categorize<E> {
+ type Ok = Category;
+ type Error = E;
+
+ fn serialize_key<T: ?Sized + ser::Serialize>(&mut self, _: &T)
+ -> Result<(), Self::Error> {
+ Ok(())
+ }
+
+ fn serialize_value<T: ?Sized + ser::Serialize>(&mut self, _: &T)
+ -> Result<(), Self::Error> {
+ Ok(())
+ }
+
+ fn end(self) -> Result<Self::Ok, Self::Error> {
+ Ok(Category::Table)
+ }
+}
+
+impl<E: ser::Error> ser::SerializeStruct for Categorize<E> {
+ type Ok = Category;
+ type Error = E;
+
+ fn serialize_field<T: ?Sized>(&mut self,
+ _: &'static str,
+ _: &T) -> Result<(), Self::Error>
+ where T: ser::Serialize,
+ {
+ Ok(())
+ }
+
+ fn end(self) -> Result<Self::Ok, Self::Error> {
+ Ok(Category::Table)
+ }
+}
diff --git a/third_party/rust/toml/src/tokens.rs b/third_party/rust/toml/src/tokens.rs
new file mode 100644
index 0000000000..11f47f5682
--- /dev/null
+++ b/third_party/rust/toml/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}');
+ 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(self, input: &str) -> Cow<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/third_party/rust/toml/src/value.rs b/third_party/rust/toml/src/value.rs
new file mode 100644
index 0000000000..056eac4f04
--- /dev/null
+++ b/third_party/rust/toml/src/value.rs
@@ -0,0 +1,946 @@
+//! Definition of a TOML value
+
+use std::collections::{BTreeMap, HashMap};
+use std::hash::Hash;
+use std::fmt;
+use std::ops;
+use std::str::FromStr;
+use std::vec;
+
+use serde::ser;
+use serde::de;
+use serde::de::IntoDeserializer;
+
+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<'de, T>(self) -> Result<T, ::de::Error>
+ where T: de::Deserialize<'de>,
+ {
+ 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 a 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 a boolean
+ 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 a 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<'a> From<&'a str> for Value {
+ #[inline]
+ fn from(val: &'a str) -> Value {
+ Value::String(val.to_string())
+ }
+}
+
+impl<V: Into<Value>> From<Vec<V>> for Value {
+ fn from(val: Vec<V>) -> Value {
+ Value::Array(val.into_iter().map(|v| v.into()).collect())
+ }
+}
+
+impl<S: Into<String>, V: Into<Value>> From<BTreeMap<S, V>> for Value {
+ fn from(val: BTreeMap<S, V>) -> Value {
+ let table = val.into_iter()
+ .map(|(s, v)| (s.into(), v.into()))
+ .collect();
+
+ Value::Table(table)
+ }
+}
+
+impl<S: Into<String> + Hash + Eq, V: Into<Value>> From<HashMap<S, V>> for Value {
+ fn from(val: HashMap<S, V>) -> Value {
+ let table = val.into_iter()
+ .map(|(s, v)| (s.into(), v.into()))
+ .collect();
+
+ Value::Table(table)
+ }
+}
+
+macro_rules! impl_into_value {
+ ($variant:ident : $T:ty) => {
+ impl From<$T> for Value {
+ #[inline]
+ fn from(val: $T) -> Value {
+ Value::$variant(val.into())
+ }
+ }
+ }
+}
+
+impl_into_value!(String: String);
+impl_into_value!(Integer: i64);
+impl_into_value!(Integer: i32);
+impl_into_value!(Integer: i8);
+impl_into_value!(Integer: u8);
+impl_into_value!(Integer: u32);
+impl_into_value!(Float: f64);
+impl_into_value!(Float: f32);
+impl_into_value!(Boolean: bool);
+impl_into_value!(Datetime: Datetime);
+
+/// 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).expect("Unable to represent value as string").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_table() && !v.is_array() ||
+ (v.as_array().map(|a| !a.iter().any(|v| v.is_table())).unwrap_or(false)) {
+ map.serialize_entry(k, v)?;
+ }
+ }
+ for (k, v) in t {
+ if v.as_array().map(|a| a.iter().any(|v| v.is_table())).unwrap_or(false) {
+ map.serialize_entry(k, v)?;
+ }
+ }
+ for (k, v) in t {
+ if v.is_table() {
+ map.serialize_entry(k, v)?;
+ }
+ }
+ map.end()
+ }
+ }
+ }
+}
+
+impl<'de> de::Deserialize<'de> for Value {
+ fn deserialize<D>(deserializer: D) -> Result<Value, D::Error>
+ where D: de::Deserializer<'de>,
+ {
+ struct ValueVisitor;
+
+ impl<'de> de::Visitor<'de> 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>,
+ {
+ de::Deserialize::deserialize(deserializer)
+ }
+
+ fn visit_seq<V>(self, mut visitor: V) -> Result<Value, V::Error>
+ where V: de::SeqAccess<'de>,
+ {
+ let mut vec = Vec::new();
+ while let Some(elem) = try!(visitor.next_element()) {
+ vec.push(elem);
+ }
+ Ok(Value::Array(vec))
+ }
+
+ fn visit_map<V>(self, mut visitor: V) -> Result<Value, V::Error>
+ where V: de::MapAccess<'de>,
+ {
+ let mut key = String::new();
+ let datetime = visitor.next_key_seed(DatetimeOrTable {
+ key: &mut key,
+ })?;
+ match datetime {
+ Some(true) => {
+ let date: DatetimeFromString = visitor.next_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.next_value()?);
+ while let Some(key) = visitor.next_key()? {
+ if map.contains_key(&key) {
+ let msg = format!("duplicate key: `{}`", key);
+ return Err(de::Error::custom(msg))
+ }
+ map.insert(key, visitor.next_value()?);
+ }
+ Ok(Value::Table(map))
+ }
+ }
+
+ deserializer.deserialize_any(ValueVisitor)
+ }
+}
+
+impl<'de> de::Deserializer<'de> for Value {
+ type Error = ::de::Error;
+
+ fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, ::de::Error>
+ where V: de::Visitor<'de>,
+ {
+ 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"))
+ }
+ }
+ }
+ }
+
+ #[inline]
+ fn deserialize_enum<V>(
+ self,
+ _name: &str,
+ _variants: &'static [&'static str],
+ visitor: V,
+ ) -> Result<V::Value, ::de::Error>
+ where
+ V: de::Visitor<'de>,
+ {
+ match self {
+ Value::String(variant) => visitor.visit_enum(variant.into_deserializer()),
+ _ => Err(de::Error::invalid_type(de::Unexpected::UnitVariant, &"string only")),
+ }
+ }
+
+ // `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<'de>,
+ {
+ visitor.visit_some(self)
+ }
+
+ fn deserialize_newtype_struct<V>(
+ self,
+ _name: &'static str,
+ visitor: V
+ ) -> Result<V::Value, ::de::Error>
+ where V: de::Visitor<'de>
+ {
+ visitor.visit_newtype_struct(self)
+ }
+
+ forward_to_deserialize_any! {
+ bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string unit seq
+ bytes byte_buf map unit_struct tuple_struct struct
+ tuple ignored_any identifier
+ }
+}
+
+struct SeqDeserializer {
+ iter: vec::IntoIter<Value>,
+}
+
+impl SeqDeserializer {
+ fn new(vec: Vec<Value>) -> Self {
+ SeqDeserializer {
+ iter: vec.into_iter(),
+ }
+ }
+}
+
+impl<'de> de::SeqAccess<'de> for SeqDeserializer {
+ type Error = ::de::Error;
+
+ fn next_element_seed<T>(&mut self, seed: T)
+ -> Result<Option<T::Value>, ::de::Error>
+ where T: de::DeserializeSeed<'de>,
+ {
+ match self.iter.next() {
+ Some(value) => seed.deserialize(value).map(Some),
+ None => Ok(None),
+ }
+ }
+
+ fn size_hint(&self) -> Option<usize> {
+ match self.iter.size_hint() {
+ (lower, Some(upper)) if lower == upper => Some(upper),
+ _ => None,
+ }
+ }
+}
+
+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> de::MapAccess<'de> for MapDeserializer {
+ type Error = ::de::Error;
+
+ fn next_key_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, ::de::Error>
+ where T: de::DeserializeSeed<'de>,
+ {
+ match self.iter.next() {
+ Some((key, value)) => {
+ self.value = Some((key.clone(), value));
+ seed.deserialize(Value::String(key)).map(Some)
+ }
+ None => Ok(None),
+ }
+ }
+
+ fn next_value_seed<T>(&mut self, seed: T) -> Result<T::Value, ::de::Error>
+ where T: de::DeserializeSeed<'de>,
+ {
+ 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) -> Option<usize> {
+ match self.iter.size_hint() {
+ (lower, Some(upper)) if lower == upper => Some(upper),
+ _ => None,
+ }
+ }
+}
+
+impl<'de> de::IntoDeserializer<'de, ::de::Error> for Value {
+ type Deserializer = Self;
+
+ fn into_deserializer(self) -> Self {
+ self
+ }
+}
+
+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: u32,
+ _variant: &'static str)
+ -> Result<Value, ::ser::Error> {
+ self.serialize_str(_variant)
+ }
+
+ 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: u32,
+ _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_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: u32,
+ _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: u32,
+ _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> de::DeserializeSeed<'de> for DatetimeOrTable<'a> {
+ type Value = bool;
+
+ fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
+ where D: de::Deserializer<'de>
+ {
+ deserializer.deserialize_any(self)
+ }
+}
+
+impl<'a, 'de> de::Visitor<'de> 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/third_party/rust/toml/tests/README.md b/third_party/rust/toml/tests/README.md
new file mode 100644
index 0000000000..ebbc01ccf3
--- /dev/null
+++ b/third_party/rust/toml/tests/README.md
@@ -0,0 +1 @@
+Tests are from https://github.com/BurntSushi/toml-test
diff --git a/third_party/rust/toml/tests/backcompat.rs b/third_party/rust/toml/tests/backcompat.rs
new file mode 100644
index 0000000000..1b3f5993d0
--- /dev/null
+++ b/third_party/rust/toml/tests/backcompat.rs
@@ -0,0 +1,19 @@
+extern crate toml;
+extern crate serde;
+
+use serde::de::Deserialize;
+
+#[test]
+fn main() {
+ let s = "
+ [a] foo = 1
+ [[b]] foo = 1
+ ";
+ assert!(s.parse::<toml::Value>().is_err());
+
+ let mut d = toml::de::Deserializer::new(s);
+ d.set_require_newline_after_table(false);
+ let value = toml::Value::deserialize(&mut d).unwrap();
+ assert_eq!(value["a"]["foo"].as_integer(), Some(1));
+ assert_eq!(value["b"][0]["foo"].as_integer(), Some(1));
+}
diff --git a/third_party/rust/toml/tests/datetime.rs b/third_party/rust/toml/tests/datetime.rs
new file mode 100644
index 0000000000..948e86310c
--- /dev/null
+++ b/third_party/rust/toml/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/third_party/rust/toml/tests/display-tricky.rs b/third_party/rust/toml/tests/display-tricky.rs
new file mode 100644
index 0000000000..069e0f9e53
--- /dev/null
+++ b/third_party/rust/toml/tests/display-tricky.rs
@@ -0,0 +1,49 @@
+extern crate toml;
+#[macro_use] extern crate serde_derive;
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct Recipe {
+ pub name: String,
+ pub description: Option<String>,
+ #[serde(default)]
+ pub modules: Vec<Modules>,
+ #[serde(default)]
+ pub packages: Vec<Packages>
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct Modules {
+ pub name: String,
+ pub version: Option<String>
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct Packages {
+ pub name: String,
+ pub version: Option<String>
+}
+
+#[test]
+fn both_ends() {
+ let recipe_works = toml::from_str::<Recipe>(r#"
+ name = "testing"
+ description = "example"
+ modules = []
+
+ [[packages]]
+ name = "base"
+ "#).unwrap();
+ toml::to_string(&recipe_works).unwrap();
+
+ let recipe_fails = toml::from_str::<Recipe>(r#"
+ name = "testing"
+ description = "example"
+ packages = []
+
+ [[modules]]
+ name = "base"
+ "#).unwrap();
+
+ let recipe_toml = toml::Value::try_from(recipe_fails).unwrap();
+ recipe_toml.to_string();
+}
diff --git a/third_party/rust/toml/tests/display.rs b/third_party/rust/toml/tests/display.rs
new file mode 100644
index 0000000000..ca4fdd8ce3
--- /dev/null
+++ b/third_party/rust/toml/tests/display.rs
@@ -0,0 +1,103 @@
+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");
+ assert_eq!(Table(map! {
+ "test" => Array(vec![Integer(2)]),
+ "test2" => Integer(2)
+ }).to_string(),
+ "test = [2]\n\
+ test2 = 2\n");
+}
diff --git a/third_party/rust/toml/tests/formatting.rs b/third_party/rust/toml/tests/formatting.rs
new file mode 100644
index 0000000000..10fb165385
--- /dev/null
+++ b/third_party/rust/toml/tests/formatting.rs
@@ -0,0 +1,55 @@
+extern crate serde;
+#[macro_use]
+extern crate serde_derive;
+extern crate toml;
+
+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, Serialize, Deserialize)]
+struct Users {
+ pub user: Vec<User>,
+}
+
+#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize)]
+struct TwoUsers {
+ pub user0: User,
+ pub user1: User,
+}
+
+#[test]
+fn no_unnecessary_newlines_array() {
+ assert!(!to_string(&Users {
+ user: vec![
+ User {
+ name: "John".to_string(),
+ surname: "Doe".to_string(),
+ },
+ User {
+ name: "Jane".to_string(),
+ surname: "Dough".to_string(),
+ },
+ ],
+ }).unwrap()
+ .starts_with("\n"));
+}
+
+#[test]
+fn no_unnecessary_newlines_table() {
+ assert!(!to_string(&TwoUsers {
+ user0: User {
+ name: "John".to_string(),
+ surname: "Doe".to_string(),
+ },
+ user1: User {
+ name: "Jane".to_string(),
+ surname: "Dough".to_string(),
+ },
+ }).unwrap()
+ .starts_with("\n"));
+}
diff --git a/third_party/rust/toml/tests/invalid-encoder-misc.rs b/third_party/rust/toml/tests/invalid-encoder-misc.rs
new file mode 100644
index 0000000000..272f58f8bf
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid-encoder-misc.rs
@@ -0,0 +1,14 @@
+extern crate toml;
+
+use std::f64;
+
+#[test]
+fn test_invalid_float_encode() {
+ fn bad(value: toml::Value) {
+ assert!(toml::to_string(&value).is_err());
+ }
+
+ bad(toml::Value::Float(f64::INFINITY));
+ bad(toml::Value::Float(f64::NEG_INFINITY));
+ bad(toml::Value::Float(f64::NAN));
+}
diff --git a/third_party/rust/toml/tests/invalid-encoder/array-mixed-types-ints-and-floats.json b/third_party/rust/toml/tests/invalid-encoder/array-mixed-types-ints-and-floats.json
new file mode 100644
index 0000000000..2d42ead67e
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid-encoder/array-mixed-types-ints-and-floats.json
@@ -0,0 +1,15 @@
+{
+ "ints-and-floats": {
+ "type": "array",
+ "value": [
+ {
+ "type": "integer",
+ "value": "1"
+ },
+ {
+ "type": "float",
+ "value": "1.1"
+ }
+ ]
+ }
+}
diff --git a/third_party/rust/toml/tests/invalid-misc.rs b/third_party/rust/toml/tests/invalid-misc.rs
new file mode 100644
index 0000000000..bb70b976b4
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid-misc.rs
@@ -0,0 +1,17 @@
+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_");
+ bad("''");
+ bad("a = nan");
+ bad("a = -inf");
+ bad("a = inf");
+ bad("a = 9e99999");
+}
diff --git a/third_party/rust/toml/tests/invalid.rs b/third_party/rust/toml/tests/invalid.rs
new file mode 100644
index 0000000000..46796843a0
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid.rs
@@ -0,0 +1,98 @@
+extern crate toml;
+
+fn run(toml: &str) {
+ println!("test if invalid:\n{}", toml);
+ if let Ok(e) = toml.parse::<toml::Value>() {
+ panic!("parsed to: {:#?}", e);
+ }
+}
+
+macro_rules! test( ($name:ident, $toml:expr) => (
+ #[test]
+ fn $name() { run($toml); }
+) );
+
+test!(array_mixed_types_arrays_and_ints,
+ include_str!("invalid/array-mixed-types-arrays-and-ints.toml"));
+test!(array_mixed_types_ints_and_floats,
+ include_str!("invalid/array-mixed-types-ints-and-floats.toml"));
+test!(array_mixed_types_strings_and_ints,
+ include_str!("invalid/array-mixed-types-strings-and-ints.toml"));
+test!(datetime_malformed_no_leads,
+ include_str!("invalid/datetime-malformed-no-leads.toml"));
+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_with_milli,
+ include_str!("invalid/datetime-malformed-with-milli.toml"));
+test!(duplicate_keys,
+ include_str!("invalid/duplicate-keys.toml"));
+test!(duplicate_key_table,
+ include_str!("invalid/duplicate-key-table.toml"));
+test!(duplicate_tables,
+ include_str!("invalid/duplicate-tables.toml"));
+test!(empty_implicit_table,
+ include_str!("invalid/empty-implicit-table.toml"));
+test!(empty_table,
+ include_str!("invalid/empty-table.toml"));
+test!(float_no_leading_zero,
+ include_str!("invalid/float-no-leading-zero.toml"));
+test!(float_no_trailing_digits,
+ include_str!("invalid/float-no-trailing-digits.toml"));
+test!(key_after_array,
+ include_str!("invalid/key-after-array.toml"));
+test!(key_after_table,
+ include_str!("invalid/key-after-table.toml"));
+test!(key_empty,
+ include_str!("invalid/key-empty.toml"));
+test!(key_hash,
+ include_str!("invalid/key-hash.toml"));
+test!(key_newline,
+ include_str!("invalid/key-newline.toml"));
+test!(key_open_bracket,
+ include_str!("invalid/key-open-bracket.toml"));
+test!(key_single_open_bracket,
+ include_str!("invalid/key-single-open-bracket.toml"));
+test!(key_space,
+ include_str!("invalid/key-space.toml"));
+test!(key_start_bracket,
+ include_str!("invalid/key-start-bracket.toml"));
+test!(key_two_equals,
+ include_str!("invalid/key-two-equals.toml"));
+test!(string_bad_byte_escape,
+ include_str!("invalid/string-bad-byte-escape.toml"));
+test!(string_bad_escape,
+ include_str!("invalid/string-bad-escape.toml"));
+test!(string_byte_escapes,
+ include_str!("invalid/string-byte-escapes.toml"));
+test!(string_no_close,
+ include_str!("invalid/string-no-close.toml"));
+test!(table_array_implicit,
+ include_str!("invalid/table-array-implicit.toml"));
+test!(table_array_malformed_bracket,
+ include_str!("invalid/table-array-malformed-bracket.toml"));
+test!(table_array_malformed_empty,
+ include_str!("invalid/table-array-malformed-empty.toml"));
+test!(table_empty,
+ include_str!("invalid/table-empty.toml"));
+test!(table_nested_brackets_close,
+ include_str!("invalid/table-nested-brackets-close.toml"));
+test!(table_nested_brackets_open,
+ include_str!("invalid/table-nested-brackets-open.toml"));
+test!(table_whitespace,
+ include_str!("invalid/table-whitespace.toml"));
+test!(table_with_pound,
+ include_str!("invalid/table-with-pound.toml"));
+test!(text_after_array_entries,
+ include_str!("invalid/text-after-array-entries.toml"));
+test!(text_after_integer,
+ include_str!("invalid/text-after-integer.toml"));
+test!(text_after_string,
+ include_str!("invalid/text-after-string.toml"));
+test!(text_after_table,
+ include_str!("invalid/text-after-table.toml"));
+test!(text_before_array_separator,
+ include_str!("invalid/text-before-array-separator.toml"));
+test!(text_in_array,
+ include_str!("invalid/text-in-array.toml"));
diff --git a/third_party/rust/toml/tests/invalid/array-mixed-types-arrays-and-ints.toml b/third_party/rust/toml/tests/invalid/array-mixed-types-arrays-and-ints.toml
new file mode 100644
index 0000000000..051ec73136
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/array-mixed-types-arrays-and-ints.toml
@@ -0,0 +1 @@
+arrays-and-ints = [1, ["Arrays are not integers."]]
diff --git a/third_party/rust/toml/tests/invalid/array-mixed-types-ints-and-floats.toml b/third_party/rust/toml/tests/invalid/array-mixed-types-ints-and-floats.toml
new file mode 100644
index 0000000000..a5aa9b7a03
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/array-mixed-types-ints-and-floats.toml
@@ -0,0 +1 @@
+ints-and-floats = [1, 1.1]
diff --git a/third_party/rust/toml/tests/invalid/array-mixed-types-strings-and-ints.toml b/third_party/rust/toml/tests/invalid/array-mixed-types-strings-and-ints.toml
new file mode 100644
index 0000000000..f348308053
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/array-mixed-types-strings-and-ints.toml
@@ -0,0 +1 @@
+strings-and-ints = ["hi", 42]
diff --git a/third_party/rust/toml/tests/invalid/datetime-malformed-no-leads.toml b/third_party/rust/toml/tests/invalid/datetime-malformed-no-leads.toml
new file mode 100644
index 0000000000..123f173beb
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/datetime-malformed-no-leads.toml
@@ -0,0 +1 @@
+no-leads = 1987-7-05T17:45:00Z
diff --git a/third_party/rust/toml/tests/invalid/datetime-malformed-no-secs.toml b/third_party/rust/toml/tests/invalid/datetime-malformed-no-secs.toml
new file mode 100644
index 0000000000..ba93900762
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/datetime-malformed-no-secs.toml
@@ -0,0 +1 @@
+no-secs = 1987-07-05T17:45Z
diff --git a/third_party/rust/toml/tests/invalid/datetime-malformed-no-t.toml b/third_party/rust/toml/tests/invalid/datetime-malformed-no-t.toml
new file mode 100644
index 0000000000..617e3c56d4
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/datetime-malformed-no-t.toml
@@ -0,0 +1 @@
+no-t = 1987-07-0517:45:00Z
diff --git a/third_party/rust/toml/tests/invalid/datetime-malformed-with-milli.toml b/third_party/rust/toml/tests/invalid/datetime-malformed-with-milli.toml
new file mode 100644
index 0000000000..eef792f34d
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/datetime-malformed-with-milli.toml
@@ -0,0 +1 @@
+with-milli = 1987-07-5T17:45:00.12Z
diff --git a/third_party/rust/toml/tests/invalid/duplicate-key-table.toml b/third_party/rust/toml/tests/invalid/duplicate-key-table.toml
new file mode 100644
index 0000000000..cedf05fc53
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/duplicate-key-table.toml
@@ -0,0 +1,5 @@
+[fruit]
+type = "apple"
+
+[fruit.type]
+apple = "yes"
diff --git a/third_party/rust/toml/tests/invalid/duplicate-keys.toml b/third_party/rust/toml/tests/invalid/duplicate-keys.toml
new file mode 100644
index 0000000000..9b5aee0e59
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/duplicate-keys.toml
@@ -0,0 +1,2 @@
+dupe = false
+dupe = true
diff --git a/third_party/rust/toml/tests/invalid/duplicate-tables.toml b/third_party/rust/toml/tests/invalid/duplicate-tables.toml
new file mode 100644
index 0000000000..8ddf49b4e8
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/duplicate-tables.toml
@@ -0,0 +1,2 @@
+[a]
+[a]
diff --git a/third_party/rust/toml/tests/invalid/empty-implicit-table.toml b/third_party/rust/toml/tests/invalid/empty-implicit-table.toml
new file mode 100644
index 0000000000..0cc36d0d28
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/empty-implicit-table.toml
@@ -0,0 +1 @@
+[naughty..naughty]
diff --git a/third_party/rust/toml/tests/invalid/empty-table.toml b/third_party/rust/toml/tests/invalid/empty-table.toml
new file mode 100644
index 0000000000..fe51488c70
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/empty-table.toml
@@ -0,0 +1 @@
+[]
diff --git a/third_party/rust/toml/tests/invalid/float-no-leading-zero.toml b/third_party/rust/toml/tests/invalid/float-no-leading-zero.toml
new file mode 100644
index 0000000000..cab76bfd15
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/float-no-leading-zero.toml
@@ -0,0 +1,2 @@
+answer = .12345
+neganswer = -.12345
diff --git a/third_party/rust/toml/tests/invalid/float-no-trailing-digits.toml b/third_party/rust/toml/tests/invalid/float-no-trailing-digits.toml
new file mode 100644
index 0000000000..cbff2d06f0
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/float-no-trailing-digits.toml
@@ -0,0 +1,2 @@
+answer = 1.
+neganswer = -1.
diff --git a/third_party/rust/toml/tests/invalid/key-after-array.toml b/third_party/rust/toml/tests/invalid/key-after-array.toml
new file mode 100644
index 0000000000..5c1a1b0a9b
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/key-after-array.toml
@@ -0,0 +1 @@
+[[agencies]] owner = "S Cjelli"
diff --git a/third_party/rust/toml/tests/invalid/key-after-table.toml b/third_party/rust/toml/tests/invalid/key-after-table.toml
new file mode 100644
index 0000000000..4bc82136ce
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/key-after-table.toml
@@ -0,0 +1 @@
+[history] guard = "sleeping"
diff --git a/third_party/rust/toml/tests/invalid/key-empty.toml b/third_party/rust/toml/tests/invalid/key-empty.toml
new file mode 100644
index 0000000000..09f998f416
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/key-empty.toml
@@ -0,0 +1 @@
+ = 1
diff --git a/third_party/rust/toml/tests/invalid/key-hash.toml b/third_party/rust/toml/tests/invalid/key-hash.toml
new file mode 100644
index 0000000000..e321b1fbd0
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/key-hash.toml
@@ -0,0 +1 @@
+a# = 1
diff --git a/third_party/rust/toml/tests/invalid/key-newline.toml b/third_party/rust/toml/tests/invalid/key-newline.toml
new file mode 100644
index 0000000000..707aad54ec
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/key-newline.toml
@@ -0,0 +1,2 @@
+a
+= 1
diff --git a/third_party/rust/toml/tests/invalid/key-open-bracket.toml b/third_party/rust/toml/tests/invalid/key-open-bracket.toml
new file mode 100644
index 0000000000..f0aeb16e50
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/key-open-bracket.toml
@@ -0,0 +1 @@
+[abc = 1
diff --git a/third_party/rust/toml/tests/invalid/key-single-open-bracket.toml b/third_party/rust/toml/tests/invalid/key-single-open-bracket.toml
new file mode 100644
index 0000000000..8e2f0bef13
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/key-single-open-bracket.toml
@@ -0,0 +1 @@
+[ \ No newline at end of file
diff --git a/third_party/rust/toml/tests/invalid/key-space.toml b/third_party/rust/toml/tests/invalid/key-space.toml
new file mode 100644
index 0000000000..201806d280
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/key-space.toml
@@ -0,0 +1 @@
+a b = 1 \ No newline at end of file
diff --git a/third_party/rust/toml/tests/invalid/key-start-bracket.toml b/third_party/rust/toml/tests/invalid/key-start-bracket.toml
new file mode 100644
index 0000000000..e0597ae1c6
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/key-start-bracket.toml
@@ -0,0 +1,3 @@
+[a]
+[xyz = 5
+[b]
diff --git a/third_party/rust/toml/tests/invalid/key-two-equals.toml b/third_party/rust/toml/tests/invalid/key-two-equals.toml
new file mode 100644
index 0000000000..25a037894e
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/key-two-equals.toml
@@ -0,0 +1 @@
+key= = 1
diff --git a/third_party/rust/toml/tests/invalid/string-bad-byte-escape.toml b/third_party/rust/toml/tests/invalid/string-bad-byte-escape.toml
new file mode 100644
index 0000000000..4c7be59f4b
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/string-bad-byte-escape.toml
@@ -0,0 +1 @@
+naughty = "\xAg"
diff --git a/third_party/rust/toml/tests/invalid/string-bad-escape.toml b/third_party/rust/toml/tests/invalid/string-bad-escape.toml
new file mode 100644
index 0000000000..60acb0ccc5
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/string-bad-escape.toml
@@ -0,0 +1 @@
+invalid-escape = "This string has a bad \a escape character."
diff --git a/third_party/rust/toml/tests/invalid/string-byte-escapes.toml b/third_party/rust/toml/tests/invalid/string-byte-escapes.toml
new file mode 100644
index 0000000000..e94452a8df
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/string-byte-escapes.toml
@@ -0,0 +1 @@
+answer = "\x33"
diff --git a/third_party/rust/toml/tests/invalid/string-no-close.toml b/third_party/rust/toml/tests/invalid/string-no-close.toml
new file mode 100644
index 0000000000..0c292fcab7
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/string-no-close.toml
@@ -0,0 +1 @@
+no-ending-quote = "One time, at band camp
diff --git a/third_party/rust/toml/tests/invalid/table-array-implicit.toml b/third_party/rust/toml/tests/invalid/table-array-implicit.toml
new file mode 100644
index 0000000000..05f2507ecb
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/table-array-implicit.toml
@@ -0,0 +1,14 @@
+# This test is a bit tricky. It should fail because the first use of
+# `[[albums.songs]]` without first declaring `albums` implies that `albums`
+# must be a table. The alternative would be quite weird. Namely, it wouldn't
+# comply with the TOML spec: "Each double-bracketed sub-table will belong to
+# the most *recently* defined table element *above* it."
+#
+# This is in contrast to the *valid* test, table-array-implicit where
+# `[[albums.songs]]` works by itself, so long as `[[albums]]` isn't declared
+# later. (Although, `[albums]` could be.)
+[[albums.songs]]
+name = "Glory Days"
+
+[[albums]]
+name = "Born in the USA"
diff --git a/third_party/rust/toml/tests/invalid/table-array-malformed-bracket.toml b/third_party/rust/toml/tests/invalid/table-array-malformed-bracket.toml
new file mode 100644
index 0000000000..39c73b05c4
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/table-array-malformed-bracket.toml
@@ -0,0 +1,2 @@
+[[albums]
+name = "Born to Run"
diff --git a/third_party/rust/toml/tests/invalid/table-array-malformed-empty.toml b/third_party/rust/toml/tests/invalid/table-array-malformed-empty.toml
new file mode 100644
index 0000000000..a470ca332f
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/table-array-malformed-empty.toml
@@ -0,0 +1,2 @@
+[[]]
+name = "Born to Run"
diff --git a/third_party/rust/toml/tests/invalid/table-empty.toml b/third_party/rust/toml/tests/invalid/table-empty.toml
new file mode 100644
index 0000000000..fe51488c70
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/table-empty.toml
@@ -0,0 +1 @@
+[]
diff --git a/third_party/rust/toml/tests/invalid/table-nested-brackets-close.toml b/third_party/rust/toml/tests/invalid/table-nested-brackets-close.toml
new file mode 100644
index 0000000000..c8b5a67858
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/table-nested-brackets-close.toml
@@ -0,0 +1,2 @@
+[a]b]
+zyx = 42
diff --git a/third_party/rust/toml/tests/invalid/table-nested-brackets-open.toml b/third_party/rust/toml/tests/invalid/table-nested-brackets-open.toml
new file mode 100644
index 0000000000..246d7e91fe
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/table-nested-brackets-open.toml
@@ -0,0 +1,2 @@
+[a[b]
+zyx = 42
diff --git a/third_party/rust/toml/tests/invalid/table-whitespace.toml b/third_party/rust/toml/tests/invalid/table-whitespace.toml
new file mode 100644
index 0000000000..79bbcb1e29
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/table-whitespace.toml
@@ -0,0 +1 @@
+[invalid key] \ No newline at end of file
diff --git a/third_party/rust/toml/tests/invalid/table-with-pound.toml b/third_party/rust/toml/tests/invalid/table-with-pound.toml
new file mode 100644
index 0000000000..0d8edb524f
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/table-with-pound.toml
@@ -0,0 +1,2 @@
+[key#group]
+answer = 42 \ No newline at end of file
diff --git a/third_party/rust/toml/tests/invalid/text-after-array-entries.toml b/third_party/rust/toml/tests/invalid/text-after-array-entries.toml
new file mode 100644
index 0000000000..1a7289074e
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/text-after-array-entries.toml
@@ -0,0 +1,4 @@
+array = [
+ "Is there life after an array separator?", No
+ "Entry"
+]
diff --git a/third_party/rust/toml/tests/invalid/text-after-integer.toml b/third_party/rust/toml/tests/invalid/text-after-integer.toml
new file mode 100644
index 0000000000..42de7aff4d
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/text-after-integer.toml
@@ -0,0 +1 @@
+answer = 42 the ultimate answer?
diff --git a/third_party/rust/toml/tests/invalid/text-after-string.toml b/third_party/rust/toml/tests/invalid/text-after-string.toml
new file mode 100644
index 0000000000..c92a6f11d8
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/text-after-string.toml
@@ -0,0 +1 @@
+string = "Is there life after strings?" No.
diff --git a/third_party/rust/toml/tests/invalid/text-after-table.toml b/third_party/rust/toml/tests/invalid/text-after-table.toml
new file mode 100644
index 0000000000..87da9db26d
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/text-after-table.toml
@@ -0,0 +1 @@
+[error] this shouldn't be here
diff --git a/third_party/rust/toml/tests/invalid/text-before-array-separator.toml b/third_party/rust/toml/tests/invalid/text-before-array-separator.toml
new file mode 100644
index 0000000000..9b06a39241
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/text-before-array-separator.toml
@@ -0,0 +1,4 @@
+array = [
+ "Is there life before an array separator?" No,
+ "Entry"
+]
diff --git a/third_party/rust/toml/tests/invalid/text-in-array.toml b/third_party/rust/toml/tests/invalid/text-in-array.toml
new file mode 100644
index 0000000000..a6a6c42075
--- /dev/null
+++ b/third_party/rust/toml/tests/invalid/text-in-array.toml
@@ -0,0 +1,5 @@
+array = [
+ "Entry 1",
+ I don't belong,
+ "Entry 2",
+]
diff --git a/third_party/rust/toml/tests/parser.rs b/third_party/rust/toml/tests/parser.rs
new file mode 100644
index 0000000000..2282416f3e
--- /dev/null
+++ b/third_party/rust/toml/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.123456789-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/third_party/rust/toml/tests/pretty.rs b/third_party/rust/toml/tests/pretty.rs
new file mode 100644
index 0000000000..19ed22de4d
--- /dev/null
+++ b/third_party/rust/toml/tests/pretty.rs
@@ -0,0 +1,308 @@
+extern crate toml;
+extern crate serde;
+
+use serde::ser::Serialize;
+
+const NO_PRETTY: &'static str = "\
+[example]
+array = [\"item 1\", \"item 2\"]
+empty = []
+oneline = \"this has no newlines.\"
+text = \"\\nthis is the first line\\nthis is the second line\\n\"
+";
+
+#[test]
+fn no_pretty() {
+ let toml = NO_PRETTY;
+ let value: toml::Value = toml::from_str(toml).unwrap();
+ let mut result = String::with_capacity(128);
+ value.serialize(&mut toml::Serializer::new(&mut result)).unwrap();
+ println!("EXPECTED:\n{}", toml);
+ println!("\nRESULT:\n{}", result);
+ assert_eq!(toml, &result);
+}
+
+#[test]
+fn disable_pretty() {
+ let toml = NO_PRETTY;
+ let value: toml::Value = toml::from_str(toml).unwrap();
+ let mut result = String::with_capacity(128);
+ {
+ let mut serializer = toml::Serializer::pretty(&mut result);
+ serializer.pretty_string(false);
+ serializer.pretty_array(false);
+ value.serialize(&mut serializer).unwrap();
+ }
+ println!("EXPECTED:\n{}", toml);
+ println!("\nRESULT:\n{}", result);
+ assert_eq!(toml, &result);
+}
+
+const PRETTY_STD: &'static str = "\
+[example]
+array = [
+ 'item 1',
+ 'item 2',
+]
+empty = []
+one = ['one']
+oneline = 'this has no newlines.'
+text = '''
+this is the first line
+this is the second line
+'''
+";
+
+#[test]
+fn pretty_std() {
+ let toml = PRETTY_STD;
+ let value: toml::Value = toml::from_str(toml).unwrap();
+ let mut result = String::with_capacity(128);
+ value.serialize(&mut toml::Serializer::pretty(&mut result)).unwrap();
+ println!("EXPECTED:\n{}", toml);
+ println!("\nRESULT:\n{}", result);
+ assert_eq!(toml, &result);
+}
+
+
+const PRETTY_INDENT_2: &'static str = "\
+[example]
+array = [
+ 'item 1',
+ 'item 2',
+]
+empty = []
+one = ['one']
+oneline = 'this has no newlines.'
+text = '''
+this is the first line
+this is the second line
+'''
+three = [
+ 'one',
+ 'two',
+ 'three',
+]
+";
+
+#[test]
+fn pretty_indent_2() {
+ let toml = PRETTY_INDENT_2;
+ let value: toml::Value = toml::from_str(toml).unwrap();
+ let mut result = String::with_capacity(128);
+ {
+ let mut serializer = toml::Serializer::pretty(&mut result);
+ serializer.pretty_array_indent(2);
+ value.serialize(&mut serializer).unwrap();
+ }
+ println!(">> Result:\n{}", result);
+ assert_eq!(toml, &result);
+}
+
+const PRETTY_INDENT_2_OTHER: &'static str = "\
+[example]
+array = [
+ \"item 1\",
+ \"item 2\",
+]
+empty = []
+oneline = \"this has no newlines.\"
+text = \"\\nthis is the first line\\nthis is the second line\\n\"
+";
+
+
+#[test]
+/// Test pretty indent when gotten the other way
+fn pretty_indent_2_other() {
+ let toml = PRETTY_INDENT_2_OTHER;
+ let value: toml::Value = toml::from_str(toml).unwrap();
+ let mut result = String::with_capacity(128);
+ {
+ let mut serializer = toml::Serializer::new(&mut result);
+ serializer.pretty_array_indent(2);
+ value.serialize(&mut serializer).unwrap();
+ }
+ assert_eq!(toml, &result);
+}
+
+
+const PRETTY_ARRAY_NO_COMMA: &'static str = "\
+[example]
+array = [
+ \"item 1\",
+ \"item 2\"
+]
+empty = []
+oneline = \"this has no newlines.\"
+text = \"\\nthis is the first line\\nthis is the second line\\n\"
+";
+#[test]
+/// Test pretty indent when gotten the other way
+fn pretty_indent_array_no_comma() {
+ let toml = PRETTY_ARRAY_NO_COMMA;
+ let value: toml::Value = toml::from_str(toml).unwrap();
+ let mut result = String::with_capacity(128);
+ {
+ let mut serializer = toml::Serializer::new(&mut result);
+ serializer.pretty_array_trailing_comma(false);
+ value.serialize(&mut serializer).unwrap();
+ }
+ assert_eq!(toml, &result);
+}
+
+
+const PRETTY_NO_STRING: &'static str = "\
+[example]
+array = [
+ \"item 1\",
+ \"item 2\",
+]
+empty = []
+oneline = \"this has no newlines.\"
+text = \"\\nthis is the first line\\nthis is the second line\\n\"
+";
+#[test]
+/// Test pretty indent when gotten the other way
+fn pretty_no_string() {
+ let toml = PRETTY_NO_STRING;
+ let value: toml::Value = toml::from_str(toml).unwrap();
+ let mut result = String::with_capacity(128);
+ {
+ let mut serializer = toml::Serializer::pretty(&mut result);
+ serializer.pretty_string(false);
+ value.serialize(&mut serializer).unwrap();
+ }
+ assert_eq!(toml, &result);
+}
+
+const PRETTY_TRICKY: &'static str = r##"[example]
+f = "\f"
+glass = '''
+Nothing too unusual, except that I can eat glass in:
+- Greek: Μπορώ να φάω σπασμένα γυαλιά χωρίς να πάθω τίποτα.
+- Polish: Mogę jeść szkło, i mi nie szkodzi.
+- Hindi: मैं काँच खा सकता हूँ, मुझे उस से कोई पीडा नहीं होती.
+- Japanese: 私はガラスを食べられます。それは私を傷つけません。
+'''
+r = "\r"
+r_newline = """
+\r
+"""
+single = '''this is a single line but has '' cuz it's tricky'''
+single_tricky = "single line with ''' in it"
+tabs = '''
+this is pretty standard
+ except for some tabs right here
+'''
+text = """
+this is the first line.
+This has a ''' in it and \"\"\" cuz it's tricky yo
+Also ' and \" because why not
+this is the fourth line
+"""
+"##;
+
+#[test]
+fn pretty_tricky() {
+ let toml = PRETTY_TRICKY;
+ let value: toml::Value = toml::from_str(toml).unwrap();
+ let mut result = String::with_capacity(128);
+ value.serialize(&mut toml::Serializer::pretty(&mut result)).unwrap();
+ println!("EXPECTED:\n{}", toml);
+ println!("\nRESULT:\n{}", result);
+ assert_eq!(toml, &result);
+}
+
+const PRETTY_TABLE_ARRAY: &'static str = r##"[[array]]
+key = 'foo'
+
+[[array]]
+key = 'bar'
+
+[abc]
+doc = 'this is a table'
+
+[example]
+single = 'this is a single line string'
+"##;
+
+#[test]
+fn pretty_table_array() {
+ let toml = PRETTY_TABLE_ARRAY;
+ let value: toml::Value = toml::from_str(toml).unwrap();
+ let mut result = String::with_capacity(128);
+ value.serialize(&mut toml::Serializer::pretty(&mut result)).unwrap();
+ println!("EXPECTED:\n{}", toml);
+ println!("\nRESULT:\n{}", result);
+ assert_eq!(toml, &result);
+}
+
+const TABLE_ARRAY: &'static str = r##"[[array]]
+key = "foo"
+
+[[array]]
+key = "bar"
+
+[abc]
+doc = "this is a table"
+
+[example]
+single = "this is a single line string"
+"##;
+
+#[test]
+fn table_array() {
+ let toml = TABLE_ARRAY;
+ let value: toml::Value = toml::from_str(toml).unwrap();
+ let mut result = String::with_capacity(128);
+ value.serialize(&mut toml::Serializer::new(&mut result)).unwrap();
+ println!("EXPECTED:\n{}", toml);
+ println!("\nRESULT:\n{}", result);
+ assert_eq!(toml, &result);
+}
+
+const PRETTY_TRICKY_NON_LITERAL: &'static str = r##"[example]
+f = "\f"
+glass = """
+Nothing too unusual, except that I can eat glass in:
+- Greek: Μπορώ να φάω σπασμένα γυαλιά χωρίς να πάθω τίποτα.
+- Polish: Mogę jeść szkło, i mi nie szkodzi.
+- Hindi: मैं काँच खा सकता हूँ, मुझे उस से कोई पीडा नहीं होती.
+- Japanese: 私はガラスを食べられます。それは私を傷つけません。
+"""
+plain = """
+This has a couple of lines
+Because it likes to.
+"""
+r = "\r"
+r_newline = """
+\r
+"""
+single = "this is a single line but has '' cuz it's tricky"
+single_tricky = "single line with ''' in it"
+tabs = """
+this is pretty standard
+\texcept for some \ttabs right here
+"""
+text = """
+this is the first line.
+This has a ''' in it and \"\"\" cuz it's tricky yo
+Also ' and \" because why not
+this is the fourth line
+"""
+"##;
+
+#[test]
+fn pretty_tricky_non_literal() {
+ let toml = PRETTY_TRICKY_NON_LITERAL;
+ let value: toml::Value = toml::from_str(toml).unwrap();
+ let mut result = String::with_capacity(128);
+ {
+ let mut serializer = toml::Serializer::pretty(&mut result);
+ serializer.pretty_string_literal(false);
+ value.serialize(&mut serializer).unwrap();
+ }
+ println!("EXPECTED:\n{}", toml);
+ println!("\nRESULT:\n{}", result);
+ assert_eq!(toml, &result);
+}
diff --git a/third_party/rust/toml/tests/serde.rs b/third_party/rust/toml/tests/serde.rs
new file mode 100644
index 0000000000..57fa5db052
--- /dev/null
+++ b/third_party/rust/toml/tests/serde.rs
@@ -0,0 +1,578 @@
+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! 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<'de> Deserialize<'de> for Range10 {
+ fn deserialize<D: Deserializer<'de>>(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 parse_enum_string() {
+ #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
+ struct Foo { a: Sort }
+
+ #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
+ #[serde(rename_all = "lowercase")]
+ enum Sort {
+ Asc,
+ Desc,
+ }
+
+ equivalent! {
+ Foo { a: Sort::Desc },
+ Table(map! { a: Value::String("desc".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());
+}
+
+#[test]
+fn newtypes() {
+ #[derive(Deserialize, Serialize, PartialEq, Debug, Clone)]
+ struct A {
+ b: B
+ }
+
+ #[derive(Deserialize, Serialize, PartialEq, Debug, Clone)]
+ struct B(u32);
+
+ equivalent! {
+ A { b: B(2) },
+ Table(map! { b: Integer(2) }),
+ }
+}
+
+#[test]
+fn newtypes2() {
+ #[derive(Deserialize, Serialize, PartialEq, Debug, Clone)]
+ struct A {
+ b: B
+ }
+
+ #[derive(Deserialize, Serialize, PartialEq, Debug, Clone)]
+ struct B(Option<C>);
+
+ #[derive(Deserialize, Serialize, PartialEq, Debug, Clone)]
+ struct C {
+ x: u32,
+ y: u32,
+ z: u32
+ }
+
+ equivalent! {
+ A { b: B(Some(C { x: 0, y: 1, z: 2 })) },
+ Table(map! {
+ b: Table(map! {
+ x: Integer(0),
+ y: Integer(1),
+ z: Integer(2)
+ })
+ }),
+ }
+}
+
+#[derive(Debug, Default, PartialEq, Serialize, Deserialize)]
+struct CanBeEmpty {
+ a: Option<String>,
+ b: Option<String>,
+}
+
+#[test]
+fn table_structs_empty() {
+ let text = "[bar]\n\n[baz]\n\n[bazv]\na = \"foo\"\n\n[foo]\n";
+ let value: BTreeMap<String, CanBeEmpty> = toml::from_str(text).unwrap();
+ let mut expected: BTreeMap<String, CanBeEmpty> = BTreeMap::new();
+ expected.insert("bar".to_string(), CanBeEmpty::default());
+ expected.insert("baz".to_string(), CanBeEmpty::default());
+ expected.insert(
+ "bazv".to_string(),
+ CanBeEmpty {a: Some("foo".to_string()), b: None},
+ );
+ expected.insert("foo".to_string(), CanBeEmpty::default());
+ assert_eq!(value, expected);
+ assert_eq!(toml::to_string(&value).unwrap(), text);
+}
diff --git a/third_party/rust/toml/tests/tables-last.rs b/third_party/rust/toml/tests/tables-last.rs
new file mode 100644
index 0000000000..d05c8f0bed
--- /dev/null
+++ b/third_party/rust/toml/tests/tables-last.rs
@@ -0,0 +1,30 @@
+#[macro_use]
+extern crate serde_derive;
+extern crate toml;
+
+use std::collections::HashMap;
+
+#[derive(Serialize)]
+struct A {
+ #[serde(serialize_with = "toml::ser::tables_last")]
+ vals: HashMap<&'static str, Value>,
+}
+
+#[derive(Serialize)]
+#[serde(untagged)]
+enum Value {
+ Map(HashMap<&'static str, &'static str>),
+ Int(i32),
+}
+
+#[test]
+fn always_works() {
+ let mut a = A { vals: HashMap::new() };
+ a.vals.insert("foo", Value::Int(0));
+
+ let mut sub = HashMap::new();
+ sub.insert("foo", "bar");
+ a.vals.insert("bar", Value::Map(sub));
+
+ toml::to_string(&a).unwrap();
+}
diff --git a/third_party/rust/toml/tests/valid.rs b/third_party/rust/toml/tests/valid.rs
new file mode 100644
index 0000000000..b1868007c4
--- /dev/null
+++ b/third_party/rust/toml/tests/valid.rs
@@ -0,0 +1,249 @@
+extern crate toml;
+extern crate serde;
+extern crate serde_json;
+
+use toml::{Value as Toml, to_string_pretty};
+use serde::ser::Serialize;
+use serde_json::Value as Json;
+
+fn to_json(toml: toml::Value) -> Json {
+ fn doit(s: &str, json: Json) -> 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 {
+ 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}
+ })),
+ 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(&Toml::Table(..)) => true,
+ _ => false,
+ };
+ let json = Json::Array(arr.into_iter().map(to_json).collect());
+ if is_table {json} else {doit("array", json)}
+ }
+ 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_pretty(toml: Toml) {
+ // Assert toml == json
+ println!("### pretty round trip parse.");
+
+ // standard pretty
+ let toml_raw = to_string_pretty(&toml).expect("to string");
+ let toml2 = toml_raw.parse().expect("from string");
+ assert_eq!(toml, toml2);
+
+ // pretty with indent 2
+ let mut result = String::with_capacity(128);
+ {
+ let mut serializer = toml::Serializer::pretty(&mut result);
+ serializer.pretty_array_indent(2);
+ toml.serialize(&mut serializer).expect("to string");
+ }
+ assert_eq!(toml, result.parse().expect("from str"));
+ result.clear();
+ {
+ let mut serializer = toml::Serializer::new(&mut result);
+ serializer.pretty_array_trailing_comma(false);
+ toml.serialize(&mut serializer).expect("to string");
+ }
+ assert_eq!(toml, result.parse().expect("from str"));
+ result.clear();
+ {
+ let mut serializer = toml::Serializer::pretty(&mut result);
+ serializer.pretty_string(false);
+ toml.serialize(&mut serializer).expect("to string");
+ assert_eq!(toml, toml2);
+ }
+ assert_eq!(toml, result.parse().expect("from str"));
+ result.clear();
+ {
+ let mut serializer = toml::Serializer::pretty(&mut result);
+ serializer.pretty_array(false);
+ toml.serialize(&mut serializer).expect("to string");
+ assert_eq!(toml, toml2);
+ }
+ assert_eq!(toml, result.parse().expect("from str"));
+}
+
+fn run(toml_raw: &str, json_raw: &str) {
+ println!("parsing:\n{}", toml_raw);
+ let toml: Toml = toml_raw.parse().unwrap();
+ let json: Json = json_raw.parse().unwrap();
+
+ // Assert toml == json
+ let toml_json = to_json(toml.clone());
+ assert!(json == toml_json,
+ "expected\n{}\ngot\n{}\n",
+ serde_json::to_string_pretty(&json).unwrap(),
+ serde_json::to_string_pretty(&toml_json).unwrap());
+
+ // Assert round trip
+ println!("round trip parse: {}", toml);
+ let toml2 = toml.to_string().parse().unwrap();
+ assert_eq!(toml, toml2);
+ run_pretty(toml);
+}
+
+macro_rules! test( ($name:ident, $toml:expr, $json:expr) => (
+ #[test]
+ fn $name() { run($toml, $json); }
+) );
+
+test!(array_empty,
+ include_str!("valid/array-empty.toml"),
+ include_str!("valid/array-empty.json"));
+test!(array_nospaces,
+ include_str!("valid/array-nospaces.toml"),
+ include_str!("valid/array-nospaces.json"));
+test!(arrays_hetergeneous,
+ include_str!("valid/arrays-hetergeneous.toml"),
+ include_str!("valid/arrays-hetergeneous.json"));
+test!(arrays,
+ include_str!("valid/arrays.toml"),
+ include_str!("valid/arrays.json"));
+test!(arrays_nested,
+ include_str!("valid/arrays-nested.toml"),
+ include_str!("valid/arrays-nested.json"));
+test!(empty,
+ include_str!("valid/empty.toml"),
+ include_str!("valid/empty.json"));
+test!(bool,
+ include_str!("valid/bool.toml"),
+ include_str!("valid/bool.json"));
+test!(datetime,
+ include_str!("valid/datetime.toml"),
+ include_str!("valid/datetime.json"));
+test!(example,
+ include_str!("valid/example.toml"),
+ include_str!("valid/example.json"));
+test!(float,
+ include_str!("valid/float.toml"),
+ include_str!("valid/float.json"));
+test!(implicit_and_explicit_after,
+ include_str!("valid/implicit-and-explicit-after.toml"),
+ include_str!("valid/implicit-and-explicit-after.json"));
+test!(implicit_and_explicit_before,
+ include_str!("valid/implicit-and-explicit-before.toml"),
+ include_str!("valid/implicit-and-explicit-before.json"));
+test!(implicit_groups,
+ include_str!("valid/implicit-groups.toml"),
+ include_str!("valid/implicit-groups.json"));
+test!(integer,
+ include_str!("valid/integer.toml"),
+ include_str!("valid/integer.json"));
+test!(key_equals_nospace,
+ include_str!("valid/key-equals-nospace.toml"),
+ include_str!("valid/key-equals-nospace.json"));
+test!(key_space,
+ include_str!("valid/key-space.toml"),
+ include_str!("valid/key-space.json"));
+test!(key_special_chars,
+ include_str!("valid/key-special-chars.toml"),
+ include_str!("valid/key-special-chars.json"));
+test!(key_with_pound,
+ include_str!("valid/key-with-pound.toml"),
+ include_str!("valid/key-with-pound.json"));
+test!(long_float,
+ include_str!("valid/long-float.toml"),
+ include_str!("valid/long-float.json"));
+test!(long_integer,
+ include_str!("valid/long-integer.toml"),
+ include_str!("valid/long-integer.json"));
+test!(multiline_string,
+ include_str!("valid/multiline-string.toml"),
+ include_str!("valid/multiline-string.json"));
+test!(raw_multiline_string,
+ include_str!("valid/raw-multiline-string.toml"),
+ include_str!("valid/raw-multiline-string.json"));
+test!(raw_string,
+ include_str!("valid/raw-string.toml"),
+ include_str!("valid/raw-string.json"));
+test!(string_empty,
+ include_str!("valid/string-empty.toml"),
+ include_str!("valid/string-empty.json"));
+test!(string_escapes,
+ include_str!("valid/string-escapes.toml"),
+ include_str!("valid/string-escapes.json"));
+test!(string_simple,
+ include_str!("valid/string-simple.toml"),
+ include_str!("valid/string-simple.json"));
+test!(string_with_pound,
+ include_str!("valid/string-with-pound.toml"),
+ include_str!("valid/string-with-pound.json"));
+test!(table_array_implicit,
+ include_str!("valid/table-array-implicit.toml"),
+ include_str!("valid/table-array-implicit.json"));
+test!(table_array_many,
+ include_str!("valid/table-array-many.toml"),
+ include_str!("valid/table-array-many.json"));
+test!(table_array_nest,
+ include_str!("valid/table-array-nest.toml"),
+ include_str!("valid/table-array-nest.json"));
+test!(table_array_one,
+ include_str!("valid/table-array-one.toml"),
+ include_str!("valid/table-array-one.json"));
+test!(table_empty,
+ include_str!("valid/table-empty.toml"),
+ include_str!("valid/table-empty.json"));
+test!(table_sub_empty,
+ include_str!("valid/table-sub-empty.toml"),
+ include_str!("valid/table-sub-empty.json"));
+test!(table_multi_empty,
+ include_str!("valid/table-multi-empty.toml"),
+ include_str!("valid/table-multi-empty.json"));
+test!(table_whitespace,
+ include_str!("valid/table-whitespace.toml"),
+ include_str!("valid/table-whitespace.json"));
+test!(table_with_pound,
+ include_str!("valid/table-with-pound.toml"),
+ include_str!("valid/table-with-pound.json"));
+test!(unicode_escape,
+ include_str!("valid/unicode-escape.toml"),
+ include_str!("valid/unicode-escape.json"));
+test!(unicode_literal,
+ include_str!("valid/unicode-literal.toml"),
+ include_str!("valid/unicode-literal.json"));
+test!(hard_example,
+ include_str!("valid/hard_example.toml"),
+ include_str!("valid/hard_example.json"));
+test!(example2,
+ include_str!("valid/example2.toml"),
+ include_str!("valid/example2.json"));
+test!(example3,
+ include_str!("valid/example-v0.3.0.toml"),
+ include_str!("valid/example-v0.3.0.json"));
+test!(example4,
+ include_str!("valid/example-v0.4.0.toml"),
+ include_str!("valid/example-v0.4.0.json"));
+test!(example_bom,
+ include_str!("valid/example-bom.toml"),
+ include_str!("valid/example.json"));
+
+test!(datetime_truncate,
+ include_str!("valid/datetime-truncate.toml"),
+ include_str!("valid/datetime-truncate.json"));
+test!(key_quote_newline,
+ include_str!("valid/key-quote-newline.toml"),
+ include_str!("valid/key-quote-newline.json"));
+test!(table_array_nest_no_keys,
+ include_str!("valid/table-array-nest-no-keys.toml"),
+ include_str!("valid/table-array-nest-no-keys.json"));
diff --git a/third_party/rust/toml/tests/valid/array-empty.json b/third_party/rust/toml/tests/valid/array-empty.json
new file mode 100644
index 0000000000..2fbf2567f8
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/array-empty.json
@@ -0,0 +1,11 @@
+{
+ "thevoid": { "type": "array", "value": [
+ {"type": "array", "value": [
+ {"type": "array", "value": [
+ {"type": "array", "value": [
+ {"type": "array", "value": []}
+ ]}
+ ]}
+ ]}
+ ]}
+}
diff --git a/third_party/rust/toml/tests/valid/array-empty.toml b/third_party/rust/toml/tests/valid/array-empty.toml
new file mode 100644
index 0000000000..fa58dc63d4
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/array-empty.toml
@@ -0,0 +1 @@
+thevoid = [[[[[]]]]]
diff --git a/third_party/rust/toml/tests/valid/array-nospaces.json b/third_party/rust/toml/tests/valid/array-nospaces.json
new file mode 100644
index 0000000000..1833d61c55
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/array-nospaces.json
@@ -0,0 +1,10 @@
+{
+ "ints": {
+ "type": "array",
+ "value": [
+ {"type": "integer", "value": "1"},
+ {"type": "integer", "value": "2"},
+ {"type": "integer", "value": "3"}
+ ]
+ }
+}
diff --git a/third_party/rust/toml/tests/valid/array-nospaces.toml b/third_party/rust/toml/tests/valid/array-nospaces.toml
new file mode 100644
index 0000000000..66189367fe
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/array-nospaces.toml
@@ -0,0 +1 @@
+ints = [1,2,3]
diff --git a/third_party/rust/toml/tests/valid/arrays-hetergeneous.json b/third_party/rust/toml/tests/valid/arrays-hetergeneous.json
new file mode 100644
index 0000000000..478fa5c706
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/arrays-hetergeneous.json
@@ -0,0 +1,19 @@
+{
+ "mixed": {
+ "type": "array",
+ "value": [
+ {"type": "array", "value": [
+ {"type": "integer", "value": "1"},
+ {"type": "integer", "value": "2"}
+ ]},
+ {"type": "array", "value": [
+ {"type": "string", "value": "a"},
+ {"type": "string", "value": "b"}
+ ]},
+ {"type": "array", "value": [
+ {"type": "float", "value": "1.1"},
+ {"type": "float", "value": "2.1"}
+ ]}
+ ]
+ }
+}
diff --git a/third_party/rust/toml/tests/valid/arrays-hetergeneous.toml b/third_party/rust/toml/tests/valid/arrays-hetergeneous.toml
new file mode 100644
index 0000000000..a246fcf1de
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/arrays-hetergeneous.toml
@@ -0,0 +1 @@
+mixed = [[1, 2], ["a", "b"], [1.1, 2.1]]
diff --git a/third_party/rust/toml/tests/valid/arrays-nested.json b/third_party/rust/toml/tests/valid/arrays-nested.json
new file mode 100644
index 0000000000..d21920cc3e
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/arrays-nested.json
@@ -0,0 +1,13 @@
+{
+ "nest": {
+ "type": "array",
+ "value": [
+ {"type": "array", "value": [
+ {"type": "string", "value": "a"}
+ ]},
+ {"type": "array", "value": [
+ {"type": "string", "value": "b"}
+ ]}
+ ]
+ }
+}
diff --git a/third_party/rust/toml/tests/valid/arrays-nested.toml b/third_party/rust/toml/tests/valid/arrays-nested.toml
new file mode 100644
index 0000000000..ce3302249b
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/arrays-nested.toml
@@ -0,0 +1 @@
+nest = [["a"], ["b"]]
diff --git a/third_party/rust/toml/tests/valid/arrays.json b/third_party/rust/toml/tests/valid/arrays.json
new file mode 100644
index 0000000000..58aedbccbe
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/arrays.json
@@ -0,0 +1,34 @@
+{
+ "ints": {
+ "type": "array",
+ "value": [
+ {"type": "integer", "value": "1"},
+ {"type": "integer", "value": "2"},
+ {"type": "integer", "value": "3"}
+ ]
+ },
+ "floats": {
+ "type": "array",
+ "value": [
+ {"type": "float", "value": "1.1"},
+ {"type": "float", "value": "2.1"},
+ {"type": "float", "value": "3.1"}
+ ]
+ },
+ "strings": {
+ "type": "array",
+ "value": [
+ {"type": "string", "value": "a"},
+ {"type": "string", "value": "b"},
+ {"type": "string", "value": "c"}
+ ]
+ },
+ "dates": {
+ "type": "array",
+ "value": [
+ {"type": "datetime", "value": "1987-07-05T17:45:00Z"},
+ {"type": "datetime", "value": "1979-05-27T07:32:00Z"},
+ {"type": "datetime", "value": "2006-06-01T11:00:00Z"}
+ ]
+ }
+}
diff --git a/third_party/rust/toml/tests/valid/arrays.toml b/third_party/rust/toml/tests/valid/arrays.toml
new file mode 100644
index 0000000000..c435f57b62
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/arrays.toml
@@ -0,0 +1,8 @@
+ints = [1, 2, 3]
+floats = [1.1, 2.1, 3.1]
+strings = ["a", "b", "c"]
+dates = [
+ 1987-07-05T17:45:00Z,
+ 1979-05-27T07:32:00Z,
+ 2006-06-01T11:00:00Z,
+]
diff --git a/third_party/rust/toml/tests/valid/bool.json b/third_party/rust/toml/tests/valid/bool.json
new file mode 100644
index 0000000000..ae368e9492
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/bool.json
@@ -0,0 +1,4 @@
+{
+ "f": {"type": "bool", "value": "false"},
+ "t": {"type": "bool", "value": "true"}
+}
diff --git a/third_party/rust/toml/tests/valid/bool.toml b/third_party/rust/toml/tests/valid/bool.toml
new file mode 100644
index 0000000000..a8a829b34d
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/bool.toml
@@ -0,0 +1,2 @@
+t = true
+f = false
diff --git a/third_party/rust/toml/tests/valid/comments-everywhere.json b/third_party/rust/toml/tests/valid/comments-everywhere.json
new file mode 100644
index 0000000000..e69a2e9582
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/comments-everywhere.json
@@ -0,0 +1,12 @@
+{
+ "group": {
+ "answer": {"type": "integer", "value": "42"},
+ "more": {
+ "type": "array",
+ "value": [
+ {"type": "integer", "value": "42"},
+ {"type": "integer", "value": "42"}
+ ]
+ }
+ }
+}
diff --git a/third_party/rust/toml/tests/valid/comments-everywhere.toml b/third_party/rust/toml/tests/valid/comments-everywhere.toml
new file mode 100644
index 0000000000..3dca74cade
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/comments-everywhere.toml
@@ -0,0 +1,24 @@
+# Top comment.
+ # Top comment.
+# Top comment.
+
+# [no-extraneous-groups-please]
+
+[group] # Comment
+answer = 42 # Comment
+# no-extraneous-keys-please = 999
+# Inbetween comment.
+more = [ # Comment
+ # What about multiple # comments?
+ # Can you handle it?
+ #
+ # Evil.
+# Evil.
+ 42, 42, # Comments within arrays are fun.
+ # What about multiple # comments?
+ # Can you handle it?
+ #
+ # Evil.
+# Evil.
+# ] Did I fool you?
+] # Hopefully not.
diff --git a/third_party/rust/toml/tests/valid/datetime-truncate.json b/third_party/rust/toml/tests/valid/datetime-truncate.json
new file mode 100644
index 0000000000..8c512e10c6
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/datetime-truncate.json
@@ -0,0 +1,6 @@
+{
+ "bestdayever": {
+ "type": "datetime",
+ "value": "1987-07-05T17:45:00.123456789Z"
+ }
+}
diff --git a/third_party/rust/toml/tests/valid/datetime-truncate.toml b/third_party/rust/toml/tests/valid/datetime-truncate.toml
new file mode 100644
index 0000000000..05de84105b
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/datetime-truncate.toml
@@ -0,0 +1 @@
+bestdayever = 1987-07-05T17:45:00.123456789012345Z
diff --git a/third_party/rust/toml/tests/valid/datetime.json b/third_party/rust/toml/tests/valid/datetime.json
new file mode 100644
index 0000000000..2ca93ce966
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/datetime.json
@@ -0,0 +1,3 @@
+{
+ "bestdayever": {"type": "datetime", "value": "1987-07-05T17:45:00Z"}
+}
diff --git a/third_party/rust/toml/tests/valid/datetime.toml b/third_party/rust/toml/tests/valid/datetime.toml
new file mode 100644
index 0000000000..2e993407d7
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/datetime.toml
@@ -0,0 +1 @@
+bestdayever = 1987-07-05T17:45:00Z
diff --git a/third_party/rust/toml/tests/valid/empty.json b/third_party/rust/toml/tests/valid/empty.json
new file mode 100644
index 0000000000..0967ef424b
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/empty.json
@@ -0,0 +1 @@
+{}
diff --git a/third_party/rust/toml/tests/valid/empty.toml b/third_party/rust/toml/tests/valid/empty.toml
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/empty.toml
diff --git a/third_party/rust/toml/tests/valid/example-bom.toml b/third_party/rust/toml/tests/valid/example-bom.toml
new file mode 100644
index 0000000000..fb5ac815c9
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/example-bom.toml
@@ -0,0 +1,5 @@
+best-day-ever = 1987-07-05T17:45:00Z
+
+[numtheory]
+boring = false
+perfection = [6, 28, 496]
diff --git a/third_party/rust/toml/tests/valid/example-v0.3.0.json b/third_party/rust/toml/tests/valid/example-v0.3.0.json
new file mode 100644
index 0000000000..1d9dcb581c
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/example-v0.3.0.json
@@ -0,0 +1 @@
+{"Array":{"key1":{"type":"array","value":[{"type":"integer","value":"1"},{"type":"integer","value":"2"},{"type":"integer","value":"3"}]},"key2":{"type":"array","value":[{"type":"string","value":"red"},{"type":"string","value":"yellow"},{"type":"string","value":"green"}]},"key3":{"type":"array","value":[{"type":"array","value":[{"type":"integer","value":"1"},{"type":"integer","value":"2"}]},{"type":"array","value":[{"type":"integer","value":"3"},{"type":"integer","value":"4"},{"type":"integer","value":"5"}]}]},"key4":{"type":"array","value":[{"type":"array","value":[{"type":"integer","value":"1"},{"type":"integer","value":"2"}]},{"type":"array","value":[{"type":"string","value":"a"},{"type":"string","value":"b"},{"type":"string","value":"c"}]}]},"key5":{"type":"array","value":[{"type":"integer","value":"1"},{"type":"integer","value":"2"},{"type":"integer","value":"3"}]},"key6":{"type":"array","value":[{"type":"integer","value":"1"},{"type":"integer","value":"2"}]}},"Booleans":{"False":{"type":"bool","value":"false"},"True":{"type":"bool","value":"true"}},"Datetime":{"key1":{"type":"datetime","value":"1979-05-27T07:32:00Z"}},"Float":{"both":{},"exponent":{},"fractional":{"key1":{"type":"float","value":"1.0"},"key2":{"type":"float","value":"3.1415"},"key3":{"type":"float","value":"-0.01"}}},"Integer":{"key1":{"type":"integer","value":"99"},"key2":{"type":"integer","value":"42"},"key3":{"type":"integer","value":"0"},"key4":{"type":"integer","value":"-17"}},"String":{"Literal":{"Multiline":{"lines":{"type":"string","value":"The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n"},"regex2":{"type":"string","value":"I [dw]on't need \\d{2} apples"}},"quoted":{"type":"string","value":"Tom \"Dubs\" Preston-Werner"},"regex":{"type":"string","value":"\u003c\\i\\c*\\s*\u003e"},"winpath":{"type":"string","value":"C:\\Users\\nodejs\\templates"},"winpath2":{"type":"string","value":"\\\\ServerX\\admin$\\system32\\"}},"Multiline":{"key1":{"type":"string","value":"One\nTwo"},"key2":{"type":"string","value":"One\nTwo"},"key3":{"type":"string","value":"One\nTwo"}},"Multilined":{"Singleline":{"key1":{"type":"string","value":"The quick brown fox jumps over the lazy dog."},"key2":{"type":"string","value":"The quick brown fox jumps over the lazy dog."},"key3":{"type":"string","value":"The quick brown fox jumps over the lazy dog."}}},"basic":{"type":"string","value":"I'm a string. \"You can quote me\". Name\u0009José\nLocation\u0009SF."}},"Table":{"key":{"type":"string","value":"value"}},"dog":{"tater":{"type":{"type":"string","value":"pug"}}},"fruit":[{"name":{"type":"string","value":"apple"},"physical":{"color":{"type":"string","value":"red"},"shape":{"type":"string","value":"round"}},"variety":[{"name":{"type":"string","value":"red delicious"}},{"name":{"type":"string","value":"granny smith"}}]},{"name":{"type":"string","value":"banana"},"variety":[{"name":{"type":"string","value":"plantain"}}]}],"products":[{"name":{"type":"string","value":"Hammer"},"sku":{"type":"integer","value":"738594937"}},{},{"color":{"type":"string","value":"gray"},"name":{"type":"string","value":"Nail"},"sku":{"type":"integer","value":"284758393"}}],"x":{"y":{"z":{"w":{}}}}}
diff --git a/third_party/rust/toml/tests/valid/example-v0.3.0.toml b/third_party/rust/toml/tests/valid/example-v0.3.0.toml
new file mode 100644
index 0000000000..76aacc31aa
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/example-v0.3.0.toml
@@ -0,0 +1,182 @@
+# Comment
+# I am a comment. Hear me roar. Roar.
+
+# Table
+# Tables (also known as hash tables or dictionaries) are collections of key/value pairs.
+# They appear in square brackets on a line by themselves.
+
+[Table]
+
+key = "value" # Yeah, you can do this.
+
+# Nested tables are denoted by table names with dots in them. Name your tables whatever crap you please, just don't use #, ., [ or ].
+
+[dog.tater]
+type = "pug"
+
+# You don't need to specify all the super-tables if you don't want to. TOML knows how to do it for you.
+
+# [x] you
+# [x.y] don't
+# [x.y.z] need these
+[x.y.z.w] # for this to work
+
+# String
+# There are four ways to express strings: basic, multi-line basic, literal, and multi-line literal.
+# All strings must contain only valid UTF-8 characters.
+
+[String]
+basic = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF."
+
+[String.Multiline]
+
+# The following strings are byte-for-byte equivalent:
+key1 = "One\nTwo"
+key2 = """One\nTwo"""
+key3 = """
+One
+Two"""
+
+[String.Multilined.Singleline]
+
+# The following strings are byte-for-byte equivalent:
+key1 = "The quick brown fox jumps over the lazy dog."
+
+key2 = """
+The quick brown \
+
+
+ fox jumps over \
+ the lazy dog."""
+
+key3 = """\
+ The quick brown \
+ fox jumps over \
+ the lazy dog.\
+ """
+
+[String.Literal]
+
+# 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*>'
+
+
+[String.Literal.Multiline]
+
+regex2 = '''I [dw]on't need \d{2} apples'''
+lines = '''
+The first newline is
+trimmed in raw strings.
+ All other whitespace
+ is preserved.
+'''
+
+# Integer
+# Integers are whole numbers. Positive numbers may be prefixed with a plus sign.
+# Negative numbers are prefixed with a minus sign.
+
+[Integer]
+key1 = +99
+key2 = 42
+key3 = 0
+key4 = -17
+
+# Float
+# A float consists of an integer part (which may be prefixed with a plus or minus sign)
+# followed by a fractional part and/or an exponent part.
+
+[Float.fractional]
+
+# fractional
+key1 = +1.0
+key2 = 3.1415
+key3 = -0.01
+
+[Float.exponent]
+
+# exponent
+#key1 = 5e+22
+#key2 = 1e6
+#key3 = -2E-2
+
+[Float.both]
+
+# both
+#key = 6.626e-34
+
+# Boolean
+# Booleans are just the tokens you're used to. Always lowercase.
+
+[Booleans]
+True = true
+False = false
+
+# Datetime
+# Datetimes are RFC 3339 dates.
+
+[Datetime]
+key1 = 1979-05-27T07:32:00Z
+#key2 = 1979-05-27T00:32:00-07:00
+#key3 = 1979-05-27T00:32:00.999999-07:00
+
+# Array
+# Arrays are square brackets with other primitives inside. Whitespace is ignored. Elements are separated by commas. Data types may not be mixed.
+
+[Array]
+key1 = [ 1, 2, 3 ]
+key2 = [ "red", "yellow", "green" ]
+key3 = [ [ 1, 2 ], [3, 4, 5] ]
+key4 = [ [ 1, 2 ], ["a", "b", "c"] ] # this is ok
+
+#Arrays can also be multiline. So in addition to ignoring whitespace, arrays also ignore newlines between the brackets.
+# Terminating commas are ok before the closing bracket.
+
+key5 = [
+ 1, 2, 3
+]
+key6 = [
+ 1,
+ 2, # this is ok
+]
+
+# Array of Tables
+# These can be expressed by using a table name in double brackets.
+# Each table with the same double bracketed name will be an element in the array.
+# The tables are inserted in the order encountered.
+
+[[products]]
+name = "Hammer"
+sku = 738594937
+
+[[products]]
+
+[[products]]
+name = "Nail"
+sku = 284758393
+color = "gray"
+
+
+# You can create nested arrays of tables as well.
+
+[[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"
+
diff --git a/third_party/rust/toml/tests/valid/example-v0.4.0.json b/third_party/rust/toml/tests/valid/example-v0.4.0.json
new file mode 100644
index 0000000000..d5cac343a8
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/example-v0.4.0.json
@@ -0,0 +1 @@
+{"array":{"key1":{"type":"array","value":[{"type":"integer","value":"1"},{"type":"integer","value":"2"},{"type":"integer","value":"3"}]},"key2":{"type":"array","value":[{"type":"string","value":"red"},{"type":"string","value":"yellow"},{"type":"string","value":"green"}]},"key3":{"type":"array","value":[{"type":"array","value":[{"type":"integer","value":"1"},{"type":"integer","value":"2"}]},{"type":"array","value":[{"type":"integer","value":"3"},{"type":"integer","value":"4"},{"type":"integer","value":"5"}]}]},"key4":{"type":"array","value":[{"type":"array","value":[{"type":"integer","value":"1"},{"type":"integer","value":"2"}]},{"type":"array","value":[{"type":"string","value":"a"},{"type":"string","value":"b"},{"type":"string","value":"c"}]}]},"key5":{"type":"array","value":[{"type":"integer","value":"1"},{"type":"integer","value":"2"},{"type":"integer","value":"3"}]},"key6":{"type":"array","value":[{"type":"integer","value":"1"},{"type":"integer","value":"2"}]}},"boolean":{"False":{"type":"bool","value":"false"},"True":{"type":"bool","value":"true"}},"datetime":{},"float":{"both":{},"exponent":{},"fractional":{"key1":{"type":"float","value":"1.0"},"key2":{"type":"float","value":"3.1415"},"key3":{"type":"float","value":"-0.01"}},"underscores":{}},"fruit":[{"name":{"type":"string","value":"apple"},"physical":{"color":{"type":"string","value":"red"},"shape":{"type":"string","value":"round"}},"variety":[{"name":{"type":"string","value":"red delicious"}},{"name":{"type":"string","value":"granny smith"}}]},{"name":{"type":"string","value":"banana"},"variety":[{"name":{"type":"string","value":"plantain"}}]}],"integer":{"key1":{"type":"integer","value":"99"},"key2":{"type":"integer","value":"42"},"key3":{"type":"integer","value":"0"},"key4":{"type":"integer","value":"-17"},"underscores":{"key1":{"type":"integer","value":"1000"},"key2":{"type":"integer","value":"5349221"},"key3":{"type":"integer","value":"12345"}}},"products":[{"name":{"type":"string","value":"Hammer"},"sku":{"type":"integer","value":"738594937"}},{},{"color":{"type":"string","value":"gray"},"name":{"type":"string","value":"Nail"},"sku":{"type":"integer","value":"284758393"}}],"string":{"basic":{"basic":{"type":"string","value":"I'm a string. \"You can quote me\". Name\u0009José\nLocation\u0009SF."}},"literal":{"multiline":{"lines":{"type":"string","value":"The first newline is\ntrimmed in raw strings.\n All other whitespace\n is preserved.\n"},"regex2":{"type":"string","value":"I [dw]on't need \\d{2} apples"}},"quoted":{"type":"string","value":"Tom \"Dubs\" Preston-Werner"},"regex":{"type":"string","value":"\u003c\\i\\c*\\s*\u003e"},"winpath":{"type":"string","value":"C:\\Users\\nodejs\\templates"},"winpath2":{"type":"string","value":"\\\\ServerX\\admin$\\system32\\"}},"multiline":{"continued":{"key1":{"type":"string","value":"The quick brown fox jumps over the lazy dog."},"key2":{"type":"string","value":"The quick brown fox jumps over the lazy dog."},"key3":{"type":"string","value":"The quick brown fox jumps over the lazy dog."}},"key1":{"type":"string","value":"One\nTwo"},"key2":{"type":"string","value":"One\nTwo"},"key3":{"type":"string","value":"One\nTwo"}}},"table":{"inline":{"name":{"first":{"type":"string","value":"Tom"},"last":{"type":"string","value":"Preston-Werner"}},"point":{"x":{"type":"integer","value":"1"},"y":{"type":"integer","value":"2"}}},"key":{"type":"string","value":"value"},"subtable":{"key":{"type":"string","value":"another value"}}},"x":{"y":{"z":{"w":{}}}}}
diff --git a/third_party/rust/toml/tests/valid/example-v0.4.0.toml b/third_party/rust/toml/tests/valid/example-v0.4.0.toml
new file mode 100644
index 0000000000..ffbcce0d9d
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/example-v0.4.0.toml
@@ -0,0 +1,235 @@
+################################################################################
+## Comment
+
+# Speak your mind with the hash symbol. They go from the symbol to the end of
+# the line.
+
+
+################################################################################
+## Table
+
+# Tables (also known as hash tables or dictionaries) are collections of
+# key/value pairs. They appear in square brackets on a line by themselves.
+
+[table]
+
+key = "value" # Yeah, you can do this.
+
+# Nested tables are denoted by table names with dots in them. Name your tables
+# whatever crap you please, just don't use #, ., [ or ].
+
+[table.subtable]
+
+key = "another value"
+
+# You don't need to specify all the super-tables if you don't want to. TOML
+# knows how to do it for you.
+
+# [x] you
+# [x.y] don't
+# [x.y.z] need these
+[x.y.z.w] # for this to work
+
+
+################################################################################
+## Inline Table
+
+# Inline tables provide a more compact syntax for expressing tables. They are
+# especially useful for grouped data that can otherwise quickly become verbose.
+# Inline tables are enclosed in curly braces `{` and `}`. No newlines are
+# allowed between the curly braces unless they are valid within a value.
+
+[table.inline]
+
+name = { first = "Tom", last = "Preston-Werner" }
+point = { x = 1, y = 2 }
+
+
+################################################################################
+## String
+
+# There are four ways to express strings: basic, multi-line basic, literal, and
+# multi-line literal. All strings must contain only valid UTF-8 characters.
+
+[string.basic]
+
+basic = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF."
+
+[string.multiline]
+
+# The following strings are byte-for-byte equivalent:
+key1 = "One\nTwo"
+key2 = """One\nTwo"""
+key3 = """
+One
+Two"""
+
+[string.multiline.continued]
+
+# The following strings are byte-for-byte equivalent:
+key1 = "The quick brown fox jumps over the lazy dog."
+
+key2 = """
+The quick brown \
+
+
+ fox jumps over \
+ the lazy dog."""
+
+key3 = """\
+ The quick brown \
+ fox jumps over \
+ the lazy dog.\
+ """
+
+[string.literal]
+
+# 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*>'
+
+
+[string.literal.multiline]
+
+regex2 = '''I [dw]on't need \d{2} apples'''
+lines = '''
+The first newline is
+trimmed in raw strings.
+ All other whitespace
+ is preserved.
+'''
+
+
+################################################################################
+## Integer
+
+# Integers are whole numbers. Positive numbers may be prefixed with a plus sign.
+# Negative numbers are prefixed with a minus sign.
+
+[integer]
+
+key1 = +99
+key2 = 42
+key3 = 0
+key4 = -17
+
+[integer.underscores]
+
+# For large numbers, you may use underscores to enhance readability. Each
+# underscore must be surrounded by at least one digit.
+key1 = 1_000
+key2 = 5_349_221
+key3 = 1_2_3_4_5 # valid but inadvisable
+
+
+################################################################################
+## Float
+
+# A float consists of an integer part (which may be prefixed with a plus or
+# minus sign) followed by a fractional part and/or an exponent part.
+
+[float.fractional]
+
+key1 = +1.0
+key2 = 3.1415
+key3 = -0.01
+
+[float.exponent]
+
+[float.both]
+
+[float.underscores]
+
+
+################################################################################
+## Boolean
+
+# Booleans are just the tokens you're used to. Always lowercase.
+
+[boolean]
+
+True = true
+False = false
+
+
+################################################################################
+## Datetime
+
+# Datetimes are RFC 3339 dates.
+
+[datetime]
+
+#key1 = 1979-05-27T07:32:00Z
+#key2 = 1979-05-27T00:32:00-07:00
+#key3 = 1979-05-27T00:32:00.999999-07:00
+
+
+################################################################################
+## Array
+
+# Arrays are square brackets with other primitives inside. Whitespace is
+# ignored. Elements are separated by commas. Data types may not be mixed.
+
+[array]
+
+key1 = [ 1, 2, 3 ]
+key2 = [ "red", "yellow", "green" ]
+key3 = [ [ 1, 2 ], [3, 4, 5] ]
+key4 = [ [ 1, 2 ], ["a", "b", "c"] ] # this is ok
+
+# Arrays can also be multiline. So in addition to ignoring whitespace, arrays
+# also ignore newlines between the brackets. Terminating commas are ok before
+# the closing bracket.
+
+key5 = [
+ 1, 2, 3
+]
+key6 = [
+ 1,
+ 2, # this is ok
+]
+
+
+################################################################################
+## Array of Tables
+
+# These can be expressed by using a table name in double brackets. Each table
+# with the same double bracketed name will be an element in the array. The
+# tables are inserted in the order encountered.
+
+[[products]]
+
+name = "Hammer"
+sku = 738594937
+
+[[products]]
+
+[[products]]
+
+name = "Nail"
+sku = 284758393
+color = "gray"
+
+
+# You can create nested arrays of tables as well.
+
+[[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"
diff --git a/third_party/rust/toml/tests/valid/example.json b/third_party/rust/toml/tests/valid/example.json
new file mode 100644
index 0000000000..48aa90784a
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/example.json
@@ -0,0 +1,14 @@
+{
+ "best-day-ever": {"type": "datetime", "value": "1987-07-05T17:45:00Z"},
+ "numtheory": {
+ "boring": {"type": "bool", "value": "false"},
+ "perfection": {
+ "type": "array",
+ "value": [
+ {"type": "integer", "value": "6"},
+ {"type": "integer", "value": "28"},
+ {"type": "integer", "value": "496"}
+ ]
+ }
+ }
+}
diff --git a/third_party/rust/toml/tests/valid/example.toml b/third_party/rust/toml/tests/valid/example.toml
new file mode 100644
index 0000000000..8cb02e01b0
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/example.toml
@@ -0,0 +1,5 @@
+best-day-ever = 1987-07-05T17:45:00Z
+
+[numtheory]
+boring = false
+perfection = [6, 28, 496]
diff --git a/third_party/rust/toml/tests/valid/example2.json b/third_party/rust/toml/tests/valid/example2.json
new file mode 100644
index 0000000000..3249a974fb
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/example2.json
@@ -0,0 +1 @@
+{"clients":{"data":{"type":"array","value":[{"type":"array","value":[{"type":"string","value":"gamma"},{"type":"string","value":"delta"}]},{"type":"array","value":[{"type":"integer","value":"1"},{"type":"integer","value":"2"}]}]},"hosts":{"type":"array","value":[{"type":"string","value":"alpha"},{"type":"string","value":"omega"}]}},"database":{"connection_max":{"type":"integer","value":"5000"},"enabled":{"type":"bool","value":"true"},"ports":{"type":"array","value":[{"type":"integer","value":"8001"},{"type":"integer","value":"8001"},{"type":"integer","value":"8002"}]},"server":{"type":"string","value":"192.168.1.1"}},"owner":{"bio":{"type":"string","value":"GitHub Cofounder \u0026 CEO\nLikes tater tots and beer."},"dob":{"type":"datetime","value":"1979-05-27T07:32:00Z"},"name":{"type":"string","value":"Tom Preston-Werner"},"organization":{"type":"string","value":"GitHub"}},"products":[{"name":{"type":"string","value":"Hammer"},"sku":{"type":"integer","value":"738594937"}},{"color":{"type":"string","value":"gray"},"name":{"type":"string","value":"Nail"},"sku":{"type":"integer","value":"284758393"}}],"servers":{"alpha":{"dc":{"type":"string","value":"eqdc10"},"ip":{"type":"string","value":"10.0.0.1"}},"beta":{"country":{"type":"string","value":"中国"},"dc":{"type":"string","value":"eqdc10"},"ip":{"type":"string","value":"10.0.0.2"}}},"title":{"type":"string","value":"TOML Example"}}
diff --git a/third_party/rust/toml/tests/valid/example2.toml b/third_party/rust/toml/tests/valid/example2.toml
new file mode 100644
index 0000000000..bc12c99016
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/example2.toml
@@ -0,0 +1,47 @@
+# This is a TOML document. Boom.
+
+title = "TOML Example"
+
+[owner]
+name = "Tom Preston-Werner"
+organization = "GitHub"
+bio = "GitHub Cofounder & CEO\nLikes tater tots and beer."
+dob = 1979-05-27T07:32:00Z # First class dates? Why not?
+
+[database]
+server = "192.168.1.1"
+ports = [ 8001, 8001, 8002 ]
+connection_max = 5000
+enabled = true
+
+[servers]
+
+ # You can indent as you please. Tabs or spaces. TOML don't care.
+ [servers.alpha]
+ ip = "10.0.0.1"
+ dc = "eqdc10"
+
+ [servers.beta]
+ ip = "10.0.0.2"
+ dc = "eqdc10"
+ country = "中国" # This should be parsed as UTF-8
+
+[clients]
+data = [ ["gamma", "delta"], [1, 2] ] # just an update to make sure parsers support it
+
+# Line breaks are OK when inside arrays
+hosts = [
+ "alpha",
+ "omega"
+]
+
+# Products
+
+ [[products]]
+ name = "Hammer"
+ sku = 738594937
+
+ [[products]]
+ name = "Nail"
+ sku = 284758393
+ color = "gray"
diff --git a/third_party/rust/toml/tests/valid/float.json b/third_party/rust/toml/tests/valid/float.json
new file mode 100644
index 0000000000..b8a2e97581
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/float.json
@@ -0,0 +1,4 @@
+{
+ "pi": {"type": "float", "value": "3.14"},
+ "negpi": {"type": "float", "value": "-3.14"}
+}
diff --git a/third_party/rust/toml/tests/valid/float.toml b/third_party/rust/toml/tests/valid/float.toml
new file mode 100644
index 0000000000..7c528d200c
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/float.toml
@@ -0,0 +1,2 @@
+pi = 3.14
+negpi = -3.14
diff --git a/third_party/rust/toml/tests/valid/hard_example.json b/third_party/rust/toml/tests/valid/hard_example.json
new file mode 100644
index 0000000000..9762e58ef3
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/hard_example.json
@@ -0,0 +1 @@
+{"the":{"hard":{"another_test_string":{"type":"string","value":" Same thing, but with a string #"},"bit#":{"multi_line_array":{"type":"array","value":[{"type":"string","value":"]"}]},"what?":{"type":"string","value":"You don't think some user won't do that?"}},"harder_test_string":{"type":"string","value":" And when \"'s are in the string, along with # \""},"test_array":{"type":"array","value":[{"type":"string","value":"] "},{"type":"string","value":" # "}]},"test_array2":{"type":"array","value":[{"type":"string","value":"Test #11 ]proved that"},{"type":"string","value":"Experiment #9 was a success"}]}},"test_string":{"type":"string","value":"You'll hate me after this - #"}}}
diff --git a/third_party/rust/toml/tests/valid/hard_example.toml b/third_party/rust/toml/tests/valid/hard_example.toml
new file mode 100644
index 0000000000..38856c8737
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/hard_example.toml
@@ -0,0 +1,33 @@
+# Test file for TOML
+# Only this one tries to emulate a TOML file written by a user of the kind of parser writers probably hate
+# This part you'll really hate
+
+[the]
+test_string = "You'll hate me after this - #" # " Annoying, isn't it?
+
+ [the.hard]
+ test_array = [ "] ", " # "] # ] There you go, parse this!
+ test_array2 = [ "Test #11 ]proved that", "Experiment #9 was a success" ]
+ # You didn't think it'd as easy as chucking out the last #, did you?
+ another_test_string = " Same thing, but with a string #"
+ harder_test_string = " And when \"'s are in the string, along with # \"" # "and comments are there too"
+ # Things will get harder
+
+ [the.hard."bit#"]
+ "what?" = "You don't think some user won't do that?"
+ multi_line_array = [
+ "]",
+ # ] Oh yes I did
+ ]
+
+# Each of the following keygroups/key value pairs should produce an error. Uncomment to them to test
+
+#[error] if you didn't catch this, your parser is broken
+#string = "Anything other than tabs, spaces and newline after a keygroup or key value pair has ended should produce an error unless it is a comment" like this
+#array = [
+# "This might most likely happen in multiline arrays",
+# Like here,
+# "or here,
+# and here"
+# ] End of array comment, forgot the #
+#number = 3.14 pi <--again forgot the #
diff --git a/third_party/rust/toml/tests/valid/implicit-and-explicit-after.json b/third_party/rust/toml/tests/valid/implicit-and-explicit-after.json
new file mode 100644
index 0000000000..374bd09343
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/implicit-and-explicit-after.json
@@ -0,0 +1,10 @@
+{
+ "a": {
+ "better": {"type": "integer", "value": "43"},
+ "b": {
+ "c": {
+ "answer": {"type": "integer", "value": "42"}
+ }
+ }
+ }
+}
diff --git a/third_party/rust/toml/tests/valid/implicit-and-explicit-after.toml b/third_party/rust/toml/tests/valid/implicit-and-explicit-after.toml
new file mode 100644
index 0000000000..c0e8865b39
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/implicit-and-explicit-after.toml
@@ -0,0 +1,5 @@
+[a.b.c]
+answer = 42
+
+[a]
+better = 43
diff --git a/third_party/rust/toml/tests/valid/implicit-and-explicit-before.json b/third_party/rust/toml/tests/valid/implicit-and-explicit-before.json
new file mode 100644
index 0000000000..374bd09343
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/implicit-and-explicit-before.json
@@ -0,0 +1,10 @@
+{
+ "a": {
+ "better": {"type": "integer", "value": "43"},
+ "b": {
+ "c": {
+ "answer": {"type": "integer", "value": "42"}
+ }
+ }
+ }
+}
diff --git a/third_party/rust/toml/tests/valid/implicit-and-explicit-before.toml b/third_party/rust/toml/tests/valid/implicit-and-explicit-before.toml
new file mode 100644
index 0000000000..eee68ff514
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/implicit-and-explicit-before.toml
@@ -0,0 +1,5 @@
+[a]
+better = 43
+
+[a.b.c]
+answer = 42
diff --git a/third_party/rust/toml/tests/valid/implicit-groups.json b/third_party/rust/toml/tests/valid/implicit-groups.json
new file mode 100644
index 0000000000..fbae7fc71b
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/implicit-groups.json
@@ -0,0 +1,9 @@
+{
+ "a": {
+ "b": {
+ "c": {
+ "answer": {"type": "integer", "value": "42"}
+ }
+ }
+ }
+}
diff --git a/third_party/rust/toml/tests/valid/implicit-groups.toml b/third_party/rust/toml/tests/valid/implicit-groups.toml
new file mode 100644
index 0000000000..b6333e49d5
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/implicit-groups.toml
@@ -0,0 +1,2 @@
+[a.b.c]
+answer = 42
diff --git a/third_party/rust/toml/tests/valid/integer.json b/third_party/rust/toml/tests/valid/integer.json
new file mode 100644
index 0000000000..61985a1e97
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/integer.json
@@ -0,0 +1,4 @@
+{
+ "answer": {"type": "integer", "value": "42"},
+ "neganswer": {"type": "integer", "value": "-42"}
+}
diff --git a/third_party/rust/toml/tests/valid/integer.toml b/third_party/rust/toml/tests/valid/integer.toml
new file mode 100644
index 0000000000..c4f62972cb
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/integer.toml
@@ -0,0 +1,2 @@
+answer = 42
+neganswer = -42
diff --git a/third_party/rust/toml/tests/valid/key-equals-nospace.json b/third_party/rust/toml/tests/valid/key-equals-nospace.json
new file mode 100644
index 0000000000..1f8709ab9f
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/key-equals-nospace.json
@@ -0,0 +1,3 @@
+{
+ "answer": {"type": "integer", "value": "42"}
+}
diff --git a/third_party/rust/toml/tests/valid/key-equals-nospace.toml b/third_party/rust/toml/tests/valid/key-equals-nospace.toml
new file mode 100644
index 0000000000..560901c5a4
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/key-equals-nospace.toml
@@ -0,0 +1 @@
+answer=42
diff --git a/third_party/rust/toml/tests/valid/key-quote-newline.json b/third_party/rust/toml/tests/valid/key-quote-newline.json
new file mode 100644
index 0000000000..12473e4202
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/key-quote-newline.json
@@ -0,0 +1,3 @@
+{
+ "\n": {"type": "integer", "value": "1"}
+}
diff --git a/third_party/rust/toml/tests/valid/key-quote-newline.toml b/third_party/rust/toml/tests/valid/key-quote-newline.toml
new file mode 100644
index 0000000000..a2639bfbb7
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/key-quote-newline.toml
@@ -0,0 +1 @@
+"\n" = 1
diff --git a/third_party/rust/toml/tests/valid/key-space.json b/third_party/rust/toml/tests/valid/key-space.json
new file mode 100644
index 0000000000..9d1f76911d
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/key-space.json
@@ -0,0 +1,3 @@
+{
+ "a b": {"type": "integer", "value": "1"}
+}
diff --git a/third_party/rust/toml/tests/valid/key-space.toml b/third_party/rust/toml/tests/valid/key-space.toml
new file mode 100644
index 0000000000..f4f36c4f6d
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/key-space.toml
@@ -0,0 +1 @@
+"a b" = 1
diff --git a/third_party/rust/toml/tests/valid/key-special-chars.json b/third_party/rust/toml/tests/valid/key-special-chars.json
new file mode 100644
index 0000000000..6550ebda23
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/key-special-chars.json
@@ -0,0 +1,5 @@
+{
+ "~!@#$^&*()_+-`1234567890[]\\|/?><.,;:'": {
+ "type": "integer", "value": "1"
+ }
+}
diff --git a/third_party/rust/toml/tests/valid/key-special-chars.toml b/third_party/rust/toml/tests/valid/key-special-chars.toml
new file mode 100644
index 0000000000..dc43625d23
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/key-special-chars.toml
@@ -0,0 +1 @@
+"~!@#$^&*()_+-`1234567890[]\\|/?><.,;:'" = 1
diff --git a/third_party/rust/toml/tests/valid/key-with-pound.json b/third_party/rust/toml/tests/valid/key-with-pound.json
new file mode 100644
index 0000000000..ee39e1de4c
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/key-with-pound.json
@@ -0,0 +1,3 @@
+{
+ "key#name": {"type": "integer", "value": "5"}
+}
diff --git a/third_party/rust/toml/tests/valid/key-with-pound.toml b/third_party/rust/toml/tests/valid/key-with-pound.toml
new file mode 100644
index 0000000000..65b766fd15
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/key-with-pound.toml
@@ -0,0 +1 @@
+"key#name" = 5
diff --git a/third_party/rust/toml/tests/valid/long-float.json b/third_party/rust/toml/tests/valid/long-float.json
new file mode 100644
index 0000000000..8ceed47971
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/long-float.json
@@ -0,0 +1,4 @@
+{
+ "longpi": {"type": "float", "value": "3.141592653589793"},
+ "neglongpi": {"type": "float", "value": "-3.141592653589793"}
+}
diff --git a/third_party/rust/toml/tests/valid/long-float.toml b/third_party/rust/toml/tests/valid/long-float.toml
new file mode 100644
index 0000000000..9558ae47c0
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/long-float.toml
@@ -0,0 +1,2 @@
+longpi = 3.141592653589793
+neglongpi = -3.141592653589793
diff --git a/third_party/rust/toml/tests/valid/long-integer.json b/third_party/rust/toml/tests/valid/long-integer.json
new file mode 100644
index 0000000000..16c331ed39
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/long-integer.json
@@ -0,0 +1,4 @@
+{
+ "answer": {"type": "integer", "value": "9223372036854775807"},
+ "neganswer": {"type": "integer", "value": "-9223372036854775808"}
+}
diff --git a/third_party/rust/toml/tests/valid/long-integer.toml b/third_party/rust/toml/tests/valid/long-integer.toml
new file mode 100644
index 0000000000..424a13ac2a
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/long-integer.toml
@@ -0,0 +1,2 @@
+answer = 9223372036854775807
+neganswer = -9223372036854775808
diff --git a/third_party/rust/toml/tests/valid/multiline-string.json b/third_party/rust/toml/tests/valid/multiline-string.json
new file mode 100644
index 0000000000..075bf50546
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/multiline-string.json
@@ -0,0 +1,30 @@
+{
+ "multiline_empty_one": {
+ "type": "string",
+ "value": ""
+ },
+ "multiline_empty_two": {
+ "type": "string",
+ "value": ""
+ },
+ "multiline_empty_three": {
+ "type": "string",
+ "value": ""
+ },
+ "multiline_empty_four": {
+ "type": "string",
+ "value": ""
+ },
+ "equivalent_one": {
+ "type": "string",
+ "value": "The quick brown fox jumps over the lazy dog."
+ },
+ "equivalent_two": {
+ "type": "string",
+ "value": "The quick brown fox jumps over the lazy dog."
+ },
+ "equivalent_three": {
+ "type": "string",
+ "value": "The quick brown fox jumps over the lazy dog."
+ }
+}
diff --git a/third_party/rust/toml/tests/valid/multiline-string.toml b/third_party/rust/toml/tests/valid/multiline-string.toml
new file mode 100644
index 0000000000..15b11434ff
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/multiline-string.toml
@@ -0,0 +1,23 @@
+multiline_empty_one = """"""
+multiline_empty_two = """
+"""
+multiline_empty_three = """\
+ """
+multiline_empty_four = """\
+ \
+ \
+ """
+
+equivalent_one = "The quick brown fox jumps over the lazy dog."
+equivalent_two = """
+The quick brown \
+
+
+ fox jumps over \
+ the lazy dog."""
+
+equivalent_three = """\
+ The quick brown \
+ fox jumps over \
+ the lazy dog.\
+ """
diff --git a/third_party/rust/toml/tests/valid/raw-multiline-string.json b/third_party/rust/toml/tests/valid/raw-multiline-string.json
new file mode 100644
index 0000000000..b43cce5a2d
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/raw-multiline-string.json
@@ -0,0 +1,14 @@
+{
+ "oneline": {
+ "type": "string",
+ "value": "This string has a ' quote character."
+ },
+ "firstnl": {
+ "type": "string",
+ "value": "This string has a ' quote character."
+ },
+ "multiline": {
+ "type": "string",
+ "value": "This string\nhas ' a quote character\nand more than\none newline\nin it."
+ }
+}
diff --git a/third_party/rust/toml/tests/valid/raw-multiline-string.toml b/third_party/rust/toml/tests/valid/raw-multiline-string.toml
new file mode 100644
index 0000000000..8094c03e31
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/raw-multiline-string.toml
@@ -0,0 +1,9 @@
+oneline = '''This string has a ' quote character.'''
+firstnl = '''
+This string has a ' quote character.'''
+multiline = '''
+This string
+has ' a quote character
+and more than
+one newline
+in it.'''
diff --git a/third_party/rust/toml/tests/valid/raw-string.json b/third_party/rust/toml/tests/valid/raw-string.json
new file mode 100644
index 0000000000..693ab9b54a
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/raw-string.json
@@ -0,0 +1,30 @@
+{
+ "backspace": {
+ "type": "string",
+ "value": "This string has a \\b backspace character."
+ },
+ "tab": {
+ "type": "string",
+ "value": "This string has a \\t tab character."
+ },
+ "newline": {
+ "type": "string",
+ "value": "This string has a \\n new line character."
+ },
+ "formfeed": {
+ "type": "string",
+ "value": "This string has a \\f form feed character."
+ },
+ "carriage": {
+ "type": "string",
+ "value": "This string has a \\r carriage return character."
+ },
+ "slash": {
+ "type": "string",
+ "value": "This string has a \\/ slash character."
+ },
+ "backslash": {
+ "type": "string",
+ "value": "This string has a \\\\ backslash character."
+ }
+}
diff --git a/third_party/rust/toml/tests/valid/raw-string.toml b/third_party/rust/toml/tests/valid/raw-string.toml
new file mode 100644
index 0000000000..92acd2557c
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/raw-string.toml
@@ -0,0 +1,7 @@
+backspace = 'This string has a \b backspace character.'
+tab = 'This string has a \t tab character.'
+newline = 'This string has a \n new line character.'
+formfeed = 'This string has a \f form feed character.'
+carriage = 'This string has a \r carriage return character.'
+slash = 'This string has a \/ slash character.'
+backslash = 'This string has a \\ backslash character.'
diff --git a/third_party/rust/toml/tests/valid/string-empty.json b/third_party/rust/toml/tests/valid/string-empty.json
new file mode 100644
index 0000000000..6c26d695b2
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/string-empty.json
@@ -0,0 +1,6 @@
+{
+ "answer": {
+ "type": "string",
+ "value": ""
+ }
+}
diff --git a/third_party/rust/toml/tests/valid/string-empty.toml b/third_party/rust/toml/tests/valid/string-empty.toml
new file mode 100644
index 0000000000..e37e6815bc
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/string-empty.toml
@@ -0,0 +1 @@
+answer = ""
diff --git a/third_party/rust/toml/tests/valid/string-escapes.json b/third_party/rust/toml/tests/valid/string-escapes.json
new file mode 100644
index 0000000000..62dac5178f
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/string-escapes.json
@@ -0,0 +1,50 @@
+{
+ "backspace": {
+ "type": "string",
+ "value": "This string has a \u0008 backspace character."
+ },
+ "tab": {
+ "type": "string",
+ "value": "This string has a \u0009 tab character."
+ },
+ "newline": {
+ "type": "string",
+ "value": "This string has a \u000A new line character."
+ },
+ "formfeed": {
+ "type": "string",
+ "value": "This string has a \u000C form feed character."
+ },
+ "carriage": {
+ "type": "string",
+ "value": "This string has a \u000D carriage return character."
+ },
+ "quote": {
+ "type": "string",
+ "value": "This string has a \u0022 quote character."
+ },
+ "slash": {
+ "type": "string",
+ "value": "This string has a \u002F slash character."
+ },
+ "backslash": {
+ "type": "string",
+ "value": "This string has a \u005C backslash character."
+ },
+ "notunicode1": {
+ "type": "string",
+ "value": "This string does not have a unicode \\u escape."
+ },
+ "notunicode2": {
+ "type": "string",
+ "value": "This string does not have a unicode \u005Cu escape."
+ },
+ "notunicode3": {
+ "type": "string",
+ "value": "This string does not have a unicode \\u0075 escape."
+ },
+ "notunicode4": {
+ "type": "string",
+ "value": "This string does not have a unicode \\\u0075 escape."
+ }
+}
diff --git a/third_party/rust/toml/tests/valid/string-escapes.toml b/third_party/rust/toml/tests/valid/string-escapes.toml
new file mode 100644
index 0000000000..c5d495428a
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/string-escapes.toml
@@ -0,0 +1,12 @@
+backspace = "This string has a \b backspace character."
+tab = "This string has a \t tab character."
+newline = "This string has a \n new line character."
+formfeed = "This string has a \f form feed character."
+carriage = "This string has a \r carriage return character."
+quote = "This string has a \" quote character."
+slash = "This string has a / slash character."
+backslash = "This string has a \\ backslash character."
+notunicode1 = "This string does not have a unicode \\u escape."
+notunicode2 = "This string does not have a unicode \u005Cu escape."
+notunicode3 = "This string does not have a unicode \\u0075 escape."
+notunicode4 = "This string does not have a unicode \\\u0075 escape."
diff --git a/third_party/rust/toml/tests/valid/string-simple.json b/third_party/rust/toml/tests/valid/string-simple.json
new file mode 100644
index 0000000000..2e05f99b4d
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/string-simple.json
@@ -0,0 +1,6 @@
+{
+ "answer": {
+ "type": "string",
+ "value": "You are not drinking enough whisky."
+ }
+}
diff --git a/third_party/rust/toml/tests/valid/string-simple.toml b/third_party/rust/toml/tests/valid/string-simple.toml
new file mode 100644
index 0000000000..e17ade6237
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/string-simple.toml
@@ -0,0 +1 @@
+answer = "You are not drinking enough whisky."
diff --git a/third_party/rust/toml/tests/valid/string-with-pound.json b/third_party/rust/toml/tests/valid/string-with-pound.json
new file mode 100644
index 0000000000..33cdc9c4b5
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/string-with-pound.json
@@ -0,0 +1,7 @@
+{
+ "pound": {"type": "string", "value": "We see no # comments here."},
+ "poundcomment": {
+ "type": "string",
+ "value": "But there are # some comments here."
+ }
+}
diff --git a/third_party/rust/toml/tests/valid/string-with-pound.toml b/third_party/rust/toml/tests/valid/string-with-pound.toml
new file mode 100644
index 0000000000..5fd87466df
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/string-with-pound.toml
@@ -0,0 +1,2 @@
+pound = "We see no # comments here."
+poundcomment = "But there are # some comments here." # Did I # mess you up?
diff --git a/third_party/rust/toml/tests/valid/table-array-implicit.json b/third_party/rust/toml/tests/valid/table-array-implicit.json
new file mode 100644
index 0000000000..32e464012d
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/table-array-implicit.json
@@ -0,0 +1,7 @@
+{
+ "albums": {
+ "songs": [
+ {"name": {"type": "string", "value": "Glory Days"}}
+ ]
+ }
+}
diff --git a/third_party/rust/toml/tests/valid/table-array-implicit.toml b/third_party/rust/toml/tests/valid/table-array-implicit.toml
new file mode 100644
index 0000000000..3157ac981d
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/table-array-implicit.toml
@@ -0,0 +1,2 @@
+[[albums.songs]]
+name = "Glory Days"
diff --git a/third_party/rust/toml/tests/valid/table-array-many.json b/third_party/rust/toml/tests/valid/table-array-many.json
new file mode 100644
index 0000000000..84df2dabb0
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/table-array-many.json
@@ -0,0 +1,16 @@
+{
+ "people": [
+ {
+ "first_name": {"type": "string", "value": "Bruce"},
+ "last_name": {"type": "string", "value": "Springsteen"}
+ },
+ {
+ "first_name": {"type": "string", "value": "Eric"},
+ "last_name": {"type": "string", "value": "Clapton"}
+ },
+ {
+ "first_name": {"type": "string", "value": "Bob"},
+ "last_name": {"type": "string", "value": "Seger"}
+ }
+ ]
+}
diff --git a/third_party/rust/toml/tests/valid/table-array-many.toml b/third_party/rust/toml/tests/valid/table-array-many.toml
new file mode 100644
index 0000000000..46062beb8e
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/table-array-many.toml
@@ -0,0 +1,11 @@
+[[people]]
+first_name = "Bruce"
+last_name = "Springsteen"
+
+[[people]]
+first_name = "Eric"
+last_name = "Clapton"
+
+[[people]]
+first_name = "Bob"
+last_name = "Seger"
diff --git a/third_party/rust/toml/tests/valid/table-array-nest-no-keys.json b/third_party/rust/toml/tests/valid/table-array-nest-no-keys.json
new file mode 100644
index 0000000000..7537b1a194
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/table-array-nest-no-keys.json
@@ -0,0 +1,14 @@
+{
+ "albums": [
+ {
+ "songs": [{}, {}]
+ }
+ ],
+ "artists": [
+ {
+ "home": {
+ "address": {}
+ }
+ }
+ ]
+}
diff --git a/third_party/rust/toml/tests/valid/table-array-nest-no-keys.toml b/third_party/rust/toml/tests/valid/table-array-nest-no-keys.toml
new file mode 100644
index 0000000000..ad6eb10630
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/table-array-nest-no-keys.toml
@@ -0,0 +1,6 @@
+[[ albums ]]
+ [[ albums.songs ]]
+ [[ albums.songs ]]
+
+[[ artists ]]
+ [ artists.home.address ]
diff --git a/third_party/rust/toml/tests/valid/table-array-nest.json b/third_party/rust/toml/tests/valid/table-array-nest.json
new file mode 100644
index 0000000000..c117afa40d
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/table-array-nest.json
@@ -0,0 +1,18 @@
+{
+ "albums": [
+ {
+ "name": {"type": "string", "value": "Born to Run"},
+ "songs": [
+ {"name": {"type": "string", "value": "Jungleland"}},
+ {"name": {"type": "string", "value": "Meeting Across the River"}}
+ ]
+ },
+ {
+ "name": {"type": "string", "value": "Born in the USA"},
+ "songs": [
+ {"name": {"type": "string", "value": "Glory Days"}},
+ {"name": {"type": "string", "value": "Dancing in the Dark"}}
+ ]
+ }
+ ]
+}
diff --git a/third_party/rust/toml/tests/valid/table-array-nest.toml b/third_party/rust/toml/tests/valid/table-array-nest.toml
new file mode 100644
index 0000000000..d659a3d947
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/table-array-nest.toml
@@ -0,0 +1,17 @@
+[[albums]]
+name = "Born to Run"
+
+ [[albums.songs]]
+ name = "Jungleland"
+
+ [[albums.songs]]
+ name = "Meeting Across the River"
+
+[[albums]]
+name = "Born in the USA"
+
+ [[albums.songs]]
+ name = "Glory Days"
+
+ [[albums.songs]]
+ name = "Dancing in the Dark"
diff --git a/third_party/rust/toml/tests/valid/table-array-one.json b/third_party/rust/toml/tests/valid/table-array-one.json
new file mode 100644
index 0000000000..d75faaeb23
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/table-array-one.json
@@ -0,0 +1,8 @@
+{
+ "people": [
+ {
+ "first_name": {"type": "string", "value": "Bruce"},
+ "last_name": {"type": "string", "value": "Springsteen"}
+ }
+ ]
+}
diff --git a/third_party/rust/toml/tests/valid/table-array-one.toml b/third_party/rust/toml/tests/valid/table-array-one.toml
new file mode 100644
index 0000000000..cd7e1b6907
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/table-array-one.toml
@@ -0,0 +1,3 @@
+[[people]]
+first_name = "Bruce"
+last_name = "Springsteen"
diff --git a/third_party/rust/toml/tests/valid/table-empty.json b/third_party/rust/toml/tests/valid/table-empty.json
new file mode 100644
index 0000000000..6f3873af6b
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/table-empty.json
@@ -0,0 +1,3 @@
+{
+ "a": {}
+}
diff --git a/third_party/rust/toml/tests/valid/table-empty.toml b/third_party/rust/toml/tests/valid/table-empty.toml
new file mode 100644
index 0000000000..8bb6a0aa07
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/table-empty.toml
@@ -0,0 +1 @@
+[a]
diff --git a/third_party/rust/toml/tests/valid/table-multi-empty.json b/third_party/rust/toml/tests/valid/table-multi-empty.json
new file mode 100644
index 0000000000..a6e17c926d
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/table-multi-empty.json
@@ -0,0 +1,5 @@
+{
+ "a": { "b": {} },
+ "b": {},
+ "c": { "a": {} }
+}
diff --git a/third_party/rust/toml/tests/valid/table-multi-empty.toml b/third_party/rust/toml/tests/valid/table-multi-empty.toml
new file mode 100644
index 0000000000..2266ed2d48
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/table-multi-empty.toml
@@ -0,0 +1,5 @@
+[a]
+[a.b]
+[b]
+[c]
+[c.a]
diff --git a/third_party/rust/toml/tests/valid/table-sub-empty.json b/third_party/rust/toml/tests/valid/table-sub-empty.json
new file mode 100644
index 0000000000..97877708e6
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/table-sub-empty.json
@@ -0,0 +1,3 @@
+{
+ "a": { "b": {} }
+}
diff --git a/third_party/rust/toml/tests/valid/table-sub-empty.toml b/third_party/rust/toml/tests/valid/table-sub-empty.toml
new file mode 100644
index 0000000000..70b7fe11c3
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/table-sub-empty.toml
@@ -0,0 +1,2 @@
+[a]
+[a.b]
diff --git a/third_party/rust/toml/tests/valid/table-whitespace.json b/third_party/rust/toml/tests/valid/table-whitespace.json
new file mode 100644
index 0000000000..3a73ec8645
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/table-whitespace.json
@@ -0,0 +1,3 @@
+{
+ "valid key": {}
+}
diff --git a/third_party/rust/toml/tests/valid/table-whitespace.toml b/third_party/rust/toml/tests/valid/table-whitespace.toml
new file mode 100644
index 0000000000..daf881d13a
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/table-whitespace.toml
@@ -0,0 +1 @@
+["valid key"]
diff --git a/third_party/rust/toml/tests/valid/table-with-pound.json b/third_party/rust/toml/tests/valid/table-with-pound.json
new file mode 100644
index 0000000000..5e594e4191
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/table-with-pound.json
@@ -0,0 +1,5 @@
+{
+ "key#group": {
+ "answer": {"type": "integer", "value": "42"}
+ }
+}
diff --git a/third_party/rust/toml/tests/valid/table-with-pound.toml b/third_party/rust/toml/tests/valid/table-with-pound.toml
new file mode 100644
index 0000000000..33f2c4fd6c
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/table-with-pound.toml
@@ -0,0 +1,2 @@
+["key#group"]
+answer = 42
diff --git a/third_party/rust/toml/tests/valid/unicode-escape.json b/third_party/rust/toml/tests/valid/unicode-escape.json
new file mode 100644
index 0000000000..32948c6202
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/unicode-escape.json
@@ -0,0 +1,5 @@
+{
+ "answer1": {"type": "string", "value": "\u000B"},
+ "answer4": {"type": "string", "value": "\u03B4α"},
+ "answer8": {"type": "string", "value": "\u03B4β"}
+}
diff --git a/third_party/rust/toml/tests/valid/unicode-escape.toml b/third_party/rust/toml/tests/valid/unicode-escape.toml
new file mode 100644
index 0000000000..c0d5a25bfd
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/unicode-escape.toml
@@ -0,0 +1,3 @@
+answer1 = "\u000B"
+answer4 = "\u03B4α"
+answer8 = "\U000003B4β"
diff --git a/third_party/rust/toml/tests/valid/unicode-literal.json b/third_party/rust/toml/tests/valid/unicode-literal.json
new file mode 100644
index 0000000000..00aa2f8325
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/unicode-literal.json
@@ -0,0 +1,3 @@
+{
+ "answer": {"type": "string", "value": "δ"}
+}
diff --git a/third_party/rust/toml/tests/valid/unicode-literal.toml b/third_party/rust/toml/tests/valid/unicode-literal.toml
new file mode 100644
index 0000000000..c65723ca1d
--- /dev/null
+++ b/third_party/rust/toml/tests/valid/unicode-literal.toml
@@ -0,0 +1 @@
+answer = "δ"