diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/toml | |
parent | Initial commit. (diff) | |
download | firefox-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')
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 = "δ" |