diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:22:09 +0000 |
commit | 43a97878ce14b72f0981164f87f2e35e14151312 (patch) | |
tree | 620249daf56c0258faa40cbdcf9cfba06de2a846 /third_party/rust/toml | |
parent | Initial commit. (diff) | |
download | firefox-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.json | 1 | ||||
-rw-r--r-- | third_party/rust/toml/Cargo.lock | 110 | ||||
-rw-r--r-- | third_party/rust/toml/Cargo.toml | 98 | ||||
-rw-r--r-- | third_party/rust/toml/LICENSE-APACHE | 201 | ||||
-rw-r--r-- | third_party/rust/toml/LICENSE-MIT | 25 | ||||
-rw-r--r-- | third_party/rust/toml/README.md | 29 | ||||
-rw-r--r-- | third_party/rust/toml/examples/decode.rs | 54 | ||||
-rw-r--r-- | third_party/rust/toml/examples/enum_external.rs | 45 | ||||
-rw-r--r-- | third_party/rust/toml/examples/toml2json.rs | 47 | ||||
-rw-r--r-- | third_party/rust/toml/src/datetime.rs | 544 | ||||
-rw-r--r-- | third_party/rust/toml/src/de.rs | 2256 | ||||
-rw-r--r-- | third_party/rust/toml/src/lib.rs | 176 | ||||
-rw-r--r-- | third_party/rust/toml/src/macros.rs | 459 | ||||
-rw-r--r-- | third_party/rust/toml/src/map.rs | 595 | ||||
-rw-r--r-- | third_party/rust/toml/src/ser.rs | 1853 | ||||
-rw-r--r-- | third_party/rust/toml/src/spanned.rs | 166 | ||||
-rw-r--r-- | third_party/rust/toml/src/tokens.rs | 742 | ||||
-rw-r--r-- | third_party/rust/toml/src/value.rs | 1081 | ||||
-rw-r--r-- | third_party/rust/toml/tests/enum_external_deserialize.rs | 258 |
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() + ); + } +} |