summaryrefslogtreecommitdiffstats
path: root/third_party/rust/toml
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /third_party/rust/toml
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/toml')
-rw-r--r--third_party/rust/toml/.cargo-checksum.json1
-rw-r--r--third_party/rust/toml/Cargo.lock110
-rw-r--r--third_party/rust/toml/Cargo.toml98
-rw-r--r--third_party/rust/toml/LICENSE-APACHE201
-rw-r--r--third_party/rust/toml/LICENSE-MIT25
-rw-r--r--third_party/rust/toml/README.md29
-rw-r--r--third_party/rust/toml/examples/decode.rs54
-rw-r--r--third_party/rust/toml/examples/enum_external.rs45
-rw-r--r--third_party/rust/toml/examples/toml2json.rs47
-rw-r--r--third_party/rust/toml/src/datetime.rs544
-rw-r--r--third_party/rust/toml/src/de.rs2256
-rw-r--r--third_party/rust/toml/src/lib.rs176
-rw-r--r--third_party/rust/toml/src/macros.rs459
-rw-r--r--third_party/rust/toml/src/map.rs595
-rw-r--r--third_party/rust/toml/src/ser.rs1853
-rw-r--r--third_party/rust/toml/src/spanned.rs166
-rw-r--r--third_party/rust/toml/src/tokens.rs742
-rw-r--r--third_party/rust/toml/src/value.rs1081
-rw-r--r--third_party/rust/toml/tests/enum_external_deserialize.rs258
19 files changed, 8740 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..80ed323a90
--- /dev/null
+++ b/third_party/rust/toml/.cargo-checksum.json
@@ -0,0 +1 @@
+{"files":{"Cargo.lock":"07886a40f452cdca63c989a0c9988c3fe0961c9edbddfb5f69aa5af7ac0fa013","Cargo.toml":"1cc7cd84cf608c6d819db3e2097e1065188f8202dc4fb8ff8995834ee33d9c61","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"a915ead738822da2d74b883c7519d8e558299286bb823e26c7fdece901c3267e","examples/decode.rs":"5c1bb83eef7fdf41f7d7873db4681492ce181000669aadee8895ca21e85f0aed","examples/enum_external.rs":"360618cced5c6339afbbc8799df2c047aaeeb1fa7d840dc777c2ffd83871883e","examples/toml2json.rs":"b0256e8393f91633abaa5b5c1ce496b73efee14a81b51e4f242aae967eaf9548","src/datetime.rs":"fe76c72cc5da6bccbd75ae3c7cb6658e92877631a93af4b88dcf54be6b6aeaed","src/de.rs":"8646e455bd3b9147550aa5e7c9400c935f580fbc320c9573b2ece492977c606d","src/lib.rs":"d599207a47e5fef74a0eeeb12d412429c305554816a89193ebbb8242f9585681","src/macros.rs":"506f00f2c12dd973a074d3c3263b0a97288eab4bda431d76d7a2cc95c63be0e0","src/map.rs":"ffc5539c8884baea62ed0e2ea89b018797c5e2d285f778f1d0e124a69ec29ddc","src/ser.rs":"e7f1e703efc1d30b0aca55c8d3f6bd62bb163652e0680cd5aeea7bf7217ba90e","src/spanned.rs":"eb370fed2aae511150d7ece47d38c95f11519db8e0b0748524338943579fe876","src/tokens.rs":"62c38be95ac994e6ae42ff983ac3ed66d3924decfd10e929455bf9168d9a1410","src/value.rs":"185ddf60a8e85472449b03e73f7353c3374f20cc720e32cdc33c750c2871a08b","tests/enum_external_deserialize.rs":"32515d5fa80e36efed6006da6514b5054c5c903960d9d884866d608ce9d23c35"},"package":"1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f"} \ No newline at end of file
diff --git a/third_party/rust/toml/Cargo.lock b/third_party/rust/toml/Cargo.lock
new file mode 100644
index 0000000000..f80090911a
--- /dev/null
+++ b/third_party/rust/toml/Cargo.lock
@@ -0,0 +1,110 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
+[[package]]
+name = "indexmap"
+version = "1.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
+
+[[package]]
+name = "itoa"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "ryu"
+version = "1.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09"
+
+[[package]]
+name = "serde"
+version = "1.0.150"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e326c9ec8042f1b5da33252c8a37e9ffbd2c9bef0155215b6e6c80c790e05f91"
+
+[[package]]
+name = "serde_derive"
+version = "1.0.150"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "42a3df25b0713732468deadad63ab9da1f1fd75a48a15024b50363f128db627e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_json"
+version = "1.0.89"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.105"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60b9b43d45702de4c839cb9b51d9f529c5dd26a4aff255b42b1ebc03e88ee908"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "toml"
+version = "0.5.10"
+dependencies = [
+ "indexmap",
+ "serde",
+ "serde_derive",
+ "serde_json",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
diff --git a/third_party/rust/toml/Cargo.toml b/third_party/rust/toml/Cargo.toml
new file mode 100644
index 0000000000..77309fcb25
--- /dev/null
+++ b/third_party/rust/toml/Cargo.toml
@@ -0,0 +1,98 @@
+# 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 are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+rust-version = "1.48.0"
+name = "toml"
+version = "0.5.10"
+authors = ["Alex Crichton <alex@alexcrichton.com>"]
+include = [
+ "src/**/*",
+ "Cargo.toml",
+ "LICENSE*",
+ "README.md",
+ "examples/**/*",
+ "benches/**/*",
+ "tests/**/*",
+]
+description = """
+A native Rust encoder and decoder of TOML-formatted files and streams. Provides
+implementations of the standard Serialize/Deserialize traits for TOML data to
+facilitate deserializing and serializing Rust structures.
+"""
+homepage = "https://github.com/toml-rs/toml"
+documentation = "https://docs.rs/toml"
+readme = "README.md"
+keywords = [
+ "encoding",
+ "toml",
+]
+categories = [
+ "encoding",
+ "parser-implementations",
+ "parsing",
+ "config",
+]
+license = "MIT/Apache-2.0"
+repository = "https://github.com/toml-rs/toml"
+
+[[package.metadata.release.pre-release-replacements]]
+file = "CHANGELOG.md"
+search = "Unreleased"
+replace = "{{version}}"
+min = 1
+
+[[package.metadata.release.pre-release-replacements]]
+file = "CHANGELOG.md"
+search = '\.\.\.HEAD'
+replace = "...{{tag_name}}"
+exactly = 1
+
+[[package.metadata.release.pre-release-replacements]]
+file = "CHANGELOG.md"
+search = "ReleaseDate"
+replace = "{{date}}"
+min = 1
+
+[[package.metadata.release.pre-release-replacements]]
+file = "CHANGELOG.md"
+search = "<!-- next-header -->"
+replace = """
+<!-- next-header -->
+## [Unreleased] - ReleaseDate
+"""
+exactly = 1
+
+[[package.metadata.release.pre-release-replacements]]
+file = "CHANGELOG.md"
+search = "<!-- next-url -->"
+replace = """
+<!-- next-url -->
+[Unreleased]: https://github.com/toml-rs/toml_edit/compare/{{tag_name}}...HEAD"""
+exactly = 1
+
+[dependencies.indexmap]
+version = "1.0"
+optional = true
+
+[dependencies.serde]
+version = "1.0.97"
+
+[dev-dependencies.serde_derive]
+version = "1.0"
+
+[dev-dependencies.serde_json]
+version = "1.0"
+
+[features]
+default = []
+preserve_order = ["indexmap"]
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..82960dda9f
--- /dev/null
+++ b/third_party/rust/toml/README.md
@@ -0,0 +1,29 @@
+# toml
+
+[![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 [serde]-compatible [TOML][toml] decoder and encoder for Rust.
+
+For format-preserving edits or finer control over output, see [toml_edit]
+
+[serde]: https://serde.rs/
+[toml]: https://github.com/toml-lang/toml
+[toml_edit]: https://docs.rs/toml_edit
+
+# License
+
+This project is licensed under either of
+
+ * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
+ http://www.apache.org/licenses/LICENSE-2.0)
+ * MIT license ([LICENSE-MIT](LICENSE-MIT) or
+ http://opensource.org/licenses/MIT)
+
+at your option.
+
+### Contribution
+
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in toml-rs by you, as defined in the Apache-2.0 license, shall be
+dual licensed as above, without any additional terms or conditions.
diff --git a/third_party/rust/toml/examples/decode.rs b/third_party/rust/toml/examples/decode.rs
new file mode 100644
index 0000000000..256069b350
--- /dev/null
+++ b/third_party/rust/toml/examples/decode.rs
@@ -0,0 +1,54 @@
+//! An example showing off the usage of `Deserialize` to automatically decode
+//! TOML into a Rust `struct`
+
+#![deny(warnings)]
+#![allow(dead_code)]
+
+use serde_derive::Deserialize;
+
+/// 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/enum_external.rs b/third_party/rust/toml/examples/enum_external.rs
new file mode 100644
index 0000000000..7de061f614
--- /dev/null
+++ b/third_party/rust/toml/examples/enum_external.rs
@@ -0,0 +1,45 @@
+//! An example showing off the usage of `Deserialize` to automatically decode
+//! TOML into a Rust `struct`, with enums.
+
+#![deny(warnings)]
+#![allow(dead_code)]
+
+use serde_derive::Deserialize;
+
+/// This is what we're going to decode into.
+#[derive(Debug, Deserialize)]
+struct Config {
+ plain: MyEnum,
+ plain_table: MyEnum,
+ tuple: MyEnum,
+ #[serde(rename = "struct")]
+ structv: MyEnum,
+ newtype: MyEnum,
+ my_enum: Vec<MyEnum>,
+}
+
+#[derive(Debug, Deserialize)]
+enum MyEnum {
+ Plain,
+ Tuple(i64, bool),
+ NewType(String),
+ Struct { value: i64 },
+}
+
+fn main() {
+ let toml_str = r#"
+ plain = "Plain"
+ plain_table = { Plain = {} }
+ tuple = { Tuple = { 0 = 123, 1 = true } }
+ struct = { Struct = { value = 123 } }
+ newtype = { NewType = "value" }
+ my_enum = [
+ { Plain = {} },
+ { Tuple = { 0 = 123, 1 = true } },
+ { NewType = "value" },
+ { Struct = { value = 123 } }
+ ]"#;
+
+ 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..1b90c9fde5
--- /dev/null
+++ b/third_party/rust/toml/examples/toml2json.rs
@@ -0,0 +1,47 @@
+#![deny(warnings)]
+
+use std::env;
+use std::fs::File;
+use std::io;
+use std::io::prelude::*;
+
+use serde_json::Value as Json;
+use toml::Value as Toml;
+
+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..e980e6abc0
--- /dev/null
+++ b/third_party/rust/toml/src/datetime.rs
@@ -0,0 +1,544 @@
+use std::error;
+use std::fmt;
+use std::str::{self, FromStr};
+
+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.
+///
+/// Depending on how the option values are used, this struct will correspond
+/// with one of the following four datetimes from the [TOML v1.0.0 spec]:
+///
+/// | `date` | `time` | `offset` | TOML type |
+/// | --------- | --------- | --------- | ------------------ |
+/// | `Some(_)` | `Some(_)` | `Some(_)` | [Offset Date-Time] |
+/// | `Some(_)` | `Some(_)` | `None` | [Local Date-Time] |
+/// | `Some(_)` | `None` | `None` | [Local Date] |
+/// | `None` | `Some(_)` | `None` | [Local Time] |
+///
+/// **1. Offset Date-Time**: If all the optional values are used, `Datetime`
+/// corresponds to an [Offset Date-Time]. From the TOML v1.0.0 spec:
+///
+/// > To unambiguously represent a specific instant in time, you may use an
+/// > RFC 3339 formatted date-time with offset.
+/// >
+/// > ```toml
+/// > odt1 = 1979-05-27T07:32:00Z
+/// > odt2 = 1979-05-27T00:32:00-07:00
+/// > odt3 = 1979-05-27T00:32:00.999999-07:00
+/// > ```
+/// >
+/// > For the sake of readability, you may replace the T delimiter between date
+/// > and time with a space character (as permitted by RFC 3339 section 5.6).
+/// >
+/// > ```toml
+/// > odt4 = 1979-05-27 07:32:00Z
+/// > ```
+///
+/// **2. Local Date-Time**: If `date` and `time` are given but `offset` is
+/// `None`, `Datetime` corresponds to a [Local Date-Time]. From the spec:
+///
+/// > If you omit the offset from an RFC 3339 formatted date-time, it will
+/// > represent the given date-time without any relation to an offset or
+/// > timezone. It cannot be converted to an instant in time without additional
+/// > information. Conversion to an instant, if required, is implementation-
+/// > specific.
+/// >
+/// > ```toml
+/// > ldt1 = 1979-05-27T07:32:00
+/// > ldt2 = 1979-05-27T00:32:00.999999
+/// > ```
+///
+/// **3. Local Date**: If only `date` is given, `Datetime` corresponds to a
+/// [Local Date]; see the docs for [`Date`].
+///
+/// **4. Local Time**: If only `time` is given, `Datetime` corresponds to a
+/// [Local Time]; see the docs for [`Time`].
+///
+/// [TOML v1.0.0 spec]: https://toml.io/en/v1.0.0
+/// [Offset Date-Time]: https://toml.io/en/v1.0.0#offset-date-time
+/// [Local Date-Time]: https://toml.io/en/v1.0.0#local-date-time
+/// [Local Date]: https://toml.io/en/v1.0.0#local-date
+/// [Local Time]: https://toml.io/en/v1.0.0#local-time
+#[derive(PartialEq, Eq, Clone)]
+pub struct Datetime {
+ /// Optional date.
+ /// Required for: *Offset Date-Time*, *Local Date-Time*, *Local Date*.
+ pub date: Option<Date>,
+
+ /// Optional time.
+ /// Required for: *Offset Date-Time*, *Local Date-Time*, *Local Time*.
+ pub time: Option<Time>,
+
+ /// Optional offset.
+ /// Required for: *Offset Date-Time*.
+ pub 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 these 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 FIELD: &str = "$__toml_private_datetime";
+pub const NAME: &str = "$__toml_private_Datetime";
+
+/// A parsed TOML date value
+///
+/// May be part of a [`Datetime`]. Alone, `Date` corresponds to a [Local Date].
+/// From the TOML v1.0.0 spec:
+///
+/// > If you include only the date portion of an RFC 3339 formatted date-time,
+/// > it will represent that entire day without any relation to an offset or
+/// > timezone.
+/// >
+/// > ```toml
+/// > ld1 = 1979-05-27
+/// > ```
+///
+/// [Local Date]: https://toml.io/en/v1.0.0#local-date
+#[derive(PartialEq, Eq, Clone)]
+pub struct Date {
+ /// Year: four digits
+ pub year: u16,
+ /// Month: 1 to 12
+ pub month: u8,
+ /// Day: 1 to {28, 29, 30, 31} (based on month/year)
+ pub day: u8,
+}
+
+/// A parsed TOML time value
+///
+/// May be part of a [`Datetime`]. Alone, `Time` corresponds to a [Local Time].
+/// From the TOML v1.0.0 spec:
+///
+/// > If you include only the time portion of an RFC 3339 formatted date-time,
+/// > it will represent that time of day without any relation to a specific
+/// > day or any offset or timezone.
+/// >
+/// > ```toml
+/// > lt1 = 07:32:00
+/// > lt2 = 00:32:00.999999
+/// > ```
+/// >
+/// > Millisecond precision is required. Further precision of fractional
+/// > seconds is implementation-specific. If the value contains greater
+/// > precision than the implementation can support, the additional precision
+/// > must be truncated, not rounded.
+///
+/// [Local Time]: https://toml.io/en/v1.0.0#local-time
+#[derive(PartialEq, Eq, Clone)]
+pub struct Time {
+ /// Hour: 0 to 23
+ pub hour: u8,
+ /// Minute: 0 to 59
+ pub minute: u8,
+ /// Second: 0 to {58, 59, 60} (based on leap second rules)
+ pub second: u8,
+ /// Nanosecond: 0 to 999_999_999
+ pub nanosecond: u32,
+}
+
+/// A parsed TOML time offset
+///
+#[derive(PartialEq, Eq, Clone)]
+pub enum Offset {
+ /// > A suffix which, when applied to a time, denotes a UTC offset of 00:00;
+ /// > often spoken "Zulu" from the ICAO phonetic alphabet representation of
+ /// > the letter "Z". --- [RFC 3339 section 2]
+ ///
+ /// [RFC 3339 section 2]: https://datatracker.ietf.org/doc/html/rfc3339#section-2
+ Z,
+
+ /// Offset between local time and UTC
+ Custom {
+ /// Hours: -12 to +12
+ hours: i8,
+
+ /// Minutes: 0 to 59
+ 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_end_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 = u16::from(digit(&mut chars)?);
+ let y2 = u16::from(digit(&mut chars)?);
+ let y3 = u16::from(digit(&mut chars)?);
+ let y4 = u16::from(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 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 next = chars.clone().next();
+ let partial_time = if full_date.is_some()
+ && (next == Some('T') || next == Some('t') || next == Some(' '))
+ {
+ 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 * u32::from(byte - b'0');
+ }
+ }
+ _ => {
+ 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,
+ };
+
+ 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') || 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,
+ offset,
+ })
+ }
+}
+
+fn digit(chars: &mut str::Chars<'_>) -> Result<u8, DatetimeParseError> {
+ match chars.next() {
+ Some(c) if ('0'..='9').contains(&c) => 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(NAME, 1)?;
+ s.serialize_field(FIELD, &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: [&str; 1] = [FIELD];
+ deserializer.deserialize_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 == FIELD {
+ 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 {}
diff --git a/third_party/rust/toml/src/de.rs b/third_party/rust/toml/src/de.rs
new file mode 100644
index 0000000000..5dbe0aba5c
--- /dev/null
+++ b/third_party/rust/toml/src/de.rs
@@ -0,0 +1,2256 @@
+//! 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::collections::HashMap;
+use std::error;
+use std::f64;
+use std::fmt;
+use std::iter;
+use std::marker::PhantomData;
+use std::str;
+use std::vec;
+
+use serde::de;
+use serde::de::value::BorrowedStrDeserializer;
+use serde::de::IntoDeserializer;
+
+use crate::datetime;
+use crate::spanned;
+use crate::tokens::{Error as TokenError, Span, Token, Tokenizer};
+
+/// Type Alias for a TOML Table pair
+type TablePair<'a> = ((Span, Cow<'a, str>), Value<'a>);
+
+/// 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(None, 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
+///
+/// ```
+/// use serde_derive::Deserialize;
+///
+/// #[derive(Deserialize)]
+/// struct Config {
+/// title: String,
+/// owner: Owner,
+/// }
+///
+/// #[derive(Deserialize)]
+/// struct Owner {
+/// name: String,
+/// }
+///
+/// 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, PartialEq, Eq, Clone)]
+pub struct Error {
+ inner: Box<ErrorInner>,
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+struct ErrorInner {
+ kind: ErrorKind,
+ line: Option<usize>,
+ col: usize,
+ at: Option<usize>,
+ message: String,
+ key: Vec<String>,
+}
+
+/// Errors that can occur when deserializing a type.
+#[derive(Debug, PartialEq, Eq, Clone)]
+#[non_exhaustive]
+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,
+ },
+
+ /// 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,
+
+ /// Multiline strings are not allowed for key
+ MultilineStringKey,
+
+ /// A custom error which could be generated when deserializing a particular
+ /// type.
+ Custom,
+
+ /// A tuple with a certain number of elements was expected but something
+ /// else was found.
+ ExpectedTuple(usize),
+
+ /// Expected table keys to be in increasing tuple index order, but something
+ /// else was found.
+ ExpectedTupleIndex {
+ /// Expected index.
+ expected: usize,
+ /// Key that was specified.
+ found: String,
+ },
+
+ /// An empty table was expected but entries were found
+ ExpectedEmptyTable,
+
+ /// Dotted key attempted to extend something that is not a table.
+ DottedKeyInvalidType,
+
+ /// An unexpected key was encountered.
+ ///
+ /// Used when deserializing a struct with a limited set of fields.
+ UnexpectedKeys {
+ /// The unexpected keys.
+ keys: Vec<String>,
+ /// Keys that may be specified.
+ available: &'static [&'static str],
+ },
+
+ /// Unquoted string was found when quoted one was expected
+ UnquotedString,
+}
+
+/// Deserialization implementation for TOML.
+pub struct Deserializer<'a> {
+ require_newline_after_table: bool,
+ allow_duplciate_after_longer_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 = self.tables()?;
+ let table_indices = build_table_indices(&tables);
+ let table_pindices = build_table_pindices(&tables);
+
+ let res = visitor.visit_map(MapVisitor {
+ values: Vec::new().into_iter().peekable(),
+ next_value: None,
+ depth: 0,
+ cur: 0,
+ cur_parent: 0,
+ max: tables.len(),
+ table_indices: &table_indices,
+ table_pindices: &table_pindices,
+ tables: &mut tables,
+ array: false,
+ de: self,
+ });
+ res.map_err(|mut err| {
+ // Errors originating from this library (toml), have an offset
+ // attached to them already. Other errors, like those originating
+ // from serde (like "missing field") or from a custom deserializer,
+ // do not have offsets on them. Here, we do a best guess at their
+ // location, by attributing them to the "current table" (the last
+ // item in `tables`).
+ err.fix_offset(|| tables.last().map(|table| table.at));
+ err.fix_linecol(|at| self.to_linecol(at));
+ err
+ })
+ }
+
+ // Called when the type to deserialize is an enum, as opposed to a field in the type.
+ fn deserialize_enum<V>(
+ self,
+ _name: &'static str,
+ _variants: &'static [&'static str],
+ visitor: V,
+ ) -> Result<V::Value, Error>
+ where
+ V: de::Visitor<'de>,
+ {
+ let (value, name) = self.string_or_table()?;
+ match value.e {
+ E::String(val) => visitor.visit_enum(val.into_deserializer()),
+ E::InlineTable(values) => {
+ if values.len() != 1 {
+ Err(Error::from_kind(
+ Some(value.start),
+ ErrorKind::Wanted {
+ expected: "exactly 1 element",
+ found: if values.is_empty() {
+ "zero elements"
+ } else {
+ "more than 1 element"
+ },
+ },
+ ))
+ } else {
+ visitor.visit_enum(InlineTableDeserializer {
+ values: values.into_iter(),
+ next_value: None,
+ })
+ }
+ }
+ E::DottedTable(_) => visitor.visit_enum(DottedTableDeserializer {
+ name: name.expect("Expected table header to be passed."),
+ value,
+ }),
+ e => Err(Error::from_kind(
+ Some(value.start),
+ ErrorKind::Wanted {
+ expected: "string or table",
+ found: e.type_name(),
+ },
+ )),
+ }
+ }
+
+ 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 == spanned::NAME && fields == [spanned::START, spanned::END, spanned::VALUE] {
+ let start = 0;
+ let end = self.input.len();
+
+ let res = visitor.visit_map(SpannedDeserializer {
+ phantom_data: PhantomData,
+ start: Some(start),
+ value: Some(self),
+ end: Some(end),
+ });
+ return res;
+ }
+
+ self.deserialize_any(visitor)
+ }
+
+ serde::forward_to_deserialize_any! {
+ bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq
+ bytes byte_buf map unit newtype_struct
+ ignored_any unit_struct tuple_struct tuple option identifier
+ }
+}
+
+// Builds a datastructure that allows for efficient sublinear lookups.
+// The returned HashMap contains a mapping from table header (like [a.b.c])
+// to list of tables with that precise name. The tables are being identified
+// by their index in the passed slice. We use a list as the implementation
+// uses this data structure for arrays as well as tables,
+// so if any top level [[name]] array contains multiple entries,
+// there are multiple entries in the list.
+// The lookup is performed in the `SeqAccess` implementation of `MapVisitor`.
+// The lists are ordered, which we exploit in the search code by using
+// bisection.
+fn build_table_indices<'de>(tables: &[Table<'de>]) -> HashMap<Vec<Cow<'de, str>>, Vec<usize>> {
+ let mut res = HashMap::new();
+ for (i, table) in tables.iter().enumerate() {
+ let header = table.header.iter().map(|v| v.1.clone()).collect::<Vec<_>>();
+ res.entry(header).or_insert_with(Vec::new).push(i);
+ }
+ res
+}
+
+// Builds a datastructure that allows for efficient sublinear lookups.
+// The returned HashMap contains a mapping from table header (like [a.b.c])
+// to list of tables whose name at least starts with the specified
+// name. So searching for [a.b] would give both [a.b.c.d] as well as [a.b.e].
+// The tables are being identified by their index in the passed slice.
+//
+// A list is used for two reasons: First, the implementation also
+// stores arrays in the same data structure and any top level array
+// of size 2 or greater creates multiple entries in the list with the
+// same shared name. Second, there can be multiple tables sharing
+// the same prefix.
+//
+// The lookup is performed in the `MapAccess` implementation of `MapVisitor`.
+// The lists are ordered, which we exploit in the search code by using
+// bisection.
+fn build_table_pindices<'de>(tables: &[Table<'de>]) -> HashMap<Vec<Cow<'de, str>>, Vec<usize>> {
+ let mut res = HashMap::new();
+ for (i, table) in tables.iter().enumerate() {
+ let header = table.header.iter().map(|v| v.1.clone()).collect::<Vec<_>>();
+ for len in 0..=header.len() {
+ res.entry(header[..len].to_owned())
+ .or_insert_with(Vec::new)
+ .push(i);
+ }
+ }
+ res
+}
+
+fn headers_equal<'a, 'b>(hdr_a: &[(Span, Cow<'a, str>)], hdr_b: &[(Span, Cow<'b, str>)]) -> bool {
+ if hdr_a.len() != hdr_b.len() {
+ return false;
+ }
+ hdr_a.iter().zip(hdr_b.iter()).all(|(h1, h2)| h1.1 == h2.1)
+}
+
+struct Table<'a> {
+ at: usize,
+ header: Vec<(Span, Cow<'a, str>)>,
+ values: Option<Vec<TablePair<'a>>>,
+ array: bool,
+}
+
+struct MapVisitor<'de, 'b> {
+ values: iter::Peekable<vec::IntoIter<TablePair<'de>>>,
+ next_value: Option<TablePair<'de>>,
+ depth: usize,
+ cur: usize,
+ cur_parent: usize,
+ max: usize,
+ table_indices: &'b HashMap<Vec<Cow<'de, str>>, Vec<usize>>,
+ table_pindices: &'b HashMap<Vec<Cow<'de, str>>, Vec<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::spanned(key.clone()))?;
+ self.next_value = Some((key, value));
+ return Ok(Some(ret));
+ }
+
+ let next_table = {
+ let prefix_stripped = self.tables[self.cur_parent].header[..self.depth]
+ .iter()
+ .map(|v| v.1.clone())
+ .collect::<Vec<_>>();
+ self.table_pindices
+ .get(&prefix_stripped)
+ .and_then(|entries| {
+ let start = entries.binary_search(&self.cur).unwrap_or_else(|v| v);
+ if start == entries.len() || entries[start] < self.cur {
+ return None;
+ }
+ entries[start..]
+ .iter()
+ .filter_map(|i| if *i < self.max { Some(*i) } else { None })
+ .map(|i| (i, &self.tables[i]))
+ .find(|(_, table)| table.values.is_some())
+ .map(|p| p.0)
+ })
+ };
+
+ 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 {
+ if headers_equal(
+ &self.tables[self.cur_parent].header,
+ &self.tables[pos].header,
+ ) {
+ let at = self.tables[pos].at;
+ let name = self.tables[pos]
+ .header
+ .iter()
+ .map(|k| k.1.to_owned())
+ .collect::<Vec<_>>()
+ .join(".");
+ return Err(self.de.error(at, ErrorKind::DuplicateTable(name)));
+ }
+
+ // If we're here we know we should share the same prefix, and if
+ // the longer table was defined first then we want to narrow
+ // down our parent's length if possible to ensure that we catch
+ // duplicate tables defined afterwards.
+ if !self.de.allow_duplciate_after_longer_table {
+ let parent_len = self.tables[self.cur_parent].header.len();
+ let cur_len = self.tables[pos].header.len();
+ if cur_len < parent_len {
+ self.cur_parent = pos;
+ }
+ }
+ }
+
+ 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::spanned(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()
+ .peekable();
+ }
+ }
+
+ 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.1);
+ 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().peekable(),
+ next_value: None,
+ depth: self.depth + if array { 0 } else { 1 },
+ cur_parent: self.cur - 1,
+ cur: 0,
+ max: self.max,
+ array,
+ table_indices: self.table_indices,
+ table_pindices: self.table_pindices,
+ 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].1);
+ 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 header_stripped = self.tables[self.cur_parent]
+ .header
+ .iter()
+ .map(|v| v.1.clone())
+ .collect::<Vec<_>>();
+ let start_idx = self.cur_parent + 1;
+ let next = self
+ .table_indices
+ .get(&header_stripped)
+ .and_then(|entries| {
+ let start = entries.binary_search(&start_idx).unwrap_or_else(|v| v);
+ if start == entries.len() || entries[start] < start_idx {
+ return None;
+ }
+ entries[start..]
+ .iter()
+ .filter_map(|i| if *i < self.max { Some(*i) } else { None })
+ .map(|i| (i, &self.tables[i]))
+ .find(|(_, table)| table.array)
+ .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()
+ .peekable(),
+ next_value: None,
+ depth: self.depth + 1,
+ cur_parent: self.cur_parent,
+ max: next,
+ cur: 0,
+ array: false,
+ table_indices: self.table_indices,
+ table_pindices: self.table_pindices,
+ tables: self.tables,
+ de: 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)
+ }
+
+ fn deserialize_struct<V>(
+ mut self,
+ name: &'static str,
+ fields: &'static [&'static str],
+ visitor: V,
+ ) -> Result<V::Value, Error>
+ where
+ V: de::Visitor<'de>,
+ {
+ if name == spanned::NAME
+ && fields == [spanned::START, spanned::END, spanned::VALUE]
+ && !(self.array && self.values.peek().is_some())
+ {
+ // TODO we can't actually emit spans here for the *entire* table/array
+ // due to the format that toml uses. Setting the start and end to 0 is
+ // *detectable* (and no reasonable span would look like that),
+ // it would be better to expose this in the API via proper
+ // ADTs like Option<T>.
+ let start = 0;
+ let end = 0;
+
+ let res = visitor.visit_map(SpannedDeserializer {
+ phantom_data: PhantomData,
+ start: Some(start),
+ value: Some(self),
+ end: Some(end),
+ });
+ return res;
+ }
+
+ self.deserialize_any(visitor)
+ }
+
+ fn deserialize_enum<V>(
+ self,
+ _name: &'static str,
+ _variants: &'static [&'static str],
+ visitor: V,
+ ) -> Result<V::Value, Error>
+ where
+ V: de::Visitor<'de>,
+ {
+ if self.tables.len() != 1 {
+ return Err(Error::custom(
+ Some(self.cur),
+ "enum table must contain exactly one table".into(),
+ ));
+ }
+ let table = &mut self.tables[0];
+ let values = table.values.take().expect("table has no values?");
+ if table.header.is_empty() {
+ return Err(self.de.error(self.cur, ErrorKind::EmptyTableKey));
+ }
+ let name = table.header[table.header.len() - 1].1.to_owned();
+ visitor.visit_enum(DottedTableDeserializer {
+ name,
+ value: Value {
+ e: E::DottedTable(values),
+ start: 0,
+ end: 0,
+ },
+ })
+ }
+
+ serde::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
+ }
+}
+
+struct StrDeserializer<'a> {
+ span: Option<Span>,
+ key: Cow<'a, str>,
+}
+
+impl<'a> StrDeserializer<'a> {
+ fn spanned(inner: (Span, Cow<'a, str>)) -> StrDeserializer<'a> {
+ StrDeserializer {
+ span: Some(inner.0),
+ key: inner.1,
+ }
+ }
+ fn new(key: Cow<'a, str>) -> StrDeserializer<'a> {
+ StrDeserializer { span: None, key }
+ }
+}
+
+impl<'a> de::IntoDeserializer<'a, Error> for StrDeserializer<'a> {
+ type Deserializer = Self;
+
+ fn into_deserializer(self) -> Self::Deserializer {
+ self
+ }
+}
+
+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),
+ }
+ }
+
+ 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 == spanned::NAME && fields == [spanned::START, spanned::END, spanned::VALUE] {
+ if let Some(span) = self.span {
+ return visitor.visit_map(SpannedDeserializer {
+ phantom_data: PhantomData,
+ start: Some(span.start),
+ value: Some(StrDeserializer::new(self.key)),
+ end: Some(span.end),
+ });
+ }
+ }
+ self.deserialize_any(visitor)
+ }
+
+ serde::forward_to_deserialize_any! {
+ bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string seq
+ bytes byte_buf map option unit newtype_struct
+ ignored_any unit_struct tuple_struct tuple enum identifier
+ }
+}
+
+struct ValueDeserializer<'a> {
+ value: Value<'a>,
+ validate_struct_keys: bool,
+}
+
+impl<'a> ValueDeserializer<'a> {
+ fn new(value: Value<'a>) -> ValueDeserializer<'a> {
+ ValueDeserializer {
+ value,
+ validate_struct_keys: false,
+ }
+ }
+
+ fn with_struct_key_validation(mut self) -> Self {
+ self.validate_struct_keys = true;
+ self
+ }
+}
+
+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>,
+ {
+ let start = self.value.start;
+ let res = match self.value.e {
+ E::Integer(i) => visitor.visit_i64(i),
+ E::Boolean(b) => visitor.visit_bool(b),
+ E::Float(f) => visitor.visit_f64(f),
+ E::String(Cow::Borrowed(s)) => visitor.visit_borrowed_str(s),
+ E::String(Cow::Owned(s)) => visitor.visit_string(s),
+ E::Datetime(s) => visitor.visit_map(DatetimeDeserializer {
+ date: s,
+ visited: false,
+ }),
+ E::Array(values) => {
+ let mut s = de::value::SeqDeserializer::new(values.into_iter());
+ let ret = visitor.visit_seq(&mut s)?;
+ s.end()?;
+ Ok(ret)
+ }
+ E::InlineTable(values) | E::DottedTable(values) => {
+ visitor.visit_map(InlineTableDeserializer {
+ values: values.into_iter(),
+ next_value: None,
+ })
+ }
+ };
+ res.map_err(|mut err| {
+ // Attribute the error to whatever value returned the error.
+ err.fix_offset(|| Some(start));
+ err
+ })
+ }
+
+ 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 == datetime::NAME && fields == [datetime::FIELD] {
+ if let E::Datetime(s) = self.value.e {
+ return visitor.visit_map(DatetimeDeserializer {
+ date: s,
+ visited: false,
+ });
+ }
+ }
+
+ if self.validate_struct_keys {
+ match self.value.e {
+ E::InlineTable(ref values) | E::DottedTable(ref values) => {
+ let extra_fields = values
+ .iter()
+ .filter_map(|key_value| {
+ let (ref key, ref _val) = *key_value;
+ if !fields.contains(&&*(key.1)) {
+ Some(key.clone())
+ } else {
+ None
+ }
+ })
+ .collect::<Vec<_>>();
+
+ if !extra_fields.is_empty() {
+ return Err(Error::from_kind(
+ Some(self.value.start),
+ ErrorKind::UnexpectedKeys {
+ keys: extra_fields
+ .iter()
+ .map(|k| k.1.to_string())
+ .collect::<Vec<_>>(),
+ available: fields,
+ },
+ ));
+ }
+ }
+ _ => {}
+ }
+ }
+
+ if name == spanned::NAME && fields == [spanned::START, spanned::END, spanned::VALUE] {
+ let start = self.value.start;
+ let end = self.value.end;
+
+ return visitor.visit_map(SpannedDeserializer {
+ phantom_data: PhantomData,
+ start: Some(start),
+ value: Some(self.value),
+ end: Some(end),
+ });
+ }
+
+ 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.e {
+ E::String(val) => visitor.visit_enum(val.into_deserializer()),
+ E::InlineTable(values) => {
+ if values.len() != 1 {
+ Err(Error::from_kind(
+ Some(self.value.start),
+ ErrorKind::Wanted {
+ expected: "exactly 1 element",
+ found: if values.is_empty() {
+ "zero elements"
+ } else {
+ "more than 1 element"
+ },
+ },
+ ))
+ } else {
+ visitor.visit_enum(InlineTableDeserializer {
+ values: values.into_iter(),
+ next_value: None,
+ })
+ }
+ }
+ e => Err(Error::from_kind(
+ Some(self.value.start),
+ ErrorKind::Wanted {
+ expected: "string or inline table",
+ found: e.type_name(),
+ },
+ )),
+ }
+ }
+
+ 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)
+ }
+
+ serde::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, 'b> de::IntoDeserializer<'de, Error> for MapVisitor<'de, 'b> {
+ type Deserializer = MapVisitor<'de, 'b>;
+
+ fn into_deserializer(self) -> Self::Deserializer {
+ self
+ }
+}
+
+impl<'de, 'b> de::IntoDeserializer<'de, Error> for &'b mut Deserializer<'de> {
+ type Deserializer = Self;
+
+ fn into_deserializer(self) -> Self::Deserializer {
+ self
+ }
+}
+
+impl<'de> de::IntoDeserializer<'de, Error> for Value<'de> {
+ type Deserializer = ValueDeserializer<'de>;
+
+ fn into_deserializer(self) -> Self::Deserializer {
+ ValueDeserializer::new(self)
+ }
+}
+
+struct SpannedDeserializer<'de, T: de::IntoDeserializer<'de, Error>> {
+ phantom_data: PhantomData<&'de ()>,
+ start: Option<usize>,
+ end: Option<usize>,
+ value: Option<T>,
+}
+
+impl<'de, T> de::MapAccess<'de> for SpannedDeserializer<'de, T>
+where
+ T: de::IntoDeserializer<'de, Error>,
+{
+ type Error = Error;
+
+ fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Error>
+ where
+ K: de::DeserializeSeed<'de>,
+ {
+ if self.start.is_some() {
+ seed.deserialize(BorrowedStrDeserializer::new(spanned::START))
+ .map(Some)
+ } else if self.end.is_some() {
+ seed.deserialize(BorrowedStrDeserializer::new(spanned::END))
+ .map(Some)
+ } else if self.value.is_some() {
+ seed.deserialize(BorrowedStrDeserializer::new(spanned::VALUE))
+ .map(Some)
+ } else {
+ Ok(None)
+ }
+ }
+
+ fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Error>
+ where
+ V: de::DeserializeSeed<'de>,
+ {
+ if let Some(start) = self.start.take() {
+ seed.deserialize(start.into_deserializer())
+ } else if let Some(end) = self.end.take() {
+ seed.deserialize(end.into_deserializer())
+ } else if let Some(value) = self.value.take() {
+ seed.deserialize(value.into_deserializer())
+ } else {
+ panic!("next_value_seed called before next_key_seed")
+ }
+ }
+}
+
+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(datetime::FIELD)
+ }
+
+ serde::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 DottedTableDeserializer<'a> {
+ name: Cow<'a, str>,
+ value: Value<'a>,
+}
+
+impl<'de> de::EnumAccess<'de> for DottedTableDeserializer<'de> {
+ type Error = Error;
+ type Variant = TableEnumDeserializer<'de>;
+
+ fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error>
+ where
+ V: de::DeserializeSeed<'de>,
+ {
+ let (name, value) = (self.name, self.value);
+ seed.deserialize(StrDeserializer::new(name))
+ .map(|val| (val, TableEnumDeserializer { value }))
+ }
+}
+
+struct InlineTableDeserializer<'a> {
+ values: vec::IntoIter<TablePair<'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::spanned(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<'de> de::EnumAccess<'de> for InlineTableDeserializer<'de> {
+ type Error = Error;
+ type Variant = TableEnumDeserializer<'de>;
+
+ fn variant_seed<V>(mut self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error>
+ where
+ V: de::DeserializeSeed<'de>,
+ {
+ let (key, value) = match self.values.next() {
+ Some(pair) => pair,
+ None => {
+ return Err(Error::from_kind(
+ None, // FIXME: How do we get an offset here?
+ ErrorKind::Wanted {
+ expected: "table with exactly 1 entry",
+ found: "empty table",
+ },
+ ));
+ }
+ };
+
+ seed.deserialize(StrDeserializer::new(key.1))
+ .map(|val| (val, TableEnumDeserializer { value }))
+ }
+}
+
+/// Deserializes table values into enum variants.
+struct TableEnumDeserializer<'a> {
+ value: Value<'a>,
+}
+
+impl<'de> de::VariantAccess<'de> for TableEnumDeserializer<'de> {
+ type Error = Error;
+
+ fn unit_variant(self) -> Result<(), Self::Error> {
+ match self.value.e {
+ E::InlineTable(values) | E::DottedTable(values) => {
+ if values.is_empty() {
+ Ok(())
+ } else {
+ Err(Error::from_kind(
+ Some(self.value.start),
+ ErrorKind::ExpectedEmptyTable,
+ ))
+ }
+ }
+ e => Err(Error::from_kind(
+ Some(self.value.start),
+ ErrorKind::Wanted {
+ expected: "table",
+ found: e.type_name(),
+ },
+ )),
+ }
+ }
+
+ fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value, Self::Error>
+ where
+ T: de::DeserializeSeed<'de>,
+ {
+ seed.deserialize(ValueDeserializer::new(self.value))
+ }
+
+ fn tuple_variant<V>(self, len: usize, visitor: V) -> Result<V::Value, Self::Error>
+ where
+ V: de::Visitor<'de>,
+ {
+ match self.value.e {
+ E::InlineTable(values) | E::DottedTable(values) => {
+ let tuple_values = values
+ .into_iter()
+ .enumerate()
+ .map(|(index, (key, value))| match key.1.parse::<usize>() {
+ Ok(key_index) if key_index == index => Ok(value),
+ Ok(_) | Err(_) => Err(Error::from_kind(
+ Some(key.0.start),
+ ErrorKind::ExpectedTupleIndex {
+ expected: index,
+ found: key.1.to_string(),
+ },
+ )),
+ })
+ // Fold all values into a `Vec`, or return the first error.
+ .fold(Ok(Vec::with_capacity(len)), |result, value_result| {
+ result.and_then(move |mut tuple_values| match value_result {
+ Ok(value) => {
+ tuple_values.push(value);
+ Ok(tuple_values)
+ }
+ // `Result<de::Value, Self::Error>` to `Result<Vec<_>, Self::Error>`
+ Err(e) => Err(e),
+ })
+ })?;
+
+ if tuple_values.len() == len {
+ de::Deserializer::deserialize_seq(
+ ValueDeserializer::new(Value {
+ e: E::Array(tuple_values),
+ start: self.value.start,
+ end: self.value.end,
+ }),
+ visitor,
+ )
+ } else {
+ Err(Error::from_kind(
+ Some(self.value.start),
+ ErrorKind::ExpectedTuple(len),
+ ))
+ }
+ }
+ e => Err(Error::from_kind(
+ Some(self.value.start),
+ ErrorKind::Wanted {
+ expected: "table",
+ found: e.type_name(),
+ },
+ )),
+ }
+ }
+
+ fn struct_variant<V>(
+ self,
+ fields: &'static [&'static str],
+ visitor: V,
+ ) -> Result<V::Value, Self::Error>
+ where
+ V: de::Visitor<'de>,
+ {
+ de::Deserializer::deserialize_struct(
+ ValueDeserializer::new(self.value).with_struct_key_validation(),
+ "", // TODO: this should be the variant name
+ fields,
+ visitor,
+ )
+ }
+}
+
+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,
+ require_newline_after_table: true,
+ allow_duplciate_after_longer_table: false,
+ }
+ }
+
+ /// The `Deserializer::end` method should be called after a value has been
+ /// fully deserialized. This allows the `Deserializer` to validate that the
+ /// input stream is at the end or that it only has trailing
+ /// whitespace/comments.
+ pub fn end(&mut self) -> Result<(), Error> {
+ Ok(())
+ }
+
+ /// Historical versions of toml-rs accidentally allowed a newline after a
+ /// table definition, but the TOML spec requires a newline after a table
+ /// definition header.
+ ///
+ /// This option can be set to `false` (the default is `true`) to emulate
+ /// this behavior for backwards compatibility with older toml-rs versions.
+ pub fn set_require_newline_after_table(&mut self, require: bool) {
+ self.require_newline_after_table = require;
+ }
+
+ /// Historical versions of toml-rs accidentally allowed a duplicate table
+ /// header after a longer table header was previously defined. This is
+ /// invalid according to the TOML spec, however.
+ ///
+ /// This option can be set to `true` (the default is `false`) to emulate
+ /// this behavior for backwards compatibility with older toml-rs versions.
+ pub fn set_allow_duplicate_after_longer_table(&mut self, allow: bool) {
+ self.allow_duplciate_after_longer_table = allow;
+ }
+
+ fn tables(&mut self) -> Result<Vec<Table<'a>>, Error> {
+ 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,
+ header: Vec::new(),
+ values: Some(Vec::new()),
+ 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());
+ }
+ self.add_dotted_key(key, value, cur_table.values.as_mut().unwrap())?;
+ }
+ }
+ }
+ if !cur_table.header.is_empty() || cur_table.values.is_some() {
+ tables.push(cur_table);
+ }
+ Ok(tables)
+ }
+
+ 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,
+ })
+ }
+
+ fn key_value(&mut self) -> Result<Line<'a>, Error> {
+ let key = self.dotted_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((Span { start, end }, Token::String { val, .. })) => Value {
+ e: E::String(val),
+ start,
+ end,
+ },
+ Some((Span { start, end }, Token::Keylike("true"))) => Value {
+ e: E::Boolean(true),
+ start,
+ end,
+ },
+ Some((Span { start, end }, Token::Keylike("false"))) => Value {
+ e: E::Boolean(false),
+ start,
+ end,
+ },
+ Some((span, Token::Keylike(key))) => self.parse_keylike(at, span, key)?,
+ Some((span, Token::Plus)) => self.number_leading_plus(span)?,
+ Some((Span { start, .. }, Token::LeftBrace)) => {
+ self.inline_table().map(|(Span { end, .. }, table)| Value {
+ e: E::InlineTable(table),
+ start,
+ end,
+ })?
+ }
+ Some((Span { start, .. }, Token::LeftBracket)) => {
+ self.array().map(|(Span { end, .. }, array)| Value {
+ e: E::Array(array),
+ start,
+ end,
+ })?
+ }
+ Some(token) => {
+ return Err(self.error(
+ at,
+ ErrorKind::Wanted {
+ expected: "a value",
+ found: token.1.describe(),
+ },
+ ));
+ }
+ None => return Err(self.eof()),
+ };
+ Ok(value)
+ }
+
+ fn parse_keylike(&mut self, at: usize, span: Span, key: &'a str) -> Result<Value<'a>, Error> {
+ if key == "inf" || key == "nan" {
+ return self.number_or_date(span, key);
+ }
+
+ let first_char = key.chars().next().expect("key should not be empty here");
+ match first_char {
+ '-' | '0'..='9' => self.number_or_date(span, key),
+ _ => Err(self.error(at, ErrorKind::UnquotedString)),
+ }
+ }
+
+ fn number_or_date(&mut self, span: Span, s: &'a str) -> Result<Value<'a>, Error> {
+ if s.contains('T')
+ || s.contains('t')
+ || (s.len() > 1 && s[1..].contains('-') && !s.contains("e-") && !s.contains("E-"))
+ {
+ self.datetime(span, s, false)
+ .map(|(Span { start, end }, d)| Value {
+ e: E::Datetime(d),
+ start,
+ end,
+ })
+ } else if self.eat(Token::Colon)? {
+ self.datetime(span, s, true)
+ .map(|(Span { start, end }, d)| Value {
+ e: E::Datetime(d),
+ start,
+ end,
+ })
+ } else {
+ self.number(span, s)
+ }
+ }
+
+ /// Returns a string or table value type.
+ ///
+ /// Used to deserialize enums. Unit enums may be represented as a string or a table, all other
+ /// structures (tuple, newtype, struct) must be represented as a table.
+ fn string_or_table(&mut self) -> Result<(Value<'a>, Option<Cow<'a, str>>), Error> {
+ match self.peek()? {
+ Some((span, Token::LeftBracket)) => {
+ let tables = self.tables()?;
+ if tables.len() != 1 {
+ return Err(Error::from_kind(
+ Some(span.start),
+ ErrorKind::Wanted {
+ expected: "exactly 1 table",
+ found: if tables.is_empty() {
+ "zero tables"
+ } else {
+ "more than 1 table"
+ },
+ },
+ ));
+ }
+
+ let table = tables
+ .into_iter()
+ .next()
+ .expect("Expected exactly one table");
+ let header = table
+ .header
+ .last()
+ .expect("Expected at least one header value for table.");
+
+ let start = table.at;
+ let end = table
+ .values
+ .as_ref()
+ .and_then(|values| values.last())
+ .map(|&(_, ref val)| val.end)
+ .unwrap_or_else(|| header.1.len());
+ Ok((
+ Value {
+ e: E::DottedTable(table.values.unwrap_or_default()),
+ start,
+ end,
+ },
+ Some(header.1.clone()),
+ ))
+ }
+ Some(_) => self.value().map(|val| (val, None)),
+ None => Err(self.eof()),
+ }
+ }
+
+ fn number(&mut self, Span { start, end }: Span, s: &'a str) -> Result<Value<'a>, Error> {
+ let to_integer = |f| Value {
+ e: E::Integer(f),
+ start,
+ end,
+ };
+ if let Some(value) = s.strip_prefix("0x") {
+ self.integer(value, 16).map(to_integer)
+ } else if let Some(value) = s.strip_prefix("0o") {
+ self.integer(value, 8).map(to_integer)
+ } else if let Some(value) = s.strip_prefix("0b") {
+ self.integer(value, 2).map(to_integer)
+ } else if s.contains('e') || s.contains('E') {
+ self.float(s, None).map(|f| Value {
+ e: E::Float(f),
+ start,
+ end,
+ })
+ } else if self.eat(Token::Period)? {
+ let at = self.tokens.current();
+ match self.next()? {
+ Some((Span { start, end }, Token::Keylike(after))) => {
+ self.float(s, Some(after)).map(|f| Value {
+ e: E::Float(f),
+ start,
+ end,
+ })
+ }
+ _ => Err(self.error(at, ErrorKind::NumberInvalid)),
+ }
+ } else if s == "inf" {
+ Ok(Value {
+ e: E::Float(f64::INFINITY),
+ start,
+ end,
+ })
+ } else if s == "-inf" {
+ Ok(Value {
+ e: E::Float(f64::NEG_INFINITY),
+ start,
+ end,
+ })
+ } else if s == "nan" {
+ Ok(Value {
+ e: E::Float(f64::NAN),
+ start,
+ end,
+ })
+ } else if s == "-nan" {
+ Ok(Value {
+ e: E::Float(-f64::NAN),
+ start,
+ end,
+ })
+ } else {
+ self.integer(s, 10).map(to_integer)
+ }
+ }
+
+ fn number_leading_plus(&mut self, Span { start, .. }: Span) -> Result<Value<'a>, Error> {
+ let start_token = self.tokens.current();
+ match self.next()? {
+ Some((Span { end, .. }, Token::Keylike(s))) => self.number(Span { start, end }, s),
+ _ => Err(self.error(start_token, ErrorKind::NumberInvalid)),
+ }
+ }
+
+ fn integer(&self, s: &'a str, radix: u32) -> Result<i64, Error> {
+ let allow_sign = radix == 10;
+ let allow_leading_zeros = radix != 10;
+ let (prefix, suffix) = self.parse_integer(s, allow_sign, allow_leading_zeros, radix)?;
+ let start = self.tokens.substr_offset(s);
+ if !suffix.is_empty() {
+ return Err(self.error(start, ErrorKind::NumberInvalid));
+ }
+ i64::from_str_radix(prefix.replace('_', "").trim_start_matches('+'), radix)
+ .map_err(|_e| self.error(start, ErrorKind::NumberInvalid))
+ }
+
+ fn parse_integer(
+ &self,
+ s: &'a str,
+ allow_sign: bool,
+ allow_leading_zeros: bool,
+ radix: u32,
+ ) -> 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;
+ }
+
+ if c == '0' && first {
+ first_zero = true;
+ } else if c.is_digit(radix) {
+ if !first && first_zero && !allow_leading_zeros {
+ return Err(self.error(at, ErrorKind::NumberInvalid));
+ }
+ underscore = false;
+ } else if c == '_' && first {
+ return Err(self.error(at, ErrorKind::NumberInvalid));
+ } else if c == '_' && !underscore {
+ underscore = true;
+ } else {
+ 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, 10)?;
+ let start = self.tokens.substr_offset(integral);
+
+ let mut fraction = None;
+ if let Some(after) = after_decimal {
+ if !suffix.is_empty() {
+ return Err(self.error(start, ErrorKind::NumberInvalid));
+ }
+ let (a, b) = self.parse_integer(after, false, true, 10)?;
+ 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, true, 10)?,
+ _ => return Err(self.error(start, ErrorKind::NumberInvalid)),
+ }
+ } else {
+ self.parse_integer(&suffix[1..], true, true, 10)?
+ };
+ if !b.is_empty() {
+ return Err(self.error(start, ErrorKind::NumberInvalid));
+ }
+ exponent = Some(a);
+ } else if !suffix.is_empty() {
+ return Err(self.error(start, ErrorKind::NumberInvalid));
+ }
+
+ let mut number = integral
+ .trim_start_matches('+')
+ .chars()
+ .filter(|c| *c != '_')
+ .collect::<String>();
+ if let Some(fraction) = fraction {
+ number.push('.');
+ number.extend(fraction.chars().filter(|c| *c != '_'));
+ }
+ if let Some(exponent) = exponent {
+ number.push('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,
+ mut span: Span,
+ date: &'a str,
+ colon_eaten: bool,
+ ) -> Result<(Span, &'a str), Error> {
+ let start = self.tokens.substr_offset(date);
+
+ // Check for space separated date and time.
+ let mut lookahead = self.tokens.clone();
+ if let Ok(Some((_, Token::Whitespace(" ")))) = lookahead.next() {
+ // Check if hour follows.
+ if let Ok(Some((_, Token::Keylike(_)))) = lookahead.next() {
+ self.next()?; // skip space
+ self.next()?; // skip keylike hour
+ }
+ }
+
+ 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((Span { end, .. }, Token::Keylike(_))) => {
+ span.end = end;
+ }
+ _ => return Err(self.error(start, ErrorKind::DateInvalid)),
+ }
+ // Fractional seconds
+ if self.eat(Token::Period)? {
+ match self.next()? {
+ Some((Span { end, .. }, Token::Keylike(_))) => {
+ span.end = end;
+ }
+ _ => return Err(self.error(start, ErrorKind::DateInvalid)),
+ }
+ }
+
+ // offset
+ if self.eat(Token::Plus)? {
+ match self.next()? {
+ Some((Span { end, .. }, Token::Keylike(_))) => {
+ span.end = end;
+ }
+ _ => return Err(self.error(start, ErrorKind::DateInvalid)),
+ }
+ }
+ if self.eat(Token::Colon)? {
+ match self.next()? {
+ Some((Span { end, .. }, Token::Keylike(_))) => {
+ span.end = end;
+ }
+ _ => return Err(self.error(start, ErrorKind::DateInvalid)),
+ }
+ }
+ }
+
+ let end = self.tokens.current();
+ Ok((span, &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<(Span, Vec<TablePair<'a>>), Error> {
+ let mut ret = Vec::new();
+ self.eat_whitespace()?;
+ if let Some(span) = self.eat_spanned(Token::RightBrace)? {
+ return Ok((span, ret));
+ }
+ loop {
+ let key = self.dotted_key()?;
+ self.eat_whitespace()?;
+ self.expect(Token::Equals)?;
+ self.eat_whitespace()?;
+ let value = self.value()?;
+ self.add_dotted_key(key, value, &mut ret)?;
+
+ self.eat_whitespace()?;
+ if let Some(span) = self.eat_spanned(Token::RightBrace)? {
+ return Ok((span, 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<(Span, 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 let Some(span) = self.eat_spanned(Token::RightBracket)? {
+ return Ok((span, ret));
+ }
+ let value = self.value()?;
+ ret.push(value);
+ intermediate(self)?;
+ if !self.eat(Token::Comma)? {
+ break;
+ }
+ }
+ intermediate(self)?;
+ let span = self.expect_spanned(Token::RightBracket)?;
+ Ok((span, ret))
+ }
+
+ fn table_key(&mut self) -> Result<(Span, Cow<'a, str>), Error> {
+ self.tokens.table_key().map_err(|e| self.token_error(e))
+ }
+
+ fn dotted_key(&mut self) -> Result<Vec<(Span, Cow<'a, str>)>, Error> {
+ let mut result = vec![self.table_key()?];
+ self.eat_whitespace()?;
+ while self.eat(Token::Period)? {
+ self.eat_whitespace()?;
+ result.push(self.table_key()?);
+ self.eat_whitespace()?;
+ }
+ Ok(result)
+ }
+
+ /// Stores a value in the appropriate hierarchical structure positioned based on the dotted key.
+ ///
+ /// Given the following definition: `multi.part.key = "value"`, `multi` and `part` are
+ /// intermediate parts which are mapped to the relevant fields in the deserialized type's data
+ /// hierarchy.
+ ///
+ /// # Parameters
+ ///
+ /// * `key_parts`: Each segment of the dotted key, e.g. `part.one` maps to
+ /// `vec![Cow::Borrowed("part"), Cow::Borrowed("one")].`
+ /// * `value`: The parsed value.
+ /// * `values`: The `Vec` to store the value in.
+ fn add_dotted_key(
+ &self,
+ mut key_parts: Vec<(Span, Cow<'a, str>)>,
+ value: Value<'a>,
+ values: &mut Vec<TablePair<'a>>,
+ ) -> Result<(), Error> {
+ let key = key_parts.remove(0);
+ if key_parts.is_empty() {
+ values.push((key, value));
+ return Ok(());
+ }
+ match values.iter_mut().find(|&&mut (ref k, _)| *k.1 == key.1) {
+ Some(&mut (
+ _,
+ Value {
+ e: E::DottedTable(ref mut v),
+ ..
+ },
+ )) => {
+ return self.add_dotted_key(key_parts, value, v);
+ }
+ Some(&mut (_, Value { start, .. })) => {
+ return Err(self.error(start, ErrorKind::DottedKeyInvalidType));
+ }
+ None => {}
+ }
+ // The start/end value is somewhat misleading here.
+ let table_values = Value {
+ e: E::DottedTable(Vec::new()),
+ start: value.start,
+ end: value.end,
+ };
+ values.push((key, table_values));
+ let last_i = values.len() - 1;
+ if let (
+ _,
+ Value {
+ e: E::DottedTable(ref mut v),
+ ..
+ },
+ ) = values[last_i]
+ {
+ self.add_dotted_key(key_parts, value, v)?;
+ }
+ Ok(())
+ }
+
+ 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 eat_spanned(&mut self, expected: Token<'a>) -> Result<Option<Span>, Error> {
+ self.tokens
+ .eat_spanned(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 expect_spanned(&mut self, expected: Token<'a>) -> Result<Span, Error> {
+ self.tokens
+ .expect_spanned(expected)
+ .map_err(|e| self.token_error(e))
+ }
+
+ fn next(&mut self) -> Result<Option<(Span, Token<'a>)>, Error> {
+ self.tokens.next().map_err(|e| self.token_error(e))
+ }
+
+ fn peek(&mut self) -> Result<Option<(Span, 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, found }),
+ TokenError::MultilineStringKey(at) => self.error(at, ErrorKind::MultilineStringKey),
+ }
+ }
+
+ fn error(&self, at: usize, kind: ErrorKind) -> Error {
+ let mut err = Error::from_kind(Some(at), kind);
+ err.fix_linecol(|at| self.to_linecol(at));
+ 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;
+ // Use split_terminator instead of lines so that if there is a `\r`,
+ // it is included in the offset calculation. The `+1` values below
+ // account for the `\n`.
+ for (i, line) in self.input.split_terminator('\n').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(at: Option<usize>, kind: ErrorKind) -> Error {
+ Error {
+ inner: Box::new(ErrorInner {
+ kind,
+ line: None,
+ col: 0,
+ at,
+ message: String::new(),
+ key: Vec::new(),
+ }),
+ }
+ }
+
+ fn custom(at: Option<usize>, s: String) -> Error {
+ Error {
+ inner: Box::new(ErrorInner {
+ kind: ErrorKind::Custom,
+ line: None,
+ col: 0,
+ at,
+ message: s,
+ key: Vec::new(),
+ }),
+ }
+ }
+
+ pub(crate) fn add_key_context(&mut self, key: &str) {
+ self.inner.key.insert(0, key.to_string());
+ }
+
+ fn fix_offset<F>(&mut self, f: F)
+ where
+ F: FnOnce() -> Option<usize>,
+ {
+ // An existing offset is always better positioned than anything we
+ // might want to add later.
+ if self.inner.at.is_none() {
+ self.inner.at = f();
+ }
+ }
+
+ fn fix_linecol<F>(&mut self, f: F)
+ where
+ F: FnOnce(usize) -> (usize, usize),
+ {
+ if let Some(at) = self.inner.at {
+ let (line, col) = f(at);
+ self.inner.line = Some(line);
+ self.inner.col = col;
+ }
+ }
+}
+
+impl std::convert::From<Error> for std::io::Error {
+ fn from(e: Error) -> Self {
+ std::io::Error::new(std::io::ErrorKind::InvalidData, e.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::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::MultilineStringKey => "multiline strings are not allowed for key".fmt(f)?,
+ ErrorKind::Custom => self.inner.message.fmt(f)?,
+ ErrorKind::ExpectedTuple(l) => write!(f, "expected table with length {}", l)?,
+ ErrorKind::ExpectedTupleIndex {
+ expected,
+ ref found,
+ } => write!(f, "expected table key `{}`, but was `{}`", expected, found)?,
+ ErrorKind::ExpectedEmptyTable => "expected empty table".fmt(f)?,
+ ErrorKind::DottedKeyInvalidType => {
+ "dotted key attempted to extend non-table type".fmt(f)?
+ }
+ ErrorKind::UnexpectedKeys {
+ ref keys,
+ available,
+ } => write!(
+ f,
+ "unexpected keys in table: `{:?}`, available keys: `{:?}`",
+ keys, available
+ )?,
+ ErrorKind::UnquotedString => write!(
+ f,
+ "invalid TOML value, did you mean to use a quoted string?"
+ )?,
+ }
+
+ 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 {} column {}", line + 1, self.inner.col + 1)?;
+ }
+
+ Ok(())
+ }
+}
+
+impl error::Error for Error {}
+
+impl de::Error for Error {
+ fn custom<T: fmt::Display>(msg: T) -> Error {
+ Error::custom(None, msg.to_string())
+ }
+}
+
+enum Line<'a> {
+ Table {
+ at: usize,
+ header: Header<'a>,
+ array: bool,
+ },
+ KeyValue(Vec<(Span, 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,
+ tokens,
+ require_newline_after_table,
+ }
+ }
+
+ fn next(&mut self) -> Result<Option<(Span, 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 && !self.tokens.eat_comment()? {
+ self.tokens.eat_newline_or_eof()?;
+ }
+ Ok(None)
+ }
+ }
+}
+
+#[derive(Debug)]
+struct Value<'a> {
+ e: E<'a>,
+ start: usize,
+ end: usize,
+}
+
+#[derive(Debug)]
+enum E<'a> {
+ Integer(i64),
+ Float(f64),
+ Boolean(bool),
+ String(Cow<'a, str>),
+ Datetime(&'a str),
+ Array(Vec<Value<'a>>),
+ InlineTable(Vec<TablePair<'a>>),
+ DottedTable(Vec<TablePair<'a>>),
+}
+
+impl<'a> E<'a> {
+ fn type_name(&self) -> &'static str {
+ match *self {
+ E::String(..) => "string",
+ E::Integer(..) => "integer",
+ E::Float(..) => "float",
+ E::Boolean(..) => "boolean",
+ E::Datetime(..) => "datetime",
+ E::Array(..) => "array",
+ E::InlineTable(..) => "inline table",
+ E::DottedTable(..) => "dotted table",
+ }
+ }
+}
diff --git a/third_party/rust/toml/src/lib.rs b/third_party/rust/toml/src/lib.rs
new file mode 100644
index 0000000000..b20b42d2df
--- /dev/null
+++ b/third_party/rust/toml/src/lib.rs
@@ -0,0 +1,176 @@
+//! A [serde]-compatible [TOML]-parsing library
+//!
+//! 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
+//! use serde_derive::Deserialize;
+//!
+//! #[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
+//! use serde_derive::Serialize;
+//!
+//! #[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/
+//! [serde]: https://serde.rs/
+
+#![deny(missing_docs)]
+#![warn(rust_2018_idioms)]
+// Makes rustc abort compilation if there are any unsafe blocks in the crate.
+// Presence of this annotation is picked up by tools such as cargo-geiger
+// and lets them ensure that there is indeed no unsafe code as opposed to
+// something they couldn't detect (e.g. unsafe added via macro expansion, etc).
+#![forbid(unsafe_code)]
+
+pub mod map;
+pub mod value;
+#[doc(no_inline)]
+pub use crate::value::Value;
+mod datetime;
+
+pub mod ser;
+#[doc(no_inline)]
+pub use crate::ser::{to_string, to_string_pretty, to_vec, Serializer};
+pub mod de;
+#[doc(no_inline)]
+pub use crate::de::{from_slice, from_str, Deserializer};
+mod tokens;
+
+#[doc(hidden)]
+pub mod macros;
+
+mod spanned;
+pub use crate::spanned::Spanned;
+
+// Just for rustdoc
+#[allow(unused_imports)]
+use crate::datetime::Datetime;
+#[allow(unused_imports)]
+use core::str::FromStr;
diff --git a/third_party/rust/toml/src/macros.rs b/third_party/rust/toml/src/macros.rs
new file mode 100644
index 0000000000..19b2ce2286
--- /dev/null
+++ b/third_party/rust/toml/src/macros.rs
@@ -0,0 +1,459 @@
+pub use serde::de::{Deserialize, IntoDeserializer};
+
+use crate::value::{Array, Table, Value};
+
+/// Construct a [`toml::Value`] from TOML syntax.
+///
+/// [`toml::Value`]: value/enum.Value.html
+///
+/// ```rust
+/// let cargo_toml = toml::toml! {
+/// [package]
+/// name = "toml"
+/// version = "0.4.5"
+/// authors = ["Alex Crichton <alex@alexcrichton.com>"]
+///
+/// [badges]
+/// travis-ci = { repository = "alexcrichton/toml-rs" }
+///
+/// [dependencies]
+/// serde = "1.0"
+///
+/// [dev-dependencies]
+/// serde_derive = "1.0"
+/// serde_json = "1.0"
+/// };
+///
+/// println!("{:#?}", cargo_toml);
+/// ```
+#[macro_export]
+macro_rules! toml {
+ ($($toml:tt)+) => {{
+ let table = $crate::value::Table::new();
+ let mut root = $crate::Value::Table(table);
+ $crate::toml_internal!(@toplevel root [] $($toml)+);
+ root
+ }};
+}
+
+// TT-muncher to parse TOML syntax into a toml::Value.
+//
+// @toplevel -- Parse tokens outside of an inline table or inline array. In
+// this state, `[table headers]` and `[[array headers]]` are
+// allowed and `key = value` pairs are not separated by commas.
+//
+// @topleveldatetime -- Helper to parse a Datetime from string and insert it
+// into a table, continuing in the @toplevel state.
+//
+// @path -- Turn a path segment into a string. Segments that look like idents
+// are stringified, while quoted segments like `"cfg(windows)"`
+// are not.
+//
+// @value -- Parse the value part of a `key = value` pair, which may be a
+// primitive or inline table or inline array.
+//
+// @table -- Parse the contents of an inline table, returning them as a
+// toml::Value::Table.
+//
+// @tabledatetime -- Helper to parse a Datetime from string and insert it
+// into a table, continuing in the @table state.
+//
+// @array -- Parse the contents of an inline array, returning them as a
+// toml::Value::Array.
+//
+// @arraydatetime -- Helper to parse a Datetime from string and push it into
+// an array, continuing in the @array state.
+//
+// @trailingcomma -- Helper to append a comma to a sequence of tokens if the
+// sequence is non-empty and does not already end in a trailing
+// comma.
+//
+#[macro_export]
+#[doc(hidden)]
+macro_rules! toml_internal {
+ // Base case, no elements remaining.
+ (@toplevel $root:ident [$($path:tt)*]) => {};
+
+ // Parse negative number `key = -value`.
+ (@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = - $v:tt $($rest:tt)*) => {
+ $crate::toml_internal!(@toplevel $root [$($path)*] $($($k)-+).+ = (-$v) $($rest)*);
+ };
+
+ // Parse positive number `key = +value`.
+ (@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = + $v:tt $($rest:tt)*) => {
+ $crate::toml_internal!(@toplevel $root [$($path)*] $($($k)-+).+ = ($v) $($rest)*);
+ };
+
+ // Parse offset datetime `key = 1979-05-27T00:32:00.999999-07:00`.
+ (@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt . $frac:tt - $tzh:tt : $tzm:tt $($rest:tt)*) => {
+ $crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec . $frac - $tzh : $tzm) $($rest)*);
+ };
+ // Space instead of T.
+ (@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt . $frac:tt - $tzh:tt : $tzm:tt $($rest:tt)*) => {
+ $crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec . $frac - $tzh : $tzm) $($rest)*);
+ };
+
+ // Parse offset datetime `key = 1979-05-27T00:32:00-07:00`.
+ (@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt - $tzh:tt : $tzm:tt $($rest:tt)*) => {
+ $crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec - $tzh : $tzm) $($rest)*);
+ };
+ // Space instead of T.
+ (@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt - $tzh:tt : $tzm:tt $($rest:tt)*) => {
+ $crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec - $tzh : $tzm) $($rest)*);
+ };
+
+ // Parse local datetime `key = 1979-05-27T00:32:00.999999`.
+ (@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt . $frac:tt $($rest:tt)*) => {
+ $crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec . $frac) $($rest)*);
+ };
+ // Space instead of T.
+ (@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt . $frac:tt $($rest:tt)*) => {
+ $crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec . $frac) $($rest)*);
+ };
+
+ // Parse offset datetime `key = 1979-05-27T07:32:00Z` and local datetime `key = 1979-05-27T07:32:00`.
+ (@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt $($rest:tt)*) => {
+ $crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec) $($rest)*);
+ };
+ // Space instead of T.
+ (@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt $($rest:tt)*) => {
+ $crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec) $($rest)*);
+ };
+
+ // Parse local date `key = 1979-05-27`.
+ (@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $($rest:tt)*) => {
+ $crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($yr - $mo - $day) $($rest)*);
+ };
+
+ // Parse local time `key = 00:32:00.999999`.
+ (@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $hr:tt : $min:tt : $sec:tt . $frac:tt $($rest:tt)*) => {
+ $crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($hr : $min : $sec . $frac) $($rest)*);
+ };
+
+ // Parse local time `key = 07:32:00`.
+ (@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $hr:tt : $min:tt : $sec:tt $($rest:tt)*) => {
+ $crate::toml_internal!(@topleveldatetime $root [$($path)*] $($($k)-+).+ = ($hr : $min : $sec) $($rest)*);
+ };
+
+ // Parse any other `key = value` including string, inline array, inline
+ // table, number, and boolean.
+ (@toplevel $root:ident [$($path:tt)*] $($($k:tt)-+).+ = $v:tt $($rest:tt)*) => {{
+ $crate::macros::insert_toml(
+ &mut $root,
+ &[$($path)* $(&concat!($("-", $crate::toml_internal!(@path $k),)+)[1..], )+],
+ $crate::toml_internal!(@value $v));
+ $crate::toml_internal!(@toplevel $root [$($path)*] $($rest)*);
+ }};
+
+ // Parse array header `[[bin]]`.
+ (@toplevel $root:ident $oldpath:tt [[$($($path:tt)-+).+]] $($rest:tt)*) => {
+ $crate::macros::push_toml(
+ &mut $root,
+ &[$(&concat!($("-", $crate::toml_internal!(@path $path),)+)[1..],)+]);
+ $crate::toml_internal!(@toplevel $root [$(&concat!($("-", $crate::toml_internal!(@path $path),)+)[1..],)+] $($rest)*);
+ };
+
+ // Parse table header `[patch.crates-io]`.
+ (@toplevel $root:ident $oldpath:tt [$($($path:tt)-+).+] $($rest:tt)*) => {
+ $crate::macros::insert_toml(
+ &mut $root,
+ &[$(&concat!($("-", $crate::toml_internal!(@path $path),)+)[1..],)+],
+ $crate::Value::Table($crate::value::Table::new()));
+ $crate::toml_internal!(@toplevel $root [$(&concat!($("-", $crate::toml_internal!(@path $path),)+)[1..],)+] $($rest)*);
+ };
+
+ // Parse datetime from string and insert into table.
+ (@topleveldatetime $root:ident [$($path:tt)*] $($($k:tt)-+).+ = ($($datetime:tt)+) $($rest:tt)*) => {
+ $crate::macros::insert_toml(
+ &mut $root,
+ &[$($path)* $(&concat!($("-", $crate::toml_internal!(@path $k),)+)[1..], )+],
+ $crate::Value::Datetime(concat!($(stringify!($datetime)),+).parse().unwrap()));
+ $crate::toml_internal!(@toplevel $root [$($path)*] $($rest)*);
+ };
+
+ // Turn a path segment into a string.
+ (@path $ident:ident) => {
+ stringify!($ident)
+ };
+
+ // For a path segment that is not an ident, expect that it is already a
+ // quoted string, like in `[target."cfg(windows)".dependencies]`.
+ (@path $quoted:tt) => {
+ $quoted
+ };
+
+ // Construct a Value from an inline table.
+ (@value { $($inline:tt)* }) => {{
+ let mut table = $crate::Value::Table($crate::value::Table::new());
+ $crate::toml_internal!(@trailingcomma (@table table) $($inline)*);
+ table
+ }};
+
+ // Construct a Value from an inline array.
+ (@value [ $($inline:tt)* ]) => {{
+ let mut array = $crate::value::Array::new();
+ $crate::toml_internal!(@trailingcomma (@array array) $($inline)*);
+ $crate::Value::Array(array)
+ }};
+
+ (@value (-nan)) => {
+ $crate::Value::Float(-::std::f64::NAN)
+ };
+
+ (@value (nan)) => {
+ $crate::Value::Float(::std::f64::NAN)
+ };
+
+ (@value nan) => {
+ $crate::Value::Float(::std::f64::NAN)
+ };
+
+ (@value (-inf)) => {
+ $crate::Value::Float(::std::f64::NEG_INFINITY)
+ };
+
+ (@value (inf)) => {
+ $crate::Value::Float(::std::f64::INFINITY)
+ };
+
+ (@value inf) => {
+ $crate::Value::Float(::std::f64::INFINITY)
+ };
+
+ // Construct a Value from any other type, probably string or boolean or number.
+ (@value $v:tt) => {{
+ // TODO: Implement this with something like serde_json::to_value instead.
+ let de = $crate::macros::IntoDeserializer::<$crate::de::Error>::into_deserializer($v);
+ <$crate::Value as $crate::macros::Deserialize>::deserialize(de).unwrap()
+ }};
+
+ // Base case of inline table.
+ (@table $root:ident) => {};
+
+ // Parse negative number `key = -value`.
+ (@table $root:ident $($($k:tt)-+).+ = - $v:tt , $($rest:tt)*) => {
+ $crate::toml_internal!(@table $root $($($k)-+).+ = (-$v) , $($rest)*);
+ };
+
+ // Parse positive number `key = +value`.
+ (@table $root:ident $($($k:tt)-+).+ = + $v:tt , $($rest:tt)*) => {
+ $crate::toml_internal!(@table $root $($($k)-+).+ = ($v) , $($rest)*);
+ };
+
+ // Parse offset datetime `key = 1979-05-27T00:32:00.999999-07:00`.
+ (@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt . $frac:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
+ $crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec . $frac - $tzh : $tzm) $($rest)*);
+ };
+ // Space instead of T.
+ (@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt . $frac:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
+ $crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec . $frac - $tzh : $tzm) $($rest)*);
+ };
+
+ // Parse offset datetime `key = 1979-05-27T00:32:00-07:00`.
+ (@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
+ $crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec - $tzh : $tzm) $($rest)*);
+ };
+ // Space instead of T.
+ (@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
+ $crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec - $tzh : $tzm) $($rest)*);
+ };
+
+ // Parse local datetime `key = 1979-05-27T00:32:00.999999`.
+ (@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt . $frac:tt , $($rest:tt)*) => {
+ $crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec . $frac) $($rest)*);
+ };
+ // Space instead of T.
+ (@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt . $frac:tt , $($rest:tt)*) => {
+ $crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec . $frac) $($rest)*);
+ };
+
+ // Parse offset datetime `key = 1979-05-27T07:32:00Z` and local datetime `key = 1979-05-27T07:32:00`.
+ (@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt , $($rest:tt)*) => {
+ $crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $dhr : $min : $sec) $($rest)*);
+ };
+ // Space instead of T.
+ (@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt , $($rest:tt)*) => {
+ $crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $day T $hr : $min : $sec) $($rest)*);
+ };
+
+ // Parse local date `key = 1979-05-27`.
+ (@table $root:ident $($($k:tt)-+).+ = $yr:tt - $mo:tt - $day:tt , $($rest:tt)*) => {
+ $crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($yr - $mo - $day) $($rest)*);
+ };
+
+ // Parse local time `key = 00:32:00.999999`.
+ (@table $root:ident $($($k:tt)-+).+ = $hr:tt : $min:tt : $sec:tt . $frac:tt , $($rest:tt)*) => {
+ $crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($hr : $min : $sec . $frac) $($rest)*);
+ };
+
+ // Parse local time `key = 07:32:00`.
+ (@table $root:ident $($($k:tt)-+).+ = $hr:tt : $min:tt : $sec:tt , $($rest:tt)*) => {
+ $crate::toml_internal!(@tabledatetime $root $($($k)-+).+ = ($hr : $min : $sec) $($rest)*);
+ };
+
+ // Parse any other type, probably string or boolean or number.
+ (@table $root:ident $($($k:tt)-+).+ = $v:tt , $($rest:tt)*) => {
+ $crate::macros::insert_toml(
+ &mut $root,
+ &[$(&concat!($("-", $crate::toml_internal!(@path $k),)+)[1..], )+],
+ $crate::toml_internal!(@value $v));
+ $crate::toml_internal!(@table $root $($rest)*);
+ };
+
+ // Parse a Datetime from string and continue in @table state.
+ (@tabledatetime $root:ident $($($k:tt)-+).+ = ($($datetime:tt)*) $($rest:tt)*) => {
+ $crate::macros::insert_toml(
+ &mut $root,
+ &[$(&concat!($("-", $crate::toml_internal!(@path $k),)+)[1..], )+],
+ $crate::Value::Datetime(concat!($(stringify!($datetime)),+).parse().unwrap()));
+ $crate::toml_internal!(@table $root $($rest)*);
+ };
+
+ // Base case of inline array.
+ (@array $root:ident) => {};
+
+ // Parse negative number `-value`.
+ (@array $root:ident - $v:tt , $($rest:tt)*) => {
+ $crate::toml_internal!(@array $root (-$v) , $($rest)*);
+ };
+
+ // Parse positive number `+value`.
+ (@array $root:ident + $v:tt , $($rest:tt)*) => {
+ $crate::toml_internal!(@array $root ($v) , $($rest)*);
+ };
+
+ // Parse offset datetime `1979-05-27T00:32:00.999999-07:00`.
+ (@array $root:ident $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt . $frac:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
+ $crate::toml_internal!(@arraydatetime $root ($yr - $mo - $dhr : $min : $sec . $frac - $tzh : $tzm) $($rest)*);
+ };
+ // Space instead of T.
+ (@array $root:ident $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt . $frac:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
+ $crate::toml_internal!(@arraydatetime $root ($yr - $mo - $day T $hr : $min : $sec . $frac - $tzh : $tzm) $($rest)*);
+ };
+
+ // Parse offset datetime `1979-05-27T00:32:00-07:00`.
+ (@array $root:ident $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
+ $crate::toml_internal!(@arraydatetime $root ($yr - $mo - $dhr : $min : $sec - $tzh : $tzm) $($rest)*);
+ };
+ // Space instead of T.
+ (@array $root:ident $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt - $tzh:tt : $tzm:tt , $($rest:tt)*) => {
+ $crate::toml_internal!(@arraydatetime $root ($yr - $mo - $day T $hr : $min : $sec - $tzh : $tzm) $($rest)*);
+ };
+
+ // Parse local datetime `1979-05-27T00:32:00.999999`.
+ (@array $root:ident $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt . $frac:tt , $($rest:tt)*) => {
+ $crate::toml_internal!(@arraydatetime $root ($yr - $mo - $dhr : $min : $sec . $frac) $($rest)*);
+ };
+ // Space instead of T.
+ (@array $root:ident $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt . $frac:tt , $($rest:tt)*) => {
+ $crate::toml_internal!(@arraydatetime $root ($yr - $mo - $day T $hr : $min : $sec . $frac) $($rest)*);
+ };
+
+ // Parse offset datetime `1979-05-27T07:32:00Z` and local datetime `1979-05-27T07:32:00`.
+ (@array $root:ident $yr:tt - $mo:tt - $dhr:tt : $min:tt : $sec:tt , $($rest:tt)*) => {
+ $crate::toml_internal!(@arraydatetime $root ($yr - $mo - $dhr : $min : $sec) $($rest)*);
+ };
+ // Space instead of T.
+ (@array $root:ident $yr:tt - $mo:tt - $day:tt $hr:tt : $min:tt : $sec:tt , $($rest:tt)*) => {
+ $crate::toml_internal!(@arraydatetime $root ($yr - $mo - $day T $hr : $min : $sec) $($rest)*);
+ };
+
+ // Parse local date `1979-05-27`.
+ (@array $root:ident $yr:tt - $mo:tt - $day:tt , $($rest:tt)*) => {
+ $crate::toml_internal!(@arraydatetime $root ($yr - $mo - $day) $($rest)*);
+ };
+
+ // Parse local time `00:32:00.999999`.
+ (@array $root:ident $hr:tt : $min:tt : $sec:tt . $frac:tt , $($rest:tt)*) => {
+ $crate::toml_internal!(@arraydatetime $root ($hr : $min : $sec . $frac) $($rest)*);
+ };
+
+ // Parse local time `07:32:00`.
+ (@array $root:ident $hr:tt : $min:tt : $sec:tt , $($rest:tt)*) => {
+ $crate::toml_internal!(@arraydatetime $root ($hr : $min : $sec) $($rest)*);
+ };
+
+ // Parse any other type, probably string or boolean or number.
+ (@array $root:ident $v:tt , $($rest:tt)*) => {
+ $root.push($crate::toml_internal!(@value $v));
+ $crate::toml_internal!(@array $root $($rest)*);
+ };
+
+ // Parse a Datetime from string and continue in @array state.
+ (@arraydatetime $root:ident ($($datetime:tt)*) $($rest:tt)*) => {
+ $root.push($crate::Value::Datetime(concat!($(stringify!($datetime)),+).parse().unwrap()));
+ $crate::toml_internal!(@array $root $($rest)*);
+ };
+
+ // No trailing comma required if the tokens are empty.
+ (@trailingcomma ($($args:tt)*)) => {
+ $crate::toml_internal!($($args)*);
+ };
+
+ // Tokens end with a trailing comma, do not append another one.
+ (@trailingcomma ($($args:tt)*) ,) => {
+ $crate::toml_internal!($($args)* ,);
+ };
+
+ // Tokens end with something other than comma, append a trailing comma.
+ (@trailingcomma ($($args:tt)*) $last:tt) => {
+ $crate::toml_internal!($($args)* $last ,);
+ };
+
+ // Not yet at the last token.
+ (@trailingcomma ($($args:tt)*) $first:tt $($rest:tt)+) => {
+ $crate::toml_internal!(@trailingcomma ($($args)* $first) $($rest)+);
+ };
+}
+
+// Called when parsing a `key = value` pair.
+// Inserts an entry into the table at the given path.
+pub fn insert_toml(root: &mut Value, path: &[&str], value: Value) {
+ *traverse(root, path) = value;
+}
+
+// Called when parsing an `[[array header]]`.
+// Pushes an empty table onto the array at the given path.
+pub fn push_toml(root: &mut Value, path: &[&str]) {
+ let target = traverse(root, path);
+ if !target.is_array() {
+ *target = Value::Array(Array::new());
+ }
+ target
+ .as_array_mut()
+ .unwrap()
+ .push(Value::Table(Table::new()));
+}
+
+fn traverse<'a>(root: &'a mut Value, path: &[&str]) -> &'a mut Value {
+ let mut cur = root;
+ for &key in path {
+ // Lexical lifetimes :D
+ let cur1 = cur;
+
+ // From the TOML spec:
+ //
+ // > Each double-bracketed sub-table will belong to the most recently
+ // > defined table element above it.
+ let cur2 = if cur1.is_array() {
+ cur1.as_array_mut().unwrap().last_mut().unwrap()
+ } else {
+ cur1
+ };
+
+ // We are about to index into this value, so it better be a table.
+ if !cur2.is_table() {
+ *cur2 = Value::Table(Table::new());
+ }
+
+ if !cur2.as_table().unwrap().contains_key(key) {
+ // Insert an empty table for the next loop iteration to point to.
+ let empty = Value::Table(Table::new());
+ cur2.as_table_mut().unwrap().insert(key.to_owned(), empty);
+ }
+
+ // Step into the current table.
+ cur = cur2.as_table_mut().unwrap().get_mut(key).unwrap();
+ }
+ cur
+}
diff --git a/third_party/rust/toml/src/map.rs b/third_party/rust/toml/src/map.rs
new file mode 100644
index 0000000000..d130a1d540
--- /dev/null
+++ b/third_party/rust/toml/src/map.rs
@@ -0,0 +1,595 @@
+// Copyright 2017 Serde Developers
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! A map of String to toml::Value.
+//!
+//! By default the map is backed by a [`BTreeMap`]. Enable the `preserve_order`
+//! feature of toml-rs to use [`LinkedHashMap`] instead.
+//!
+//! [`BTreeMap`]: https://doc.rust-lang.org/std/collections/struct.BTreeMap.html
+//! [`LinkedHashMap`]: https://docs.rs/linked-hash-map/*/linked_hash_map/struct.LinkedHashMap.html
+
+use crate::value::Value;
+use serde::{de, ser};
+use std::borrow::Borrow;
+use std::fmt::{self, Debug};
+use std::hash::Hash;
+use std::iter::FromIterator;
+use std::ops;
+
+#[cfg(not(feature = "preserve_order"))]
+use std::collections::{btree_map, BTreeMap};
+
+#[cfg(feature = "preserve_order")]
+use indexmap::{self, IndexMap};
+
+/// Represents a TOML key/value type.
+pub struct Map<K, V> {
+ map: MapImpl<K, V>,
+}
+
+#[cfg(not(feature = "preserve_order"))]
+type MapImpl<K, V> = BTreeMap<K, V>;
+#[cfg(feature = "preserve_order")]
+type MapImpl<K, V> = IndexMap<K, V>;
+
+impl Map<String, Value> {
+ /// Makes a new empty Map.
+ #[inline]
+ pub fn new() -> Self {
+ Map {
+ map: MapImpl::new(),
+ }
+ }
+
+ #[cfg(not(feature = "preserve_order"))]
+ /// Makes a new empty Map with the given initial capacity.
+ #[inline]
+ pub fn with_capacity(capacity: usize) -> Self {
+ // does not support with_capacity
+ let _ = capacity;
+ Map {
+ map: BTreeMap::new(),
+ }
+ }
+
+ #[cfg(feature = "preserve_order")]
+ /// Makes a new empty Map with the given initial capacity.
+ #[inline]
+ pub fn with_capacity(capacity: usize) -> Self {
+ Map {
+ map: IndexMap::with_capacity(capacity),
+ }
+ }
+
+ /// Clears the map, removing all values.
+ #[inline]
+ pub fn clear(&mut self) {
+ self.map.clear()
+ }
+
+ /// Returns a reference to the value corresponding to the key.
+ ///
+ /// The key may be any borrowed form of the map's key type, but the ordering
+ /// on the borrowed form *must* match the ordering on the key type.
+ #[inline]
+ pub fn get<Q: ?Sized>(&self, key: &Q) -> Option<&Value>
+ where
+ String: Borrow<Q>,
+ Q: Ord + Eq + Hash,
+ {
+ self.map.get(key)
+ }
+
+ /// Returns true if the map contains a value for the specified key.
+ ///
+ /// The key may be any borrowed form of the map's key type, but the ordering
+ /// on the borrowed form *must* match the ordering on the key type.
+ #[inline]
+ pub fn contains_key<Q: ?Sized>(&self, key: &Q) -> bool
+ where
+ String: Borrow<Q>,
+ Q: Ord + Eq + Hash,
+ {
+ self.map.contains_key(key)
+ }
+
+ /// Returns a mutable reference to the value corresponding to the key.
+ ///
+ /// The key may be any borrowed form of the map's key type, but the ordering
+ /// on the borrowed form *must* match the ordering on the key type.
+ #[inline]
+ pub fn get_mut<Q: ?Sized>(&mut self, key: &Q) -> Option<&mut Value>
+ where
+ String: Borrow<Q>,
+ Q: Ord + Eq + Hash,
+ {
+ self.map.get_mut(key)
+ }
+
+ /// Inserts a key-value pair into the map.
+ ///
+ /// If the map did not have this key present, `None` is returned.
+ ///
+ /// If the map did have this key present, the value is updated, and the old
+ /// value is returned. The key is not updated, though; this matters for
+ /// types that can be `==` without being identical.
+ #[inline]
+ pub fn insert(&mut self, k: String, v: Value) -> Option<Value> {
+ self.map.insert(k, v)
+ }
+
+ /// Removes a key from the map, returning the value at the key if the key
+ /// was previously in the map.
+ ///
+ /// The key may be any borrowed form of the map's key type, but the ordering
+ /// on the borrowed form *must* match the ordering on the key type.
+ #[inline]
+ pub fn remove<Q: ?Sized>(&mut self, key: &Q) -> Option<Value>
+ where
+ String: Borrow<Q>,
+ Q: Ord + Eq + Hash,
+ {
+ self.map.remove(key)
+ }
+
+ /// Gets the given key's corresponding entry in the map for in-place
+ /// manipulation.
+ pub fn entry<S>(&mut self, key: S) -> Entry<'_>
+ where
+ S: Into<String>,
+ {
+ #[cfg(feature = "preserve_order")]
+ use indexmap::map::Entry as EntryImpl;
+ #[cfg(not(feature = "preserve_order"))]
+ use std::collections::btree_map::Entry as EntryImpl;
+
+ match self.map.entry(key.into()) {
+ EntryImpl::Vacant(vacant) => Entry::Vacant(VacantEntry { vacant }),
+ EntryImpl::Occupied(occupied) => Entry::Occupied(OccupiedEntry { occupied }),
+ }
+ }
+
+ /// Returns the number of elements in the map.
+ #[inline]
+ pub fn len(&self) -> usize {
+ self.map.len()
+ }
+
+ /// Returns true if the map contains no elements.
+ #[inline]
+ pub fn is_empty(&self) -> bool {
+ self.map.is_empty()
+ }
+
+ /// Gets an iterator over the entries of the map.
+ #[inline]
+ pub fn iter(&self) -> Iter<'_> {
+ Iter {
+ iter: self.map.iter(),
+ }
+ }
+
+ /// Gets a mutable iterator over the entries of the map.
+ #[inline]
+ pub fn iter_mut(&mut self) -> IterMut<'_> {
+ IterMut {
+ iter: self.map.iter_mut(),
+ }
+ }
+
+ /// Gets an iterator over the keys of the map.
+ #[inline]
+ pub fn keys(&self) -> Keys<'_> {
+ Keys {
+ iter: self.map.keys(),
+ }
+ }
+
+ /// Gets an iterator over the values of the map.
+ #[inline]
+ pub fn values(&self) -> Values<'_> {
+ Values {
+ iter: self.map.values(),
+ }
+ }
+}
+
+impl Default for Map<String, Value> {
+ #[inline]
+ fn default() -> Self {
+ Map {
+ map: MapImpl::new(),
+ }
+ }
+}
+
+impl Clone for Map<String, Value> {
+ #[inline]
+ fn clone(&self) -> Self {
+ Map {
+ map: self.map.clone(),
+ }
+ }
+}
+
+impl PartialEq for Map<String, Value> {
+ #[inline]
+ fn eq(&self, other: &Self) -> bool {
+ self.map.eq(&other.map)
+ }
+}
+
+/// Access an element of this map. Panics if the given key is not present in the
+/// map.
+impl<'a, Q: ?Sized> ops::Index<&'a Q> for Map<String, Value>
+where
+ String: Borrow<Q>,
+ Q: Ord + Eq + Hash,
+{
+ type Output = Value;
+
+ fn index(&self, index: &Q) -> &Value {
+ self.map.index(index)
+ }
+}
+
+/// Mutably access an element of this map. Panics if the given key is not
+/// present in the map.
+impl<'a, Q: ?Sized> ops::IndexMut<&'a Q> for Map<String, Value>
+where
+ String: Borrow<Q>,
+ Q: Ord + Eq + Hash,
+{
+ fn index_mut(&mut self, index: &Q) -> &mut Value {
+ self.map.get_mut(index).expect("no entry found for key")
+ }
+}
+
+impl Debug for Map<String, Value> {
+ #[inline]
+ fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ self.map.fmt(formatter)
+ }
+}
+
+impl ser::Serialize for Map<String, Value> {
+ #[inline]
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: ser::Serializer,
+ {
+ use serde::ser::SerializeMap;
+ let mut map = serializer.serialize_map(Some(self.len()))?;
+ for (k, v) in self {
+ map.serialize_key(k)?;
+ map.serialize_value(v)?;
+ }
+ map.end()
+ }
+}
+
+impl<'de> de::Deserialize<'de> for Map<String, Value> {
+ #[inline]
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ struct Visitor;
+
+ impl<'de> de::Visitor<'de> for Visitor {
+ type Value = Map<String, Value>;
+
+ fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+ formatter.write_str("a map")
+ }
+
+ #[inline]
+ fn visit_unit<E>(self) -> Result<Self::Value, E>
+ where
+ E: de::Error,
+ {
+ Ok(Map::new())
+ }
+
+ #[inline]
+ fn visit_map<V>(self, mut visitor: V) -> Result<Self::Value, V::Error>
+ where
+ V: de::MapAccess<'de>,
+ {
+ let mut values = Map::new();
+
+ while let Some((key, value)) = visitor.next_entry()? {
+ values.insert(key, value);
+ }
+
+ Ok(values)
+ }
+ }
+
+ deserializer.deserialize_map(Visitor)
+ }
+}
+
+impl FromIterator<(String, Value)> for Map<String, Value> {
+ fn from_iter<T>(iter: T) -> Self
+ where
+ T: IntoIterator<Item = (String, Value)>,
+ {
+ Map {
+ map: FromIterator::from_iter(iter),
+ }
+ }
+}
+
+impl Extend<(String, Value)> for Map<String, Value> {
+ fn extend<T>(&mut self, iter: T)
+ where
+ T: IntoIterator<Item = (String, Value)>,
+ {
+ self.map.extend(iter);
+ }
+}
+
+macro_rules! delegate_iterator {
+ (($name:ident $($generics:tt)*) => $item:ty) => {
+ impl $($generics)* Iterator for $name $($generics)* {
+ type Item = $item;
+ #[inline]
+ fn next(&mut self) -> Option<Self::Item> {
+ self.iter.next()
+ }
+ #[inline]
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.iter.size_hint()
+ }
+ }
+
+ impl $($generics)* DoubleEndedIterator for $name $($generics)* {
+ #[inline]
+ fn next_back(&mut self) -> Option<Self::Item> {
+ self.iter.next_back()
+ }
+ }
+
+ impl $($generics)* ExactSizeIterator for $name $($generics)* {
+ #[inline]
+ fn len(&self) -> usize {
+ self.iter.len()
+ }
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+/// A view into a single entry in a map, which may either be vacant or occupied.
+/// This enum is constructed from the [`entry`] method on [`Map`].
+///
+/// [`entry`]: struct.Map.html#method.entry
+/// [`Map`]: struct.Map.html
+pub enum Entry<'a> {
+ /// A vacant Entry.
+ Vacant(VacantEntry<'a>),
+ /// An occupied Entry.
+ Occupied(OccupiedEntry<'a>),
+}
+
+/// A vacant Entry. It is part of the [`Entry`] enum.
+///
+/// [`Entry`]: enum.Entry.html
+pub struct VacantEntry<'a> {
+ vacant: VacantEntryImpl<'a>,
+}
+
+/// An occupied Entry. It is part of the [`Entry`] enum.
+///
+/// [`Entry`]: enum.Entry.html
+pub struct OccupiedEntry<'a> {
+ occupied: OccupiedEntryImpl<'a>,
+}
+
+#[cfg(not(feature = "preserve_order"))]
+type VacantEntryImpl<'a> = btree_map::VacantEntry<'a, String, Value>;
+#[cfg(feature = "preserve_order")]
+type VacantEntryImpl<'a> = indexmap::map::VacantEntry<'a, String, Value>;
+
+#[cfg(not(feature = "preserve_order"))]
+type OccupiedEntryImpl<'a> = btree_map::OccupiedEntry<'a, String, Value>;
+#[cfg(feature = "preserve_order")]
+type OccupiedEntryImpl<'a> = indexmap::map::OccupiedEntry<'a, String, Value>;
+
+impl<'a> Entry<'a> {
+ /// Returns a reference to this entry's key.
+ pub fn key(&self) -> &String {
+ match *self {
+ Entry::Vacant(ref e) => e.key(),
+ Entry::Occupied(ref e) => e.key(),
+ }
+ }
+
+ /// Ensures a value is in the entry by inserting the default if empty, and
+ /// returns a mutable reference to the value in the entry.
+ pub fn or_insert(self, default: Value) -> &'a mut Value {
+ match self {
+ Entry::Vacant(entry) => entry.insert(default),
+ Entry::Occupied(entry) => entry.into_mut(),
+ }
+ }
+
+ /// Ensures a value is in the entry by inserting the result of the default
+ /// function if empty, and returns a mutable reference to the value in the
+ /// entry.
+ pub fn or_insert_with<F>(self, default: F) -> &'a mut Value
+ where
+ F: FnOnce() -> Value,
+ {
+ match self {
+ Entry::Vacant(entry) => entry.insert(default()),
+ Entry::Occupied(entry) => entry.into_mut(),
+ }
+ }
+}
+
+impl<'a> VacantEntry<'a> {
+ /// Gets a reference to the key that would be used when inserting a value
+ /// through the VacantEntry.
+ #[inline]
+ pub fn key(&self) -> &String {
+ self.vacant.key()
+ }
+
+ /// Sets the value of the entry with the VacantEntry's key, and returns a
+ /// mutable reference to it.
+ #[inline]
+ pub fn insert(self, value: Value) -> &'a mut Value {
+ self.vacant.insert(value)
+ }
+}
+
+impl<'a> OccupiedEntry<'a> {
+ /// Gets a reference to the key in the entry.
+ #[inline]
+ pub fn key(&self) -> &String {
+ self.occupied.key()
+ }
+
+ /// Gets a reference to the value in the entry.
+ #[inline]
+ pub fn get(&self) -> &Value {
+ self.occupied.get()
+ }
+
+ /// Gets a mutable reference to the value in the entry.
+ #[inline]
+ pub fn get_mut(&mut self) -> &mut Value {
+ self.occupied.get_mut()
+ }
+
+ /// Converts the entry into a mutable reference to its value.
+ #[inline]
+ pub fn into_mut(self) -> &'a mut Value {
+ self.occupied.into_mut()
+ }
+
+ /// Sets the value of the entry with the `OccupiedEntry`'s key, and returns
+ /// the entry's old value.
+ #[inline]
+ pub fn insert(&mut self, value: Value) -> Value {
+ self.occupied.insert(value)
+ }
+
+ /// Takes the value of the entry out of the map, and returns it.
+ #[inline]
+ pub fn remove(self) -> Value {
+ self.occupied.remove()
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+impl<'a> IntoIterator for &'a Map<String, Value> {
+ type Item = (&'a String, &'a Value);
+ type IntoIter = Iter<'a>;
+ #[inline]
+ fn into_iter(self) -> Self::IntoIter {
+ Iter {
+ iter: self.map.iter(),
+ }
+ }
+}
+
+/// An iterator over a toml::Map's entries.
+pub struct Iter<'a> {
+ iter: IterImpl<'a>,
+}
+
+#[cfg(not(feature = "preserve_order"))]
+type IterImpl<'a> = btree_map::Iter<'a, String, Value>;
+#[cfg(feature = "preserve_order")]
+type IterImpl<'a> = indexmap::map::Iter<'a, String, Value>;
+
+delegate_iterator!((Iter<'a>) => (&'a String, &'a Value));
+
+//////////////////////////////////////////////////////////////////////////////
+
+impl<'a> IntoIterator for &'a mut Map<String, Value> {
+ type Item = (&'a String, &'a mut Value);
+ type IntoIter = IterMut<'a>;
+ #[inline]
+ fn into_iter(self) -> Self::IntoIter {
+ IterMut {
+ iter: self.map.iter_mut(),
+ }
+ }
+}
+
+/// A mutable iterator over a toml::Map's entries.
+pub struct IterMut<'a> {
+ iter: IterMutImpl<'a>,
+}
+
+#[cfg(not(feature = "preserve_order"))]
+type IterMutImpl<'a> = btree_map::IterMut<'a, String, Value>;
+#[cfg(feature = "preserve_order")]
+type IterMutImpl<'a> = indexmap::map::IterMut<'a, String, Value>;
+
+delegate_iterator!((IterMut<'a>) => (&'a String, &'a mut Value));
+
+//////////////////////////////////////////////////////////////////////////////
+
+impl IntoIterator for Map<String, Value> {
+ type Item = (String, Value);
+ type IntoIter = IntoIter;
+ #[inline]
+ fn into_iter(self) -> Self::IntoIter {
+ IntoIter {
+ iter: self.map.into_iter(),
+ }
+ }
+}
+
+/// An owning iterator over a toml::Map's entries.
+pub struct IntoIter {
+ iter: IntoIterImpl,
+}
+
+#[cfg(not(feature = "preserve_order"))]
+type IntoIterImpl = btree_map::IntoIter<String, Value>;
+#[cfg(feature = "preserve_order")]
+type IntoIterImpl = indexmap::map::IntoIter<String, Value>;
+
+delegate_iterator!((IntoIter) => (String, Value));
+
+//////////////////////////////////////////////////////////////////////////////
+
+/// An iterator over a toml::Map's keys.
+pub struct Keys<'a> {
+ iter: KeysImpl<'a>,
+}
+
+#[cfg(not(feature = "preserve_order"))]
+type KeysImpl<'a> = btree_map::Keys<'a, String, Value>;
+#[cfg(feature = "preserve_order")]
+type KeysImpl<'a> = indexmap::map::Keys<'a, String, Value>;
+
+delegate_iterator!((Keys<'a>) => &'a String);
+
+//////////////////////////////////////////////////////////////////////////////
+
+/// An iterator over a toml::Map's values.
+pub struct Values<'a> {
+ iter: ValuesImpl<'a>,
+}
+
+#[cfg(not(feature = "preserve_order"))]
+type ValuesImpl<'a> = btree_map::Values<'a, String, Value>;
+#[cfg(feature = "preserve_order")]
+type ValuesImpl<'a> = indexmap::map::Values<'a, String, Value>;
+
+delegate_iterator!((Values<'a>) => &'a Value);
diff --git a/third_party/rust/toml/src/ser.rs b/third_party/rust/toml/src/ser.rs
new file mode 100644
index 0000000000..ecee1a296b
--- /dev/null
+++ b/third_party/rust/toml/src/ser.rs
@@ -0,0 +1,1853 @@
+//! 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
+//! # use serde_derive::Serialize;
+//! # 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 crate::datetime;
+use serde::ser;
+
+/// 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
+///
+/// ```
+/// use serde_derive::Serialize;
+///
+/// #[derive(Serialize)]
+/// struct Config {
+/// database: Database,
+/// }
+///
+/// #[derive(Serialize)]
+/// struct Database {
+/// ip: String,
+/// port: Vec<u16>,
+/// connection_max: u32,
+/// enabled: bool,
+/// }
+///
+/// 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)]
+#[non_exhaustive]
+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,
+
+ /// An array had to be homogeneous, but now it is allowed to be heterogeneous.
+ #[doc(hidden)]
+ 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),
+}
+
+#[derive(Debug, Default, Clone)]
+/// Internal place for holding array settings
+struct ArraySettings {
+ indent: usize,
+ trailing_comma: bool,
+}
+
+impl ArraySettings {
+ fn pretty() -> ArraySettings {
+ ArraySettings {
+ indent: 4,
+ trailing_comma: true,
+ }
+ }
+}
+
+#[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)]
+/// 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, Copy, Clone)]
+enum ArrayState {
+ Started,
+ StartedAsATable,
+}
+
+#[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<ArrayState>>,
+ len: Option<usize>,
+ },
+ End,
+}
+
+#[doc(hidden)]
+pub struct SerializeSeq<'a, 'b> {
+ ser: &'b mut Serializer<'a>,
+ first: Cell<bool>,
+ type_: Cell<Option<ArrayState>>,
+ len: Option<usize>,
+}
+
+#[doc(hidden)]
+pub enum SerializeTable<'a, '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,
+ 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,
+ 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 Some(ref mut s) = 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 Some(ref mut a) = 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 Some(ref mut a) = 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_: ArrayState) -> Result<(), Error> {
+ self.emit_key(type_)?;
+ write!(self.dst, "{}", t).map_err(ser::Error::custom)?;
+ if let State::Table { .. } = self.state {
+ self.dst.push('\n');
+ }
+ Ok(())
+ }
+
+ fn emit_key(&mut self, type_: ArrayState) -> 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('[')
+ } 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(' ');
+ }
+ }
+ }
+ Ok(())
+ }
+
+ fn array_type(&mut self, type_: ArrayState) -> Result<(), Error> {
+ let prev = match self.state {
+ State::Array { type_, .. } => type_,
+ _ => return Ok(()),
+ };
+ if prev.get().is_none() {
+ prev.set(Some(type_));
+ }
+ Ok(())
+ }
+
+ fn escape_key(&mut self, key: &str) -> Result<(), Error> {
+ let ok = !key.is_empty()
+ && key
+ .chars()
+ .all(|c| matches!(c,'a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '_'));
+ if ok {
+ write!(self.dst, "{}", key).map_err(ser::Error::custom)?;
+ } 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,
+ // Escape codes are needed if any ascii control
+ // characters are present, including \b \f \r.
+ c if c <= '\u{1f}' || c == '\u{7f}' => can_be_pretty = false,
+ _ => {}
+ }
+ 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 && found_singles > 0 && value.ends_with('\'') {
+ // We cannot escape the ending quote so we must use """
+ can_be_pretty = false;
+ }
+ 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}' || c == '\u{7f}' => {
+ write!(self.dst, "\\u{:04X}", ch as u32).map_err(ser::Error::custom)?;
+ }
+ 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('[');
+ if array_of_tables {
+ self.dst.push('[');
+ }
+ self.emit_key_part(state)?;
+ if array_of_tables {
+ self.dst.push(']');
+ }
+ 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('.');
+ }
+ self.escape_key(key)?;
+ Ok(false)
+ }
+ }
+ }
+}
+
+macro_rules! serialize_float {
+ ($this:expr, $v:expr) => {{
+ $this.emit_key(ArrayState::Started)?;
+ match ($v.is_sign_negative(), $v.is_nan(), $v == 0.0) {
+ (true, true, _) => write!($this.dst, "-nan"),
+ (false, true, _) => write!($this.dst, "nan"),
+ (true, false, true) => write!($this.dst, "-0.0"),
+ (false, false, true) => write!($this.dst, "0.0"),
+ (_, false, false) => write!($this.dst, "{}", $v).and_then(|_| {
+ if $v % 1.0 == 0.0 {
+ write!($this.dst, ".0")
+ } else {
+ Ok(())
+ }
+ }),
+ }
+ .map_err(ser::Error::custom)?;
+
+ if let State::Table { .. } = $this.state {
+ $this.dst.push_str("\n");
+ }
+ return Ok(());
+ }};
+}
+
+impl<'a, 'b> ser::Serializer for &'b mut Serializer<'a> {
+ type Ok = ();
+ type Error = Error;
+ type SerializeSeq = SerializeSeq<'a, 'b>;
+ type SerializeTuple = SerializeSeq<'a, 'b>;
+ type SerializeTupleStruct = SerializeSeq<'a, 'b>;
+ type SerializeTupleVariant = SerializeSeq<'a, 'b>;
+ 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, ArrayState::Started)
+ }
+
+ fn serialize_i8(self, v: i8) -> Result<(), Self::Error> {
+ self.display(v, ArrayState::Started)
+ }
+
+ fn serialize_i16(self, v: i16) -> Result<(), Self::Error> {
+ self.display(v, ArrayState::Started)
+ }
+
+ fn serialize_i32(self, v: i32) -> Result<(), Self::Error> {
+ self.display(v, ArrayState::Started)
+ }
+
+ fn serialize_i64(self, v: i64) -> Result<(), Self::Error> {
+ self.display(v, ArrayState::Started)
+ }
+
+ fn serialize_u8(self, v: u8) -> Result<(), Self::Error> {
+ self.display(v, ArrayState::Started)
+ }
+
+ fn serialize_u16(self, v: u16) -> Result<(), Self::Error> {
+ self.display(v, ArrayState::Started)
+ }
+
+ fn serialize_u32(self, v: u32) -> Result<(), Self::Error> {
+ self.display(v, ArrayState::Started)
+ }
+
+ fn serialize_u64(self, v: u64) -> Result<(), Self::Error> {
+ self.display(v, ArrayState::Started)
+ }
+
+ fn serialize_f32(self, v: f32) -> Result<(), Self::Error> {
+ serialize_float!(self, v)
+ }
+
+ fn serialize_f64(self, v: f64) -> Result<(), Self::Error> {
+ serialize_float!(self, v)
+ }
+
+ 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(self, value: &str) -> Result<(), Self::Error> {
+ self.emit_key(ArrayState::Started)?;
+ self.emit_str(value, false)?;
+ if let State::Table { .. } = self.state {
+ self.dst.push('\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(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
+ self.array_type(ArrayState::Started)?;
+ Ok(SerializeSeq {
+ ser: self,
+ first: Cell::new(true),
+ type_: Cell::new(None),
+ len,
+ })
+ }
+
+ fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> {
+ self.serialize_seq(Some(len))
+ }
+
+ fn serialize_tuple_struct(
+ self,
+ _name: &'static str,
+ len: usize,
+ ) -> Result<Self::SerializeTupleStruct, Self::Error> {
+ self.serialize_seq(Some(len))
+ }
+
+ fn serialize_tuple_variant(
+ self,
+ _name: &'static str,
+ _variant_index: u32,
+ _variant: &'static str,
+ len: usize,
+ ) -> Result<Self::SerializeTupleVariant, Self::Error> {
+ self.serialize_seq(Some(len))
+ }
+
+ fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
+ self.array_type(ArrayState::StartedAsATable)?;
+ Ok(SerializeTable::Table {
+ ser: self,
+ key: String::new(),
+ first: Cell::new(true),
+ table_emitted: Cell::new(false),
+ })
+ }
+
+ fn serialize_struct(
+ self,
+ name: &'static str,
+ _len: usize,
+ ) -> Result<Self::SerializeStruct, Self::Error> {
+ if name == datetime::NAME {
+ self.array_type(ArrayState::Started)?;
+ Ok(SerializeTable::Datetime(self))
+ } else {
+ self.array_type(ArrayState::StartedAsATable)?;
+ 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(ArrayState::StartedAsATable) => return Ok(()),
+ Some(ArrayState::Started) => match (self.len, &self.ser.settings.array) {
+ (Some(0..=1), _) | (_, &None) => {
+ self.ser.dst.push(']');
+ }
+ (_, &Some(ref a)) => {
+ if a.trailing_comma {
+ self.ser.dst.push(',');
+ }
+ self.ser.dst.push_str("\n]");
+ }
+ },
+ None => {
+ assert!(self.first.get());
+ self.ser.emit_key(ArrayState::Started)?;
+ self.ser.dst.push_str("[]")
+ }
+ }
+ if let State::Table { .. } = self.ser.state {
+ self.ser.dst.push('\n');
+ }
+ Ok(())
+ }
+}
+
+impl<'a, 'b> ser::SerializeTuple for SerializeSeq<'a, 'b> {
+ type Ok = ();
+ type Error = Error;
+
+ fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), Error>
+ where
+ T: ser::Serialize,
+ {
+ ser::SerializeSeq::serialize_element(self, value)
+ }
+
+ fn end(self) -> Result<(), Error> {
+ ser::SerializeSeq::end(self)
+ }
+}
+
+impl<'a, 'b> ser::SerializeTupleVariant for SerializeSeq<'a, 'b> {
+ type Ok = ();
+ type Error = Error;
+
+ fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), Error>
+ where
+ T: ser::Serialize,
+ {
+ ser::SerializeSeq::serialize_element(self, value)
+ }
+
+ fn end(self) -> Result<(), Error> {
+ ser::SerializeSeq::end(self)
+ }
+}
+
+impl<'a, 'b> ser::SerializeTupleStruct for SerializeSeq<'a, 'b> {
+ type Ok = ();
+ type Error = Error;
+
+ fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), Error>
+ where
+ T: ser::Serialize,
+ {
+ ser::SerializeSeq::serialize_element(self, value)
+ }
+
+ fn end(self) -> Result<(), Error> {
+ ser::SerializeSeq::end(self)
+ }
+}
+
+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,
+ parent: &ser.state,
+ first,
+ 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 { 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 == datetime::FIELD {
+ value.serialize(DateStrEmitter(*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,
+ parent: &ser.state,
+ first,
+ 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 { ser, first, .. } => {
+ if first.get() {
+ let state = ser.state.clone();
+ ser.emit_table_header(&state)?;
+ }
+ }
+ }
+ Ok(())
+ }
+}
+
+struct DateStrEmitter<'a, '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, ArrayState::Started)?;
+ 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::DateInvalid)
+ }
+
+ fn serialize_unit(self) -> Result<(), Self::Error> {
+ Err(Error::DateInvalid)
+ }
+
+ 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,
+ {
+ value.serialize(self)
+ }
+
+ 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::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::ArrayMixedType => unreachable!(),
+ }
+ }
+}
+
+impl error::Error for Error {}
+
+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
+/// # use serde_derive::Serialize;
+/// # 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 = Self;
+ type SerializeTupleStruct = Self;
+ type SerializeTupleVariant = Self;
+ 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> {
+ Ok(self)
+ }
+
+ fn serialize_tuple_struct(
+ self,
+ _: &'static str,
+ _: usize,
+ ) -> Result<Self::SerializeTupleStruct, Self::Error> {
+ Ok(self)
+ }
+
+ fn serialize_tuple_variant(
+ self,
+ _: &'static str,
+ _: u32,
+ _: &'static str,
+ _: usize,
+ ) -> Result<Self::SerializeTupleVariant, Self::Error> {
+ Ok(self)
+ }
+
+ 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::SerializeTuple 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::SerializeTupleVariant for Categorize<E> {
+ type Ok = Category;
+ type Error = E;
+
+ fn serialize_field<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::SerializeTupleStruct for Categorize<E> {
+ type Ok = Category;
+ type Error = E;
+
+ fn serialize_field<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/spanned.rs b/third_party/rust/toml/src/spanned.rs
new file mode 100644
index 0000000000..f90c5492d9
--- /dev/null
+++ b/third_party/rust/toml/src/spanned.rs
@@ -0,0 +1,166 @@
+use serde::{de, ser};
+use std::borrow::Borrow;
+use std::cmp::Ordering;
+use std::fmt;
+use std::hash::{Hash, Hasher};
+
+pub(crate) const NAME: &str = "$__toml_private_Spanned";
+pub(crate) const START: &str = "$__toml_private_start";
+pub(crate) const END: &str = "$__toml_private_end";
+pub(crate) const VALUE: &str = "$__toml_private_value";
+
+/// A spanned value, indicating the range at which it is defined in the source.
+///
+/// ```
+/// use serde_derive::Deserialize;
+/// use toml::Spanned;
+///
+/// #[derive(Deserialize)]
+/// struct Value {
+/// s: Spanned<String>,
+/// }
+///
+/// let t = "s = \"value\"\n";
+///
+/// let u: Value = toml::from_str(t).unwrap();
+///
+/// assert_eq!(u.s.start(), 4);
+/// assert_eq!(u.s.end(), 11);
+/// assert_eq!(u.s.get_ref(), "value");
+/// assert_eq!(u.s.into_inner(), String::from("value"));
+/// ```
+#[derive(Clone, Debug)]
+pub struct Spanned<T> {
+ /// The start range.
+ start: usize,
+ /// The end range (exclusive).
+ end: usize,
+ /// The spanned value.
+ value: T,
+}
+
+impl<T> Spanned<T> {
+ /// Access the start of the span of the contained value.
+ pub fn start(&self) -> usize {
+ self.start
+ }
+
+ /// Access the end of the span of the contained value.
+ pub fn end(&self) -> usize {
+ self.end
+ }
+
+ /// Get the span of the contained value.
+ pub fn span(&self) -> (usize, usize) {
+ (self.start, self.end)
+ }
+
+ /// Consumes the spanned value and returns the contained value.
+ pub fn into_inner(self) -> T {
+ self.value
+ }
+
+ /// Returns a reference to the contained value.
+ pub fn get_ref(&self) -> &T {
+ &self.value
+ }
+
+ /// Returns a mutable reference to the contained value.
+ pub fn get_mut(&mut self) -> &mut T {
+ &mut self.value
+ }
+}
+
+impl Borrow<str> for Spanned<String> {
+ fn borrow(&self) -> &str {
+ self.get_ref()
+ }
+}
+
+impl<T: PartialEq> PartialEq for Spanned<T> {
+ fn eq(&self, other: &Self) -> bool {
+ self.value.eq(&other.value)
+ }
+}
+
+impl<T: Eq> Eq for Spanned<T> {}
+
+impl<T: Hash> Hash for Spanned<T> {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.value.hash(state);
+ }
+}
+
+impl<T: PartialOrd> PartialOrd for Spanned<T> {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ self.value.partial_cmp(&other.value)
+ }
+}
+
+impl<T: Ord> Ord for Spanned<T> {
+ fn cmp(&self, other: &Self) -> Ordering {
+ self.value.cmp(&other.value)
+ }
+}
+
+impl<'de, T> de::Deserialize<'de> for Spanned<T>
+where
+ T: de::Deserialize<'de>,
+{
+ fn deserialize<D>(deserializer: D) -> Result<Spanned<T>, D::Error>
+ where
+ D: de::Deserializer<'de>,
+ {
+ struct SpannedVisitor<T>(::std::marker::PhantomData<T>);
+
+ impl<'de, T> de::Visitor<'de> for SpannedVisitor<T>
+ where
+ T: de::Deserialize<'de>,
+ {
+ type Value = Spanned<T>;
+
+ fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+ formatter.write_str("a TOML spanned")
+ }
+
+ fn visit_map<V>(self, mut visitor: V) -> Result<Spanned<T>, V::Error>
+ where
+ V: de::MapAccess<'de>,
+ {
+ if visitor.next_key()? != Some(START) {
+ return Err(de::Error::custom("spanned start key not found"));
+ }
+
+ let start: usize = visitor.next_value()?;
+
+ if visitor.next_key()? != Some(END) {
+ return Err(de::Error::custom("spanned end key not found"));
+ }
+
+ let end: usize = visitor.next_value()?;
+
+ if visitor.next_key()? != Some(VALUE) {
+ return Err(de::Error::custom("spanned value key not found"));
+ }
+
+ let value: T = visitor.next_value()?;
+
+ Ok(Spanned { start, end, value })
+ }
+ }
+
+ let visitor = SpannedVisitor(::std::marker::PhantomData);
+
+ static FIELDS: [&str; 3] = [START, END, VALUE];
+ deserializer.deserialize_struct(NAME, &FIELDS, visitor)
+ }
+}
+
+impl<T: ser::Serialize> ser::Serialize for Spanned<T> {
+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: ser::Serializer,
+ {
+ self.value.serialize(serializer)
+ }
+}
diff --git a/third_party/rust/toml/src/tokens.rs b/third_party/rust/toml/src/tokens.rs
new file mode 100644
index 0000000000..ea1e7ebd83
--- /dev/null
+++ b/third_party/rust/toml/src/tokens.rs
@@ -0,0 +1,742 @@
+use std::borrow::Cow;
+use std::char;
+use std::str;
+use std::string;
+use std::string::String as StdString;
+
+use self::Token::*;
+
+/// A span, designating a range of bytes where a token is located.
+#[derive(Eq, PartialEq, Debug, Clone, Copy)]
+pub struct Span {
+ /// The start of the range.
+ pub start: usize,
+ /// The end of the range (exclusive).
+ pub end: usize,
+}
+
+impl From<Span> for (usize, usize) {
+ fn from(Span { start, end }: Span) -> (usize, usize) {
+ (start, end)
+ }
+}
+
+#[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>,
+ multiline: bool,
+ },
+}
+
+#[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),
+ MultilineStringKey(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,
+ chars: CrlfFold {
+ chars: input.char_indices(),
+ },
+ };
+ // Eat utf-8 BOM
+ t.eatc('\u{feff}');
+ t
+ }
+
+ pub fn next(&mut self) -> Result<Option<(Span, Token<'a>)>, Error> {
+ let (start, token) = match self.one() {
+ Some((start, '\n')) => (start, Newline),
+ Some((start, ' ')) => (start, self.whitespace_token(start)),
+ Some((start, '\t')) => (start, self.whitespace_token(start)),
+ Some((start, '#')) => (start, self.comment_token(start)),
+ Some((start, '=')) => (start, Equals),
+ Some((start, '.')) => (start, Period),
+ Some((start, ',')) => (start, Comma),
+ Some((start, ':')) => (start, Colon),
+ Some((start, '+')) => (start, Plus),
+ Some((start, '{')) => (start, LeftBrace),
+ Some((start, '}')) => (start, RightBrace),
+ Some((start, '[')) => (start, LeftBracket),
+ Some((start, ']')) => (start, RightBracket),
+ Some((start, '\'')) => {
+ return self
+ .literal_string(start)
+ .map(|t| Some((self.step_span(start), t)))
+ }
+ Some((start, '"')) => {
+ return self
+ .basic_string(start)
+ .map(|t| Some((self.step_span(start), t)))
+ }
+ Some((start, ch)) if is_keylike(ch) => (start, self.keylike(start)),
+
+ Some((start, ch)) => return Err(Error::Unexpected(start, ch)),
+ None => return Ok(None),
+ };
+
+ let span = self.step_span(start);
+ Ok(Some((span, token)))
+ }
+
+ pub fn peek(&mut self) -> Result<Option<(Span, Token<'a>)>, Error> {
+ self.clone().next()
+ }
+
+ pub fn eat(&mut self, expected: Token<'a>) -> Result<bool, Error> {
+ self.eat_spanned(expected).map(|s| s.is_some())
+ }
+
+ /// Eat a value, returning it's span if it was consumed.
+ pub fn eat_spanned(&mut self, expected: Token<'a>) -> Result<Option<Span>, Error> {
+ let span = match self.peek()? {
+ Some((span, ref found)) if expected == *found => span,
+ Some(_) => return Ok(None),
+ None => return Ok(None),
+ };
+
+ drop(self.next());
+ Ok(Some(span))
+ }
+
+ pub fn expect(&mut self, expected: Token<'a>) -> Result<(), Error> {
+ // ignore span
+ let _ = self.expect_spanned(expected)?;
+ Ok(())
+ }
+
+ /// Expect the given token returning its span.
+ pub fn expect_spanned(&mut self, expected: Token<'a>) -> Result<Span, Error> {
+ let current = self.current();
+ match self.next()? {
+ Some((span, found)) => {
+ if expected == found {
+ Ok(span)
+ } 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<(Span, Cow<'a, str>), Error> {
+ let current = self.current();
+ match self.next()? {
+ Some((span, Token::Keylike(k))) => Ok((span, k.into())),
+ Some((
+ span,
+ Token::String {
+ src,
+ val,
+ multiline,
+ },
+ )) => {
+ let offset = self.substr_offset(src);
+ if multiline {
+ return Err(Error::MultilineStringKey(offset));
+ }
+ match src.find('\n') {
+ None => Ok((span, 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.one() {
+ Some((_, '\n')) | None => break,
+ _ => {}
+ }
+ }
+ }
+
+ fn eatc(&mut self, ch: char) -> bool {
+ match self.chars.clone().next() {
+ Some((_, ch2)) if ch == ch2 => {
+ self.one();
+ true
+ }
+ _ => false,
+ }
+ }
+
+ pub fn current(&mut self) -> usize {
+ self.chars
+ .clone()
+ .next()
+ .map(|i| i.0)
+ .unwrap_or_else(|| 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' && !('\u{20}'..='\u{10ffff}').contains(&ch) {
+ break;
+ }
+ self.one();
+ }
+ Comment(&self.input[start..self.current()])
+ }
+
+ #[allow(clippy::type_complexity)]
+ fn read_string(
+ &mut self,
+ delim: char,
+ start: usize,
+ new_ch: &mut dyn 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(""),
+ multiline: false,
+ });
+ }
+ }
+ let mut val = MaybeString::NotEscaped(self.current());
+ let mut n = 0;
+ 'outer: loop {
+ n += 1;
+ match self.one() {
+ 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((mut i, ch)) if ch == delim => {
+ if multiline {
+ if !self.eatc(delim) {
+ val.push(delim);
+ continue 'outer;
+ }
+ if !self.eatc(delim) {
+ val.push(delim);
+ val.push(delim);
+ continue 'outer;
+ }
+ if self.eatc(delim) {
+ val.push(delim);
+ i += 1;
+ }
+ if self.eatc(delim) {
+ val.push(delim);
+ i += 1;
+ }
+ }
+ return Ok(String {
+ src: &self.input[start..self.current()],
+ val: val.into_cow(&self.input[..i]),
+ multiline,
+ });
+ }
+ Some((i, c)) => 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}'..='\u{10ffff}').contains(&ch) && ch != '\u{7f}') {
+ 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(me.hex(start, i, len)?);
+ }
+ Some((i, c @ ' ')) | Some((i, c @ '\t')) | Some((i, c @ '\n')) if multi => {
+ if c != '\n' {
+ while let Some((_, ch)) = me.chars.clone().next() {
+ match ch {
+ ' ' | '\t' => {
+ me.chars.next();
+ continue;
+ }
+ '\n' => {
+ me.chars.next();
+ break;
+ }
+ _ => return Err(Error::InvalidEscape(i, c)),
+ }
+ }
+ }
+ 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 ch == '\u{09}' || (('\u{20}'..='\u{10ffff}').contains(&ch) && ch != '\u{7f}') => {
+ val.push(ch);
+ Ok(())
+ }
+ _ => Err(Error::InvalidCharInString(i, ch)),
+ })
+ }
+
+ fn hex(&mut self, start: usize, i: usize, len: usize) -> Result<char, Error> {
+ let mut buf = StdString::with_capacity(len);
+ for _ in 0..len {
+ match self.one() {
+ Some((_, ch)) if ch as u32 <= 0x7F && ch.is_ascii_hexdigit() => buf.push(ch),
+ Some((i, ch)) => return Err(Error::InvalidHexEscape(i, ch)),
+ None => return Err(Error::UnterminatedString(start)),
+ }
+ }
+ let val = u32::from_str_radix(&buf, 16).unwrap();
+ 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.peek_one() {
+ if !is_keylike(ch) {
+ break;
+ }
+ self.one();
+ }
+ 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
+ }
+
+ /// Calculate the span of a single character.
+ fn step_span(&mut self, start: usize) -> Span {
+ let end = self
+ .peek_one()
+ .map(|t| t.0)
+ .unwrap_or_else(|| self.input.len());
+ Span { start, end }
+ }
+
+ /// Peek one char without consuming it.
+ fn peek_one(&mut self) -> Option<(usize, char)> {
+ self.chars.clone().next()
+ }
+
+ /// Take one char.
+ pub fn one(&mut self) -> Option<(usize, char)> {
+ self.chars.next()
+ }
+}
+
+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),
+ }
+ }
+
+ #[allow(clippy::wrong_self_convention)]
+ 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'..='Z').contains(&ch)
+ || ('a'..='z').contains(&ch)
+ || ('0'..='9').contains(&ch)
+ || 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 { multiline, .. } => {
+ if multiline {
+ "a multiline string"
+ } else {
+ "a string"
+ }
+ }
+ Token::Colon => "a colon",
+ Token::Plus => "a plus",
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::{Error, Token, Tokenizer};
+ use std::borrow::Cow;
+
+ 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, multiline: bool) {
+ let mut t = Tokenizer::new(input);
+ let (_, token) = t.next().unwrap().unwrap();
+ assert_eq!(
+ token,
+ Token::String {
+ src: input,
+ val: Cow::Borrowed(val),
+ multiline,
+ }
+ );
+ assert!(t.next().unwrap().is_none());
+ }
+
+ t("''", "", false);
+ t("''''''", "", true);
+ t("'''\n'''", "", true);
+ t("'a'", "a", false);
+ t("'\"a'", "\"a", false);
+ t("''''a'''", "'a", true);
+ t("'''\n'a\n'''", "'a\n", true);
+ t("'''a\n'a\r\n'''", "a\n'a\n", true);
+ }
+
+ #[test]
+ fn basic_strings() {
+ fn t(input: &str, val: &str, multiline: bool) {
+ let mut t = Tokenizer::new(input);
+ let (_, token) = t.next().unwrap().unwrap();
+ assert_eq!(
+ token,
+ Token::String {
+ src: input,
+ val: Cow::Borrowed(val),
+ multiline,
+ }
+ );
+ assert!(t.next().unwrap().is_none());
+ }
+
+ t(r#""""#, "", false);
+ t(r#""""""""#, "", true);
+ t(r#""a""#, "a", false);
+ t(r#""""a""""#, "a", true);
+ t(r#""\t""#, "\t", false);
+ t(r#""\u0000""#, "\0", false);
+ t(r#""\U00000000""#, "\0", false);
+ t(r#""\U000A0000""#, "\u{A0000}", false);
+ t(r#""\\t""#, "\\t", false);
+ t("\"\t\"", "\t", false);
+ t("\"\"\"\n\t\"\"\"", "\t", true);
+ t("\"\"\"\\\n\"\"\"", "", true);
+ t(
+ "\"\"\"\\\n \t \t \\\r\n \t \n \t \r\n\"\"\"",
+ "",
+ true,
+ );
+ t(r#""\r""#, "\r", false);
+ t(r#""\n""#, "\n", false);
+ t(r#""\b""#, "\u{8}", false);
+ t(r#""a\fa""#, "a\u{c}a", false);
+ t(r#""\"a""#, "\"a", false);
+ t("\"\"\"\na\"\"\"", "a", true);
+ t("\"\"\"\n\"\"\"", "", true);
+ t(r#""""a\"""b""""#, "a\"\"\"b", true);
+ 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, 0xffff_ffff));
+ }
+
+ #[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: &[((usize, usize), Token<'_>, &str)]) {
+ let mut tokens = Tokenizer::new(input);
+ let mut actual: Vec<((usize, usize), Token<'_>, &str)> = Vec::new();
+ while let Some((span, token)) = tokens.next().unwrap() {
+ actual.push((span.into(), token, &input[span.start..span.end]));
+ }
+ for (a, b) in actual.iter().zip(expected) {
+ assert_eq!(a, b);
+ }
+ assert_eq!(actual.len(), expected.len());
+ }
+
+ t(
+ " a ",
+ &[
+ ((0, 1), Token::Whitespace(" "), " "),
+ ((1, 2), Token::Keylike("a"), "a"),
+ ((2, 3), Token::Whitespace(" "), " "),
+ ],
+ );
+
+ t(
+ " a\t [[]] \t [] {} , . =\n# foo \r\n#foo \n ",
+ &[
+ ((0, 1), Token::Whitespace(" "), " "),
+ ((1, 2), Token::Keylike("a"), "a"),
+ ((2, 4), Token::Whitespace("\t "), "\t "),
+ ((4, 5), Token::LeftBracket, "["),
+ ((5, 6), Token::LeftBracket, "["),
+ ((6, 7), Token::RightBracket, "]"),
+ ((7, 8), Token::RightBracket, "]"),
+ ((8, 11), Token::Whitespace(" \t "), " \t "),
+ ((11, 12), Token::LeftBracket, "["),
+ ((12, 13), Token::RightBracket, "]"),
+ ((13, 14), Token::Whitespace(" "), " "),
+ ((14, 15), Token::LeftBrace, "{"),
+ ((15, 16), Token::RightBrace, "}"),
+ ((16, 17), Token::Whitespace(" "), " "),
+ ((17, 18), Token::Comma, ","),
+ ((18, 19), Token::Whitespace(" "), " "),
+ ((19, 20), Token::Period, "."),
+ ((20, 21), Token::Whitespace(" "), " "),
+ ((21, 22), Token::Equals, "="),
+ ((22, 23), Token::Newline, "\n"),
+ ((23, 29), Token::Comment("# foo "), "# foo "),
+ ((29, 31), Token::Newline, "\r\n"),
+ ((31, 36), Token::Comment("#foo "), "#foo "),
+ ((36, 37), Token::Newline, "\n"),
+ ((37, 38), 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..1a1756d102
--- /dev/null
+++ b/third_party/rust/toml/src/value.rs
@@ -0,0 +1,1081 @@
+//! Definition of a TOML value
+
+use std::collections::{BTreeMap, HashMap};
+use std::fmt;
+use std::hash::Hash;
+use std::mem::discriminant;
+use std::ops;
+use std::str::FromStr;
+use std::vec;
+
+use serde::de;
+use serde::de::IntoDeserializer;
+use serde::ser;
+
+use crate::datetime::{self, DatetimeFromString};
+pub use crate::datetime::{Date, Datetime, DatetimeParseError, Offset, Time};
+
+pub use crate::map::{Entry, Map};
+
+/// 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.
+/// By default it is backed by a BTreeMap, enable the `preserve_order` feature
+/// to use a LinkedHashMap instead.
+pub type Table = Map<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, crate::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, crate::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,
+ }
+ }
+
+ /// Tests whether this value 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 {
+ discriminant(self) == discriminant(other)
+ }
+
+ /// 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);
+impl_into_value!(Table: Table);
+
+/// 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 {
+ crate::ser::to_string(self)
+ .expect("Unable to represent value as string")
+ .fmt(f)
+ }
+}
+
+impl FromStr for Value {
+ type Err = crate::de::Error;
+ fn from_str(s: &str) -> Result<Value, Self::Err> {
+ crate::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_u64<E: de::Error>(self, value: u64) -> Result<Value, E> {
+ if value <= i64::max_value() as u64 {
+ Ok(Value::Integer(value as i64))
+ } else {
+ Err(de::Error::custom("u64 value was too large"))
+ }
+ }
+
+ fn visit_u32<E>(self, value: u32) -> Result<Value, E> {
+ Ok(Value::Integer(value.into()))
+ }
+
+ fn visit_i32<E>(self, value: i32) -> Result<Value, E> {
+ Ok(Value::Integer(value.into()))
+ }
+
+ 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) = 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(Map::new())),
+ Some(false) => {}
+ }
+ let mut map = Map::new();
+ map.insert(key, visitor.next_value()?);
+ while let Some(key) = visitor.next_key::<String>()? {
+ if let Entry::Vacant(vacant) = map.entry(&key) {
+ vacant.insert(visitor.next_value()?);
+ } else {
+ let msg = format!("duplicate key: `{}`", key);
+ return Err(de::Error::custom(msg));
+ }
+ }
+ Ok(Value::Table(map))
+ }
+ }
+
+ deserializer.deserialize_any(ValueVisitor)
+ }
+}
+
+impl<'de> de::Deserializer<'de> for Value {
+ type Error = crate::de::Error;
+
+ fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, crate::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, crate::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, crate::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, crate::de::Error>
+ where
+ V: de::Visitor<'de>,
+ {
+ visitor.visit_newtype_struct(self)
+ }
+
+ serde::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 = crate::de::Error;
+
+ fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, crate::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: <Map<String, Value> as IntoIterator>::IntoIter,
+ value: Option<(String, Value)>,
+}
+
+impl MapDeserializer {
+ fn new(map: Map<String, Value>) -> Self {
+ MapDeserializer {
+ iter: map.into_iter(),
+ value: None,
+ }
+ }
+}
+
+impl<'de> de::MapAccess<'de> for MapDeserializer {
+ type Error = crate::de::Error;
+
+ fn next_key_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, crate::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, crate::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, crate::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 = crate::ser::Error;
+
+ type SerializeSeq = SerializeVec;
+ type SerializeTuple = SerializeVec;
+ type SerializeTupleStruct = SerializeVec;
+ type SerializeTupleVariant = SerializeVec;
+ type SerializeMap = SerializeMap;
+ type SerializeStruct = SerializeMap;
+ type SerializeStructVariant = ser::Impossible<Value, crate::ser::Error>;
+
+ fn serialize_bool(self, value: bool) -> Result<Value, crate::ser::Error> {
+ Ok(Value::Boolean(value))
+ }
+
+ fn serialize_i8(self, value: i8) -> Result<Value, crate::ser::Error> {
+ self.serialize_i64(value.into())
+ }
+
+ fn serialize_i16(self, value: i16) -> Result<Value, crate::ser::Error> {
+ self.serialize_i64(value.into())
+ }
+
+ fn serialize_i32(self, value: i32) -> Result<Value, crate::ser::Error> {
+ self.serialize_i64(value.into())
+ }
+
+ fn serialize_i64(self, value: i64) -> Result<Value, crate::ser::Error> {
+ Ok(Value::Integer(value))
+ }
+
+ fn serialize_u8(self, value: u8) -> Result<Value, crate::ser::Error> {
+ self.serialize_i64(value.into())
+ }
+
+ fn serialize_u16(self, value: u16) -> Result<Value, crate::ser::Error> {
+ self.serialize_i64(value.into())
+ }
+
+ fn serialize_u32(self, value: u32) -> Result<Value, crate::ser::Error> {
+ self.serialize_i64(value.into())
+ }
+
+ fn serialize_u64(self, value: u64) -> Result<Value, crate::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, crate::ser::Error> {
+ self.serialize_f64(value.into())
+ }
+
+ fn serialize_f64(self, value: f64) -> Result<Value, crate::ser::Error> {
+ Ok(Value::Float(value))
+ }
+
+ fn serialize_char(self, value: char) -> Result<Value, crate::ser::Error> {
+ let mut s = String::new();
+ s.push(value);
+ self.serialize_str(&s)
+ }
+
+ fn serialize_str(self, value: &str) -> Result<Value, crate::ser::Error> {
+ Ok(Value::String(value.to_owned()))
+ }
+
+ fn serialize_bytes(self, value: &[u8]) -> Result<Value, crate::ser::Error> {
+ let vec = value.iter().map(|&b| Value::Integer(b.into())).collect();
+ Ok(Value::Array(vec))
+ }
+
+ fn serialize_unit(self) -> Result<Value, crate::ser::Error> {
+ Err(crate::ser::Error::UnsupportedType)
+ }
+
+ fn serialize_unit_struct(self, _name: &'static str) -> Result<Value, crate::ser::Error> {
+ Err(crate::ser::Error::UnsupportedType)
+ }
+
+ fn serialize_unit_variant(
+ self,
+ _name: &'static str,
+ _variant_index: u32,
+ _variant: &'static str,
+ ) -> Result<Value, crate::ser::Error> {
+ self.serialize_str(_variant)
+ }
+
+ fn serialize_newtype_struct<T: ?Sized>(
+ self,
+ _name: &'static str,
+ value: &T,
+ ) -> Result<Value, crate::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, crate::ser::Error>
+ where
+ T: ser::Serialize,
+ {
+ Err(crate::ser::Error::UnsupportedType)
+ }
+
+ fn serialize_none(self) -> Result<Value, crate::ser::Error> {
+ Err(crate::ser::Error::UnsupportedNone)
+ }
+
+ fn serialize_some<T: ?Sized>(self, value: &T) -> Result<Value, crate::ser::Error>
+ where
+ T: ser::Serialize,
+ {
+ value.serialize(self)
+ }
+
+ fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, crate::ser::Error> {
+ Ok(SerializeVec {
+ vec: Vec::with_capacity(len.unwrap_or(0)),
+ })
+ }
+
+ fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, crate::ser::Error> {
+ self.serialize_seq(Some(len))
+ }
+
+ fn serialize_tuple_struct(
+ self,
+ _name: &'static str,
+ len: usize,
+ ) -> Result<Self::SerializeTupleStruct, crate::ser::Error> {
+ self.serialize_seq(Some(len))
+ }
+
+ fn serialize_tuple_variant(
+ self,
+ _name: &'static str,
+ _variant_index: u32,
+ _variant: &'static str,
+ len: usize,
+ ) -> Result<Self::SerializeTupleVariant, crate::ser::Error> {
+ self.serialize_seq(Some(len))
+ }
+
+ fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, crate::ser::Error> {
+ Ok(SerializeMap {
+ map: Map::new(),
+ next_key: None,
+ })
+ }
+
+ fn serialize_struct(
+ self,
+ _name: &'static str,
+ len: usize,
+ ) -> Result<Self::SerializeStruct, crate::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, crate::ser::Error> {
+ Err(crate::ser::Error::UnsupportedType)
+ }
+}
+
+struct SerializeVec {
+ vec: Vec<Value>,
+}
+
+struct SerializeMap {
+ map: Map<String, Value>,
+ next_key: Option<String>,
+}
+
+impl ser::SerializeSeq for SerializeVec {
+ type Ok = Value;
+ type Error = crate::ser::Error;
+
+ fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), crate::ser::Error>
+ where
+ T: ser::Serialize,
+ {
+ self.vec.push(Value::try_from(value)?);
+ Ok(())
+ }
+
+ fn end(self) -> Result<Value, crate::ser::Error> {
+ Ok(Value::Array(self.vec))
+ }
+}
+
+impl ser::SerializeTuple for SerializeVec {
+ type Ok = Value;
+ type Error = crate::ser::Error;
+
+ fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<(), crate::ser::Error>
+ where
+ T: ser::Serialize,
+ {
+ ser::SerializeSeq::serialize_element(self, value)
+ }
+
+ fn end(self) -> Result<Value, crate::ser::Error> {
+ ser::SerializeSeq::end(self)
+ }
+}
+
+impl ser::SerializeTupleStruct for SerializeVec {
+ type Ok = Value;
+ type Error = crate::ser::Error;
+
+ fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), crate::ser::Error>
+ where
+ T: ser::Serialize,
+ {
+ ser::SerializeSeq::serialize_element(self, value)
+ }
+
+ fn end(self) -> Result<Value, crate::ser::Error> {
+ ser::SerializeSeq::end(self)
+ }
+}
+
+impl ser::SerializeTupleVariant for SerializeVec {
+ type Ok = Value;
+ type Error = crate::ser::Error;
+
+ fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<(), crate::ser::Error>
+ where
+ T: ser::Serialize,
+ {
+ ser::SerializeSeq::serialize_element(self, value)
+ }
+
+ fn end(self) -> Result<Value, crate::ser::Error> {
+ ser::SerializeSeq::end(self)
+ }
+}
+
+impl ser::SerializeMap for SerializeMap {
+ type Ok = Value;
+ type Error = crate::ser::Error;
+
+ fn serialize_key<T: ?Sized>(&mut self, key: &T) -> Result<(), crate::ser::Error>
+ where
+ T: ser::Serialize,
+ {
+ match Value::try_from(key)? {
+ Value::String(s) => self.next_key = Some(s),
+ _ => return Err(crate::ser::Error::KeyNotString),
+ };
+ Ok(())
+ }
+
+ fn serialize_value<T: ?Sized>(&mut self, value: &T) -> Result<(), crate::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(crate::ser::Error::UnsupportedNone) => {}
+ Err(e) => return Err(e),
+ }
+ Ok(())
+ }
+
+ fn end(self) -> Result<Value, crate::ser::Error> {
+ Ok(Value::Table(self.map))
+ }
+}
+
+impl ser::SerializeStruct for SerializeMap {
+ type Ok = Value;
+ type Error = crate::ser::Error;
+
+ fn serialize_field<T: ?Sized>(
+ &mut self,
+ key: &'static str,
+ value: &T,
+ ) -> Result<(), crate::ser::Error>
+ where
+ T: ser::Serialize,
+ {
+ ser::SerializeMap::serialize_key(self, key)?;
+ ser::SerializeMap::serialize_value(self, value)
+ }
+
+ fn end(self) -> Result<Value, crate::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 == datetime::FIELD {
+ 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 == datetime::FIELD {
+ Ok(true)
+ } else {
+ *self.key = s;
+ Ok(false)
+ }
+ }
+}
diff --git a/third_party/rust/toml/tests/enum_external_deserialize.rs b/third_party/rust/toml/tests/enum_external_deserialize.rs
new file mode 100644
index 0000000000..6d45b48488
--- /dev/null
+++ b/third_party/rust/toml/tests/enum_external_deserialize.rs
@@ -0,0 +1,258 @@
+#[macro_use]
+extern crate serde_derive;
+extern crate toml;
+
+#[derive(Debug, Deserialize, PartialEq)]
+struct OuterStruct {
+ inner: TheEnum,
+}
+
+#[derive(Debug, Deserialize, PartialEq)]
+enum TheEnum {
+ Plain,
+ Tuple(i64, bool),
+ NewType(String),
+ Struct { value: i64 },
+}
+
+#[derive(Debug, Deserialize, PartialEq)]
+struct Val {
+ val: TheEnum,
+}
+
+#[derive(Debug, Deserialize, PartialEq)]
+struct Multi {
+ enums: Vec<TheEnum>,
+}
+
+#[test]
+fn invalid_variant_returns_error_with_good_message_string() {
+ let error = toml::from_str::<TheEnum>("\"NonExistent\"").unwrap_err();
+
+ assert_eq!(
+ error.to_string(),
+ "unknown variant `NonExistent`, expected one of `Plain`, `Tuple`, `NewType`, `Struct`"
+ );
+}
+
+#[test]
+fn invalid_variant_returns_error_with_good_message_inline_table() {
+ let error = toml::from_str::<TheEnum>("{ NonExistent = {} }").unwrap_err();
+ assert_eq!(
+ error.to_string(),
+ "unknown variant `NonExistent`, expected one of `Plain`, `Tuple`, `NewType`, `Struct`"
+ );
+}
+
+#[test]
+fn extra_field_returns_expected_empty_table_error() {
+ let error = toml::from_str::<TheEnum>("{ Plain = { extra_field = 404 } }").unwrap_err();
+
+ assert_eq!(error.to_string(), "expected empty table");
+}
+
+#[test]
+fn extra_field_returns_expected_empty_table_error_struct_variant() {
+ let error = toml::from_str::<TheEnum>("{ Struct = { value = 123, extra_0 = 0, extra_1 = 1 } }")
+ .unwrap_err();
+
+ assert_eq!(
+ error.to_string(),
+ r#"unexpected keys in table: `["extra_0", "extra_1"]`, available keys: `["value"]`"#
+ );
+}
+
+mod enum_unit {
+ use super::*;
+
+ #[test]
+ fn from_str() {
+ assert_eq!(TheEnum::Plain, toml::from_str("\"Plain\"").unwrap());
+ }
+
+ #[test]
+ fn from_inline_table() {
+ assert_eq!(TheEnum::Plain, toml::from_str("{ Plain = {} }").unwrap());
+ assert_eq!(
+ Val {
+ val: TheEnum::Plain
+ },
+ toml::from_str("val = { Plain = {} }").unwrap()
+ );
+ }
+
+ #[test]
+ fn from_dotted_table() {
+ assert_eq!(TheEnum::Plain, toml::from_str("[Plain]\n").unwrap());
+ }
+}
+
+mod enum_tuple {
+ use super::*;
+
+ #[test]
+ fn from_inline_table() {
+ assert_eq!(
+ TheEnum::Tuple(-123, true),
+ toml::from_str("{ Tuple = { 0 = -123, 1 = true } }").unwrap()
+ );
+ assert_eq!(
+ Val {
+ val: TheEnum::Tuple(-123, true)
+ },
+ toml::from_str("val = { Tuple = { 0 = -123, 1 = true } }").unwrap()
+ );
+ }
+
+ #[test]
+ fn from_dotted_table() {
+ assert_eq!(
+ TheEnum::Tuple(-123, true),
+ toml::from_str(
+ r#"[Tuple]
+ 0 = -123
+ 1 = true
+ "#
+ )
+ .unwrap()
+ );
+ }
+}
+
+mod enum_newtype {
+ use super::*;
+
+ #[test]
+ fn from_inline_table() {
+ assert_eq!(
+ TheEnum::NewType("value".to_string()),
+ toml::from_str(r#"{ NewType = "value" }"#).unwrap()
+ );
+ assert_eq!(
+ Val {
+ val: TheEnum::NewType("value".to_string()),
+ },
+ toml::from_str(r#"val = { NewType = "value" }"#).unwrap()
+ );
+ }
+
+ #[test]
+ #[ignore = "Unimplemented: https://github.com/alexcrichton/toml-rs/pull/264#issuecomment-431707209"]
+ fn from_dotted_table() {
+ assert_eq!(
+ TheEnum::NewType("value".to_string()),
+ toml::from_str(r#"NewType = "value""#).unwrap()
+ );
+ assert_eq!(
+ Val {
+ val: TheEnum::NewType("value".to_string()),
+ },
+ toml::from_str(
+ r#"[val]
+ NewType = "value"
+ "#
+ )
+ .unwrap()
+ );
+ }
+}
+
+mod enum_struct {
+ use super::*;
+
+ #[test]
+ fn from_inline_table() {
+ assert_eq!(
+ TheEnum::Struct { value: -123 },
+ toml::from_str("{ Struct = { value = -123 } }").unwrap()
+ );
+ assert_eq!(
+ Val {
+ val: TheEnum::Struct { value: -123 }
+ },
+ toml::from_str("val = { Struct = { value = -123 } }").unwrap()
+ );
+ }
+
+ #[test]
+ fn from_dotted_table() {
+ assert_eq!(
+ TheEnum::Struct { value: -123 },
+ toml::from_str(
+ r#"[Struct]
+ value = -123
+ "#
+ )
+ .unwrap()
+ );
+ }
+
+ #[test]
+ fn from_nested_dotted_table() {
+ assert_eq!(
+ OuterStruct {
+ inner: TheEnum::Struct { value: -123 }
+ },
+ toml::from_str(
+ r#"[inner.Struct]
+ value = -123
+ "#
+ )
+ .unwrap()
+ );
+ }
+}
+
+mod enum_array {
+ use super::*;
+
+ #[test]
+ fn from_inline_tables() {
+ let toml_str = r#"
+ enums = [
+ { Plain = {} },
+ { Tuple = { 0 = -123, 1 = true } },
+ { NewType = "value" },
+ { Struct = { value = -123 } }
+ ]"#;
+ assert_eq!(
+ Multi {
+ enums: vec![
+ TheEnum::Plain,
+ TheEnum::Tuple(-123, true),
+ TheEnum::NewType("value".to_string()),
+ TheEnum::Struct { value: -123 },
+ ]
+ },
+ toml::from_str(toml_str).unwrap()
+ );
+ }
+
+ #[test]
+ #[ignore = "Unimplemented: https://github.com/alexcrichton/toml-rs/pull/264#issuecomment-431707209"]
+ fn from_dotted_table() {
+ let toml_str = r#"[[enums]]
+ Plain = {}
+
+ [[enums]]
+ Tuple = { 0 = -123, 1 = true }
+
+ [[enums]]
+ NewType = "value"
+
+ [[enums]]
+ Struct = { value = -123 }
+ "#;
+ assert_eq!(
+ Multi {
+ enums: vec![
+ TheEnum::Plain,
+ TheEnum::Tuple(-123, true),
+ TheEnum::NewType("value".to_string()),
+ TheEnum::Struct { value: -123 },
+ ]
+ },
+ toml::from_str(toml_str).unwrap()
+ );
+ }
+}