Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

View File

@@ -0,0 +1 @@
{"files":{"Cargo.lock":"7dd10bed19eda17c1fce057eb0c0e18105ef8183b0d47c892304dadb5dc3528f","Cargo.toml":"58fbce212c22e56a096a000ec10af471f77aa616ed9fa2a75894cee41e848287","LICENSE-APACHE":"c6596eb7be8581c18be736c846fb9173b69eccf6ef94c5135893ec56bd92ba08","LICENSE-MIT":"6efb0476a1cc085077ed49357026d8c173bf33017278ef440f222fb9cbcb66e6","README.md":"751d746789806a3df90f5e95da6d764ba459b65fd2b1ec31a1e0d0ebf7d7ab87","examples/visit.rs":"3be11e0d9af8caf35c6c9957a62cab77dea755b0727fd74814d7c20fe2b961aa","src/array.rs":"e4f791b5a0ced4982101160d608f3d2aaaf5a7837095087c9cc6598a8621351f","src/array_of_tables.rs":"538ae0ef21cce5786495abb20c5f450e550a243fc57b115a3aa4ac71ce989cdb","src/de/array.rs":"b55858e270874b8832e5ee4c6baa203cf68527b29c2b0e7b8587099ec489e864","src/de/datetime.rs":"a29e76372fc382686c31ee93fd54c749ea3d775c8d4fcc2b2643f9464ea23730","src/de/key.rs":"9144d8b58af313511dda439e19aa9e3483c00ef88e0b49ce7692d625f41a3652","src/de/mod.rs":"e1b3024fb17f18a2356c02966c5c218ede9c67dab16430888cfe59ab020db9a8","src/de/spanned.rs":"5c959639bf4b3d19c6b18e625a57b28b311164bf7b191bbc4d30aa97b7239679","src/de/table.rs":"e5391ba4db30f1c11aa656ed8bd9bf09892aee11393b2f80b4ad5f408f67227a","src/de/table_enum.rs":"70540182d1e453caabae5a1fdea83aadc53a42ca6e24dbc3c8aaf883fe9c21ee","src/de/value.rs":"c2ab3251718bb0a00b9f287319c93bf814b13f320c3620ea22f4421ad233a3b1","src/document.rs":"91b91e8482ad4f92deadc07cbbcba05eecd6a8f16603bc7c8d2c4a487fa977db","src/encode.rs":"46d6b8cc620c64a6bf47685bd98610a1d79252342222f23d28be0f0bc4d078ec","src/error.rs":"8567dd5259aa43a2203717d7dce0002a437f285cd26ccab38bd0d3af5525c2ca","src/index.rs":"925f60cf02079435217d0d62f8eb6b2252d9025a421b9a3d5fc5bcb9ad0eaf54","src/inline_table.rs":"6521f9a5d13017a837c21daa2ee2e58343ca42c35b49f55c959c868343b327d0","src/internal_string.rs":"f5bdce8b6796de810617b2b26c085cc471a0102137d036da0a4ce4da2767e640","src/item.rs":"e1ccc0c7683bcef7934fe02205792a1593fbada538b6f20427181872c009ceb8","src/key.rs":"fc53af40a170a1d79b0ea3e6c3b76baf697ef7729180494b380d0631c0a73599","src/lib.rs":"6a8e1bb0f40fbfef0530e69f0247435edc0270dbbbd05e78413c8be5984d1849","src/parser/array.rs":"8c62991b8824b4d0a87d939a2e84f0d788d51aaa68c7cb88772753164dde7320","src/parser/datetime.rs":"bad98b1191ebb015d55530de8e6ab39000bf2c5eca6b6e2e9bcf5539bb70a8ee","src/parser/document.rs":"c848c1fa5256b5fabf962589310f81784d3dd26393b34b3ed8858e00b73795ff","src/parser/error.rs":"b58edd6d8148eba39bfec61792e186d41c9f4caafb15914b66a79ffb4be95d1c","src/parser/inline_table.rs":"3647a56094ada1758e5af9c286d600f8281863044141967ac5af89d830976751","src/parser/key.rs":"699aa0f75b2e64db16477640473366eb0c283d256644014d475b4dae449fc18f","src/parser/mod.rs":"9d3f8ba1ef0843b6ba7b9c58a8b7b38a2902db760c5b6c69f950c913bb419620","src/parser/numbers.rs":"3e1b30bcaaf6b85141b67b58e49951d927b990a3dbaaa43594b84d40c2c21a0b","src/parser/state.rs":"4ec231779194a15a8c307405ac7c1dd983b684afd4073339a572cbe68bb4eda0","src/parser/strings.rs":"c2b7caf984a0471bc753a8768796db9db379859c403517fad1c54a96f13fd5b6","src/parser/table.rs":"38fdd191c1f9fb445775f2aa0f5b31c9bc232922fc62d29c2fc81342751dae83","src/parser/trivia.rs":"cb28780bf48d3238d46d3fbba9a7cedafb4ac57b5addb49f27aced676cb8fff6","src/parser/value.rs":"9a1a51c1a9d473faac97bcad2d38b58342dfe193e2c46cc49ec789f2a90722e6","src/raw_string.rs":"d3869ee889c63af75b2143ccd0165417c784d582694bf76d617f16649f752d9f","src/repr.rs":"19665850aa069880ce62f4466ee44bc15940ed47ab0d2f69b6c02cbaa0773d95","src/ser/array.rs":"dbf450661cb2263450b3d3047dff31b2563818e000227719bf4f32d36413a148","src/ser/key.rs":"7479534c53c35db9d339ae30ec2fb56bf313345746491711f14748ef90422401","src/ser/map.rs":"752a93b618677841345e00a912b2b04ef1478f70b80d865082ec4500a2fee250","src/ser/mod.rs":"1843315e546157ddbcf3c51b3b57e75960eec9d84e226365874a5f6aaf599302","src/ser/pretty.rs":"c5edeb2c42cea1ed9b4b74be66fe2ab6d21b5e71d98b10c20675aaaa18f724ac","src/ser/value.rs":"3a789ac8886ea22cc4ce4bdcd6d91e03ba1045e1833857de3de70f81dd04411d","src/table.rs":"fde5982aa69897e481b68992d8aeaaf817cdbee8983d34cbdefc259770d6e798","src/value.rs":"4e31a06b1a8f532b5ffefc999f66c4872cd864cb0ae896c2fadd73a590157f7d","src/visit.rs":"732d9970455c62dae0523949dd9bdd16e76c05ed9de57720ea4c62d70e7efb2b","src/visit_mut.rs":"77934daa4ec1df159fc72f13431b5a635e4bbce15267a82da4647aa860a1e37f"},"package":"41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"}

1015
vendor/toml_edit-0.22.27/Cargo.lock generated vendored Normal file

File diff suppressed because it is too large Load Diff

245
vendor/toml_edit-0.22.27/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,245 @@
# 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 = "2021"
rust-version = "1.66"
name = "toml_edit"
version = "0.22.27"
build = false
include = [
"build.rs",
"src/**/*",
"Cargo.toml",
"Cargo.lock",
"LICENSE*",
"README.md",
"examples/**/*",
]
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Yet another format-preserving TOML parser."
readme = "README.md"
keywords = [
"encoding",
"toml",
]
categories = [
"encoding",
"parser-implementations",
"parsing",
"config",
]
license = "MIT OR Apache-2.0"
repository = "https://github.com/toml-rs/toml"
[package.metadata.docs.rs]
all-features = true
rustdoc-args = [
"--cfg",
"docsrs",
"--generate-link-to-definition",
]
[package.metadata.release]
tag-name = "v{{version}}"
[[package.metadata.release.pre-release-replacements]]
file = "CHANGELOG.md"
min = 1
replace = "{{version}}"
search = "Unreleased"
[[package.metadata.release.pre-release-replacements]]
exactly = 1
file = "CHANGELOG.md"
replace = "...{{tag_name}}"
search = '\.\.\.HEAD'
[[package.metadata.release.pre-release-replacements]]
file = "CHANGELOG.md"
min = 1
replace = "{{date}}"
search = "ReleaseDate"
[[package.metadata.release.pre-release-replacements]]
exactly = 1
file = "CHANGELOG.md"
replace = """
<!-- next-header -->
## [Unreleased] - ReleaseDate
"""
search = "<!-- next-header -->"
[[package.metadata.release.pre-release-replacements]]
exactly = 1
file = "CHANGELOG.md"
replace = """
<!-- next-url -->
[Unreleased]: https://github.com/toml-rs/toml/compare/{{tag_name}}...HEAD"""
search = "<!-- next-url -->"
[features]
default = [
"parse",
"display",
]
display = ["dep:toml_write"]
parse = ["dep:winnow"]
perf = ["dep:kstring"]
serde = [
"dep:serde",
"toml_datetime/serde",
"dep:serde_spanned",
]
unbounded = []
unstable-debug = ["winnow?/debug"]
[lib]
name = "toml_edit"
path = "src/lib.rs"
[[example]]
name = "visit"
path = "examples/visit.rs"
test = true
required-features = [
"parse",
"display",
]
[dependencies.indexmap]
version = "2.3.0"
features = ["std"]
[dependencies.kstring]
version = "2.0.0"
features = ["max_inline"]
optional = true
[dependencies.serde]
version = "1.0.145"
optional = true
[dependencies.serde_spanned]
version = "0.6.9"
features = ["serde"]
optional = true
[dependencies.toml_datetime]
version = "0.6.11"
[dependencies.toml_write]
version = "0.1.2"
optional = true
[dependencies.winnow]
version = "0.7.10"
optional = true
[dev-dependencies.proptest]
version = "1.5.0"
[dev-dependencies.serde]
version = "1.0.199"
features = ["derive"]
[dev-dependencies.serde_json]
version = "1.0.116"
[dev-dependencies.snapbox]
version = "0.6.0"
[dev-dependencies.toml-test-data]
version = "2.3.0"
[dev-dependencies.toml-test-harness]
version = "1.3.2"
features = ["snapshot"]
[dev-dependencies.walkdir]
version = "2.5.0"
[lints.clippy]
bool_assert_comparison = "allow"
branches_sharing_code = "allow"
checked_conversions = "warn"
collapsible_else_if = "allow"
create_dir = "warn"
dbg_macro = "warn"
debug_assert_with_mut_call = "warn"
doc_markdown = "warn"
empty_enum = "warn"
enum_glob_use = "warn"
expl_impl_clone_on_copy = "warn"
explicit_deref_methods = "warn"
explicit_into_iter_loop = "warn"
fallible_impl_from = "warn"
filter_map_next = "warn"
flat_map_option = "warn"
float_cmp_const = "warn"
fn_params_excessive_bools = "warn"
from_iter_instead_of_collect = "warn"
if_same_then_else = "allow"
implicit_clone = "warn"
imprecise_flops = "warn"
inconsistent_struct_constructor = "warn"
inefficient_to_string = "warn"
infinite_loop = "warn"
invalid_upcast_comparisons = "warn"
large_digit_groups = "warn"
large_stack_arrays = "warn"
large_types_passed_by_value = "warn"
let_and_return = "allow"
linkedlist = "warn"
lossy_float_literal = "warn"
macro_use_imports = "warn"
mem_forget = "warn"
mutex_integer = "warn"
needless_continue = "allow"
needless_for_each = "warn"
negative_feature_names = "warn"
path_buf_push_overwrite = "warn"
ptr_as_ptr = "warn"
rc_mutex = "warn"
redundant_feature_names = "warn"
ref_option_ref = "warn"
rest_pat_in_fully_bound_structs = "warn"
result_large_err = "allow"
same_functions_in_if_condition = "warn"
self_named_module_files = "warn"
semicolon_if_nothing_returned = "warn"
str_to_string = "warn"
string_add = "warn"
string_add_assign = "warn"
string_lit_as_bytes = "warn"
string_to_string = "warn"
todo = "warn"
trait_duplication_in_bounds = "warn"
uninlined_format_args = "warn"
verbose_file_reads = "warn"
wildcard_imports = "warn"
zero_sized_map_values = "warn"
[lints.rust]
unnameable_types = "allow"
unreachable_pub = "warn"
unsafe_op_in_unsafe_fn = "warn"
unused_lifetimes = "warn"
unused_macro_rules = "warn"
unused_qualifications = "warn"
[lints.rust.rust_2018_idioms]
level = "warn"
priority = -1

202
vendor/toml_edit-0.22.27/LICENSE-APACHE vendored Normal file
View File

@@ -0,0 +1,202 @@
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.

19
vendor/toml_edit-0.22.27/LICENSE-MIT vendored Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) Individual contributors
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.

61
vendor/toml_edit-0.22.27/README.md vendored Normal file
View File

@@ -0,0 +1,61 @@
# toml_edit
[![Build Status](https://github.com/toml-rs/toml/workflows/Continuous%20integration/badge.svg)](https://github.com/toml-rs/toml/actions)
[![codecov](https://codecov.io/gh/toml-rs/toml/branch/master/graph/badge.svg)](https://codecov.io/gh/toml-rs/toml)
[![crates.io](https://img.shields.io/crates/v/toml_edit.svg)](https://crates.io/crates/toml_edit)
[![docs](https://docs.rs/toml_edit/badge.svg)](https://docs.rs/toml_edit)
[![Join the chat at https://gitter.im/toml_edit/Lobby](https://badges.gitter.im/a.svg)](https://gitter.im/toml_edit/Lobby)
This crate allows you to parse and modify toml
documents, while preserving comments, spaces *and
relative order* of items.
`toml_edit` is primarily tailored for [cargo-edit](https://github.com/killercup/cargo-edit/) needs.
## Example
```rust
use toml_edit::{DocumentMut, value};
fn main() {
let toml = r#"
"hello" = 'toml!' # comment
['a'.b]
"#;
let mut doc = toml.parse::<DocumentMut>().expect("invalid doc");
assert_eq!(doc.to_string(), toml);
// let's add a new key/value pair inside a.b: c = {d = "hello"}
doc["a"]["b"]["c"]["d"] = value("hello");
// autoformat inline table a.b.c: { d = "hello" }
doc["a"]["b"]["c"].as_inline_table_mut().map(|t| t.fmt());
let expected = r#"
"hello" = 'toml!' # comment
['a'.b]
c = { d = "hello" }
"#;
assert_eq!(doc.to_string(), expected);
}
```
## Limitations
Things it does not preserve:
* Order of dotted keys, see [issue](https://github.com/toml-rs/toml/issues/163).
## License
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 the work by you, as defined in the Apache-2.0
license, shall be dual-licensed as above, without any additional terms or
conditions.

View File

@@ -0,0 +1,284 @@
//! Example for how to use `VisitMut` to iterate over a table.
use std::collections::BTreeSet;
use toml_edit::visit::{visit_table_like_kv, Visit};
use toml_edit::visit_mut::{visit_table_like_kv_mut, visit_table_mut, VisitMut};
use toml_edit::{Array, DocumentMut, InlineTable, Item, KeyMut, Table, Value};
/// This models the visit state for dependency keys in a `Cargo.toml`.
///
/// Dependencies can be specified as:
///
/// ```toml
/// [dependencies]
/// dep1 = "0.2"
///
/// [build-dependencies]
/// dep2 = "0.3"
///
/// [dev-dependencies]
/// dep3 = "0.4"
///
/// [target.'cfg(windows)'.dependencies]
/// dep4 = "0.5"
///
/// # and target build- and dev-dependencies
/// ```
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
enum VisitState {
/// Represents the root of the table.
Root,
/// Represents "dependencies", "build-dependencies" or "dev-dependencies", or the target
/// forms of these.
Dependencies,
/// A table within dependencies.
SubDependencies,
/// Represents "target".
Target,
/// "target.[TARGET]".
TargetWithSpec,
/// Represents some other state.
Other,
}
impl VisitState {
/// Figures out the next visit state, given the current state and the given key.
fn descend(self, key: &str) -> Self {
match (self, key) {
(
VisitState::Root | VisitState::TargetWithSpec,
"dependencies" | "build-dependencies" | "dev-dependencies",
) => VisitState::Dependencies,
(VisitState::Root, "target") => VisitState::Target,
(VisitState::Root | VisitState::TargetWithSpec, _) => VisitState::Other,
(VisitState::Target, _) => VisitState::TargetWithSpec,
(VisitState::Dependencies, _) => VisitState::SubDependencies,
(VisitState::SubDependencies, _) => VisitState::SubDependencies,
(VisitState::Other, _) => VisitState::Other,
}
}
}
/// Collect the names of every dependency key.
#[derive(Debug)]
struct DependencyNameVisitor<'doc> {
state: VisitState,
names: BTreeSet<&'doc str>,
}
impl<'doc> Visit<'doc> for DependencyNameVisitor<'doc> {
fn visit_table_like_kv(&mut self, key: &'doc str, node: &'doc Item) {
if self.state == VisitState::Dependencies {
self.names.insert(key);
} else {
// Since we're only interested in collecting the top-level keys right under
// [dependencies], don't recurse unconditionally.
let old_state = self.state;
// Figure out the next state given the key.
self.state = self.state.descend(key);
// Recurse further into the document tree.
visit_table_like_kv(self, key, node);
// Restore the old state after it's done.
self.state = old_state;
}
}
}
/// Normalize all dependency tables into the format:
///
/// ```toml
/// [dependencies]
/// dep = { version = "1.0", features = ["foo", "bar"], ... }
/// ```
///
/// leaving other tables untouched.
#[derive(Debug)]
struct NormalizeDependencyTablesVisitor {
state: VisitState,
}
impl VisitMut for NormalizeDependencyTablesVisitor {
fn visit_table_mut(&mut self, node: &mut Table) {
visit_table_mut(self, node);
// The conversion from regular tables into inline ones might leave some explicit parent
// tables hanging, so convert them to implicit.
if matches!(self.state, VisitState::Target | VisitState::TargetWithSpec) {
node.set_implicit(true);
}
}
fn visit_table_like_kv_mut(&mut self, mut key: KeyMut<'_>, node: &mut Item) {
let old_state = self.state;
// Figure out the next state given the key.
self.state = self.state.descend(key.get());
match self.state {
VisitState::Target | VisitState::TargetWithSpec | VisitState::Dependencies => {
// Top-level dependency row, or above: turn inline tables into regular ones.
if let Item::Value(Value::InlineTable(inline_table)) = node {
let inline_table = std::mem::replace(inline_table, InlineTable::new());
let table = inline_table.into_table();
key.fmt();
*node = Item::Table(table);
}
}
VisitState::SubDependencies => {
// Individual dependency: turn regular tables into inline ones.
if let Item::Table(table) = node {
// Turn the table into an inline table.
let table = std::mem::replace(table, Table::new());
let inline_table = table.into_inline_table();
key.fmt();
*node = Item::Value(Value::InlineTable(inline_table));
}
}
_ => {}
}
// Recurse further into the document tree.
visit_table_like_kv_mut(self, key, node);
// Restore the old state after it's done.
self.state = old_state;
}
fn visit_array_mut(&mut self, node: &mut Array) {
// Format any arrays within dependencies to be on the same line.
if matches!(
self.state,
VisitState::Dependencies | VisitState::SubDependencies
) {
node.fmt();
}
}
}
/// This is the input provided to `visit_mut_example`.
static INPUT: &str = r#"
[package]
name = "my-package"
[package.metadata.foo]
bar = 42
[dependencies]
atty = "0.2"
cargo-platform = { path = "crates/cargo-platform", version = "0.1.2" }
[dependencies.pretty_env_logger]
version = "0.4"
optional = true
[target.'cfg(windows)'.dependencies]
fwdansi = "1.1.0"
[target.'cfg(windows)'.dependencies.winapi]
version = "0.3"
features = [
"handleapi",
"jobapi",
]
[target.'cfg(unix)']
dev-dependencies = { miniz_oxide = "0.5" }
[dev-dependencies.cargo-test-macro]
path = "crates/cargo-test-macro"
[build-dependencies.flate2]
version = "0.4"
"#;
/// This is the output produced by `visit_mut_example`.
#[cfg(test)]
static VISIT_MUT_OUTPUT: &str = r#"
[package]
name = "my-package"
[package.metadata.foo]
bar = 42
[dependencies]
atty = "0.2"
cargo-platform = { path = "crates/cargo-platform", version = "0.1.2" }
pretty_env_logger = { version = "0.4", optional = true }
[target.'cfg(windows)'.dependencies]
fwdansi = "1.1.0"
winapi = { version = "0.3", features = ["handleapi", "jobapi"] }
[target.'cfg(unix)'.dev-dependencies]
miniz_oxide = "0.5"
[dev-dependencies]
cargo-test-macro = { path = "crates/cargo-test-macro" }
[build-dependencies]
flate2 = { version = "0.4" }
"#;
fn visit_example(document: &DocumentMut) -> BTreeSet<&str> {
let mut visitor = DependencyNameVisitor {
state: VisitState::Root,
names: BTreeSet::new(),
};
visitor.visit_document(document);
visitor.names
}
fn visit_mut_example(document: &mut DocumentMut) {
let mut visitor = NormalizeDependencyTablesVisitor {
state: VisitState::Root,
};
visitor.visit_document_mut(document);
}
fn main() {
let mut document: DocumentMut = INPUT.parse().expect("input is valid TOML");
println!("** visit example");
println!("{:?}", visit_example(&document));
println!("** visit_mut example");
visit_mut_example(&mut document);
println!("{document}");
}
#[cfg(test)]
#[test]
fn visit_correct() {
let document: DocumentMut = INPUT.parse().expect("input is valid TOML");
let names = visit_example(&document);
let expected = vec![
"atty",
"cargo-platform",
"pretty_env_logger",
"fwdansi",
"winapi",
"miniz_oxide",
"cargo-test-macro",
"flate2",
]
.into_iter()
.collect();
assert_eq!(names, expected);
}
#[cfg(test)]
#[test]
fn visit_mut_correct() {
let mut document: DocumentMut = INPUT.parse().expect("input is valid TOML");
visit_mut_example(&mut document);
assert_eq!(format!("{document}"), VISIT_MUT_OUTPUT);
}

461
vendor/toml_edit-0.22.27/src/array.rs vendored Normal file
View File

@@ -0,0 +1,461 @@
use std::iter::FromIterator;
use std::mem;
use crate::repr::Decor;
use crate::value::{DEFAULT_LEADING_VALUE_DECOR, DEFAULT_VALUE_DECOR};
use crate::{Item, RawString, Value};
/// A TOML [`Value`] that contains a sequence of [`Value`]s
#[derive(Debug, Default, Clone)]
pub struct Array {
// `trailing` represents whitespaces, newlines
// and comments in an empty array or after the trailing comma
trailing: RawString,
trailing_comma: bool,
// prefix before `[` and suffix after `]`
decor: Decor,
pub(crate) span: Option<std::ops::Range<usize>>,
// always Vec<Item::Value>
pub(crate) values: Vec<Item>,
}
/// An owned iterator type over [`Array`]'s [`Value`]s
pub type ArrayIntoIter = Box<dyn Iterator<Item = Value>>;
/// An iterator type over [`Array`]'s [`Value`]s
pub type ArrayIter<'a> = Box<dyn Iterator<Item = &'a Value> + 'a>;
/// An iterator type over [`Array`]'s [`Value`]s
pub type ArrayIterMut<'a> = Box<dyn Iterator<Item = &'a mut Value> + 'a>;
/// Constructors
///
/// See also `FromIterator`
impl Array {
/// Create an empty `Array`
///
/// # Examples
///
/// ```rust
/// let mut arr = toml_edit::Array::new();
/// ```
pub fn new() -> Self {
Default::default()
}
pub(crate) fn with_vec(values: Vec<Item>) -> Self {
Self {
values,
..Default::default()
}
}
}
/// Formatting
impl Array {
/// Auto formats the array.
pub fn fmt(&mut self) {
decorate_array(self);
}
/// Set whether the array will use a trailing comma
pub fn set_trailing_comma(&mut self, yes: bool) {
self.trailing_comma = yes;
}
/// Whether the array will use a trailing comma
pub fn trailing_comma(&self) -> bool {
self.trailing_comma
}
/// Set whitespace after last element
pub fn set_trailing(&mut self, trailing: impl Into<RawString>) {
self.trailing = trailing.into();
}
/// Whitespace after last element
pub fn trailing(&self) -> &RawString {
&self.trailing
}
/// Returns the surrounding whitespace
pub fn decor_mut(&mut self) -> &mut Decor {
&mut self.decor
}
/// Returns the surrounding whitespace
pub fn decor(&self) -> &Decor {
&self.decor
}
/// The location within the original document
///
/// This generally requires an [`ImDocument`][crate::ImDocument].
pub fn span(&self) -> Option<std::ops::Range<usize>> {
self.span.clone()
}
pub(crate) fn despan(&mut self, input: &str) {
self.span = None;
self.decor.despan(input);
self.trailing.despan(input);
for value in &mut self.values {
value.despan(input);
}
}
}
impl Array {
/// Returns an iterator over all values.
pub fn iter(&self) -> ArrayIter<'_> {
Box::new(self.values.iter().filter_map(Item::as_value))
}
/// Returns an iterator over all values.
pub fn iter_mut(&mut self) -> ArrayIterMut<'_> {
Box::new(self.values.iter_mut().filter_map(Item::as_value_mut))
}
/// Returns the length of the underlying Vec.
///
/// In some rare cases, placeholder elements will exist. For a more accurate count, call
/// `a.iter().count()`
///
/// # Examples
///
/// ```rust
/// let mut arr = toml_edit::Array::new();
/// arr.push(1);
/// arr.push("foo");
/// assert_eq!(arr.len(), 2);
/// ```
pub fn len(&self) -> usize {
self.values.len()
}
/// Return true if `self.len() == 0`.
///
/// # Examples
///
/// ```rust
/// let mut arr = toml_edit::Array::new();
/// assert!(arr.is_empty());
///
/// arr.push(1);
/// arr.push("foo");
/// assert!(! arr.is_empty());
/// ```
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Clears the array, removing all values. Keeps the allocated memory for reuse.
pub fn clear(&mut self) {
self.values.clear();
}
/// Returns a reference to the value at the given index, or `None` if the index is out of
/// bounds.
pub fn get(&self, index: usize) -> Option<&Value> {
self.values.get(index).and_then(Item::as_value)
}
/// Returns a reference to the value at the given index, or `None` if the index is out of
/// bounds.
pub fn get_mut(&mut self, index: usize) -> Option<&mut Value> {
self.values.get_mut(index).and_then(Item::as_value_mut)
}
/// Appends a new value to the end of the array, applying default formatting to it.
///
/// # Examples
///
/// ```rust
/// let mut arr = toml_edit::Array::new();
/// arr.push(1);
/// arr.push("foo");
/// ```
pub fn push<V: Into<Value>>(&mut self, v: V) {
self.value_op(v.into(), true, |items, value| {
items.push(Item::Value(value));
});
}
/// Appends a new, already formatted value to the end of the array.
///
/// # Examples
///
/// ```rust
/// # #[cfg(feature = "parse")] {
/// let formatted_value = "'literal'".parse::<toml_edit::Value>().unwrap();
/// let mut arr = toml_edit::Array::new();
/// arr.push_formatted(formatted_value);
/// # }
/// ```
pub fn push_formatted(&mut self, v: Value) {
self.values.push(Item::Value(v));
}
/// Inserts an element at the given position within the array, applying default formatting to
/// it and shifting all values after it to the right.
///
/// # Panics
///
/// Panics if `index > len`.
///
/// # Examples
///
/// ```rust
/// let mut arr = toml_edit::Array::new();
/// arr.push(1);
/// arr.push("foo");
///
/// arr.insert(0, "start");
/// ```
pub fn insert<V: Into<Value>>(&mut self, index: usize, v: V) {
self.value_op(v.into(), true, |items, value| {
items.insert(index, Item::Value(value));
});
}
/// Inserts an already formatted value at the given position within the array, shifting all
/// values after it to the right.
///
/// # Panics
///
/// Panics if `index > len`.
///
/// # Examples
///
/// ```rust
/// # #[cfg(feature = "parse")] {
/// let mut arr = toml_edit::Array::new();
/// arr.push(1);
/// arr.push("foo");
///
/// let formatted_value = "'start'".parse::<toml_edit::Value>().unwrap();
/// arr.insert_formatted(0, formatted_value);
/// # }
/// ```
pub fn insert_formatted(&mut self, index: usize, v: Value) {
self.values.insert(index, Item::Value(v));
}
/// Replaces the element at the given position within the array, preserving existing formatting.
///
/// # Panics
///
/// Panics if `index >= len`.
///
/// # Examples
///
/// ```rust
/// let mut arr = toml_edit::Array::new();
/// arr.push(1);
/// arr.push("foo");
///
/// arr.replace(0, "start");
/// ```
pub fn replace<V: Into<Value>>(&mut self, index: usize, v: V) -> Value {
// Read the existing value's decor and preserve it.
let existing_decor = self
.get(index)
.unwrap_or_else(|| panic!("index {} out of bounds (len = {})", index, self.len()))
.decor();
let mut value = v.into();
*value.decor_mut() = existing_decor.clone();
self.replace_formatted(index, value)
}
/// Replaces the element at the given position within the array with an already formatted value.
///
/// # Panics
///
/// Panics if `index >= len`.
///
/// # Examples
///
/// ```rust
/// # #[cfg(feature = "parse")] {
/// let mut arr = toml_edit::Array::new();
/// arr.push(1);
/// arr.push("foo");
///
/// let formatted_value = "'start'".parse::<toml_edit::Value>().unwrap();
/// arr.replace_formatted(0, formatted_value);
/// # }
/// ```
pub fn replace_formatted(&mut self, index: usize, v: Value) -> Value {
match mem::replace(&mut self.values[index], Item::Value(v)) {
Item::Value(old_value) => old_value,
x => panic!("non-value item {x:?} in an array"),
}
}
/// Removes the value at the given index.
///
/// # Examples
///
/// ```rust
/// let mut arr = toml_edit::Array::new();
/// arr.push(1);
/// arr.push("foo");
///
/// arr.remove(0);
/// assert_eq!(arr.len(), 1);
/// ```
pub fn remove(&mut self, index: usize) -> Value {
let removed = self.values.remove(index);
match removed {
Item::Value(v) => v,
x => panic!("non-value item {x:?} in an array"),
}
}
/// Retains only the values specified by the `keep` predicate.
///
/// In other words, remove all values for which `keep(&value)` returns `false`.
///
/// This method operates in place, visiting each element exactly once in the
/// original order, and preserves the order of the retained elements.
pub fn retain<F>(&mut self, mut keep: F)
where
F: FnMut(&Value) -> bool,
{
self.values
.retain(|item| item.as_value().map(&mut keep).unwrap_or(false));
}
/// Sorts the slice with a comparator function.
///
/// This sort is stable (i.e., does not reorder equal elements) and *O*(*n* \* log(*n*)) worst-case.
///
/// The comparator function must define a total ordering for the elements in the slice. If
/// the ordering is not total, the order of the elements is unspecified. An order is a
/// total order if it is (for all `a`, `b` and `c`):
///
/// * total and antisymmetric: exactly one of `a < b`, `a == b` or `a > b` is true, and
/// * transitive, `a < b` and `b < c` implies `a < c`. The same must hold for both `==` and `>`.
///
/// For example, while [`f64`] doesn't implement [`Ord`] because `NaN != NaN`, we can use
/// `partial_cmp` as our sort function when we know the slice doesn't contain a `NaN`.
#[inline]
pub fn sort_by<F>(&mut self, mut compare: F)
where
F: FnMut(&Value, &Value) -> std::cmp::Ordering,
{
self.values.sort_by(move |lhs, rhs| {
let lhs = lhs.as_value();
let rhs = rhs.as_value();
match (lhs, rhs) {
(None, None) => std::cmp::Ordering::Equal,
(Some(_), None) => std::cmp::Ordering::Greater,
(None, Some(_)) => std::cmp::Ordering::Less,
(Some(lhs), Some(rhs)) => compare(lhs, rhs),
}
});
}
/// Sorts the array with a key extraction function.
///
/// This sort is stable (i.e., does not reorder equal elements) and *O*(*m* \* *n* \* log(*n*))
/// worst-case, where the key function is *O*(*m*).
#[inline]
pub fn sort_by_key<K, F>(&mut self, mut f: F)
where
F: FnMut(&Value) -> K,
K: Ord,
{
#[allow(clippy::manual_map)] // needed for lifetimes
self.values.sort_by_key(move |item| {
if let Some(value) = item.as_value() {
Some(f(value))
} else {
None
}
});
}
fn value_op<T>(
&mut self,
v: Value,
decorate: bool,
op: impl FnOnce(&mut Vec<Item>, Value) -> T,
) -> T {
let mut value = v;
if !self.is_empty() && decorate {
value.decorate(" ", "");
} else if decorate {
value.decorate("", "");
}
op(&mut self.values, value)
}
}
#[cfg(feature = "display")]
impl std::fmt::Display for Array {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
crate::encode::encode_array(self, f, None, ("", ""))
}
}
impl<V: Into<Value>> Extend<V> for Array {
fn extend<T: IntoIterator<Item = V>>(&mut self, iter: T) {
for value in iter {
self.push_formatted(value.into());
}
}
}
impl<V: Into<Value>> FromIterator<V> for Array {
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = V>,
{
let v = iter.into_iter().map(|a| Item::Value(a.into()));
Array {
values: v.collect(),
..Default::default()
}
}
}
impl IntoIterator for Array {
type Item = Value;
type IntoIter = ArrayIntoIter;
fn into_iter(self) -> Self::IntoIter {
Box::new(
self.values
.into_iter()
.filter(|v| v.is_value())
.map(|v| v.into_value().unwrap()),
)
}
}
impl<'s> IntoIterator for &'s Array {
type Item = &'s Value;
type IntoIter = ArrayIter<'s>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
fn decorate_array(array: &mut Array) {
for (i, value) in array
.values
.iter_mut()
.filter_map(Item::as_value_mut)
.enumerate()
{
// [value1, value2, value3]
if i == 0 {
value.decorate(DEFAULT_LEADING_VALUE_DECOR.0, DEFAULT_LEADING_VALUE_DECOR.1);
} else {
value.decorate(DEFAULT_VALUE_DECOR.0, DEFAULT_VALUE_DECOR.1);
}
}
// Since everything is now on the same line, remove trailing commas and whitespace.
array.set_trailing_comma(false);
array.set_trailing("");
}

View File

@@ -0,0 +1,169 @@
use std::iter::FromIterator;
use crate::{Array, Item, Table};
/// A top-level sequence of [`Table`]s, each under their own header
#[derive(Clone, Debug, Default)]
pub struct ArrayOfTables {
// Always Vec<Item::Table>, just `Item` to make `Index` work
pub(crate) span: Option<std::ops::Range<usize>>,
pub(crate) values: Vec<Item>,
}
/// Constructors
///
/// See also `FromIterator`
impl ArrayOfTables {
/// Creates an empty array of tables.
pub fn new() -> Self {
Default::default()
}
}
/// Formatting
impl ArrayOfTables {
/// Convert to an inline array
pub fn into_array(mut self) -> Array {
for value in self.values.iter_mut() {
value.make_value();
}
let mut a = Array::with_vec(self.values);
a.fmt();
a
}
/// The location within the original document
///
/// This generally requires an [`ImDocument`][crate::ImDocument].
pub fn span(&self) -> Option<std::ops::Range<usize>> {
self.span.clone()
}
pub(crate) fn despan(&mut self, input: &str) {
self.span = None;
for value in &mut self.values {
value.despan(input);
}
}
}
impl ArrayOfTables {
/// Returns an iterator over tables.
pub fn iter(&self) -> ArrayOfTablesIter<'_> {
Box::new(self.values.iter().filter_map(Item::as_table))
}
/// Returns an iterator over tables.
pub fn iter_mut(&mut self) -> ArrayOfTablesIterMut<'_> {
Box::new(self.values.iter_mut().filter_map(Item::as_table_mut))
}
/// Returns the length of the underlying Vec.
/// To get the actual number of items use `a.iter().count()`.
pub fn len(&self) -> usize {
self.values.len()
}
/// Returns true if `self.len() == 0`.
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Removes all the tables.
pub fn clear(&mut self) {
self.values.clear();
}
/// Returns an optional reference to the table.
pub fn get(&self, index: usize) -> Option<&Table> {
self.values.get(index).and_then(Item::as_table)
}
/// Returns an optional mutable reference to the table.
pub fn get_mut(&mut self, index: usize) -> Option<&mut Table> {
self.values.get_mut(index).and_then(Item::as_table_mut)
}
/// Appends a table to the array.
pub fn push(&mut self, table: Table) {
self.values.push(Item::Table(table));
}
/// Removes a table with the given index.
pub fn remove(&mut self, index: usize) {
self.values.remove(index);
}
/// Retains only the elements specified by the `keep` predicate.
///
/// In other words, remove all tables for which `keep(&table)` returns `false`.
///
/// This method operates in place, visiting each element exactly once in the
/// original order, and preserves the order of the retained elements.
pub fn retain<F>(&mut self, mut keep: F)
where
F: FnMut(&Table) -> bool,
{
self.values
.retain(|item| item.as_table().map(&mut keep).unwrap_or(false));
}
}
/// An iterator type over [`ArrayOfTables`]'s [`Table`]s
pub type ArrayOfTablesIter<'a> = Box<dyn Iterator<Item = &'a Table> + 'a>;
/// An iterator type over [`ArrayOfTables`]'s [`Table`]s
pub type ArrayOfTablesIterMut<'a> = Box<dyn Iterator<Item = &'a mut Table> + 'a>;
/// An iterator type over [`ArrayOfTables`]'s [`Table`]s
pub type ArrayOfTablesIntoIter = Box<dyn Iterator<Item = Table>>;
impl Extend<Table> for ArrayOfTables {
fn extend<T: IntoIterator<Item = Table>>(&mut self, iter: T) {
for value in iter {
self.push(value);
}
}
}
impl FromIterator<Table> for ArrayOfTables {
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = Table>,
{
let v = iter.into_iter().map(Item::Table);
ArrayOfTables {
values: v.collect(),
span: None,
}
}
}
impl IntoIterator for ArrayOfTables {
type Item = Table;
type IntoIter = ArrayOfTablesIntoIter;
fn into_iter(self) -> Self::IntoIter {
Box::new(
self.values
.into_iter()
.filter(|v| v.is_table())
.map(|v| v.into_table().unwrap()),
)
}
}
impl<'s> IntoIterator for &'s ArrayOfTables {
type Item = &'s Table;
type IntoIter = ArrayOfTablesIter<'s>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
#[cfg(feature = "display")]
impl std::fmt::Display for ArrayOfTables {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// HACK: Without the header, we don't really have a proper way of printing this
self.clone().into_array().fmt(f)
}
}

View File

@@ -0,0 +1,97 @@
use crate::de::Error;
pub(crate) struct ArrayDeserializer {
input: Vec<crate::Item>,
span: Option<std::ops::Range<usize>>,
}
impl ArrayDeserializer {
pub(crate) fn new(input: Vec<crate::Item>, span: Option<std::ops::Range<usize>>) -> Self {
Self { input, span }
}
}
// Note: this is wrapped by `ValueDeserializer` and any trait methods
// implemented here need to be wrapped there
impl<'de> serde::Deserializer<'de> for ArrayDeserializer {
type Error = Error;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: serde::de::Visitor<'de>,
{
visitor.visit_seq(ArraySeqAccess::new(self.input))
}
fn deserialize_struct<V>(
self,
name: &'static str,
fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Error>
where
V: serde::de::Visitor<'de>,
{
if serde_spanned::__unstable::is_spanned(name, fields) {
if let Some(span) = self.span.clone() {
return visitor.visit_map(super::SpannedDeserializer::new(self, span));
}
}
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
}
}
impl serde::de::IntoDeserializer<'_, Error> for ArrayDeserializer {
type Deserializer = Self;
fn into_deserializer(self) -> Self::Deserializer {
self
}
}
impl crate::Array {
pub(crate) fn into_deserializer(self) -> ArrayDeserializer {
ArrayDeserializer::new(self.values, self.span)
}
}
impl crate::ArrayOfTables {
pub(crate) fn into_deserializer(self) -> ArrayDeserializer {
ArrayDeserializer::new(self.values, self.span)
}
}
pub(crate) struct ArraySeqAccess {
iter: std::vec::IntoIter<crate::Item>,
}
impl ArraySeqAccess {
pub(crate) fn new(input: Vec<crate::Item>) -> Self {
Self {
iter: input.into_iter(),
}
}
}
impl<'de> serde::de::SeqAccess<'de> for ArraySeqAccess {
type Error = Error;
fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
where
T: serde::de::DeserializeSeed<'de>,
{
match self.iter.next() {
Some(v) => seed
.deserialize(crate::de::ValueDeserializer::new(v))
.map(Some),
None => Ok(None),
}
}
}

View File

@@ -0,0 +1,43 @@
use serde::de::value::BorrowedStrDeserializer;
use serde::de::IntoDeserializer;
use crate::de::Error;
pub(crate) struct DatetimeDeserializer {
date: Option<crate::Datetime>,
}
impl DatetimeDeserializer {
pub(crate) fn new(date: crate::Datetime) -> Self {
Self { date: Some(date) }
}
}
impl<'de> serde::de::MapAccess<'de> for DatetimeDeserializer {
type Error = Error;
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Error>
where
K: serde::de::DeserializeSeed<'de>,
{
if self.date.is_some() {
seed.deserialize(BorrowedStrDeserializer::new(
toml_datetime::__unstable::FIELD,
))
.map(Some)
} else {
Ok(None)
}
}
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Error>
where
V: serde::de::DeserializeSeed<'de>,
{
if let Some(date) = self.date.take() {
seed.deserialize(date.to_string().into_deserializer())
} else {
panic!("next_value_seed called before next_key_seed")
}
}
}

151
vendor/toml_edit-0.22.27/src/de/key.rs vendored Normal file
View File

@@ -0,0 +1,151 @@
use serde::de::IntoDeserializer;
use super::Error;
pub(crate) struct KeyDeserializer {
span: Option<std::ops::Range<usize>>,
key: crate::Key,
}
impl KeyDeserializer {
pub(crate) fn new(key: crate::Key, span: Option<std::ops::Range<usize>>) -> Self {
KeyDeserializer { span, key }
}
}
impl IntoDeserializer<'_, Error> for KeyDeserializer {
type Deserializer = Self;
fn into_deserializer(self) -> Self::Deserializer {
self
}
}
impl<'de> serde::de::Deserializer<'de> for KeyDeserializer {
type Error = Error;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Error>
where
V: serde::de::Visitor<'de>,
{
self.key.into_deserializer().deserialize_any(visitor)
}
fn deserialize_enum<V>(
self,
name: &str,
variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: serde::de::Visitor<'de>,
{
let _ = name;
let _ = variants;
visitor.visit_enum(self)
}
fn deserialize_struct<V>(
self,
name: &'static str,
fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Error>
where
V: serde::de::Visitor<'de>,
{
if serde_spanned::__unstable::is_spanned(name, fields) {
if let Some(span) = self.span.clone() {
return visitor.visit_map(super::SpannedDeserializer::new(self.key.get(), span));
}
}
self.deserialize_any(visitor)
}
fn deserialize_newtype_struct<V>(
self,
_name: &'static str,
visitor: V,
) -> Result<V::Value, Error>
where
V: serde::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 option unit
ignored_any unit_struct tuple_struct tuple identifier
}
}
impl<'de> serde::de::EnumAccess<'de> for KeyDeserializer {
type Error = Error;
type Variant = UnitOnly<Self::Error>;
fn variant_seed<T>(self, seed: T) -> Result<(T::Value, Self::Variant), Self::Error>
where
T: serde::de::DeserializeSeed<'de>,
{
seed.deserialize(self).map(unit_only)
}
}
pub(crate) struct UnitOnly<E> {
marker: std::marker::PhantomData<E>,
}
fn unit_only<T, E>(t: T) -> (T, UnitOnly<E>) {
(
t,
UnitOnly {
marker: std::marker::PhantomData,
},
)
}
impl<'de, E> serde::de::VariantAccess<'de> for UnitOnly<E>
where
E: serde::de::Error,
{
type Error = E;
fn unit_variant(self) -> Result<(), Self::Error> {
Ok(())
}
fn newtype_variant_seed<T>(self, _seed: T) -> Result<T::Value, Self::Error>
where
T: serde::de::DeserializeSeed<'de>,
{
Err(serde::de::Error::invalid_type(
serde::de::Unexpected::UnitVariant,
&"newtype variant",
))
}
fn tuple_variant<V>(self, _len: usize, _visitor: V) -> Result<V::Value, Self::Error>
where
V: serde::de::Visitor<'de>,
{
Err(serde::de::Error::invalid_type(
serde::de::Unexpected::UnitVariant,
&"tuple variant",
))
}
fn struct_variant<V>(
self,
_fields: &'static [&'static str],
_visitor: V,
) -> Result<V::Value, Self::Error>
where
V: serde::de::Visitor<'de>,
{
Err(serde::de::Error::invalid_type(
serde::de::Unexpected::UnitVariant,
&"struct variant",
))
}
}

327
vendor/toml_edit-0.22.27/src/de/mod.rs vendored Normal file
View File

@@ -0,0 +1,327 @@
//! Deserializing TOML into Rust structures.
//!
//! This module contains all the Serde support for deserializing TOML documents into Rust structures.
use serde::de::DeserializeOwned;
mod array;
mod datetime;
mod key;
mod spanned;
mod table;
mod table_enum;
mod value;
use array::ArrayDeserializer;
use datetime::DatetimeDeserializer;
use key::KeyDeserializer;
use spanned::SpannedDeserializer;
use table_enum::TableEnumDeserializer;
pub use value::ValueDeserializer;
/// Errors that can occur when deserializing a type.
#[derive(Clone, PartialEq, Eq)]
pub struct Error {
inner: crate::TomlError,
}
impl Error {
pub(crate) fn custom<T>(msg: T, span: Option<std::ops::Range<usize>>) -> Self
where
T: std::fmt::Display,
{
Error {
inner: crate::TomlError::custom(msg.to_string(), span),
}
}
/// Add key while unwinding
pub fn add_key(&mut self, key: String) {
self.inner.add_key(key);
}
/// What went wrong
pub fn message(&self) -> &str {
self.inner.message()
}
/// The start/end index into the original document where the error occurred
pub fn span(&self) -> Option<std::ops::Range<usize>> {
self.inner.span()
}
pub(crate) fn set_span(&mut self, span: Option<std::ops::Range<usize>>) {
self.inner.set_span(span);
}
}
impl serde::de::Error for Error {
fn custom<T>(msg: T) -> Self
where
T: std::fmt::Display,
{
Error::custom(msg, None)
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.inner.fmt(f)
}
}
impl std::fmt::Debug for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.inner.fmt(f)
}
}
impl From<crate::TomlError> for Error {
fn from(e: crate::TomlError) -> Error {
Self { inner: e }
}
}
impl From<Error> for crate::TomlError {
fn from(e: Error) -> crate::TomlError {
e.inner
}
}
impl std::error::Error for Error {}
/// Convert a TOML [documents][crate::DocumentMut] into `T`.
#[cfg(feature = "parse")]
pub fn from_str<T>(s: &'_ str) -> Result<T, Error>
where
T: DeserializeOwned,
{
let de = Deserializer::parse(s)?;
T::deserialize(de)
}
/// Convert a TOML [documents][crate::DocumentMut] into `T`.
#[cfg(feature = "parse")]
pub fn from_slice<T>(s: &'_ [u8]) -> Result<T, Error>
where
T: DeserializeOwned,
{
let s = std::str::from_utf8(s).map_err(|e| Error::custom(e, None))?;
from_str(s)
}
/// Convert a [`DocumentMut`][crate::DocumentMut] into `T`.
pub fn from_document<T>(d: impl Into<Deserializer>) -> Result<T, Error>
where
T: DeserializeOwned,
{
let deserializer = d.into();
T::deserialize(deserializer)
}
/// Deserialization for TOML [documents][crate::DocumentMut].
pub struct Deserializer<S = String> {
root: crate::Item,
raw: Option<S>,
}
impl Deserializer {
/// Deserialization implementation for TOML.
#[deprecated(since = "0.22.6", note = "Replaced with `Deserializer::from`")]
pub fn new(input: crate::DocumentMut) -> Self {
Self::from(input)
}
}
#[cfg(feature = "parse")]
impl<S: AsRef<str>> Deserializer<S> {
/// Parse a TOML document
pub fn parse(raw: S) -> Result<Self, Error> {
crate::ImDocument::parse(raw)
.map(Self::from)
.map_err(Into::into)
}
}
impl From<crate::DocumentMut> for Deserializer {
fn from(doc: crate::DocumentMut) -> Self {
let crate::DocumentMut { root, .. } = doc;
Self { root, raw: None }
}
}
impl<S> From<crate::ImDocument<S>> for Deserializer<S> {
fn from(doc: crate::ImDocument<S>) -> Self {
let crate::ImDocument { root, raw, .. } = doc;
let raw = Some(raw);
Self { root, raw }
}
}
#[cfg(feature = "parse")]
impl std::str::FromStr for Deserializer {
type Err = Error;
/// Parses a document from a &str
fn from_str(s: &str) -> Result<Self, Self::Err> {
let doc: crate::ImDocument<_> = s.parse().map_err(Error::from)?;
Ok(Deserializer::from(doc))
}
}
// Note: this is wrapped by `toml::de::Deserializer` and any trait methods
// implemented here need to be wrapped there
impl<'de, S: Into<String>> serde::Deserializer<'de> for Deserializer<S> {
type Error = Error;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: serde::de::Visitor<'de>,
{
let raw = self.raw;
self.root
.into_deserializer()
.deserialize_any(visitor)
.map_err(|mut e: Self::Error| {
e.inner.set_raw(raw.map(|r| r.into()));
e
})
}
// `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: serde::de::Visitor<'de>,
{
let raw = self.raw;
self.root
.into_deserializer()
.deserialize_option(visitor)
.map_err(|mut e: Self::Error| {
e.inner.set_raw(raw.map(|r| r.into()));
e
})
}
fn deserialize_newtype_struct<V>(
self,
name: &'static str,
visitor: V,
) -> Result<V::Value, Error>
where
V: serde::de::Visitor<'de>,
{
let raw = self.raw;
self.root
.into_deserializer()
.deserialize_newtype_struct(name, visitor)
.map_err(|mut e: Self::Error| {
e.inner.set_raw(raw.map(|r| r.into()));
e
})
}
fn deserialize_struct<V>(
self,
name: &'static str,
fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Error>
where
V: serde::de::Visitor<'de>,
{
let raw = self.raw;
self.root
.into_deserializer()
.deserialize_struct(name, fields, visitor)
.map_err(|mut e: Self::Error| {
e.inner.set_raw(raw.map(|r| r.into()));
e
})
}
// 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: serde::de::Visitor<'de>,
{
let raw = self.raw;
self.root
.into_deserializer()
.deserialize_enum(name, variants, visitor)
.map_err(|mut e: Self::Error| {
e.inner.set_raw(raw.map(|r| r.into()));
e
})
}
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
ignored_any unit_struct tuple_struct tuple identifier
}
}
impl serde::de::IntoDeserializer<'_, Error> for Deserializer {
type Deserializer = Deserializer;
fn into_deserializer(self) -> Self::Deserializer {
self
}
}
impl serde::de::IntoDeserializer<'_, Error> for crate::DocumentMut {
type Deserializer = Deserializer;
fn into_deserializer(self) -> Self::Deserializer {
Deserializer::from(self)
}
}
impl serde::de::IntoDeserializer<'_, Error> for crate::ImDocument<String> {
type Deserializer = Deserializer;
fn into_deserializer(self) -> Self::Deserializer {
Deserializer::from(self)
}
}
pub(crate) fn validate_struct_keys(
table: &crate::table::KeyValuePairs,
fields: &'static [&'static str],
) -> Result<(), Error> {
let extra_fields = table
.keys()
.filter_map(|key| {
if !fields.contains(&key.get()) {
Some(key.clone())
} else {
None
}
})
.collect::<Vec<_>>();
if extra_fields.is_empty() {
Ok(())
} else {
Err(Error::custom(
format!(
"unexpected keys in table: {}, available keys: {}",
extra_fields
.iter()
.map(|k| k.get())
.collect::<Vec<_>>()
.join(", "),
fields.join(", "),
),
extra_fields[0].span(),
))
}
}

View File

@@ -0,0 +1,70 @@
use serde::de::value::BorrowedStrDeserializer;
use serde::de::IntoDeserializer as _;
use super::Error;
pub(crate) struct SpannedDeserializer<'de, T: serde::de::IntoDeserializer<'de, Error>> {
phantom_data: std::marker::PhantomData<&'de ()>,
start: Option<usize>,
end: Option<usize>,
value: Option<T>,
}
impl<'de, T> SpannedDeserializer<'de, T>
where
T: serde::de::IntoDeserializer<'de, Error>,
{
pub(crate) fn new(value: T, span: std::ops::Range<usize>) -> Self {
Self {
phantom_data: Default::default(),
start: Some(span.start),
end: Some(span.end),
value: Some(value),
}
}
}
impl<'de, T> serde::de::MapAccess<'de> for SpannedDeserializer<'de, T>
where
T: serde::de::IntoDeserializer<'de, Error>,
{
type Error = Error;
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Error>
where
K: serde::de::DeserializeSeed<'de>,
{
if self.start.is_some() {
seed.deserialize(BorrowedStrDeserializer::new(
serde_spanned::__unstable::START_FIELD,
))
.map(Some)
} else if self.end.is_some() {
seed.deserialize(BorrowedStrDeserializer::new(
serde_spanned::__unstable::END_FIELD,
))
.map(Some)
} else if self.value.is_some() {
seed.deserialize(BorrowedStrDeserializer::new(
serde_spanned::__unstable::VALUE_FIELD,
))
.map(Some)
} else {
Ok(None)
}
}
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Error>
where
V: serde::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")
}
}
}

214
vendor/toml_edit-0.22.27/src/de/table.rs vendored Normal file
View File

@@ -0,0 +1,214 @@
use serde::de::IntoDeserializer;
use crate::de::Error;
pub(crate) struct TableDeserializer {
span: Option<std::ops::Range<usize>>,
items: crate::table::KeyValuePairs,
}
// Note: this is wrapped by `Deserializer` and `ValueDeserializer` and any trait methods
// implemented here need to be wrapped there
impl<'de> serde::Deserializer<'de> for TableDeserializer {
type Error = Error;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: serde::de::Visitor<'de>,
{
visitor.visit_map(TableMapAccess::new(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: serde::de::Visitor<'de>,
{
visitor.visit_some(self)
}
fn deserialize_newtype_struct<V>(
self,
_name: &'static str,
visitor: V,
) -> Result<V::Value, Error>
where
V: serde::de::Visitor<'de>,
{
visitor.visit_newtype_struct(self)
}
fn deserialize_struct<V>(
self,
name: &'static str,
fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Error>
where
V: serde::de::Visitor<'de>,
{
if serde_spanned::__unstable::is_spanned(name, fields) {
if let Some(span) = self.span.clone() {
return visitor.visit_map(super::SpannedDeserializer::new(self, span));
}
}
self.deserialize_any(visitor)
}
// 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: serde::de::Visitor<'de>,
{
if self.items.is_empty() {
Err(Error::custom(
"wanted exactly 1 element, found 0 elements",
self.span,
))
} else if self.items.len() != 1 {
Err(Error::custom(
"wanted exactly 1 element, more than 1 element",
self.span,
))
} else {
visitor.visit_enum(TableMapAccess::new(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
ignored_any unit_struct tuple_struct tuple identifier
}
}
impl IntoDeserializer<'_, Error> for TableDeserializer {
type Deserializer = TableDeserializer;
fn into_deserializer(self) -> Self::Deserializer {
self
}
}
impl crate::Table {
pub(crate) fn into_deserializer(self) -> TableDeserializer {
TableDeserializer {
span: self.span(),
items: self.items,
}
}
}
impl crate::InlineTable {
pub(crate) fn into_deserializer(self) -> TableDeserializer {
TableDeserializer {
span: self.span(),
items: self.items,
}
}
}
pub(crate) struct TableMapAccess {
iter: indexmap::map::IntoIter<crate::Key, crate::Item>,
span: Option<std::ops::Range<usize>>,
value: Option<(crate::Key, crate::Item)>,
}
impl TableMapAccess {
pub(crate) fn new(input: TableDeserializer) -> Self {
Self {
iter: input.items.into_iter(),
span: input.span,
value: None,
}
}
}
impl<'de> serde::de::MapAccess<'de> for TableMapAccess {
type Error = Error;
fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>, Self::Error>
where
K: serde::de::DeserializeSeed<'de>,
{
match self.iter.next() {
Some((k, v)) => {
let key_span = k.span();
let ret = seed
.deserialize(super::KeyDeserializer::new(k.clone(), key_span.clone()))
.map(Some)
.map_err(|mut e: Self::Error| {
if e.span().is_none() {
e.set_span(key_span);
}
e
});
self.value = Some((k, v));
ret
}
None => Ok(None),
}
}
fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value, Self::Error>
where
V: serde::de::DeserializeSeed<'de>,
{
match self.value.take() {
Some((k, v)) => {
let span = v.span().or_else(|| k.span());
seed.deserialize(crate::de::ValueDeserializer::new(v))
.map_err(|mut e: Self::Error| {
if e.span().is_none() {
e.set_span(span);
}
e.add_key(k.get().to_owned());
e
})
}
None => {
panic!("no more values in next_value_seed, internal error in ValueDeserializer")
}
}
}
}
impl<'de> serde::de::EnumAccess<'de> for TableMapAccess {
type Error = Error;
type Variant = super::TableEnumDeserializer;
fn variant_seed<V>(mut self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error>
where
V: serde::de::DeserializeSeed<'de>,
{
let (key, value) = match self.iter.next() {
Some(pair) => pair,
None => {
return Err(Error::custom(
"expected table with exactly 1 entry, found empty table",
self.span,
));
}
};
let val = seed
.deserialize(key.into_deserializer())
.map_err(|mut e: Self::Error| {
if e.span().is_none() {
e.set_span(key.span());
}
e
})?;
let variant = super::TableEnumDeserializer::new(value);
Ok((val, variant))
}
}

View File

@@ -0,0 +1,176 @@
use crate::de::Error;
/// Deserializes table values into enum variants.
pub(crate) struct TableEnumDeserializer {
value: crate::Item,
}
impl TableEnumDeserializer {
pub(crate) fn new(value: crate::Item) -> Self {
TableEnumDeserializer { value }
}
}
impl<'de> serde::de::VariantAccess<'de> for TableEnumDeserializer {
type Error = Error;
fn unit_variant(self) -> Result<(), Self::Error> {
match self.value {
crate::Item::ArrayOfTables(values) => {
if values.is_empty() {
Ok(())
} else {
Err(Error::custom("expected empty array", values.span()))
}
}
crate::Item::Value(crate::Value::Array(values)) => {
if values.is_empty() {
Ok(())
} else {
Err(Error::custom("expected empty table", values.span()))
}
}
crate::Item::Table(values) => {
if values.is_empty() {
Ok(())
} else {
Err(Error::custom("expected empty table", values.span()))
}
}
crate::Item::Value(crate::Value::InlineTable(values)) => {
if values.is_empty() {
Ok(())
} else {
Err(Error::custom("expected empty table", values.span()))
}
}
e => Err(Error::custom(
format!("expected table, found {}", e.type_name()),
e.span(),
)),
}
}
fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value, Self::Error>
where
T: serde::de::DeserializeSeed<'de>,
{
seed.deserialize(super::ValueDeserializer::new(self.value))
}
fn tuple_variant<V>(self, len: usize, visitor: V) -> Result<V::Value, Self::Error>
where
V: serde::de::Visitor<'de>,
{
match self.value {
crate::Item::ArrayOfTables(values) => {
let values_span = values.span();
let tuple_values = values.values.into_iter().collect::<Vec<_>>();
if tuple_values.len() == len {
serde::de::Deserializer::deserialize_seq(
super::ArrayDeserializer::new(tuple_values, values_span),
visitor,
)
} else {
Err(Error::custom(
format!("expected tuple with length {len}"),
values_span,
))
}
}
crate::Item::Value(crate::Value::Array(values)) => {
let values_span = values.span();
let tuple_values = values.values.into_iter().collect::<Vec<_>>();
if tuple_values.len() == len {
serde::de::Deserializer::deserialize_seq(
super::ArrayDeserializer::new(tuple_values, values_span),
visitor,
)
} else {
Err(Error::custom(
format!("expected tuple with length {len}"),
values_span,
))
}
}
crate::Item::Table(values) => {
let values_span = values.span();
let tuple_values: Result<Vec<_>, _> = values
.items
.into_iter()
.enumerate()
.map(|(index, (key, value))| match key.get().parse::<usize>() {
Ok(key_index) if key_index == index => Ok(value),
Ok(_) | Err(_) => Err(Error::custom(
format!("expected table key `{}`, but was `{}`", index, key.get()),
key.span(),
)),
})
.collect();
let tuple_values = tuple_values?;
if tuple_values.len() == len {
serde::de::Deserializer::deserialize_seq(
super::ArrayDeserializer::new(tuple_values, values_span),
visitor,
)
} else {
Err(Error::custom(
format!("expected tuple with length {len}"),
values_span,
))
}
}
crate::Item::Value(crate::Value::InlineTable(values)) => {
let values_span = values.span();
let tuple_values: Result<Vec<_>, _> = values
.items
.into_iter()
.enumerate()
.map(|(index, (key, value))| match key.get().parse::<usize>() {
Ok(key_index) if key_index == index => Ok(value),
Ok(_) | Err(_) => Err(Error::custom(
format!("expected table key `{}`, but was `{}`", index, key.get()),
key.span(),
)),
})
.collect();
let tuple_values = tuple_values?;
if tuple_values.len() == len {
serde::de::Deserializer::deserialize_seq(
super::ArrayDeserializer::new(tuple_values, values_span),
visitor,
)
} else {
Err(Error::custom(
format!("expected tuple with length {len}"),
values_span,
))
}
}
e => Err(Error::custom(
format!("expected table, found {}", e.type_name()),
e.span(),
)),
}
}
fn struct_variant<V>(
self,
fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Self::Error>
where
V: serde::de::Visitor<'de>,
{
serde::de::Deserializer::deserialize_struct(
super::ValueDeserializer::new(self.value).with_struct_key_validation(),
"", // TODO: this should be the variant name
fields,
visitor,
)
}
}

257
vendor/toml_edit-0.22.27/src/de/value.rs vendored Normal file
View File

@@ -0,0 +1,257 @@
use serde::de::IntoDeserializer as _;
use crate::de::DatetimeDeserializer;
use crate::de::Error;
/// Deserialization implementation for TOML [values][crate::Value].
///
/// Can be created either directly from TOML strings, using [`std::str::FromStr`],
/// or from parsed [values][crate::Value] using [`serde::de::IntoDeserializer::into_deserializer`].
///
/// # Example
///
/// ```
/// # #[cfg(feature = "parse")] {
/// # #[cfg(feature = "display")] {
/// use serde::Deserialize;
///
/// #[derive(Deserialize)]
/// struct Config {
/// title: String,
/// owner: Owner,
/// }
///
/// #[derive(Deserialize)]
/// struct Owner {
/// name: String,
/// }
///
/// let value = r#"{ title = 'TOML Example', owner = { name = 'Lisa' } }"#;
/// let deserializer = value.parse::<toml_edit::de::ValueDeserializer>().unwrap();
/// let config = Config::deserialize(deserializer).unwrap();
/// assert_eq!(config.title, "TOML Example");
/// assert_eq!(config.owner.name, "Lisa");
/// # }
/// # }
/// ```
pub struct ValueDeserializer {
input: crate::Item,
validate_struct_keys: bool,
}
impl ValueDeserializer {
pub(crate) fn new(input: crate::Item) -> Self {
Self {
input,
validate_struct_keys: false,
}
}
pub(crate) fn with_struct_key_validation(mut self) -> Self {
self.validate_struct_keys = true;
self
}
}
// Note: this is wrapped by `toml::de::ValueDeserializer` and any trait methods
// implemented here need to be wrapped there
impl<'de> serde::Deserializer<'de> for ValueDeserializer {
type Error = Error;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: serde::de::Visitor<'de>,
{
let span = self.input.span();
match self.input {
crate::Item::None => visitor.visit_none(),
crate::Item::Value(crate::Value::String(v)) => visitor.visit_string(v.into_value()),
crate::Item::Value(crate::Value::Integer(v)) => visitor.visit_i64(v.into_value()),
crate::Item::Value(crate::Value::Float(v)) => visitor.visit_f64(v.into_value()),
crate::Item::Value(crate::Value::Boolean(v)) => visitor.visit_bool(v.into_value()),
crate::Item::Value(crate::Value::Datetime(v)) => {
visitor.visit_map(DatetimeDeserializer::new(v.into_value()))
}
crate::Item::Value(crate::Value::Array(v)) => {
v.into_deserializer().deserialize_any(visitor)
}
crate::Item::Value(crate::Value::InlineTable(v)) => {
v.into_deserializer().deserialize_any(visitor)
}
crate::Item::Table(v) => v.into_deserializer().deserialize_any(visitor),
crate::Item::ArrayOfTables(v) => v.into_deserializer().deserialize_any(visitor),
}
.map_err(|mut e: Self::Error| {
if e.span().is_none() {
e.set_span(span);
}
e
})
}
// `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: serde::de::Visitor<'de>,
{
let span = self.input.span();
visitor.visit_some(self).map_err(|mut e: Self::Error| {
if e.span().is_none() {
e.set_span(span);
}
e
})
}
fn deserialize_newtype_struct<V>(
self,
_name: &'static str,
visitor: V,
) -> Result<V::Value, Error>
where
V: serde::de::Visitor<'de>,
{
let span = self.input.span();
visitor
.visit_newtype_struct(self)
.map_err(|mut e: Self::Error| {
if e.span().is_none() {
e.set_span(span);
}
e
})
}
fn deserialize_struct<V>(
self,
name: &'static str,
fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, Error>
where
V: serde::de::Visitor<'de>,
{
if serde_spanned::__unstable::is_spanned(name, fields) {
if let Some(span) = self.input.span() {
return visitor.visit_map(super::SpannedDeserializer::new(self, span));
}
}
if name == toml_datetime::__unstable::NAME && fields == [toml_datetime::__unstable::FIELD] {
let span = self.input.span();
if let crate::Item::Value(crate::Value::Datetime(d)) = self.input {
return visitor
.visit_map(DatetimeDeserializer::new(d.into_value()))
.map_err(|mut e: Self::Error| {
if e.span().is_none() {
e.set_span(span);
}
e
});
}
}
if self.validate_struct_keys {
let span = self.input.span();
match &self.input {
crate::Item::Table(values) => super::validate_struct_keys(&values.items, fields),
crate::Item::Value(crate::Value::InlineTable(values)) => {
super::validate_struct_keys(&values.items, fields)
}
_ => Ok(()),
}
.map_err(|mut e: Self::Error| {
if e.span().is_none() {
e.set_span(span);
}
e
})?;
}
self.deserialize_any(visitor)
}
// 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: serde::de::Visitor<'de>,
{
let span = self.input.span();
match self.input {
crate::Item::Value(crate::Value::String(v)) => {
visitor.visit_enum(v.into_value().into_deserializer())
}
crate::Item::Value(crate::Value::InlineTable(v)) => {
if v.is_empty() {
Err(Error::custom(
"wanted exactly 1 element, found 0 elements",
v.span(),
))
} else if v.len() != 1 {
Err(Error::custom(
"wanted exactly 1 element, more than 1 element",
v.span(),
))
} else {
v.into_deserializer()
.deserialize_enum(name, variants, visitor)
}
}
crate::Item::Table(v) => v
.into_deserializer()
.deserialize_enum(name, variants, visitor),
e => Err(Error::custom("wanted string or table", e.span())),
}
.map_err(|mut e: Self::Error| {
if e.span().is_none() {
e.set_span(span);
}
e
})
}
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
ignored_any unit_struct tuple_struct tuple identifier
}
}
impl serde::de::IntoDeserializer<'_, Error> for ValueDeserializer {
type Deserializer = Self;
fn into_deserializer(self) -> Self::Deserializer {
self
}
}
impl serde::de::IntoDeserializer<'_, Error> for crate::Value {
type Deserializer = ValueDeserializer;
fn into_deserializer(self) -> Self::Deserializer {
ValueDeserializer::new(crate::Item::Value(self))
}
}
impl crate::Item {
pub(crate) fn into_deserializer(self) -> ValueDeserializer {
ValueDeserializer::new(self)
}
}
#[cfg(feature = "parse")]
impl std::str::FromStr for ValueDeserializer {
type Err = Error;
/// Parses a value from a &str
fn from_str(s: &str) -> Result<Self, Self::Err> {
let value = s.parse::<crate::Value>().map_err(Error::from)?;
Ok(value.into_deserializer())
}
}

235
vendor/toml_edit-0.22.27/src/document.rs vendored Normal file
View File

@@ -0,0 +1,235 @@
use std::str::FromStr;
use crate::table::Iter;
use crate::{Item, RawString, Table};
/// The root TOML [`Table`], containing [`Key`][crate::Key]/[`Value`][crate::Value] pairs and all other logic [`Table`]s
#[derive(Debug, Clone)]
pub struct ImDocument<S> {
pub(crate) root: Item,
// Trailing comments and whitespaces
pub(crate) trailing: RawString,
pub(crate) raw: S,
}
impl ImDocument<&'static str> {
/// Creates an empty document
pub fn new() -> Self {
Default::default()
}
}
#[cfg(feature = "parse")]
impl<S: AsRef<str>> ImDocument<S> {
/// Parse a TOML document
pub fn parse(raw: S) -> Result<Self, crate::TomlError> {
crate::parser::parse_document(raw)
}
}
impl<S: AsRef<str>> ImDocument<S> {
/// # Panics
///
/// If run on a [`DocumentMut`] not generated by the parser
pub(crate) fn despan(&mut self) {
self.root.despan(self.raw.as_ref());
self.trailing.despan(self.raw.as_ref());
}
}
impl<S> ImDocument<S> {
/// Returns a reference to the root item.
pub fn as_item(&self) -> &Item {
&self.root
}
/// Returns the root item.
pub fn into_item(self) -> Item {
self.root
}
/// Returns a reference to the root table.
pub fn as_table(&self) -> &Table {
self.root.as_table().expect("root should always be a table")
}
/// Returns the root table.
pub fn into_table(self) -> Table {
self.root
.into_table()
.expect("root should always be a table")
}
/// Returns an iterator over the root table.
pub fn iter(&self) -> Iter<'_> {
self.as_table().iter()
}
/// Whitespace after last element
pub fn trailing(&self) -> &RawString {
&self.trailing
}
}
impl<S: AsRef<str>> ImDocument<S> {
/// Access the raw, unparsed document
pub fn raw(&self) -> &str {
self.raw.as_ref()
}
}
impl<S: AsRef<str>> ImDocument<S> {
/// Allow editing of the [`DocumentMut`]
pub fn into_mut(mut self) -> DocumentMut {
self.despan();
DocumentMut {
root: self.root,
trailing: self.trailing,
}
}
}
impl Default for ImDocument<&'static str> {
fn default() -> Self {
Self {
root: Item::Table(Table::with_pos(Some(0))),
trailing: Default::default(),
raw: "",
}
}
}
#[cfg(feature = "parse")]
impl FromStr for ImDocument<String> {
type Err = crate::TomlError;
/// Parses a document from a &str
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::parse(s.to_owned())
}
}
impl<S> std::ops::Deref for ImDocument<S> {
type Target = Table;
fn deref(&self) -> &Self::Target {
self.as_table()
}
}
/// The editable root TOML [`Table`], containing [`Key`][crate::Key]/[`Value`][crate::Value] pairs and all other logic [`Table`]s
#[derive(Debug, Clone)]
pub struct DocumentMut {
pub(crate) root: Item,
// Trailing comments and whitespaces
pub(crate) trailing: RawString,
}
impl DocumentMut {
/// Creates an empty document
pub fn new() -> Self {
Default::default()
}
/// Returns a reference to the root item.
pub fn as_item(&self) -> &Item {
&self.root
}
/// Returns a mutable reference to the root item.
pub fn as_item_mut(&mut self) -> &mut Item {
&mut self.root
}
/// Returns the root item.
pub fn into_item(self) -> Item {
self.root
}
/// Returns a reference to the root table.
pub fn as_table(&self) -> &Table {
self.root.as_table().expect("root should always be a table")
}
/// Returns a mutable reference to the root table.
pub fn as_table_mut(&mut self) -> &mut Table {
self.root
.as_table_mut()
.expect("root should always be a table")
}
/// Returns the root table.
pub fn into_table(self) -> Table {
self.root
.into_table()
.expect("root should always be a table")
}
/// Returns an iterator over the root table.
pub fn iter(&self) -> Iter<'_> {
self.as_table().iter()
}
/// Set whitespace after last element
pub fn set_trailing(&mut self, trailing: impl Into<RawString>) {
self.trailing = trailing.into();
}
/// Whitespace after last element
pub fn trailing(&self) -> &RawString {
&self.trailing
}
}
impl Default for DocumentMut {
fn default() -> Self {
Self {
root: Item::Table(Table::with_pos(Some(0))),
trailing: Default::default(),
}
}
}
#[cfg(feature = "parse")]
impl FromStr for DocumentMut {
type Err = crate::TomlError;
/// Parses a document from a &str
fn from_str(s: &str) -> Result<Self, Self::Err> {
let im = ImDocument::from_str(s)?;
Ok(im.into_mut())
}
}
impl std::ops::Deref for DocumentMut {
type Target = Table;
fn deref(&self) -> &Self::Target {
self.as_table()
}
}
impl std::ops::DerefMut for DocumentMut {
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_table_mut()
}
}
impl From<Table> for DocumentMut {
fn from(root: Table) -> Self {
Self {
root: Item::Table(root),
..Default::default()
}
}
}
#[test]
#[cfg(feature = "parse")]
#[cfg(feature = "display")]
fn default_roundtrip() {
DocumentMut::default()
.to_string()
.parse::<DocumentMut>()
.unwrap();
}

410
vendor/toml_edit-0.22.27/src/encode.rs vendored Normal file
View File

@@ -0,0 +1,410 @@
use std::borrow::Cow;
use std::fmt::{Display, Formatter, Result, Write};
use toml_datetime::Datetime;
use toml_write::ToTomlValue as _;
use toml_write::TomlWrite as _;
use crate::inline_table::DEFAULT_INLINE_KEY_DECOR;
use crate::key::Key;
use crate::repr::{Formatted, Repr, ValueRepr};
use crate::table::{
DEFAULT_KEY_DECOR, DEFAULT_KEY_PATH_DECOR, DEFAULT_ROOT_DECOR, DEFAULT_TABLE_DECOR,
};
use crate::value::{
DEFAULT_LEADING_VALUE_DECOR, DEFAULT_TRAILING_VALUE_DECOR, DEFAULT_VALUE_DECOR,
};
use crate::DocumentMut;
use crate::{Array, InlineTable, Item, Table, Value};
pub(crate) fn encode_key(this: &Key, buf: &mut dyn Write, input: Option<&str>) -> Result {
if let Some(input) = input {
let repr = this
.as_repr()
.map(Cow::Borrowed)
.unwrap_or_else(|| Cow::Owned(this.default_repr()));
repr.encode(buf, input)?;
} else {
let repr = this.display_repr();
write!(buf, "{repr}")?;
};
Ok(())
}
fn encode_key_path(
this: &[Key],
mut buf: &mut dyn Write,
input: Option<&str>,
default_decor: (&str, &str),
) -> Result {
let leaf_decor = this.last().expect("always at least one key").leaf_decor();
for (i, key) in this.iter().enumerate() {
let dotted_decor = key.dotted_decor();
let first = i == 0;
let last = i + 1 == this.len();
if first {
leaf_decor.prefix_encode(buf, input, default_decor.0)?;
} else {
buf.key_sep()?;
dotted_decor.prefix_encode(buf, input, DEFAULT_KEY_PATH_DECOR.0)?;
}
encode_key(key, buf, input)?;
if last {
leaf_decor.suffix_encode(buf, input, default_decor.1)?;
} else {
dotted_decor.suffix_encode(buf, input, DEFAULT_KEY_PATH_DECOR.1)?;
}
}
Ok(())
}
pub(crate) fn encode_key_path_ref(
this: &[&Key],
mut buf: &mut dyn Write,
input: Option<&str>,
default_decor: (&str, &str),
) -> Result {
let leaf_decor = this.last().expect("always at least one key").leaf_decor();
for (i, key) in this.iter().enumerate() {
let dotted_decor = key.dotted_decor();
let first = i == 0;
let last = i + 1 == this.len();
if first {
leaf_decor.prefix_encode(buf, input, default_decor.0)?;
} else {
buf.key_sep()?;
dotted_decor.prefix_encode(buf, input, DEFAULT_KEY_PATH_DECOR.0)?;
}
encode_key(key, buf, input)?;
if last {
leaf_decor.suffix_encode(buf, input, default_decor.1)?;
} else {
dotted_decor.suffix_encode(buf, input, DEFAULT_KEY_PATH_DECOR.1)?;
}
}
Ok(())
}
pub(crate) fn encode_formatted<T: ValueRepr>(
this: &Formatted<T>,
buf: &mut dyn Write,
input: Option<&str>,
default_decor: (&str, &str),
) -> Result {
let decor = this.decor();
decor.prefix_encode(buf, input, default_decor.0)?;
if let Some(input) = input {
let repr = this
.as_repr()
.map(Cow::Borrowed)
.unwrap_or_else(|| Cow::Owned(this.default_repr()));
repr.encode(buf, input)?;
} else {
let repr = this.display_repr();
write!(buf, "{repr}")?;
};
decor.suffix_encode(buf, input, default_decor.1)?;
Ok(())
}
pub(crate) fn encode_array(
this: &Array,
mut buf: &mut dyn Write,
input: Option<&str>,
default_decor: (&str, &str),
) -> Result {
let decor = this.decor();
decor.prefix_encode(buf, input, default_decor.0)?;
buf.open_array()?;
for (i, elem) in this.iter().enumerate() {
let inner_decor;
if i == 0 {
inner_decor = DEFAULT_LEADING_VALUE_DECOR;
} else {
inner_decor = DEFAULT_VALUE_DECOR;
buf.val_sep()?;
}
encode_value(elem, buf, input, inner_decor)?;
}
if this.trailing_comma() && !this.is_empty() {
buf.val_sep()?;
}
this.trailing().encode_with_default(buf, input, "")?;
buf.close_array()?;
decor.suffix_encode(buf, input, default_decor.1)?;
Ok(())
}
pub(crate) fn encode_table(
this: &InlineTable,
mut buf: &mut dyn Write,
input: Option<&str>,
default_decor: (&str, &str),
) -> Result {
let decor = this.decor();
decor.prefix_encode(buf, input, default_decor.0)?;
buf.open_inline_table()?;
this.preamble().encode_with_default(buf, input, "")?;
let children = this.get_values();
let len = children.len();
for (i, (key_path, value)) in children.into_iter().enumerate() {
if i != 0 {
buf.val_sep()?;
}
let inner_decor = if i == len - 1 {
DEFAULT_TRAILING_VALUE_DECOR
} else {
DEFAULT_VALUE_DECOR
};
encode_key_path_ref(&key_path, buf, input, DEFAULT_INLINE_KEY_DECOR)?;
buf.keyval_sep()?;
encode_value(value, buf, input, inner_decor)?;
}
buf.close_inline_table()?;
decor.suffix_encode(buf, input, default_decor.1)?;
Ok(())
}
pub(crate) fn encode_value(
this: &Value,
buf: &mut dyn Write,
input: Option<&str>,
default_decor: (&str, &str),
) -> Result {
match this {
Value::String(repr) => encode_formatted(repr, buf, input, default_decor),
Value::Integer(repr) => encode_formatted(repr, buf, input, default_decor),
Value::Float(repr) => encode_formatted(repr, buf, input, default_decor),
Value::Boolean(repr) => encode_formatted(repr, buf, input, default_decor),
Value::Datetime(repr) => encode_formatted(repr, buf, input, default_decor),
Value::Array(array) => encode_array(array, buf, input, default_decor),
Value::InlineTable(table) => encode_table(table, buf, input, default_decor),
}
}
impl Display for DocumentMut {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
let decor = self.decor();
decor.prefix_encode(f, None, DEFAULT_ROOT_DECOR.0)?;
let mut path = Vec::new();
let mut last_position = 0;
let mut tables = Vec::new();
visit_nested_tables(self.as_table(), &mut path, false, &mut |t, p, is_array| {
if let Some(pos) = t.position() {
last_position = pos;
}
tables.push((last_position, t, p.clone(), is_array));
Ok(())
})
.unwrap();
tables.sort_by_key(|&(id, _, _, _)| id);
let mut first_table = true;
for (_, table, path, is_array) in tables {
visit_table(f, None, table, &path, is_array, &mut first_table)?;
}
decor.suffix_encode(f, None, DEFAULT_ROOT_DECOR.1)?;
self.trailing().encode_with_default(f, None, "")
}
}
fn visit_nested_tables<'t, F>(
table: &'t Table,
path: &mut Vec<Key>,
is_array_of_tables: bool,
callback: &mut F,
) -> Result
where
F: FnMut(&'t Table, &Vec<Key>, bool) -> Result,
{
if !table.is_dotted() {
callback(table, path, is_array_of_tables)?;
}
for (key, value) in table.items.iter() {
match value {
Item::Table(ref t) => {
let key = key.clone();
path.push(key);
visit_nested_tables(t, path, false, callback)?;
path.pop();
}
Item::ArrayOfTables(ref a) => {
for t in a.iter() {
let key = key.clone();
path.push(key);
visit_nested_tables(t, path, true, callback)?;
path.pop();
}
}
_ => {}
}
}
Ok(())
}
fn visit_table(
mut buf: &mut dyn Write,
input: Option<&str>,
table: &Table,
path: &[Key],
is_array_of_tables: bool,
first_table: &mut bool,
) -> Result {
let children = table.get_values();
// We are intentionally hiding implicit tables without any tables nested under them (ie
// `table.is_empty()` which is in contrast to `table.get_values().is_empty()`). We are
// trusting the user that an empty implicit table is not semantically meaningful
//
// This allows a user to delete all tables under this implicit table and the implicit table
// will disappear.
//
// However, this means that users need to take care in deciding what tables get marked as
// implicit.
let is_visible_std_table = !(table.implicit && children.is_empty());
if path.is_empty() {
// don't print header for the root node
if !children.is_empty() {
*first_table = false;
}
} else if is_array_of_tables {
let default_decor = if *first_table {
*first_table = false;
("", DEFAULT_TABLE_DECOR.1)
} else {
DEFAULT_TABLE_DECOR
};
table.decor.prefix_encode(buf, input, default_decor.0)?;
buf.open_array_of_tables_header()?;
encode_key_path(path, buf, input, DEFAULT_KEY_PATH_DECOR)?;
buf.close_array_of_tables_header()?;
table.decor.suffix_encode(buf, input, default_decor.1)?;
writeln!(buf)?;
} else if is_visible_std_table {
let default_decor = if *first_table {
*first_table = false;
("", DEFAULT_TABLE_DECOR.1)
} else {
DEFAULT_TABLE_DECOR
};
table.decor.prefix_encode(buf, input, default_decor.0)?;
buf.open_table_header()?;
encode_key_path(path, buf, input, DEFAULT_KEY_PATH_DECOR)?;
buf.close_table_header()?;
table.decor.suffix_encode(buf, input, default_decor.1)?;
writeln!(buf)?;
}
// print table body
for (key_path, value) in children {
encode_key_path_ref(&key_path, buf, input, DEFAULT_KEY_DECOR)?;
buf.keyval_sep()?;
encode_value(value, buf, input, DEFAULT_VALUE_DECOR)?;
writeln!(buf)?;
}
Ok(())
}
impl ValueRepr for String {
fn to_repr(&self) -> Repr {
let output = toml_write::TomlStringBuilder::new(self.as_str())
.as_default()
.to_toml_value();
Repr::new_unchecked(output)
}
}
impl ValueRepr for i64 {
fn to_repr(&self) -> Repr {
let repr = self.to_toml_value();
Repr::new_unchecked(repr)
}
}
impl ValueRepr for f64 {
fn to_repr(&self) -> Repr {
let repr = self.to_toml_value();
Repr::new_unchecked(repr)
}
}
impl ValueRepr for bool {
fn to_repr(&self) -> Repr {
let repr = self.to_toml_value();
Repr::new_unchecked(repr)
}
}
impl ValueRepr for Datetime {
fn to_repr(&self) -> Repr {
Repr::new_unchecked(self.to_string())
}
}
#[cfg(test)]
mod test {
use super::*;
use proptest::prelude::*;
proptest! {
#[test]
#[cfg(feature = "parse")]
fn parseable_string(string in "\\PC*") {
let value = Value::from(string.clone());
let encoded = value.to_string();
let _: Value = encoded.parse().unwrap_or_else(|err| {
panic!("error: {err}
string:
```
{string}
```
value:
```
{value}
```
")
});
}
}
proptest! {
#[test]
#[cfg(feature = "parse")]
fn parseable_key(string in "\\PC*") {
let key = Key::new(string.clone());
let encoded = key.to_string();
let _: Key = encoded.parse().unwrap_or_else(|err| {
panic!("error: {err}
string:
```
{string}
```
key:
```
{key}
```
")
});
}
}
}

239
vendor/toml_edit-0.22.27/src/error.rs vendored Normal file
View File

@@ -0,0 +1,239 @@
use std::error::Error as StdError;
use std::fmt::{Display, Formatter, Result};
/// A TOML parse error
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub struct TomlError {
message: String,
raw: Option<String>,
keys: Vec<String>,
span: Option<std::ops::Range<usize>>,
}
impl TomlError {
#[cfg(feature = "parse")]
pub(crate) fn new(
error: winnow::error::ParseError<
crate::parser::prelude::Input<'_>,
winnow::error::ContextError,
>,
mut raw: crate::parser::prelude::Input<'_>,
) -> Self {
use winnow::stream::Stream;
let message = error.inner().to_string();
let raw = raw.finish();
let raw = String::from_utf8(raw.to_owned()).expect("original document was utf8");
let span = error.char_span();
Self {
message,
raw: Some(raw),
keys: Vec::new(),
span: Some(span),
}
}
#[cfg(any(feature = "serde", feature = "parse"))]
pub(crate) fn custom(message: String, span: Option<std::ops::Range<usize>>) -> Self {
Self {
message,
raw: None,
keys: Vec::new(),
span,
}
}
#[cfg(feature = "serde")]
pub(crate) fn add_key(&mut self, key: String) {
self.keys.insert(0, key);
}
/// What went wrong
pub fn message(&self) -> &str {
&self.message
}
/// The start/end index into the original document where the error occurred
pub fn span(&self) -> Option<std::ops::Range<usize>> {
self.span.clone()
}
#[cfg(feature = "serde")]
pub(crate) fn set_span(&mut self, span: Option<std::ops::Range<usize>>) {
self.span = span;
}
#[cfg(feature = "serde")]
pub(crate) fn set_raw(&mut self, raw: Option<String>) {
self.raw = raw;
}
}
/// Displays a TOML parse error
///
/// # Example
///
/// TOML parse error at line 1, column 10
/// |
/// 1 | 00:32:00.a999999
/// | ^
/// Unexpected `a`
/// Expected `digit`
/// While parsing a Time
/// While parsing a Date-Time
impl Display for TomlError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
let mut context = false;
if let (Some(raw), Some(span)) = (&self.raw, self.span()) {
context = true;
let (line, column) = translate_position(raw.as_bytes(), span.start);
let line_num = line + 1;
let col_num = column + 1;
let gutter = line_num.to_string().len();
let content = raw.split('\n').nth(line).expect("valid line number");
let highlight_len = span.end - span.start;
// Allow highlight to go one past the line
let highlight_len = highlight_len.min(content.len().saturating_sub(column));
writeln!(f, "TOML parse error at line {line_num}, column {col_num}")?;
// |
for _ in 0..=gutter {
write!(f, " ")?;
}
writeln!(f, "|")?;
// 1 | 00:32:00.a999999
write!(f, "{line_num} | ")?;
writeln!(f, "{content}")?;
// | ^
for _ in 0..=gutter {
write!(f, " ")?;
}
write!(f, "|")?;
for _ in 0..=column {
write!(f, " ")?;
}
// The span will be empty at eof, so we need to make sure we always print at least
// one `^`
write!(f, "^")?;
for _ in 1..highlight_len {
write!(f, "^")?;
}
writeln!(f)?;
}
writeln!(f, "{}", self.message)?;
if !context && !self.keys.is_empty() {
writeln!(f, "in `{}`", self.keys.join("."))?;
}
Ok(())
}
}
impl StdError for TomlError {
fn description(&self) -> &'static str {
"TOML parse error"
}
}
fn translate_position(input: &[u8], index: usize) -> (usize, usize) {
if input.is_empty() {
return (0, index);
}
let safe_index = index.min(input.len() - 1);
let column_offset = index - safe_index;
let index = safe_index;
let nl = input[0..index]
.iter()
.rev()
.enumerate()
.find(|(_, b)| **b == b'\n')
.map(|(nl, _)| index - nl - 1);
let line_start = match nl {
Some(nl) => nl + 1,
None => 0,
};
let line = input[0..line_start].iter().filter(|b| **b == b'\n').count();
let column = std::str::from_utf8(&input[line_start..=index])
.map(|s| s.chars().count() - 1)
.unwrap_or_else(|_| index - line_start);
let column = column + column_offset;
(line, column)
}
#[cfg(test)]
mod test_translate_position {
use super::*;
#[test]
fn empty() {
let input = b"";
let index = 0;
let position = translate_position(&input[..], index);
assert_eq!(position, (0, 0));
}
#[test]
fn start() {
let input = b"Hello";
let index = 0;
let position = translate_position(&input[..], index);
assert_eq!(position, (0, 0));
}
#[test]
fn end() {
let input = b"Hello";
let index = input.len() - 1;
let position = translate_position(&input[..], index);
assert_eq!(position, (0, input.len() - 1));
}
#[test]
fn after() {
let input = b"Hello";
let index = input.len();
let position = translate_position(&input[..], index);
assert_eq!(position, (0, input.len()));
}
#[test]
fn first_line() {
let input = b"Hello\nWorld\n";
let index = 2;
let position = translate_position(&input[..], index);
assert_eq!(position, (0, 2));
}
#[test]
fn end_of_line() {
let input = b"Hello\nWorld\n";
let index = 5;
let position = translate_position(&input[..], index);
assert_eq!(position, (0, 5));
}
#[test]
fn start_of_second_line() {
let input = b"Hello\nWorld\n";
let index = 6;
let position = translate_position(&input[..], index);
assert_eq!(position, (1, 0));
}
#[test]
fn second_line() {
let input = b"Hello\nWorld\n";
let index = 8;
let position = translate_position(&input[..], index);
assert_eq!(position, (1, 2));
}
}

142
vendor/toml_edit-0.22.27/src/index.rs vendored Normal file
View File

@@ -0,0 +1,142 @@
use std::ops;
use crate::key::Key;
use crate::DocumentMut;
use crate::{value, InlineTable, Item, Table, Value};
// copied from
// https://github.com/serde-rs/json/blob/master/src/value/index.rs
pub trait Index: crate::private::Sealed {
#[doc(hidden)]
fn index<'v>(&self, val: &'v Item) -> Option<&'v Item>;
#[doc(hidden)]
fn index_mut<'v>(&self, val: &'v mut Item) -> Option<&'v mut Item>;
}
impl Index for usize {
fn index<'v>(&self, v: &'v Item) -> Option<&'v Item> {
match *v {
Item::ArrayOfTables(ref aot) => aot.values.get(*self),
Item::Value(ref a) if a.is_array() => a.as_array().and_then(|a| a.values.get(*self)),
_ => None,
}
}
fn index_mut<'v>(&self, v: &'v mut Item) -> Option<&'v mut Item> {
match *v {
Item::ArrayOfTables(ref mut vec) => vec.values.get_mut(*self),
Item::Value(ref mut a) => a.as_array_mut().and_then(|a| a.values.get_mut(*self)),
_ => None,
}
}
}
impl Index for str {
fn index<'v>(&self, v: &'v Item) -> Option<&'v Item> {
match *v {
Item::Table(ref t) => t.get(self),
Item::Value(ref v) => v
.as_inline_table()
.and_then(|t| t.items.get(self))
.and_then(|value| if !value.is_none() { Some(value) } else { None }),
_ => None,
}
}
fn index_mut<'v>(&self, v: &'v mut Item) -> Option<&'v mut Item> {
if let Item::None = *v {
let mut t = InlineTable::default();
t.items.insert(Key::new(self), Item::None);
*v = value(Value::InlineTable(t));
}
match *v {
Item::Table(ref mut t) => Some(t.entry(self).or_insert(Item::None)),
Item::Value(ref mut v) => v
.as_inline_table_mut()
.map(|t| t.items.entry(Key::new(self)).or_insert_with(|| Item::None)),
_ => None,
}
}
}
impl Index for String {
fn index<'v>(&self, v: &'v Item) -> Option<&'v Item> {
self[..].index(v)
}
fn index_mut<'v>(&self, v: &'v mut Item) -> Option<&'v mut Item> {
self[..].index_mut(v)
}
}
impl<T: ?Sized> Index for &T
where
T: Index,
{
fn index<'v>(&self, v: &'v Item) -> Option<&'v Item> {
(**self).index(v)
}
fn index_mut<'v>(&self, v: &'v mut Item) -> Option<&'v mut Item> {
(**self).index_mut(v)
}
}
impl<I> ops::Index<I> for Item
where
I: Index,
{
type Output = Item;
fn index(&self, index: I) -> &Item {
index.index(self).expect("index not found")
}
}
impl<I> ops::IndexMut<I> for Item
where
I: Index,
{
fn index_mut(&mut self, index: I) -> &mut Item {
index.index_mut(self).expect("index not found")
}
}
impl<'s> ops::Index<&'s str> for Table {
type Output = Item;
fn index(&self, key: &'s str) -> &Item {
self.get(key).expect("index not found")
}
}
impl<'s> ops::IndexMut<&'s str> for Table {
fn index_mut(&mut self, key: &'s str) -> &mut Item {
self.entry(key).or_insert(Item::None)
}
}
impl<'s> ops::Index<&'s str> for InlineTable {
type Output = Value;
fn index(&self, key: &'s str) -> &Value {
self.get(key).expect("index not found")
}
}
impl<'s> ops::IndexMut<&'s str> for InlineTable {
fn index_mut(&mut self, key: &'s str) -> &mut Value {
self.get_mut(key).expect("index not found")
}
}
impl<'s> ops::Index<&'s str> for DocumentMut {
type Output = Item;
fn index(&self, key: &'s str) -> &Item {
self.root.index(key)
}
}
impl<'s> ops::IndexMut<&'s str> for DocumentMut {
fn index_mut(&mut self, key: &'s str) -> &mut Item {
self.root.index_mut(key)
}
}

View File

@@ -0,0 +1,756 @@
use std::iter::FromIterator;
use crate::key::Key;
use crate::repr::Decor;
use crate::table::{Iter, IterMut, KeyValuePairs, TableLike};
use crate::{InternalString, Item, KeyMut, RawString, Table, Value};
/// A TOML [`Value`] that contains a collection of [`Key`]/[`Value`] pairs
#[derive(Debug, Default, Clone)]
pub struct InlineTable {
// `preamble` represents whitespaces in an empty table
preamble: RawString,
// Whether to hide an empty table
pub(crate) implicit: bool,
// prefix before `{` and suffix after `}`
decor: Decor,
pub(crate) span: Option<std::ops::Range<usize>>,
// whether this is a proxy for dotted keys
dotted: bool,
pub(crate) items: KeyValuePairs,
}
/// Constructors
///
/// See also `FromIterator`
impl InlineTable {
/// Creates an empty table.
pub fn new() -> Self {
Default::default()
}
pub(crate) fn with_pairs(items: KeyValuePairs) -> Self {
Self {
items,
..Default::default()
}
}
/// Convert to a table
pub fn into_table(self) -> Table {
let mut t = Table::with_pairs(self.items);
t.fmt();
t
}
}
/// Formatting
impl InlineTable {
/// Get key/values for values that are visually children of this table
///
/// For example, this will return dotted keys
pub fn get_values(&self) -> Vec<(Vec<&Key>, &Value)> {
let mut values = Vec::new();
let root = Vec::new();
self.append_values(&root, &mut values);
values
}
pub(crate) fn append_values<'s>(
&'s self,
parent: &[&'s Key],
values: &mut Vec<(Vec<&'s Key>, &'s Value)>,
) {
for (key, value) in self.items.iter() {
let mut path = parent.to_vec();
path.push(key);
match value {
Item::Value(Value::InlineTable(table)) if table.is_dotted() => {
table.append_values(&path, values);
}
Item::Value(value) => {
values.push((path, value));
}
_ => {}
}
}
}
/// Auto formats the table.
pub fn fmt(&mut self) {
decorate_inline_table(self);
}
/// Sorts [Key]/[Value]-pairs of the table
///
/// <div class="warning">
///
/// This is not recursive.
///
/// </div>
pub fn sort_values(&mut self) {
// Assuming standard tables have their position set and this won't negatively impact them
self.items.sort_keys();
for value in self.items.values_mut() {
match value {
Item::Value(Value::InlineTable(table)) if table.is_dotted() => {
table.sort_values();
}
_ => {}
}
}
}
/// Sort [Key]/[Value]-pairs of the table using the using the comparison function `compare`
///
/// The comparison function receives two key and value pairs to compare (you can sort by keys or
/// values or their combination as needed).
///
/// <div class="warning">
///
/// This is not recursive.
///
/// </div>
pub fn sort_values_by<F>(&mut self, mut compare: F)
where
F: FnMut(&Key, &Value, &Key, &Value) -> std::cmp::Ordering,
{
self.sort_values_by_internal(&mut compare);
}
fn sort_values_by_internal<F>(&mut self, compare: &mut F)
where
F: FnMut(&Key, &Value, &Key, &Value) -> std::cmp::Ordering,
{
let modified_cmp =
|key1: &Key, val1: &Item, key2: &Key, val2: &Item| -> std::cmp::Ordering {
match (val1.as_value(), val2.as_value()) {
(Some(v1), Some(v2)) => compare(key1, v1, key2, v2),
(Some(_), None) => std::cmp::Ordering::Greater,
(None, Some(_)) => std::cmp::Ordering::Less,
(None, None) => std::cmp::Ordering::Equal,
}
};
self.items.sort_by(modified_cmp);
for value in self.items.values_mut() {
match value {
Item::Value(Value::InlineTable(table)) if table.is_dotted() => {
table.sort_values_by_internal(compare);
}
_ => {}
}
}
}
/// If a table has no key/value pairs and implicit, it will not be displayed.
///
/// # Examples
///
/// ```notrust
/// [target."x86_64/windows.json".dependencies]
/// ```
///
/// In the document above, tables `target` and `target."x86_64/windows.json"` are implicit.
///
/// ```
/// # #[cfg(feature = "parse")] {
/// # #[cfg(feature = "display")] {
/// use toml_edit::DocumentMut;
/// let mut doc = "[a]\n[a.b]\n".parse::<DocumentMut>().expect("invalid toml");
///
/// doc["a"].as_table_mut().unwrap().set_implicit(true);
/// assert_eq!(doc.to_string(), "[a.b]\n");
/// # }
/// # }
/// ```
pub(crate) fn set_implicit(&mut self, implicit: bool) {
self.implicit = implicit;
}
/// If a table has no key/value pairs and implicit, it will not be displayed.
pub(crate) fn is_implicit(&self) -> bool {
self.implicit
}
/// Change this table's dotted status
pub fn set_dotted(&mut self, yes: bool) {
self.dotted = yes;
}
/// Check if this is a wrapper for dotted keys, rather than a standard table
pub fn is_dotted(&self) -> bool {
self.dotted
}
/// Returns the surrounding whitespace
pub fn decor_mut(&mut self) -> &mut Decor {
&mut self.decor
}
/// Returns the surrounding whitespace
pub fn decor(&self) -> &Decor {
&self.decor
}
/// Returns an accessor to a key's formatting
pub fn key(&self, key: &str) -> Option<&'_ Key> {
self.items.get_full(key).map(|(_, key, _)| key)
}
/// Returns an accessor to a key's formatting
pub fn key_mut(&mut self, key: &str) -> Option<KeyMut<'_>> {
use indexmap::map::MutableKeys;
self.items
.get_full_mut2(key)
.map(|(_, key, _)| key.as_mut())
}
/// Returns the decor associated with a given key of the table.
#[deprecated(since = "0.21.1", note = "Replaced with `key_mut`")]
pub fn key_decor_mut(&mut self, key: &str) -> Option<&mut Decor> {
#![allow(deprecated)]
use indexmap::map::MutableKeys;
self.items
.get_full_mut2(key)
.map(|(_, key, _)| key.leaf_decor_mut())
}
/// Returns the decor associated with a given key of the table.
#[deprecated(since = "0.21.1", note = "Replaced with `key_mut`")]
pub fn key_decor(&self, key: &str) -> Option<&Decor> {
#![allow(deprecated)]
self.items.get_full(key).map(|(_, key, _)| key.leaf_decor())
}
/// Set whitespace after before element
pub fn set_preamble(&mut self, preamble: impl Into<RawString>) {
self.preamble = preamble.into();
}
/// Whitespace after before element
pub fn preamble(&self) -> &RawString {
&self.preamble
}
/// The location within the original document
///
/// This generally requires an [`ImDocument`][crate::ImDocument].
pub fn span(&self) -> Option<std::ops::Range<usize>> {
self.span.clone()
}
pub(crate) fn despan(&mut self, input: &str) {
use indexmap::map::MutableKeys;
self.span = None;
self.decor.despan(input);
self.preamble.despan(input);
for (key, value) in self.items.iter_mut2() {
key.despan(input);
value.despan(input);
}
}
}
impl InlineTable {
/// Returns an iterator over key/value pairs.
pub fn iter(&self) -> InlineTableIter<'_> {
Box::new(
self.items
.iter()
.filter(|(_, value)| !value.is_none())
.map(|(key, value)| (key.get(), value.as_value().unwrap())),
)
}
/// Returns an iterator over key/value pairs.
pub fn iter_mut(&mut self) -> InlineTableIterMut<'_> {
use indexmap::map::MutableKeys;
Box::new(
self.items
.iter_mut2()
.filter(|(_, value)| value.is_value())
.map(|(key, value)| (key.as_mut(), value.as_value_mut().unwrap())),
)
}
/// Returns the number of key/value pairs.
pub fn len(&self) -> usize {
self.iter().count()
}
/// Returns true if the table is empty.
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Clears the table, removing all key-value pairs. Keeps the allocated memory for reuse.
pub fn clear(&mut self) {
self.items.clear();
}
/// Gets the given key's corresponding entry in the Table for in-place manipulation.
pub fn entry(&'_ mut self, key: impl Into<InternalString>) -> InlineEntry<'_> {
match self.items.entry(key.into().into()) {
indexmap::map::Entry::Occupied(mut entry) => {
// Ensure it is a `Value` to simplify `InlineOccupiedEntry`'s code.
let scratch = std::mem::take(entry.get_mut());
let scratch = Item::Value(
scratch
.into_value()
// HACK: `Item::None` is a corner case of a corner case, let's just pick a
// "safe" value
.unwrap_or_else(|_| Value::InlineTable(Default::default())),
);
*entry.get_mut() = scratch;
InlineEntry::Occupied(InlineOccupiedEntry { entry })
}
indexmap::map::Entry::Vacant(entry) => InlineEntry::Vacant(InlineVacantEntry { entry }),
}
}
/// Gets the given key's corresponding entry in the Table for in-place manipulation.
pub fn entry_format<'a>(&'a mut self, key: &Key) -> InlineEntry<'a> {
// Accept a `&Key` to be consistent with `entry`
match self.items.entry(key.clone()) {
indexmap::map::Entry::Occupied(mut entry) => {
// Ensure it is a `Value` to simplify `InlineOccupiedEntry`'s code.
let scratch = std::mem::take(entry.get_mut());
let scratch = Item::Value(
scratch
.into_value()
// HACK: `Item::None` is a corner case of a corner case, let's just pick a
// "safe" value
.unwrap_or_else(|_| Value::InlineTable(Default::default())),
);
*entry.get_mut() = scratch;
InlineEntry::Occupied(InlineOccupiedEntry { entry })
}
indexmap::map::Entry::Vacant(entry) => InlineEntry::Vacant(InlineVacantEntry { entry }),
}
}
/// Return an optional reference to the value at the given the key.
pub fn get(&self, key: &str) -> Option<&Value> {
self.items.get(key).and_then(|value| value.as_value())
}
/// Return an optional mutable reference to the value at the given the key.
pub fn get_mut(&mut self, key: &str) -> Option<&mut Value> {
self.items
.get_mut(key)
.and_then(|value| value.as_value_mut())
}
/// Return references to the key-value pair stored for key, if it is present, else None.
pub fn get_key_value<'a>(&'a self, key: &str) -> Option<(&'a Key, &'a Item)> {
self.items.get_full(key).and_then(|(_, key, value)| {
if !value.is_none() {
Some((key, value))
} else {
None
}
})
}
/// Return mutable references to the key-value pair stored for key, if it is present, else None.
pub fn get_key_value_mut<'a>(&'a mut self, key: &str) -> Option<(KeyMut<'a>, &'a mut Item)> {
use indexmap::map::MutableKeys;
self.items.get_full_mut2(key).and_then(|(_, key, value)| {
if !value.is_none() {
Some((key.as_mut(), value))
} else {
None
}
})
}
/// Returns true if the table contains given key.
pub fn contains_key(&self, key: &str) -> bool {
if let Some(value) = self.items.get(key) {
value.is_value()
} else {
false
}
}
/// Inserts a key/value pair if the table does not contain the key.
/// Returns a mutable reference to the corresponding value.
pub fn get_or_insert<V: Into<Value>>(
&mut self,
key: impl Into<InternalString>,
value: V,
) -> &mut Value {
let key = key.into();
self.items
.entry(Key::new(key))
.or_insert(Item::Value(value.into()))
.as_value_mut()
.expect("non-value type in inline table")
}
/// Inserts a key-value pair into the map.
pub fn insert(&mut self, key: impl Into<InternalString>, value: Value) -> Option<Value> {
use indexmap::map::MutableEntryKey;
let key = Key::new(key);
let value = Item::Value(value);
match self.items.entry(key.clone()) {
indexmap::map::Entry::Occupied(mut entry) => {
entry.key_mut().fmt();
let old = std::mem::replace(entry.get_mut(), value);
old.into_value().ok()
}
indexmap::map::Entry::Vacant(entry) => {
entry.insert(value);
None
}
}
}
/// Inserts a key-value pair into the map.
pub fn insert_formatted(&mut self, key: &Key, value: Value) -> Option<Value> {
use indexmap::map::MutableEntryKey;
let value = Item::Value(value);
match self.items.entry(key.clone()) {
indexmap::map::Entry::Occupied(mut entry) => {
*entry.key_mut() = key.clone();
let old = std::mem::replace(entry.get_mut(), value);
old.into_value().ok()
}
indexmap::map::Entry::Vacant(entry) => {
entry.insert(value);
None
}
}
}
/// Removes an item given the key.
pub fn remove(&mut self, key: &str) -> Option<Value> {
self.items
.shift_remove(key)
.and_then(|value| value.into_value().ok())
}
/// Removes a key from the map, returning the stored key and value if the key was previously in the map.
pub fn remove_entry(&mut self, key: &str) -> Option<(Key, Value)> {
self.items
.shift_remove_entry(key)
.and_then(|(key, value)| Some((key, value.into_value().ok()?)))
}
/// Retains only the elements specified by the `keep` predicate.
///
/// In other words, remove all pairs `(key, value)` for which
/// `keep(&key, &mut value)` returns `false`.
///
/// The elements are visited in iteration order.
pub fn retain<F>(&mut self, mut keep: F)
where
F: FnMut(&str, &mut Value) -> bool,
{
self.items.retain(|key, item| {
item.as_value_mut()
.map(|value| keep(key, value))
.unwrap_or(false)
});
}
}
#[cfg(feature = "display")]
impl std::fmt::Display for InlineTable {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
crate::encode::encode_table(self, f, None, ("", ""))
}
}
impl<K: Into<Key>, V: Into<Value>> Extend<(K, V)> for InlineTable {
fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) {
for (key, value) in iter {
let key = key.into();
let value = Item::Value(value.into());
self.items.insert(key, value);
}
}
}
impl<K: Into<Key>, V: Into<Value>> FromIterator<(K, V)> for InlineTable {
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = (K, V)>,
{
let mut table = InlineTable::new();
table.extend(iter);
table
}
}
impl IntoIterator for InlineTable {
type Item = (InternalString, Value);
type IntoIter = InlineTableIntoIter;
fn into_iter(self) -> Self::IntoIter {
Box::new(
self.items
.into_iter()
.filter(|(_, value)| value.is_value())
.map(|(key, value)| (key.into(), value.into_value().unwrap())),
)
}
}
impl<'s> IntoIterator for &'s InlineTable {
type Item = (&'s str, &'s Value);
type IntoIter = InlineTableIter<'s>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
fn decorate_inline_table(table: &mut InlineTable) {
use indexmap::map::MutableKeys;
for (mut key, value) in table
.items
.iter_mut2()
.filter(|(_, value)| value.is_value())
.map(|(key, value)| (key.as_mut(), value.as_value_mut().unwrap()))
{
key.leaf_decor_mut().clear();
key.dotted_decor_mut().clear();
value.decor_mut().clear();
}
}
/// An owned iterator type over an [`InlineTable`]'s [`Key`]/[`Value`] pairs
pub type InlineTableIntoIter = Box<dyn Iterator<Item = (InternalString, Value)>>;
/// An iterator type over [`InlineTable`]'s [`Key`]/[`Value`] pairs
pub type InlineTableIter<'a> = Box<dyn Iterator<Item = (&'a str, &'a Value)> + 'a>;
/// A mutable iterator type over [`InlineTable`]'s [`Key`]/[`Value`] pairs
pub type InlineTableIterMut<'a> = Box<dyn Iterator<Item = (KeyMut<'a>, &'a mut Value)> + 'a>;
impl TableLike for InlineTable {
fn iter(&self) -> Iter<'_> {
Box::new(self.items.iter().map(|(key, value)| (key.get(), value)))
}
fn iter_mut(&mut self) -> IterMut<'_> {
use indexmap::map::MutableKeys;
Box::new(
self.items
.iter_mut2()
.map(|(key, value)| (key.as_mut(), value)),
)
}
fn clear(&mut self) {
self.clear();
}
fn entry<'a>(&'a mut self, key: &str) -> crate::Entry<'a> {
// Accept a `&str` rather than an owned type to keep `InternalString`, well, internal
match self.items.entry(key.into()) {
indexmap::map::Entry::Occupied(entry) => {
crate::Entry::Occupied(crate::OccupiedEntry { entry })
}
indexmap::map::Entry::Vacant(entry) => {
crate::Entry::Vacant(crate::VacantEntry { entry })
}
}
}
fn entry_format<'a>(&'a mut self, key: &Key) -> crate::Entry<'a> {
// Accept a `&Key` to be consistent with `entry`
match self.items.entry(key.get().into()) {
indexmap::map::Entry::Occupied(entry) => {
crate::Entry::Occupied(crate::OccupiedEntry { entry })
}
indexmap::map::Entry::Vacant(entry) => {
crate::Entry::Vacant(crate::VacantEntry { entry })
}
}
}
fn get<'s>(&'s self, key: &str) -> Option<&'s Item> {
self.items.get(key)
}
fn get_mut<'s>(&'s mut self, key: &str) -> Option<&'s mut Item> {
self.items.get_mut(key)
}
fn get_key_value<'a>(&'a self, key: &str) -> Option<(&'a Key, &'a Item)> {
self.get_key_value(key)
}
fn get_key_value_mut<'a>(&'a mut self, key: &str) -> Option<(KeyMut<'a>, &'a mut Item)> {
self.get_key_value_mut(key)
}
fn contains_key(&self, key: &str) -> bool {
self.contains_key(key)
}
fn insert(&mut self, key: &str, value: Item) -> Option<Item> {
self.insert(key, value.into_value().unwrap())
.map(Item::Value)
}
fn remove(&mut self, key: &str) -> Option<Item> {
self.remove(key).map(Item::Value)
}
fn get_values(&self) -> Vec<(Vec<&Key>, &Value)> {
self.get_values()
}
fn fmt(&mut self) {
self.fmt();
}
fn sort_values(&mut self) {
self.sort_values();
}
fn set_dotted(&mut self, yes: bool) {
self.set_dotted(yes);
}
fn is_dotted(&self) -> bool {
self.is_dotted()
}
fn key(&self, key: &str) -> Option<&'_ Key> {
self.key(key)
}
fn key_mut(&mut self, key: &str) -> Option<KeyMut<'_>> {
self.key_mut(key)
}
fn key_decor_mut(&mut self, key: &str) -> Option<&mut Decor> {
#![allow(deprecated)]
self.key_decor_mut(key)
}
fn key_decor(&self, key: &str) -> Option<&Decor> {
#![allow(deprecated)]
self.key_decor(key)
}
}
// `{ key1 = value1, ... }`
pub(crate) const DEFAULT_INLINE_KEY_DECOR: (&str, &str) = (" ", " ");
/// A view into a single location in an [`InlineTable`], which may be vacant or occupied.
pub enum InlineEntry<'a> {
/// An occupied Entry.
Occupied(InlineOccupiedEntry<'a>),
/// A vacant Entry.
Vacant(InlineVacantEntry<'a>),
}
impl<'a> InlineEntry<'a> {
/// Returns the entry key
///
/// # Examples
///
/// ```
/// use toml_edit::Table;
///
/// let mut map = Table::new();
///
/// assert_eq!("hello", map.entry("hello").key());
/// ```
pub fn key(&self) -> &str {
match self {
InlineEntry::Occupied(e) => e.key(),
InlineEntry::Vacant(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 {
InlineEntry::Occupied(entry) => entry.into_mut(),
InlineEntry::Vacant(entry) => entry.insert(default),
}
}
/// 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: FnOnce() -> Value>(self, default: F) -> &'a mut Value {
match self {
InlineEntry::Occupied(entry) => entry.into_mut(),
InlineEntry::Vacant(entry) => entry.insert(default()),
}
}
}
/// A view into a single occupied location in an [`InlineTable`].
pub struct InlineOccupiedEntry<'a> {
entry: indexmap::map::OccupiedEntry<'a, Key, Item>,
}
impl<'a> InlineOccupiedEntry<'a> {
/// Gets a reference to the entry key
///
/// # Examples
///
/// ```
/// use toml_edit::Table;
///
/// let mut map = Table::new();
///
/// assert_eq!("foo", map.entry("foo").key());
/// ```
pub fn key(&self) -> &str {
self.entry.key().get()
}
/// Gets a mutable reference to the entry key
pub fn key_mut(&mut self) -> KeyMut<'_> {
use indexmap::map::MutableEntryKey;
self.entry.key_mut().as_mut()
}
/// Gets a reference to the value in the entry.
pub fn get(&self) -> &Value {
self.entry.get().as_value().unwrap()
}
/// Gets a mutable reference to the value in the entry.
pub fn get_mut(&mut self) -> &mut Value {
self.entry.get_mut().as_value_mut().unwrap()
}
/// Converts the `OccupiedEntry` into a mutable reference to the value in the entry
/// with a lifetime bound to the map itself
pub fn into_mut(self) -> &'a mut Value {
self.entry.into_mut().as_value_mut().unwrap()
}
/// Sets the value of the entry, and returns the entry's old value
pub fn insert(&mut self, value: Value) -> Value {
let value = Item::Value(value);
self.entry.insert(value).into_value().unwrap()
}
/// Takes the value out of the entry, and returns it
pub fn remove(self) -> Value {
self.entry.shift_remove().into_value().unwrap()
}
}
/// A view into a single empty location in an [`InlineTable`].
pub struct InlineVacantEntry<'a> {
entry: indexmap::map::VacantEntry<'a, Key, Item>,
}
impl<'a> InlineVacantEntry<'a> {
/// Gets a reference to the entry key
///
/// # Examples
///
/// ```
/// use toml_edit::Table;
///
/// let mut map = Table::new();
///
/// assert_eq!("foo", map.entry("foo").key());
/// ```
pub fn key(&self) -> &str {
self.entry.key().get()
}
/// Sets the value of the entry with the `VacantEntry`'s key,
/// and returns a mutable reference to it
pub fn insert(self, value: Value) -> &'a mut Value {
let entry = self.entry;
let value = Item::Value(value);
entry.insert(value).as_value_mut().unwrap()
}
}

View File

@@ -0,0 +1,183 @@
use std::borrow::Borrow;
use std::str::FromStr;
/// Opaque string storage internal to `toml_edit`
#[derive(Default, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct InternalString(Inner);
#[cfg(feature = "perf")]
type Inner = kstring::KString;
#[cfg(not(feature = "perf"))]
type Inner = String;
impl InternalString {
/// Create an empty string
pub fn new() -> Self {
InternalString(Inner::new())
}
/// Access the underlying string
#[inline]
pub fn as_str(&self) -> &str {
self.0.as_str()
}
}
impl std::fmt::Debug for InternalString {
#[inline]
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
self.0.fmt(formatter)
}
}
impl std::ops::Deref for InternalString {
type Target = str;
#[inline]
fn deref(&self) -> &str {
self.as_str()
}
}
impl Borrow<str> for InternalString {
#[inline]
fn borrow(&self) -> &str {
self.as_str()
}
}
impl AsRef<str> for InternalString {
#[inline]
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl From<&str> for InternalString {
#[inline]
fn from(s: &str) -> Self {
#[cfg(feature = "perf")]
let inner = kstring::KString::from_ref(s);
#[cfg(not(feature = "perf"))]
let inner = String::from(s);
InternalString(inner)
}
}
impl From<String> for InternalString {
#[inline]
fn from(s: String) -> Self {
#[allow(clippy::useless_conversion)] // handle any string type
InternalString(s.into())
}
}
impl From<&String> for InternalString {
#[inline]
fn from(s: &String) -> Self {
InternalString(s.into())
}
}
impl From<&InternalString> for InternalString {
#[inline]
fn from(s: &InternalString) -> Self {
s.clone()
}
}
impl From<Box<str>> for InternalString {
#[inline]
fn from(s: Box<str>) -> Self {
InternalString(s.into())
}
}
impl FromStr for InternalString {
type Err = core::convert::Infallible;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self::from(s))
}
}
impl std::fmt::Display for InternalString {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.as_str().fmt(f)
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for InternalString {
#[inline]
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.as_str())
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for InternalString {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_string(StringVisitor)
}
}
#[cfg(feature = "serde")]
struct StringVisitor;
#[cfg(feature = "serde")]
impl serde::de::Visitor<'_> for StringVisitor {
type Value = InternalString;
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("a string")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(InternalString::from(v))
}
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(InternalString::from(v))
}
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
match std::str::from_utf8(v) {
Ok(s) => Ok(InternalString::from(s)),
Err(_) => Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Bytes(v),
&self,
)),
}
}
fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
match String::from_utf8(v) {
Ok(s) => Ok(InternalString::from(s)),
Err(e) => Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Bytes(&e.into_bytes()),
&self,
)),
}
}
}

450
vendor/toml_edit-0.22.27/src/item.rs vendored Normal file
View File

@@ -0,0 +1,450 @@
use std::str::FromStr;
use toml_datetime::Datetime;
use crate::array_of_tables::ArrayOfTables;
use crate::table::TableLike;
use crate::{Array, InlineTable, Table, Value};
/// Type representing either a value, a table, an array of tables, or none.
#[derive(Debug, Default)]
pub enum Item {
/// Type representing none.
#[default]
None,
/// Type representing value.
Value(Value),
/// Type representing table.
Table(Table),
/// Type representing array of tables.
ArrayOfTables(ArrayOfTables),
}
impl Item {
/// Sets `self` to the given item if `self` is none and
/// returns a mutable reference to `self`.
pub fn or_insert(&mut self, item: Item) -> &mut Item {
if self.is_none() {
*self = item;
}
self
}
}
// TODO: This should be generated by macro or derive
/// Downcasting
impl Item {
/// Text description of value type
pub fn type_name(&self) -> &'static str {
match self {
Item::None => "none",
Item::Value(v) => v.type_name(),
Item::Table(..) => "table",
Item::ArrayOfTables(..) => "array of tables",
}
}
/// 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.
/// - 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: crate::index::Index>(&self, index: I) -> Option<&Item> {
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.
/// - 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: crate::index::Index>(&mut self, index: I) -> Option<&mut Item> {
index.index_mut(self)
}
/// Casts `self` to [`Value`]
pub fn as_value(&self) -> Option<&Value> {
match *self {
Item::Value(ref v) => Some(v),
_ => None,
}
}
/// Casts `self` to [`Table`]
///
/// <div class="warning">
///
/// To operate on both [`Table`]s and [`InlineTable`]s, see [`Item::as_table_like`]
///
/// </div>
pub fn as_table(&self) -> Option<&Table> {
match *self {
Item::Table(ref t) => Some(t),
_ => None,
}
}
/// Casts `self` to [`ArrayOfTables`]
pub fn as_array_of_tables(&self) -> Option<&ArrayOfTables> {
match *self {
Item::ArrayOfTables(ref a) => Some(a),
_ => None,
}
}
/// Casts `self` to mutable [`Value`].
pub fn as_value_mut(&mut self) -> Option<&mut Value> {
match *self {
Item::Value(ref mut v) => Some(v),
_ => None,
}
}
/// Casts `self` to mutable [`Table`]
///
/// <div class="warning">
///
/// To operate on both [`Table`]s and [`InlineTable`]s, see [`Item::as_table_like_mut`]
///
/// </div>
pub fn as_table_mut(&mut self) -> Option<&mut Table> {
match *self {
Item::Table(ref mut t) => Some(t),
_ => None,
}
}
/// Casts `self` to mutable [`ArrayOfTables`]
pub fn as_array_of_tables_mut(&mut self) -> Option<&mut ArrayOfTables> {
match *self {
Item::ArrayOfTables(ref mut a) => Some(a),
_ => None,
}
}
/// Casts `self` to [`Value`]
pub fn into_value(self) -> Result<Value, Self> {
match self {
Item::None => Err(self),
Item::Value(v) => Ok(v),
Item::Table(v) => {
let v = v.into_inline_table();
Ok(Value::InlineTable(v))
}
Item::ArrayOfTables(v) => {
let v = v.into_array();
Ok(Value::Array(v))
}
}
}
/// In-place convert to a value
pub fn make_value(&mut self) {
let other = std::mem::take(self);
let other = other.into_value().map(Item::Value).unwrap_or(Item::None);
*self = other;
}
/// Casts `self` to [`Table`]
///
/// <div class="warning">
///
/// This does not include [`InlineTable`]s
///
/// </div>
pub fn into_table(self) -> Result<Table, Self> {
match self {
Item::Table(t) => Ok(t),
Item::Value(Value::InlineTable(t)) => Ok(t.into_table()),
_ => Err(self),
}
}
/// Casts `self` to [`ArrayOfTables`]
pub fn into_array_of_tables(self) -> Result<ArrayOfTables, Self> {
match self {
Item::ArrayOfTables(a) => Ok(a),
Item::Value(Value::Array(a)) => {
if a.is_empty() {
Err(Item::Value(Value::Array(a)))
} else if a.iter().all(|v| v.is_inline_table()) {
let mut aot = ArrayOfTables::new();
aot.values = a.values;
for value in aot.values.iter_mut() {
value.make_item();
}
Ok(aot)
} else {
Err(Item::Value(Value::Array(a)))
}
}
_ => Err(self),
}
}
// Starting private because the name is unclear
pub(crate) fn make_item(&mut self) {
let other = std::mem::take(self);
let other = match other.into_table().map(Item::Table) {
Ok(i) => i,
Err(i) => i,
};
let other = match other.into_array_of_tables().map(Item::ArrayOfTables) {
Ok(i) => i,
Err(i) => i,
};
*self = other;
}
/// Returns true if `self` is a [`Value`]
pub fn is_value(&self) -> bool {
self.as_value().is_some()
}
/// Returns true if `self` is a [`Table`]
///
/// <div class="warning">
///
/// To operate on both [`Table`]s and [`InlineTable`]s, see [`Item::is_table_like`]
///
/// </div>
pub fn is_table(&self) -> bool {
self.as_table().is_some()
}
/// Returns true if `self` is an [`ArrayOfTables`]
pub fn is_array_of_tables(&self) -> bool {
self.as_array_of_tables().is_some()
}
/// Returns true if `self` is `None`.
pub fn is_none(&self) -> bool {
matches!(*self, Item::None)
}
// Duplicate Value downcasting API
/// Casts `self` to integer.
pub fn as_integer(&self) -> Option<i64> {
self.as_value().and_then(Value::as_integer)
}
/// Returns true if `self` is an integer.
pub fn is_integer(&self) -> bool {
self.as_integer().is_some()
}
/// Casts `self` to float.
pub fn as_float(&self) -> Option<f64> {
self.as_value().and_then(Value::as_float)
}
/// Returns true if `self` is a float.
pub fn is_float(&self) -> bool {
self.as_float().is_some()
}
/// Casts `self` to boolean.
pub fn as_bool(&self) -> Option<bool> {
self.as_value().and_then(Value::as_bool)
}
/// Returns true if `self` is a boolean.
pub fn is_bool(&self) -> bool {
self.as_bool().is_some()
}
/// Casts `self` to str.
pub fn as_str(&self) -> Option<&str> {
self.as_value().and_then(Value::as_str)
}
/// Returns true if `self` is a string.
pub fn is_str(&self) -> bool {
self.as_str().is_some()
}
/// Casts `self` to date-time.
pub fn as_datetime(&self) -> Option<&Datetime> {
self.as_value().and_then(Value::as_datetime)
}
/// Returns true if `self` is a date-time.
pub fn is_datetime(&self) -> bool {
self.as_datetime().is_some()
}
/// Casts `self` to array.
pub fn as_array(&self) -> Option<&Array> {
self.as_value().and_then(Value::as_array)
}
/// Casts `self` to mutable array.
pub fn as_array_mut(&mut self) -> Option<&mut Array> {
self.as_value_mut().and_then(Value::as_array_mut)
}
/// Returns true if `self` is an array.
pub fn is_array(&self) -> bool {
self.as_array().is_some()
}
/// Casts `self` to inline table.
pub fn as_inline_table(&self) -> Option<&InlineTable> {
self.as_value().and_then(Value::as_inline_table)
}
/// Casts `self` to mutable inline table.
pub fn as_inline_table_mut(&mut self) -> Option<&mut InlineTable> {
self.as_value_mut().and_then(Value::as_inline_table_mut)
}
/// Returns true if `self` is an inline table.
pub fn is_inline_table(&self) -> bool {
self.as_inline_table().is_some()
}
/// Casts `self` to either a table or an inline table.
pub fn as_table_like(&self) -> Option<&dyn TableLike> {
self.as_table()
.map(|t| t as &dyn TableLike)
.or_else(|| self.as_inline_table().map(|t| t as &dyn TableLike))
}
/// Casts `self` to either a table or an inline table.
pub fn as_table_like_mut(&mut self) -> Option<&mut dyn TableLike> {
match self {
Item::Table(t) => Some(t as &mut dyn TableLike),
Item::Value(Value::InlineTable(t)) => Some(t as &mut dyn TableLike),
_ => None,
}
}
/// Returns true if `self` is either a table, or an inline table.
pub fn is_table_like(&self) -> bool {
self.as_table_like().is_some()
}
/// The location within the original document
///
/// This generally requires an [`ImDocument`][crate::ImDocument].
pub fn span(&self) -> Option<std::ops::Range<usize>> {
match self {
Item::None => None,
Item::Value(v) => v.span(),
Item::Table(v) => v.span(),
Item::ArrayOfTables(v) => v.span(),
}
}
pub(crate) fn despan(&mut self, input: &str) {
match self {
Item::None => {}
Item::Value(v) => v.despan(input),
Item::Table(v) => v.despan(input),
Item::ArrayOfTables(v) => v.despan(input),
}
}
}
impl Clone for Item {
#[inline(never)]
fn clone(&self) -> Self {
match self {
Item::None => Item::None,
Item::Value(v) => Item::Value(v.clone()),
Item::Table(v) => Item::Table(v.clone()),
Item::ArrayOfTables(v) => Item::ArrayOfTables(v.clone()),
}
}
}
#[cfg(feature = "parse")]
impl FromStr for Item {
type Err = crate::TomlError;
/// Parses a value from a &str
fn from_str(s: &str) -> Result<Self, Self::Err> {
let value = s.parse::<Value>()?;
Ok(Item::Value(value))
}
}
impl<'b> From<&'b Item> for Item {
fn from(s: &'b Item) -> Self {
s.clone()
}
}
impl From<Table> for Item {
fn from(s: Table) -> Self {
Item::Table(s)
}
}
impl From<ArrayOfTables> for Item {
fn from(s: ArrayOfTables) -> Self {
Item::ArrayOfTables(s)
}
}
impl<V: Into<Value>> From<V> for Item {
fn from(s: V) -> Self {
Item::Value(s.into())
}
}
#[cfg(feature = "display")]
impl std::fmt::Display for Item {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self {
Item::None => Ok(()),
Item::Value(v) => v.fmt(f),
Item::Table(v) => v.fmt(f),
Item::ArrayOfTables(v) => v.fmt(f),
}
}
}
/// Returns a formatted value.
///
/// Since formatting is part of a `Value`, the right hand side of the
/// assignment needs to be decorated with a space before the value.
/// The `value` function does just that.
///
/// # Examples
/// ```rust
/// # #[cfg(feature = "display")] {
/// # #[cfg(feature = "parse")] {
/// # use toml_edit::*;
/// let mut table = Table::default();
/// let mut array = Array::default();
/// array.push("hello");
/// array.push("\\, world"); // \ is only allowed in a literal string
/// table["key1"] = value("value1");
/// table["key2"] = value(42);
/// table["key3"] = value(array);
/// assert_eq!(table.to_string(),
/// r#"key1 = "value1"
/// key2 = 42
/// key3 = ["hello", '\, world']
/// "#);
/// # }
/// # }
/// ```
pub fn value<V: Into<Value>>(v: V) -> Item {
Item::Value(v.into())
}
/// Returns an empty table.
pub fn table() -> Item {
Item::Table(Table::new())
}
/// Returns an empty array of tables.
pub fn array() -> Item {
Item::ArrayOfTables(ArrayOfTables::new())
}
#[test]
#[cfg(feature = "parse")]
#[cfg(feature = "display")]
fn string_roundtrip() {
value("hello").to_string().parse::<Item>().unwrap();
}

430
vendor/toml_edit-0.22.27/src/key.rs vendored Normal file
View File

@@ -0,0 +1,430 @@
use std::borrow::Cow;
use std::str::FromStr;
#[cfg(feature = "display")]
use toml_write::ToTomlKey as _;
use crate::repr::{Decor, Repr};
use crate::InternalString;
/// For Key/[`Value`][crate::Value] pairs under a [`Table`][crate::Table] header or inside an
/// [`InlineTable`][crate::InlineTable]
///
/// # Examples
///
/// ```notrust
/// [dependencies."nom"]
/// version = "5.0"
/// 'literal key' = "nonsense"
/// "basic string key" = 42
/// ```
///
/// There are 3 types of keys:
///
/// 1. Bare keys (`version` and `dependencies`)
///
/// 2. Basic quoted keys (`"basic string key"` and `"nom"`)
///
/// 3. Literal quoted keys (`'literal key'`)
///
/// For details see [toml spec](https://github.com/toml-lang/toml/#keyvalue-pair).
///
/// To parse a key use `FromStr` trait implementation: `"string".parse::<Key>()`.
#[derive(Debug)]
pub struct Key {
key: InternalString,
pub(crate) repr: Option<Repr>,
pub(crate) leaf_decor: Decor,
pub(crate) dotted_decor: Decor,
}
impl Key {
/// Create a new table key
pub fn new(key: impl Into<InternalString>) -> Self {
Self {
key: key.into(),
repr: None,
leaf_decor: Default::default(),
dotted_decor: Default::default(),
}
}
/// Parse a TOML key expression
///
/// Unlike `"".parse<Key>()`, this supports dotted keys.
#[cfg(feature = "parse")]
pub fn parse(repr: &str) -> Result<Vec<Self>, crate::TomlError> {
Self::try_parse_path(repr)
}
pub(crate) fn with_repr_unchecked(mut self, repr: Repr) -> Self {
self.repr = Some(repr);
self
}
/// While creating the `Key`, add `Decor` to it
#[deprecated(since = "0.21.1", note = "Replaced with `with_leaf_decor`")]
pub fn with_decor(self, decor: Decor) -> Self {
self.with_leaf_decor(decor)
}
/// While creating the `Key`, add `Decor` to it for the line entry
pub fn with_leaf_decor(mut self, decor: Decor) -> Self {
self.leaf_decor = decor;
self
}
/// While creating the `Key`, add `Decor` to it for between dots
pub fn with_dotted_decor(mut self, decor: Decor) -> Self {
self.dotted_decor = decor;
self
}
/// Access a mutable proxy for the `Key`.
pub fn as_mut(&mut self) -> KeyMut<'_> {
KeyMut { key: self }
}
/// Returns the parsed key value.
pub fn get(&self) -> &str {
&self.key
}
/// Returns key raw representation, if available.
pub fn as_repr(&self) -> Option<&Repr> {
self.repr.as_ref()
}
/// Returns the default raw representation.
#[cfg(feature = "display")]
pub fn default_repr(&self) -> Repr {
let output = toml_write::TomlKeyBuilder::new(&self.key)
.as_default()
.to_toml_key();
Repr::new_unchecked(output)
}
/// Returns a raw representation.
#[cfg(feature = "display")]
pub fn display_repr(&self) -> Cow<'_, str> {
self.as_repr()
.and_then(|r| r.as_raw().as_str())
.map(Cow::Borrowed)
.unwrap_or_else(|| {
Cow::Owned(self.default_repr().as_raw().as_str().unwrap().to_owned())
})
}
/// Returns the surrounding whitespace
#[deprecated(
since = "0.21.1",
note = "Replaced with `dotted_decor_mut`, `leaf_decor_mut"
)]
pub fn decor_mut(&mut self) -> &mut Decor {
self.leaf_decor_mut()
}
/// Returns the surrounding whitespace for the line entry
pub fn leaf_decor_mut(&mut self) -> &mut Decor {
&mut self.leaf_decor
}
/// Returns the surrounding whitespace for between dots
pub fn dotted_decor_mut(&mut self) -> &mut Decor {
&mut self.dotted_decor
}
/// Returns the surrounding whitespace
#[deprecated(since = "0.21.1", note = "Replaced with `dotted_decor`, `leaf_decor")]
pub fn decor(&self) -> &Decor {
self.leaf_decor()
}
/// Returns the surrounding whitespace for the line entry
pub fn leaf_decor(&self) -> &Decor {
&self.leaf_decor
}
/// Returns the surrounding whitespace for between dots
pub fn dotted_decor(&self) -> &Decor {
&self.dotted_decor
}
/// The location within the original document
///
/// This generally requires an [`ImDocument`][crate::ImDocument].
pub fn span(&self) -> Option<std::ops::Range<usize>> {
self.repr.as_ref().and_then(|r| r.span())
}
pub(crate) fn despan(&mut self, input: &str) {
self.leaf_decor.despan(input);
self.dotted_decor.despan(input);
if let Some(repr) = &mut self.repr {
repr.despan(input);
}
}
/// Auto formats the key.
pub fn fmt(&mut self) {
self.repr = None;
self.leaf_decor.clear();
self.dotted_decor.clear();
}
#[cfg(feature = "parse")]
fn try_parse_simple(s: &str) -> Result<Key, crate::TomlError> {
let mut key = crate::parser::parse_key(s)?;
key.despan(s);
Ok(key)
}
#[cfg(feature = "parse")]
fn try_parse_path(s: &str) -> Result<Vec<Key>, crate::TomlError> {
let mut keys = crate::parser::parse_key_path(s)?;
for key in &mut keys {
key.despan(s);
}
Ok(keys)
}
}
impl Clone for Key {
#[inline(never)]
fn clone(&self) -> Self {
Self {
key: self.key.clone(),
repr: self.repr.clone(),
leaf_decor: self.leaf_decor.clone(),
dotted_decor: self.dotted_decor.clone(),
}
}
}
impl std::ops::Deref for Key {
type Target = str;
fn deref(&self) -> &Self::Target {
self.get()
}
}
impl std::borrow::Borrow<str> for Key {
#[inline]
fn borrow(&self) -> &str {
self.get()
}
}
impl std::hash::Hash for Key {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.get().hash(state);
}
}
impl Ord for Key {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.get().cmp(other.get())
}
}
impl PartialOrd for Key {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Eq for Key {}
impl PartialEq for Key {
#[inline]
fn eq(&self, other: &Key) -> bool {
PartialEq::eq(self.get(), other.get())
}
}
impl PartialEq<str> for Key {
#[inline]
fn eq(&self, other: &str) -> bool {
PartialEq::eq(self.get(), other)
}
}
impl PartialEq<&str> for Key {
#[inline]
fn eq(&self, other: &&str) -> bool {
PartialEq::eq(self.get(), *other)
}
}
impl PartialEq<String> for Key {
#[inline]
fn eq(&self, other: &String) -> bool {
PartialEq::eq(self.get(), other.as_str())
}
}
#[cfg(feature = "display")]
impl std::fmt::Display for Key {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
crate::encode::encode_key(self, f, None)
}
}
#[cfg(feature = "parse")]
impl FromStr for Key {
type Err = crate::TomlError;
/// Tries to parse a key from a &str,
/// if fails, tries as basic quoted key (surrounds with "")
/// and then literal quoted key (surrounds with '')
fn from_str(s: &str) -> Result<Self, Self::Err> {
Key::try_parse_simple(s)
}
}
impl<'b> From<&'b str> for Key {
fn from(s: &'b str) -> Self {
Key::new(s)
}
}
impl<'b> From<&'b String> for Key {
fn from(s: &'b String) -> Self {
Key::new(s)
}
}
impl From<String> for Key {
fn from(s: String) -> Self {
Key::new(s)
}
}
impl From<InternalString> for Key {
fn from(s: InternalString) -> Self {
Key::new(s)
}
}
#[doc(hidden)]
impl From<Key> for InternalString {
fn from(key: Key) -> InternalString {
key.key
}
}
/// A mutable reference to a [`Key`]'s formatting
#[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
pub struct KeyMut<'k> {
key: &'k mut Key,
}
impl KeyMut<'_> {
/// Returns the parsed key value.
pub fn get(&self) -> &str {
self.key.get()
}
/// Returns the raw representation, if available.
pub fn as_repr(&self) -> Option<&Repr> {
self.key.as_repr()
}
/// Returns the default raw representation.
#[cfg(feature = "display")]
pub fn default_repr(&self) -> Repr {
self.key.default_repr()
}
/// Returns a raw representation.
#[cfg(feature = "display")]
pub fn display_repr(&self) -> Cow<'_, str> {
self.key.display_repr()
}
/// Returns the surrounding whitespace
#[deprecated(
since = "0.21.1",
note = "Replaced with `dotted_decor_mut`, `leaf_decor_mut"
)]
pub fn decor_mut(&mut self) -> &mut Decor {
#![allow(deprecated)]
self.key.decor_mut()
}
/// Returns the surrounding whitespace for the line entry
pub fn leaf_decor_mut(&mut self) -> &mut Decor {
self.key.leaf_decor_mut()
}
/// Returns the surrounding whitespace for between dots
pub fn dotted_decor_mut(&mut self) -> &mut Decor {
self.key.dotted_decor_mut()
}
/// Returns the surrounding whitespace
#[deprecated(since = "0.21.1", note = "Replaced with `dotted_decor`, `leaf_decor")]
pub fn decor(&self) -> &Decor {
#![allow(deprecated)]
self.key.decor()
}
/// Returns the surrounding whitespace for the line entry
pub fn leaf_decor(&self) -> &Decor {
self.key.leaf_decor()
}
/// Returns the surrounding whitespace for between dots
pub fn dotted_decor(&self) -> &Decor {
self.key.dotted_decor()
}
/// Auto formats the key.
pub fn fmt(&mut self) {
self.key.fmt();
}
}
impl std::ops::Deref for KeyMut<'_> {
type Target = str;
fn deref(&self) -> &Self::Target {
self.get()
}
}
impl PartialEq<str> for KeyMut<'_> {
#[inline]
fn eq(&self, other: &str) -> bool {
PartialEq::eq(self.get(), other)
}
}
impl<'s> PartialEq<&'s str> for KeyMut<'s> {
#[inline]
fn eq(&self, other: &&str) -> bool {
PartialEq::eq(self.get(), *other)
}
}
impl PartialEq<String> for KeyMut<'_> {
#[inline]
fn eq(&self, other: &String) -> bool {
PartialEq::eq(self.get(), other.as_str())
}
}
#[cfg(feature = "display")]
impl std::fmt::Display for KeyMut<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.key, f)
}
}
#[test]
#[cfg(feature = "parse")]
#[cfg(feature = "display")]
fn string_roundtrip() {
Key::new("hello").to_string().parse::<Key>().unwrap();
}

149
vendor/toml_edit-0.22.27/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,149 @@
//! # `toml_edit`
//!
//! This crate allows you to parse and modify toml
//! documents, while preserving comments, spaces *and
//! relative order* of items.
//!
//! If you also need the ease of a more traditional API, see the [`toml`] crate.
//!
//! # Example
//!
//! ```rust
//! # #[cfg(feature = "parse")] {
//! # #[cfg(feature = "display")] {
//! use toml_edit::{DocumentMut, value};
//!
//! let toml = r#"
//! "hello" = 'toml!' # comment
//! ['a'.b]
//! "#;
//! let mut doc = toml.parse::<DocumentMut>().expect("invalid doc");
//! assert_eq!(doc.to_string(), toml);
//! // let's add a new key/value pair inside a.b: c = {d = "hello"}
//! doc["a"]["b"]["c"]["d"] = value("hello");
//! // autoformat inline table a.b.c: { d = "hello" }
//! doc["a"]["b"]["c"].as_inline_table_mut().map(|t| t.fmt());
//! let expected = r#"
//! "hello" = 'toml!' # comment
//! ['a'.b]
//! c = { d = "hello" }
//! "#;
//! assert_eq!(doc.to_string(), expected);
//! # }
//! # }
//! ```
//!
//! ## Controlling formatting
//!
//! By default, values are created with default formatting
//! ```rust
//! # #[cfg(feature = "display")] {
//! # #[cfg(feature = "parse")] {
//! let mut doc = toml_edit::DocumentMut::new();
//! doc["foo"] = toml_edit::value("bar");
//! let expected = r#"foo = "bar"
//! "#;
//! assert_eq!(doc.to_string(), expected);
//! # }
//! # }
//! ```
//!
//! You can choose a custom TOML representation by parsing the value.
//! ```rust
//! # #[cfg(feature = "display")] {
//! # #[cfg(feature = "parse")] {
//! let mut doc = toml_edit::DocumentMut::new();
//! doc["foo"] = "'bar'".parse::<toml_edit::Item>().unwrap();
//! let expected = r#"foo = 'bar'
//! "#;
//! assert_eq!(doc.to_string(), expected);
//! # }
//! # }
//! ```
//!
//! ## Limitations
//!
//! Things it does not preserve:
//!
//! * Order of dotted keys, see [issue](https://github.com/toml-rs/toml/issues/163).
//!
//! [`toml`]: https://docs.rs/toml/latest/toml/
// https://github.com/Marwes/combine/issues/172
#![recursion_limit = "256"]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![warn(missing_docs)]
#![warn(clippy::print_stderr)]
#![warn(clippy::print_stdout)]
mod array;
mod array_of_tables;
mod document;
#[cfg(feature = "display")]
mod encode;
mod error;
mod index;
mod inline_table;
mod internal_string;
mod item;
mod key;
#[cfg(feature = "parse")]
mod parser;
mod raw_string;
mod repr;
mod table;
mod value;
#[cfg(feature = "serde")]
pub mod de;
#[cfg(feature = "serde")]
pub mod ser;
pub mod visit;
pub mod visit_mut;
pub use crate::array::{Array, ArrayIntoIter, ArrayIter, ArrayIterMut};
pub use crate::array_of_tables::{
ArrayOfTables, ArrayOfTablesIntoIter, ArrayOfTablesIter, ArrayOfTablesIterMut,
};
/// Deprecated, replaced with [`DocumentMut`]
#[deprecated(since = "0.22.6", note = "Replaced with `DocumentMut`")]
pub type Document = DocumentMut;
pub use crate::document::DocumentMut;
pub use crate::document::ImDocument;
pub use crate::error::TomlError;
pub use crate::inline_table::{
InlineEntry, InlineOccupiedEntry, InlineTable, InlineTableIntoIter, InlineTableIter,
InlineTableIterMut, InlineVacantEntry,
};
pub use crate::internal_string::InternalString;
pub use crate::item::{array, table, value, Item};
pub use crate::key::{Key, KeyMut};
pub use crate::raw_string::RawString;
pub use crate::repr::{Decor, Formatted, Repr};
pub use crate::table::{
Entry, IntoIter, Iter, IterMut, OccupiedEntry, Table, TableLike, VacantEntry,
};
pub use crate::value::Value;
pub use toml_datetime::*;
// Prevent users from some traits.
pub(crate) mod private {
pub trait Sealed {}
impl Sealed for usize {}
impl Sealed for str {}
impl Sealed for String {}
impl Sealed for i64 {}
impl Sealed for f64 {}
impl Sealed for bool {}
impl Sealed for crate::Datetime {}
impl<T: ?Sized> Sealed for &T where T: Sealed {}
impl Sealed for crate::Table {}
impl Sealed for crate::InlineTable {}
}
#[doc = include_str!("../README.md")]
#[cfg(doctest)]
#[cfg(feature = "display")]
#[cfg(feature = "parse")]
pub struct ReadmeDoctests;

View File

@@ -0,0 +1,134 @@
use winnow::combinator::cut_err;
use winnow::combinator::delimited;
use winnow::combinator::opt;
use winnow::combinator::peek;
use winnow::combinator::separated;
use winnow::combinator::trace;
use crate::parser::trivia::ws_comment_newline;
use crate::parser::value::value;
use crate::{Array, Item, RawString};
use crate::parser::prelude::*;
// ;; Array
// array = array-open array-values array-close
pub(crate) fn array<'i>(input: &mut Input<'i>) -> ModalResult<Array> {
trace("array", move |input: &mut Input<'i>| {
delimited(
ARRAY_OPEN,
cut_err(array_values),
cut_err(ARRAY_CLOSE)
.context(StrContext::Label("array"))
.context(StrContext::Expected(StrContextValue::CharLiteral(']'))),
)
.parse_next(input)
})
.parse_next(input)
}
// note: we're omitting ws and newlines here, because
// they should be part of the formatted values
// array-open = %x5B ws-newline ; [
pub(crate) const ARRAY_OPEN: u8 = b'[';
// array-close = ws-newline %x5D ; ]
const ARRAY_CLOSE: u8 = b']';
// array-sep = ws %x2C ws ; , Comma
const ARRAY_SEP: u8 = b',';
// array-values = ws-comment-newline val ws-comment-newline array-sep array-values
// array-values =/ ws-comment-newline val ws-comment-newline [ array-sep ]
fn array_values(input: &mut Input<'_>) -> ModalResult<Array> {
if peek(opt(ARRAY_CLOSE)).parse_next(input)?.is_some() {
// Optimize for empty arrays, avoiding `value` from being expected to fail
return Ok(Array::new());
}
let array = separated(0.., array_value, ARRAY_SEP).parse_next(input)?;
let mut array = Array::with_vec(array);
if !array.is_empty() {
let comma = opt(ARRAY_SEP).parse_next(input)?.is_some();
array.set_trailing_comma(comma);
}
let trailing = ws_comment_newline.span().parse_next(input)?;
array.set_trailing(RawString::with_span(trailing));
Ok(array)
}
fn array_value(input: &mut Input<'_>) -> ModalResult<Item> {
let prefix = ws_comment_newline.span().parse_next(input)?;
let value = value.parse_next(input)?;
let suffix = ws_comment_newline.span().parse_next(input)?;
let value = value.decorated(RawString::with_span(prefix), RawString::with_span(suffix));
let value = Item::Value(value);
Ok(value)
}
#[cfg(test)]
#[cfg(feature = "parse")]
#[cfg(feature = "display")]
mod test {
use super::*;
#[test]
fn arrays() {
let inputs = [
r#"[]"#,
r#"[ ]"#,
r#"[
1, 2, 3
]"#,
r#"[
1,
2, # this is ok
]"#,
r#"[# comment
# comment2
]"#,
r#"[# comment
# comment2
1
#sd
,
# comment3
]"#,
r#"[1]"#,
r#"[1,]"#,
r#"[ "all", 'strings', """are the same""", '''type''']"#,
r#"[ 100, -2,]"#,
r#"[1, 2, 3]"#,
r#"[1.1, 2.1, 3.1]"#,
r#"["a", "b", "c"]"#,
r#"[ [ 1, 2 ], [3, 4, 5] ]"#,
r#"[ [ 1, 2 ], ["a", "b", "c"] ]"#,
r#"[ { x = 1, a = "2" }, {a = "a",b = "b", c = "c"} ]"#,
];
for input in inputs {
dbg!(input);
let mut parsed = array.parse(new_input(input));
if let Ok(parsed) = &mut parsed {
parsed.despan(input);
}
assert_eq!(parsed.map(|a| a.to_string()), Ok(input.to_owned()));
}
}
#[test]
fn invalid_arrays() {
let invalid_inputs = [r#"["#, r#"[,]"#, r#"[,2]"#, r#"[1e165,,]"#];
for input in invalid_inputs {
dbg!(input);
let mut parsed = array.parse(new_input(input));
if let Ok(parsed) = &mut parsed {
parsed.despan(input);
}
assert!(parsed.is_err());
}
}
}

View File

@@ -0,0 +1,469 @@
use std::ops::RangeInclusive;
use crate::parser::error::CustomError;
use crate::parser::prelude::*;
use crate::parser::trivia::from_utf8_unchecked;
use toml_datetime::{Date, Datetime, Offset, Time};
use winnow::combinator::alt;
use winnow::combinator::cut_err;
use winnow::combinator::opt;
use winnow::combinator::preceded;
use winnow::combinator::trace;
use winnow::stream::Stream as _;
use winnow::token::one_of;
use winnow::token::take_while;
// ;; Date and Time (as defined in RFC 3339)
// date-time = offset-date-time / local-date-time / local-date / local-time
// offset-date-time = full-date time-delim full-time
// local-date-time = full-date time-delim partial-time
// local-date = full-date
// local-time = partial-time
// full-time = partial-time time-offset
pub(crate) fn date_time(input: &mut Input<'_>) -> ModalResult<Datetime> {
trace(
"date-time",
alt((
(full_date, opt((time_delim, partial_time, opt(time_offset))))
.map(|(date, opt)| {
match opt {
// Offset Date-Time
Some((_, time, offset)) => Datetime {
date: Some(date),
time: Some(time),
offset,
},
// Local Date
None => Datetime {
date: Some(date),
time: None,
offset: None,
},
}
})
.context(StrContext::Label("date-time")),
partial_time
.map(|t| t.into())
.context(StrContext::Label("time")),
)),
)
.parse_next(input)
}
// full-date = date-fullyear "-" date-month "-" date-mday
fn full_date(input: &mut Input<'_>) -> ModalResult<Date> {
trace("full-date", full_date_).parse_next(input)
}
fn full_date_(input: &mut Input<'_>) -> ModalResult<Date> {
let year = date_fullyear.parse_next(input)?;
let _ = b'-'.parse_next(input)?;
let month = cut_err(date_month).parse_next(input)?;
let _ = cut_err(b'-').parse_next(input)?;
let day_start = input.checkpoint();
let day = cut_err(date_mday).parse_next(input)?;
let is_leap_year = (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0));
let max_days_in_month = match month {
2 if is_leap_year => 29,
2 => 28,
4 | 6 | 9 | 11 => 30,
_ => 31,
};
if max_days_in_month < day {
input.reset(&day_start);
return Err(
winnow::error::ErrMode::from_external_error(input, CustomError::OutOfRange).cut(),
);
}
Ok(Date { year, month, day })
}
// partial-time = time-hour ":" time-minute ":" time-second [time-secfrac]
fn partial_time(input: &mut Input<'_>) -> ModalResult<Time> {
trace(
"partial-time",
(
time_hour,
b':',
cut_err((time_minute, b':', time_second, opt(time_secfrac))),
)
.map(|(hour, _, (minute, _, second, nanosecond))| Time {
hour,
minute,
second,
nanosecond: nanosecond.unwrap_or_default(),
}),
)
.parse_next(input)
}
// time-offset = "Z" / time-numoffset
// time-numoffset = ( "+" / "-" ) time-hour ":" time-minute
fn time_offset(input: &mut Input<'_>) -> ModalResult<Offset> {
trace(
"time-offset",
alt((
one_of((b'Z', b'z')).value(Offset::Z),
(
one_of((b'+', b'-')),
cut_err((time_hour, b':', time_minute)),
)
.map(|(sign, (hours, _, minutes))| {
let sign = match sign {
b'+' => 1,
b'-' => -1,
_ => unreachable!("Parser prevents this"),
};
sign * (hours as i16 * 60 + minutes as i16)
})
.verify(|minutes| ((-24 * 60)..=(24 * 60)).contains(minutes))
.map(|minutes| Offset::Custom { minutes }),
))
.context(StrContext::Label("time offset")),
)
.parse_next(input)
}
// date-fullyear = 4DIGIT
fn date_fullyear(input: &mut Input<'_>) -> ModalResult<u16> {
unsigned_digits::<4, 4>
.map(|s: &str| s.parse::<u16>().expect("4DIGIT should match u8"))
.parse_next(input)
}
// date-month = 2DIGIT ; 01-12
fn date_month(input: &mut Input<'_>) -> ModalResult<u8> {
unsigned_digits::<2, 2>
.try_map(|s: &str| {
let d = s.parse::<u8>().expect("2DIGIT should match u8");
if (1..=12).contains(&d) {
Ok(d)
} else {
Err(CustomError::OutOfRange)
}
})
.parse_next(input)
}
// date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on month/year
fn date_mday(input: &mut Input<'_>) -> ModalResult<u8> {
unsigned_digits::<2, 2>
.try_map(|s: &str| {
let d = s.parse::<u8>().expect("2DIGIT should match u8");
if (1..=31).contains(&d) {
Ok(d)
} else {
Err(CustomError::OutOfRange)
}
})
.parse_next(input)
}
// time-delim = "T" / %x20 ; T, t, or space
fn time_delim(input: &mut Input<'_>) -> ModalResult<u8> {
one_of(TIME_DELIM).parse_next(input)
}
const TIME_DELIM: (u8, u8, u8) = (b'T', b't', b' ');
// time-hour = 2DIGIT ; 00-23
fn time_hour(input: &mut Input<'_>) -> ModalResult<u8> {
unsigned_digits::<2, 2>
.try_map(|s: &str| {
let d = s.parse::<u8>().expect("2DIGIT should match u8");
if (0..=23).contains(&d) {
Ok(d)
} else {
Err(CustomError::OutOfRange)
}
})
.parse_next(input)
}
// time-minute = 2DIGIT ; 00-59
fn time_minute(input: &mut Input<'_>) -> ModalResult<u8> {
unsigned_digits::<2, 2>
.try_map(|s: &str| {
let d = s.parse::<u8>().expect("2DIGIT should match u8");
if (0..=59).contains(&d) {
Ok(d)
} else {
Err(CustomError::OutOfRange)
}
})
.parse_next(input)
}
// time-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second rules
fn time_second(input: &mut Input<'_>) -> ModalResult<u8> {
unsigned_digits::<2, 2>
.try_map(|s: &str| {
let d = s.parse::<u8>().expect("2DIGIT should match u8");
if (0..=60).contains(&d) {
Ok(d)
} else {
Err(CustomError::OutOfRange)
}
})
.parse_next(input)
}
// time-secfrac = "." 1*DIGIT
fn time_secfrac(input: &mut Input<'_>) -> ModalResult<u32> {
static SCALE: [u32; 10] = [
0,
100_000_000,
10_000_000,
1_000_000,
100_000,
10_000,
1_000,
100,
10,
1,
];
const INF: usize = usize::MAX;
preceded(b'.', unsigned_digits::<1, INF>)
.try_map(|mut repr: &str| -> Result<u32, CustomError> {
let max_digits = SCALE.len() - 1;
if max_digits < repr.len() {
// 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.
repr = &repr[0..max_digits];
}
let v = repr.parse::<u32>().map_err(|_| CustomError::OutOfRange)?;
let num_digits = repr.len();
// scale the number accordingly.
let scale = SCALE.get(num_digits).ok_or(CustomError::OutOfRange)?;
let v = v.checked_mul(*scale).ok_or(CustomError::OutOfRange)?;
Ok(v)
})
.parse_next(input)
}
fn unsigned_digits<'i, const MIN: usize, const MAX: usize>(
input: &mut Input<'i>,
) -> ModalResult<&'i str> {
take_while(MIN..=MAX, DIGIT)
.map(|b: &[u8]| unsafe { from_utf8_unchecked(b, "`is_ascii_digit` filters out on-ASCII") })
.parse_next(input)
}
// DIGIT = %x30-39 ; 0-9
const DIGIT: RangeInclusive<u8> = b'0'..=b'9';
#[cfg(test)]
#[cfg(feature = "parse")]
#[cfg(feature = "display")]
mod test {
use super::*;
#[test]
fn offset_date_time() {
let inputs = [
(
"1979-05-27T07:32:00Z",
Datetime {
date: Some(Date {
year: 1979,
month: 5,
day: 27,
}),
time: Some(Time {
hour: 7,
minute: 32,
second: 0,
nanosecond: 0,
}),
offset: Some(Offset::Z),
},
),
(
"1979-05-27T00:32:00-07:00",
Datetime {
date: Some(Date {
year: 1979,
month: 5,
day: 27,
}),
time: Some(Time {
hour: 0,
minute: 32,
second: 0,
nanosecond: 0,
}),
offset: Some(Offset::Custom { minutes: -7 * 60 }),
},
),
(
"1979-05-27T00:32:00-00:36",
Datetime {
date: Some(Date {
year: 1979,
month: 5,
day: 27,
}),
time: Some(Time {
hour: 0,
minute: 32,
second: 0,
nanosecond: 0,
}),
offset: Some(Offset::Custom { minutes: -36 }),
},
),
(
"1979-05-27T00:32:00.999999",
Datetime {
date: Some(Date {
year: 1979,
month: 5,
day: 27,
}),
time: Some(Time {
hour: 0,
minute: 32,
second: 0,
nanosecond: 999999000,
}),
offset: None,
},
),
];
for (input, expected) in inputs {
dbg!(input);
let actual = date_time.parse(new_input(input)).unwrap();
assert_eq!(expected, actual);
}
}
#[test]
fn local_date_time() {
let inputs = [
(
"1979-05-27T07:32:00",
Datetime {
date: Some(Date {
year: 1979,
month: 5,
day: 27,
}),
time: Some(Time {
hour: 7,
minute: 32,
second: 0,
nanosecond: 0,
}),
offset: None,
},
),
(
"1979-05-27T00:32:00.999999",
Datetime {
date: Some(Date {
year: 1979,
month: 5,
day: 27,
}),
time: Some(Time {
hour: 0,
minute: 32,
second: 0,
nanosecond: 999999000,
}),
offset: None,
},
),
];
for (input, expected) in inputs {
dbg!(input);
let actual = date_time.parse(new_input(input)).unwrap();
assert_eq!(expected, actual);
}
}
#[test]
fn local_date() {
let inputs = [
(
"1979-05-27",
Datetime {
date: Some(Date {
year: 1979,
month: 5,
day: 27,
}),
time: None,
offset: None,
},
),
(
"2017-07-20",
Datetime {
date: Some(Date {
year: 2017,
month: 7,
day: 20,
}),
time: None,
offset: None,
},
),
];
for (input, expected) in inputs {
dbg!(input);
let actual = date_time.parse(new_input(input)).unwrap();
assert_eq!(expected, actual);
}
}
#[test]
fn local_time() {
let inputs = [
(
"07:32:00",
Datetime {
date: None,
time: Some(Time {
hour: 7,
minute: 32,
second: 0,
nanosecond: 0,
}),
offset: None,
},
),
(
"00:32:00.999999",
Datetime {
date: None,
time: Some(Time {
hour: 0,
minute: 32,
second: 0,
nanosecond: 999999000,
}),
offset: None,
},
),
];
for (input, expected) in inputs {
dbg!(input);
let actual = date_time.parse(new_input(input)).unwrap();
assert_eq!(expected, actual);
}
}
#[test]
fn time_fraction_truncated() {
let input = "1987-07-05T17:45:00.123456789012345Z";
date_time.parse(new_input(input)).unwrap();
}
}

View File

@@ -0,0 +1,130 @@
use std::cell::RefCell;
use winnow::combinator::cut_err;
use winnow::combinator::eof;
use winnow::combinator::opt;
use winnow::combinator::peek;
use winnow::combinator::repeat;
use winnow::combinator::trace;
use winnow::token::any;
use winnow::token::one_of;
use crate::key::Key;
use crate::parser::inline_table::KEYVAL_SEP;
use crate::parser::key::key;
use crate::parser::prelude::*;
use crate::parser::state::ParseState;
use crate::parser::table::table;
use crate::parser::trivia::{comment, line_ending, line_trailing, newline, ws};
use crate::parser::value::value;
use crate::Item;
use crate::RawString;
// ;; TOML
// toml = expression *( newline expression )
// expression = ( ( ws comment ) /
// ( ws keyval ws [ comment ] ) /
// ( ws table ws [ comment ] ) /
// ws )
pub(crate) fn document<'s, 'i>(
state_ref: &'s RefCell<ParseState>,
) -> impl ModalParser<Input<'i>, (), ContextError> + 's {
move |i: &mut Input<'i>| {
(
// Remove BOM if present
opt(b"\xEF\xBB\xBF"),
parse_ws(state_ref),
repeat(0.., (
dispatch! {peek(any);
crate::parser::trivia::COMMENT_START_SYMBOL => cut_err(parse_comment(state_ref)),
crate::parser::table::STD_TABLE_OPEN => cut_err(table(state_ref)),
crate::parser::trivia::LF |
crate::parser::trivia::CR => parse_newline(state_ref),
_ => cut_err(keyval(state_ref)),
},
parse_ws(state_ref),
))
.map(|()| ()),
eof,
).void().parse_next(i)
}
}
fn parse_comment<'s, 'i>(
state: &'s RefCell<ParseState>,
) -> impl ModalParser<Input<'i>, (), ContextError> + 's {
move |i: &mut Input<'i>| {
(comment, line_ending)
.span()
.map(|span| {
state.borrow_mut().on_comment(span);
})
.parse_next(i)
}
}
fn parse_ws<'s, 'i>(
state: &'s RefCell<ParseState>,
) -> impl ModalParser<Input<'i>, (), ContextError> + 's {
move |i: &mut Input<'i>| {
ws.span()
.map(|span| state.borrow_mut().on_ws(span))
.parse_next(i)
}
}
fn parse_newline<'s, 'i>(
state: &'s RefCell<ParseState>,
) -> impl ModalParser<Input<'i>, (), ContextError> + 's {
move |i: &mut Input<'i>| {
newline
.span()
.map(|span| state.borrow_mut().on_ws(span))
.parse_next(i)
}
}
fn keyval<'s, 'i>(
state: &'s RefCell<ParseState>,
) -> impl ModalParser<Input<'i>, (), ContextError> + 's {
move |i: &mut Input<'i>| {
parse_keyval
.try_map(|(p, kv)| state.borrow_mut().on_keyval(p, kv))
.parse_next(i)
}
}
// keyval = key keyval-sep val
fn parse_keyval(input: &mut Input<'_>) -> ModalResult<(Vec<Key>, (Key, Item))> {
trace(
"keyval",
(
key,
cut_err((
one_of(KEYVAL_SEP)
.context(StrContext::Expected(StrContextValue::CharLiteral('.')))
.context(StrContext::Expected(StrContextValue::CharLiteral('='))),
(
ws.span(),
value,
line_trailing
.context(StrContext::Expected(StrContextValue::CharLiteral('\n')))
.context(StrContext::Expected(StrContextValue::CharLiteral('#'))),
),
)),
)
.try_map::<_, _, std::str::Utf8Error>(|(key, (_, v))| {
let mut path = key;
let key = path.pop().expect("grammar ensures at least 1");
let (pre, v, suf) = v;
let pre = RawString::with_span(pre);
let suf = RawString::with_span(suf);
let v = v.decorated(pre, suf);
Ok((path, (key, Item::Value(v))))
}),
)
.parse_next(input)
}

View File

@@ -0,0 +1,86 @@
use std::error::Error as StdError;
use std::fmt::{Display, Formatter, Result};
use crate::Key;
#[derive(Debug, Clone)]
pub(crate) enum CustomError {
DuplicateKey {
key: String,
table: Option<Vec<Key>>,
},
DottedKeyExtendWrongType {
key: Vec<Key>,
actual: &'static str,
},
OutOfRange,
#[cfg_attr(feature = "unbounded", allow(dead_code))]
RecursionLimitExceeded,
}
impl CustomError {
pub(crate) fn duplicate_key(path: &[Key], i: usize) -> Self {
assert!(i < path.len());
let key = &path[i];
let repr = key
.as_repr()
.and_then(|key| key.as_raw().as_str())
.map(|s| s.to_owned())
.unwrap_or_else(|| {
#[cfg(feature = "display")]
{
key.default_repr().as_raw().as_str().unwrap().to_owned()
}
#[cfg(not(feature = "display"))]
{
format!("{:?}", key.get())
}
});
Self::DuplicateKey {
key: repr,
table: Some(path[..i].to_vec()),
}
}
pub(crate) fn extend_wrong_type(path: &[Key], i: usize, actual: &'static str) -> Self {
assert!(i < path.len());
Self::DottedKeyExtendWrongType {
key: path[..=i].to_vec(),
actual,
}
}
}
impl StdError for CustomError {
fn description(&self) -> &'static str {
"TOML parse error"
}
}
impl Display for CustomError {
fn fmt(&self, f: &mut Formatter<'_>) -> Result {
match self {
CustomError::DuplicateKey { key, table } => {
if let Some(table) = table {
if table.is_empty() {
write!(f, "duplicate key `{key}` in document root")
} else {
let path = table.iter().map(|k| k.get()).collect::<Vec<_>>().join(".");
write!(f, "duplicate key `{key}` in table `{path}`")
}
} else {
write!(f, "duplicate key `{key}`")
}
}
CustomError::DottedKeyExtendWrongType { key, actual } => {
let path = key.iter().map(|k| k.get()).collect::<Vec<_>>().join(".");
write!(
f,
"dotted key `{path}` attempted to extend non-table type ({actual})"
)
}
CustomError::OutOfRange => write!(f, "value is out of range"),
CustomError::RecursionLimitExceeded => write!(f, "recursion limit exceeded"),
}
}
}

View File

@@ -0,0 +1,195 @@
use winnow::combinator::cut_err;
use winnow::combinator::delimited;
use winnow::combinator::separated;
use winnow::combinator::trace;
use winnow::token::one_of;
use crate::key::Key;
use crate::parser::error::CustomError;
use crate::parser::key::key;
use crate::parser::prelude::*;
use crate::parser::trivia::ws;
use crate::parser::value::value;
use crate::{InlineTable, Item, RawString, Value};
use indexmap::map::Entry;
// ;; Inline Table
// inline-table = inline-table-open inline-table-keyvals inline-table-close
pub(crate) fn inline_table<'i>(input: &mut Input<'i>) -> ModalResult<InlineTable> {
trace("inline-table", move |input: &mut Input<'i>| {
delimited(
INLINE_TABLE_OPEN,
cut_err(inline_table_keyvals.try_map(|(kv, p)| table_from_pairs(kv, p))),
cut_err(INLINE_TABLE_CLOSE)
.context(StrContext::Label("inline table"))
.context(StrContext::Expected(StrContextValue::CharLiteral('}'))),
)
.parse_next(input)
})
.parse_next(input)
}
fn table_from_pairs(
v: Vec<(Vec<Key>, (Key, Item))>,
preamble: RawString,
) -> Result<InlineTable, CustomError> {
let mut root = InlineTable::new();
root.set_preamble(preamble);
// Assuming almost all pairs will be directly in `root`
root.items.reserve(v.len());
for (path, (key, value)) in v {
let table = descend_path(&mut root, &path, true)?;
// "Likewise, using dotted keys to redefine tables already defined in [table] form is not allowed"
let mixed_table_types = table.is_dotted() == path.is_empty();
if mixed_table_types {
return Err(CustomError::DuplicateKey {
key: key.get().into(),
table: None,
});
}
match table.items.entry(key) {
Entry::Vacant(o) => {
o.insert(value);
}
Entry::Occupied(o) => {
return Err(CustomError::DuplicateKey {
key: o.key().get().into(),
table: None,
});
}
}
}
Ok(root)
}
fn descend_path<'a>(
mut table: &'a mut InlineTable,
path: &'a [Key],
dotted: bool,
) -> Result<&'a mut InlineTable, CustomError> {
for (i, key) in path.iter().enumerate() {
table = match table.entry_format(key) {
crate::InlineEntry::Vacant(entry) => {
let mut new_table = InlineTable::new();
new_table.set_implicit(true);
new_table.set_dotted(dotted);
entry
.insert(Value::InlineTable(new_table))
.as_inline_table_mut()
.unwrap()
}
crate::InlineEntry::Occupied(entry) => {
match entry.into_mut() {
Value::InlineTable(ref mut sweet_child_of_mine) => {
// Since tables cannot be defined more than once, redefining such tables using a
// [table] header is not allowed. Likewise, using dotted keys to redefine tables
// already defined in [table] form is not allowed.
if dotted && !sweet_child_of_mine.is_implicit() {
return Err(CustomError::DuplicateKey {
key: key.get().into(),
table: None,
});
}
sweet_child_of_mine
}
ref v => {
return Err(CustomError::extend_wrong_type(path, i, v.type_name()));
}
}
}
};
}
Ok(table)
}
// inline-table-open = %x7B ws ; {
pub(crate) const INLINE_TABLE_OPEN: u8 = b'{';
// inline-table-close = ws %x7D ; }
const INLINE_TABLE_CLOSE: u8 = b'}';
// inline-table-sep = ws %x2C ws ; , Comma
const INLINE_TABLE_SEP: u8 = b',';
// keyval-sep = ws %x3D ws ; =
pub(crate) const KEYVAL_SEP: u8 = b'=';
// inline-table-keyvals = [ inline-table-keyvals-non-empty ]
// inline-table-keyvals-non-empty =
// ( key keyval-sep val inline-table-sep inline-table-keyvals-non-empty ) /
// ( key keyval-sep val )
fn inline_table_keyvals(
input: &mut Input<'_>,
) -> ModalResult<(Vec<(Vec<Key>, (Key, Item))>, RawString)> {
(
separated(0.., keyval, INLINE_TABLE_SEP),
ws.span().map(RawString::with_span),
)
.parse_next(input)
}
fn keyval(input: &mut Input<'_>) -> ModalResult<(Vec<Key>, (Key, Item))> {
(
key,
cut_err((
one_of(KEYVAL_SEP)
.context(StrContext::Expected(StrContextValue::CharLiteral('.')))
.context(StrContext::Expected(StrContextValue::CharLiteral('='))),
(ws.span(), value, ws.span()),
)),
)
.map(|(key, (_, v))| {
let mut path = key;
let key = path.pop().expect("grammar ensures at least 1");
let (pre, v, suf) = v;
let pre = RawString::with_span(pre);
let suf = RawString::with_span(suf);
let v = v.decorated(pre, suf);
(path, (key, Item::Value(v)))
})
.parse_next(input)
}
#[cfg(test)]
#[cfg(feature = "parse")]
#[cfg(feature = "display")]
mod test {
use super::*;
#[test]
fn inline_tables() {
let inputs = [
r#"{}"#,
r#"{ }"#,
r#"{a = 1e165}"#,
r#"{ hello = "world", a = 1}"#,
r#"{ hello.world = "a" }"#,
];
for input in inputs {
dbg!(input);
let mut parsed = inline_table.parse(new_input(input));
if let Ok(parsed) = &mut parsed {
parsed.despan(input);
}
assert_eq!(parsed.map(|a| a.to_string()), Ok(input.to_owned()));
}
}
#[test]
fn invalid_inline_tables() {
let invalid_inputs = [r#"{a = 1e165"#, r#"{ hello = "world", a = 2, hello = 1}"#];
for input in invalid_inputs {
dbg!(input);
let mut parsed = inline_table.parse(new_input(input));
if let Ok(parsed) = &mut parsed {
parsed.despan(input);
}
assert!(parsed.is_err());
}
}
}

View File

@@ -0,0 +1,134 @@
use std::ops::RangeInclusive;
use winnow::combinator::peek;
use winnow::combinator::separated;
use winnow::combinator::trace;
use winnow::token::any;
use winnow::token::take_while;
use crate::key::Key;
use crate::parser::error::CustomError;
use crate::parser::prelude::*;
use crate::parser::strings::{basic_string, literal_string};
use crate::parser::trivia::{from_utf8_unchecked, ws};
use crate::repr::{Decor, Repr};
use crate::InternalString;
use crate::RawString;
// key = simple-key / dotted-key
// dotted-key = simple-key 1*( dot-sep simple-key )
pub(crate) fn key(input: &mut Input<'_>) -> ModalResult<Vec<Key>> {
let mut key_path = trace(
"dotted-key",
separated(
1..,
(ws.span(), simple_key, ws.span()).map(|(pre, (raw, key), suffix)| {
Key::new(key)
.with_repr_unchecked(Repr::new_unchecked(raw))
.with_dotted_decor(Decor::new(
RawString::with_span(pre),
RawString::with_span(suffix),
))
}),
DOT_SEP,
)
.context(StrContext::Label("key"))
.try_map(|k: Vec<_>| {
// Inserting the key will require recursion down the line
RecursionCheck::check_depth(k.len())?;
Ok::<_, CustomError>(k)
}),
)
.parse_next(input)?;
let mut leaf_decor = Decor::new("", "");
{
let first_dotted_decor = key_path
.first_mut()
.expect("always at least one key")
.dotted_decor_mut();
if let Some(prefix) = first_dotted_decor.prefix().cloned() {
leaf_decor.set_prefix(prefix);
first_dotted_decor.set_prefix("");
}
}
let last_key = &mut key_path.last_mut().expect("always at least one key");
{
let last_dotted_decor = last_key.dotted_decor_mut();
if let Some(suffix) = last_dotted_decor.suffix().cloned() {
leaf_decor.set_suffix(suffix);
last_dotted_decor.set_suffix("");
}
}
*last_key.leaf_decor_mut() = leaf_decor;
Ok(key_path)
}
// simple-key = quoted-key / unquoted-key
// quoted-key = basic-string / literal-string
pub(crate) fn simple_key(input: &mut Input<'_>) -> ModalResult<(RawString, InternalString)> {
trace(
"simple-key",
dispatch! {peek(any);
crate::parser::strings::QUOTATION_MARK => basic_string
.map(|s: std::borrow::Cow<'_, str>| s.as_ref().into()),
crate::parser::strings::APOSTROPHE => literal_string.map(|s: &str| s.into()),
_ => unquoted_key.map(|s: &str| s.into()),
}
.with_span()
.map(|(k, span)| {
let raw = RawString::with_span(span);
(raw, k)
}),
)
.parse_next(input)
}
// unquoted-key = 1*( ALPHA / DIGIT / %x2D / %x5F ) ; A-Z / a-z / 0-9 / - / _
fn unquoted_key<'i>(input: &mut Input<'i>) -> ModalResult<&'i str> {
trace(
"unquoted-key",
take_while(1.., UNQUOTED_CHAR)
.map(|b| unsafe { from_utf8_unchecked(b, "`is_unquoted_char` filters out on-ASCII") }),
)
.parse_next(input)
}
const UNQUOTED_CHAR: (
RangeInclusive<u8>,
RangeInclusive<u8>,
RangeInclusive<u8>,
u8,
u8,
) = (b'A'..=b'Z', b'a'..=b'z', b'0'..=b'9', b'-', b'_');
// dot-sep = ws %x2E ws ; . Period
const DOT_SEP: u8 = b'.';
#[cfg(test)]
#[cfg(feature = "parse")]
#[cfg(feature = "display")]
mod test {
use super::*;
#[test]
fn keys() {
let cases = [
("a", "a"),
(r#""hello\n ""#, "hello\n "),
(r"'hello\n '", "hello\\n "),
];
for (input, expected) in cases {
dbg!(input);
let parsed = simple_key.parse(new_input(input));
assert_eq!(
parsed,
Ok((RawString::with_span(0..(input.len())), expected.into())),
"Parsing {input:?}"
);
}
}
}

View File

@@ -0,0 +1,255 @@
#![allow(clippy::type_complexity)]
use std::cell::RefCell;
pub(crate) mod array;
pub(crate) mod datetime;
pub(crate) mod document;
pub(crate) mod error;
pub(crate) mod inline_table;
pub(crate) mod key;
pub(crate) mod numbers;
pub(crate) mod state;
pub(crate) mod strings;
pub(crate) mod table;
pub(crate) mod trivia;
pub(crate) mod value;
pub(crate) use crate::error::TomlError;
pub(crate) fn parse_document<S: AsRef<str>>(raw: S) -> Result<crate::ImDocument<S>, TomlError> {
use prelude::*;
let b = new_input(raw.as_ref());
let state = RefCell::new(state::ParseState::new());
let state_ref = &state;
document::document(state_ref)
.parse(b.clone())
.map_err(|e| TomlError::new(e, b))?;
let doc = state
.into_inner()
.into_document(raw)
.map_err(|e| TomlError::custom(e.to_string(), None))?;
Ok(doc)
}
pub(crate) fn parse_key(raw: &str) -> Result<crate::Key, TomlError> {
use prelude::*;
let b = new_input(raw);
let result = key::simple_key.parse(b.clone());
match result {
Ok((raw, key)) => {
Ok(crate::Key::new(key).with_repr_unchecked(crate::Repr::new_unchecked(raw)))
}
Err(e) => Err(TomlError::new(e, b)),
}
}
pub(crate) fn parse_key_path(raw: &str) -> Result<Vec<crate::Key>, TomlError> {
use prelude::*;
let b = new_input(raw);
let result = key::key.parse(b.clone());
match result {
Ok(keys) => Ok(keys),
Err(e) => Err(TomlError::new(e, b)),
}
}
pub(crate) fn parse_value(raw: &str) -> Result<crate::Value, TomlError> {
use prelude::*;
let b = new_input(raw);
let parsed = value::value.parse(b.clone());
match parsed {
Ok(value) => Ok(value),
Err(e) => Err(TomlError::new(e, b)),
}
}
pub(crate) mod prelude {
pub(crate) use winnow::combinator::dispatch;
pub(crate) use winnow::error::ContextError;
pub(crate) use winnow::error::FromExternalError;
pub(crate) use winnow::error::StrContext;
pub(crate) use winnow::error::StrContextValue;
pub(crate) use winnow::ModalParser;
pub(crate) use winnow::ModalResult;
pub(crate) use winnow::Parser as _;
pub(crate) type Input<'b> =
winnow::Stateful<winnow::LocatingSlice<&'b winnow::BStr>, RecursionCheck>;
pub(crate) fn new_input(s: &str) -> Input<'_> {
winnow::Stateful {
input: winnow::LocatingSlice::new(winnow::BStr::new(s)),
state: Default::default(),
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub(crate) struct RecursionCheck {
#[cfg(not(feature = "unbounded"))]
current: usize,
}
#[cfg(not(feature = "unbounded"))]
const LIMIT: usize = 80;
impl RecursionCheck {
pub(crate) fn check_depth(_depth: usize) -> Result<(), super::error::CustomError> {
#[cfg(not(feature = "unbounded"))]
if LIMIT <= _depth {
return Err(super::error::CustomError::RecursionLimitExceeded);
}
Ok(())
}
fn enter(&mut self) -> Result<(), super::error::CustomError> {
#[cfg(not(feature = "unbounded"))]
{
self.current += 1;
if LIMIT <= self.current {
return Err(super::error::CustomError::RecursionLimitExceeded);
}
}
Ok(())
}
fn exit(&mut self) {
#[cfg(not(feature = "unbounded"))]
{
self.current -= 1;
}
}
}
pub(crate) fn check_recursion<'b, O>(
mut parser: impl ModalParser<Input<'b>, O, ContextError>,
) -> impl ModalParser<Input<'b>, O, ContextError> {
move |input: &mut Input<'b>| {
input
.state
.enter()
.map_err(|err| winnow::error::ErrMode::from_external_error(input, err).cut())?;
let result = parser.parse_next(input);
input.state.exit();
result
}
}
}
#[cfg(test)]
#[cfg(feature = "parse")]
#[cfg(feature = "display")]
mod test {
use super::*;
use snapbox::assert_data_eq;
use snapbox::prelude::*;
#[test]
fn documents() {
let documents = [
"",
r#"
# This is a TOML document.
title = "TOML Example"
[owner]
name = "Tom Preston-Werner"
dob = 1979-05-27T07:32:00-08:00 # First class dates
[database]
server = "192.168.1.1"
ports = [ 8001, 8001, 8002 ]
connection_max = 5000
enabled = true
[servers]
# Indentation (tabs and/or spaces) is allowed but not required
[servers.alpha]
ip = "10.0.0.1"
dc = "eqdc10"
[servers.beta]
ip = "10.0.0.2"
dc = "eqdc10"
[clients]
data = [ ["gamma", "delta"], [1, 2] ]
# Line breaks are OK when inside arrays
hosts = [
"alpha",
"omega"
]
'some.weird .stuff' = """
like
that
# """ # this broke my syntax highlighting
" also. like " = '''
that
'''
double = 2e39 # this number looks familiar
# trailing comment"#,
r#""#,
r#" "#,
r#" hello = 'darkness' # my old friend
"#,
r#"[parent . child]
key = "value"
"#,
r#"hello.world = "a"
"#,
r#"foo = 1979-05-27 # Comment
"#,
];
for input in documents {
dbg!(input);
let parsed = parse_document(input).map(|d| d.into_mut());
let doc = match parsed {
Ok(doc) => doc,
Err(err) => {
panic!("Parse error: {err:?}\nFailed to parse:\n```\n{input}\n```")
}
};
assert_data_eq!(doc.to_string(), input.raw());
}
}
#[test]
fn documents_parse_only() {
let parse_only = ["\u{FEFF}
[package]
name = \"foo\"
version = \"0.0.1\"
authors = []
"];
for input in parse_only {
dbg!(input);
let parsed = parse_document(input).map(|d| d.into_mut());
match parsed {
Ok(_) => (),
Err(err) => {
panic!("Parse error: {err:?}\nFailed to parse:\n```\n{input}\n```")
}
}
}
}
#[test]
fn invalid_documents() {
let invalid_inputs = [r#" hello = 'darkness' # my old friend
$"#];
for input in invalid_inputs {
dbg!(input);
let parsed = parse_document(input).map(|d| d.into_mut());
assert!(parsed.is_err(), "Input: {input:?}");
}
}
}

View File

@@ -0,0 +1,399 @@
use std::ops::RangeInclusive;
use winnow::combinator::alt;
use winnow::combinator::cut_err;
use winnow::combinator::opt;
use winnow::combinator::peek;
use winnow::combinator::preceded;
use winnow::combinator::repeat;
use winnow::combinator::trace;
use winnow::token::one_of;
use winnow::token::rest;
use winnow::token::take;
use crate::parser::prelude::*;
use crate::parser::trivia::from_utf8_unchecked;
// ;; Boolean
// boolean = true / false
#[allow(dead_code)] // directly define in `fn value`
pub(crate) fn boolean(input: &mut Input<'_>) -> ModalResult<bool> {
trace("boolean", alt((true_, false_))).parse_next(input)
}
pub(crate) fn true_(input: &mut Input<'_>) -> ModalResult<bool> {
(peek(TRUE[0]), cut_err(TRUE)).value(true).parse_next(input)
}
const TRUE: &[u8] = b"true";
pub(crate) fn false_(input: &mut Input<'_>) -> ModalResult<bool> {
(peek(FALSE[0]), cut_err(FALSE))
.value(false)
.parse_next(input)
}
const FALSE: &[u8] = b"false";
// ;; Integer
// integer = dec-int / hex-int / oct-int / bin-int
pub(crate) fn integer(input: &mut Input<'_>) -> ModalResult<i64> {
trace("integer",
dispatch! {peek(opt::<_, &[u8], _, _>(take(2usize)));
Some(b"0x") => cut_err(hex_int.try_map(|s| i64::from_str_radix(&s.replace('_', ""), 16))),
Some(b"0o") => cut_err(oct_int.try_map(|s| i64::from_str_radix(&s.replace('_', ""), 8))),
Some(b"0b") => cut_err(bin_int.try_map(|s| i64::from_str_radix(&s.replace('_', ""), 2))),
_ => dec_int.and_then(cut_err(rest
.try_map(|s: &str| s.replace('_', "").parse())))
})
.parse_next(input)
}
// dec-int = [ minus / plus ] unsigned-dec-int
// unsigned-dec-int = DIGIT / digit1-9 1*( DIGIT / underscore DIGIT )
pub(crate) fn dec_int<'i>(input: &mut Input<'i>) -> ModalResult<&'i str> {
trace(
"dec-int",
(
opt(one_of((b'+', b'-'))),
alt((
(
one_of(DIGIT1_9),
repeat(
0..,
alt((
digit.void(),
(
one_of(b'_'),
cut_err(digit).context(StrContext::Expected(
StrContextValue::Description("digit"),
)),
)
.void(),
)),
)
.map(|()| ()),
)
.void(),
digit.void(),
)),
)
.take()
.map(|b: &[u8]| unsafe {
from_utf8_unchecked(b, "`digit` and `_` filter out non-ASCII")
})
.context(StrContext::Label("integer")),
)
.parse_next(input)
}
const DIGIT1_9: RangeInclusive<u8> = b'1'..=b'9';
// hex-prefix = %x30.78 ; 0x
// hex-int = hex-prefix HEXDIG *( HEXDIG / underscore HEXDIG )
pub(crate) fn hex_int<'i>(input: &mut Input<'i>) -> ModalResult<&'i str> {
trace(
"hex-int",
preceded(
HEX_PREFIX,
cut_err((
hexdig,
repeat(
0..,
alt((
hexdig.void(),
(
one_of(b'_'),
cut_err(hexdig).context(StrContext::Expected(
StrContextValue::Description("digit"),
)),
)
.void(),
)),
)
.map(|()| ()),
))
.take(),
)
.map(|b| unsafe { from_utf8_unchecked(b, "`hexdig` and `_` filter out non-ASCII") })
.context(StrContext::Label("hexadecimal integer")),
)
.parse_next(input)
}
const HEX_PREFIX: &[u8] = b"0x";
// oct-prefix = %x30.6F ; 0o
// oct-int = oct-prefix digit0-7 *( digit0-7 / underscore digit0-7 )
pub(crate) fn oct_int<'i>(input: &mut Input<'i>) -> ModalResult<&'i str> {
trace(
"oct-int",
preceded(
OCT_PREFIX,
cut_err((
one_of(DIGIT0_7),
repeat(
0..,
alt((
one_of(DIGIT0_7).void(),
(
one_of(b'_'),
cut_err(one_of(DIGIT0_7)).context(StrContext::Expected(
StrContextValue::Description("digit"),
)),
)
.void(),
)),
)
.map(|()| ()),
))
.take(),
)
.map(|b| unsafe { from_utf8_unchecked(b, "`DIGIT0_7` and `_` filter out non-ASCII") })
.context(StrContext::Label("octal integer")),
)
.parse_next(input)
}
const OCT_PREFIX: &[u8] = b"0o";
const DIGIT0_7: RangeInclusive<u8> = b'0'..=b'7';
// bin-prefix = %x30.62 ; 0b
// bin-int = bin-prefix digit0-1 *( digit0-1 / underscore digit0-1 )
pub(crate) fn bin_int<'i>(input: &mut Input<'i>) -> ModalResult<&'i str> {
trace(
"bin-int",
preceded(
BIN_PREFIX,
cut_err((
one_of(DIGIT0_1),
repeat(
0..,
alt((
one_of(DIGIT0_1).void(),
(
one_of(b'_'),
cut_err(one_of(DIGIT0_1)).context(StrContext::Expected(
StrContextValue::Description("digit"),
)),
)
.void(),
)),
)
.map(|()| ()),
))
.take(),
)
.map(|b| unsafe { from_utf8_unchecked(b, "`DIGIT0_1` and `_` filter out non-ASCII") })
.context(StrContext::Label("binary integer")),
)
.parse_next(input)
}
const BIN_PREFIX: &[u8] = b"0b";
const DIGIT0_1: RangeInclusive<u8> = b'0'..=b'1';
// ;; Float
// float = float-int-part ( exp / frac [ exp ] )
// float =/ special-float
// float-int-part = dec-int
pub(crate) fn float(input: &mut Input<'_>) -> ModalResult<f64> {
trace(
"float",
alt((
float_.and_then(cut_err(
rest.try_map(|s: &str| s.replace('_', "").parse())
.verify(|f: &f64| *f != f64::INFINITY),
)),
special_float,
))
.context(StrContext::Label("floating-point number")),
)
.parse_next(input)
}
fn float_<'i>(input: &mut Input<'i>) -> ModalResult<&'i str> {
(
dec_int,
alt((exp.void(), (frac.void(), opt(exp.void())).void())),
)
.take()
.map(|b: &[u8]| unsafe {
from_utf8_unchecked(
b,
"`dec_int`, `one_of`, `exp`, and `frac` filter out non-ASCII",
)
})
.parse_next(input)
}
// frac = decimal-point zero-prefixable-int
// decimal-point = %x2E ; .
fn frac<'i>(input: &mut Input<'i>) -> ModalResult<&'i str> {
(
b'.',
cut_err(zero_prefixable_int)
.context(StrContext::Expected(StrContextValue::Description("digit"))),
)
.take()
.map(|b: &[u8]| unsafe {
from_utf8_unchecked(
b,
"`.` and `parse_zero_prefixable_int` filter out non-ASCII",
)
})
.parse_next(input)
}
// zero-prefixable-int = DIGIT *( DIGIT / underscore DIGIT )
fn zero_prefixable_int<'i>(input: &mut Input<'i>) -> ModalResult<&'i str> {
(
digit,
repeat(
0..,
alt((
digit.void(),
(
one_of(b'_'),
cut_err(digit)
.context(StrContext::Expected(StrContextValue::Description("digit"))),
)
.void(),
)),
)
.map(|()| ()),
)
.take()
.map(|b: &[u8]| unsafe { from_utf8_unchecked(b, "`digit` and `_` filter out non-ASCII") })
.parse_next(input)
}
// exp = "e" float-exp-part
// float-exp-part = [ minus / plus ] zero-prefixable-int
fn exp<'i>(input: &mut Input<'i>) -> ModalResult<&'i str> {
(
one_of((b'e', b'E')),
opt(one_of([b'+', b'-'])),
cut_err(zero_prefixable_int),
)
.take()
.map(|b: &[u8]| unsafe {
from_utf8_unchecked(
b,
"`one_of` and `parse_zero_prefixable_int` filter out non-ASCII",
)
})
.parse_next(input)
}
// special-float = [ minus / plus ] ( inf / nan )
fn special_float(input: &mut Input<'_>) -> ModalResult<f64> {
(opt(one_of((b'+', b'-'))), alt((inf, nan)))
.map(|(s, f)| match s {
Some(b'+') | None => f,
Some(b'-') => -f,
_ => unreachable!("one_of should prevent this"),
})
.parse_next(input)
}
// inf = %x69.6e.66 ; inf
pub(crate) fn inf(input: &mut Input<'_>) -> ModalResult<f64> {
INF.value(f64::INFINITY).parse_next(input)
}
const INF: &[u8] = b"inf";
// nan = %x6e.61.6e ; nan
pub(crate) fn nan(input: &mut Input<'_>) -> ModalResult<f64> {
NAN.value(f64::NAN.copysign(1.0)).parse_next(input)
}
const NAN: &[u8] = b"nan";
// DIGIT = %x30-39 ; 0-9
fn digit(input: &mut Input<'_>) -> ModalResult<u8> {
one_of(DIGIT).parse_next(input)
}
const DIGIT: RangeInclusive<u8> = b'0'..=b'9';
// HEXDIG = DIGIT / "A" / "B" / "C" / "D" / "E" / "F"
fn hexdig(input: &mut Input<'_>) -> ModalResult<u8> {
one_of(HEXDIG).parse_next(input)
}
pub(crate) const HEXDIG: (RangeInclusive<u8>, RangeInclusive<u8>, RangeInclusive<u8>) =
(DIGIT, b'A'..=b'F', b'a'..=b'f');
#[cfg(test)]
#[cfg(feature = "parse")]
#[cfg(feature = "display")]
mod test {
use super::*;
#[test]
fn integers() {
let cases = [
("+99", 99),
("42", 42),
("0", 0),
("-17", -17),
("1_000", 1_000),
("5_349_221", 5_349_221),
("1_2_3_4_5", 1_2_3_4_5),
("0xF", 15),
("0o0_755", 493),
("0b1_0_1", 5),
(&i64::MIN.to_string()[..], i64::MIN),
(&i64::MAX.to_string()[..], i64::MAX),
];
for &(input, expected) in &cases {
dbg!(input);
let parsed = integer.parse(new_input(input));
assert_eq!(parsed, Ok(expected), "Parsing {input:?}");
}
let overflow = "1000000000000000000000000000000000";
let parsed = integer.parse(new_input(overflow));
assert!(parsed.is_err());
}
#[track_caller]
fn assert_float_eq(actual: f64, expected: f64) {
if expected.is_nan() {
assert!(actual.is_nan());
assert_eq!(expected.is_sign_positive(), actual.is_sign_positive());
} else if expected.is_infinite() {
assert!(actual.is_infinite());
assert_eq!(expected.is_sign_positive(), actual.is_sign_positive());
} else {
dbg!(expected);
dbg!(actual);
assert!((expected - actual).abs() < f64::EPSILON);
}
}
#[test]
fn floats() {
let cases = [
("+1.0", 1.0),
("3.1419", 3.1419),
("-0.01", -0.01),
("5e+22", 5e+22),
("1e6", 1e6),
("-2E-2", -2E-2),
("6.626e-34", 6.626e-34),
("9_224_617.445_991_228_313", 9_224_617.445_991_227),
("-1.7976931348623157e+308", f64::MIN),
("1.7976931348623157e+308", f64::MAX),
("nan", f64::NAN.copysign(1.0)),
("+nan", f64::NAN.copysign(1.0)),
("-nan", f64::NAN.copysign(-1.0)),
("inf", f64::INFINITY),
("+inf", f64::INFINITY),
("-inf", f64::NEG_INFINITY),
// ("1e+400", f64::INFINITY),
];
for &(input, expected) in &cases {
dbg!(input);
let parsed = float.parse(new_input(input)).unwrap();
assert_float_eq(parsed, expected);
let overflow = "9e99999";
let parsed = float.parse(new_input(overflow));
assert!(parsed.is_err(), "{parsed:?}");
}
}
}

View File

@@ -0,0 +1,320 @@
use crate::key::Key;
use crate::parser::error::CustomError;
use crate::repr::Decor;
use crate::{ArrayOfTables, ImDocument, Item, RawString, Table};
pub(crate) struct ParseState {
root: Table,
trailing: Option<std::ops::Range<usize>>,
current_table_position: usize,
current_table: Table,
current_is_array: bool,
current_table_path: Vec<Key>,
}
impl ParseState {
pub(crate) fn new() -> Self {
let mut root = Table::new();
root.span = Some(0..0);
Self {
root: Table::new(),
trailing: None,
current_table_position: 0,
current_table: root,
current_is_array: false,
current_table_path: Vec::new(),
}
}
pub(crate) fn into_document<S>(mut self, raw: S) -> Result<ImDocument<S>, CustomError> {
self.finalize_table()?;
let trailing = self.trailing.map(RawString::with_span).unwrap_or_default();
Ok(ImDocument {
root: Item::Table(self.root),
trailing,
raw,
})
}
pub(crate) fn on_ws(&mut self, span: std::ops::Range<usize>) {
if let Some(old) = self.trailing.take() {
self.trailing = Some(old.start..span.end);
} else {
self.trailing = Some(span);
}
}
pub(crate) fn on_comment(&mut self, span: std::ops::Range<usize>) {
if let Some(old) = self.trailing.take() {
self.trailing = Some(old.start..span.end);
} else {
self.trailing = Some(span);
}
}
pub(crate) fn on_keyval(
&mut self,
path: Vec<Key>,
(mut key, value): (Key, Item),
) -> Result<(), CustomError> {
{
let mut prefix = self.trailing.take();
let prefix = match (
prefix.take(),
key.leaf_decor.prefix().and_then(|d| d.span()),
) {
(Some(p), Some(k)) => Some(p.start..k.end),
(Some(p), None) | (None, Some(p)) => Some(p),
(None, None) => None,
};
key.leaf_decor
.set_prefix(prefix.map(RawString::with_span).unwrap_or_default());
}
if let (Some(existing), Some(value)) = (self.current_table.span(), value.span()) {
self.current_table.span = Some((existing.start)..(value.end));
}
let table = &mut self.current_table;
let table = Self::descend_path(table, &path, true)?;
// "Likewise, using dotted keys to redefine tables already defined in [table] form is not allowed"
let mixed_table_types = table.is_dotted() == path.is_empty();
if mixed_table_types {
return Err(CustomError::DuplicateKey {
key: key.get().into(),
table: None,
});
}
match table.items.entry(key) {
indexmap::map::Entry::Vacant(o) => {
o.insert(value);
}
indexmap::map::Entry::Occupied(o) => {
// "Since tables cannot be defined more than once, redefining such tables using a [table] header is not allowed"
return Err(CustomError::DuplicateKey {
key: o.key().get().into(),
table: Some(self.current_table_path.clone()),
});
}
}
Ok(())
}
pub(crate) fn start_array_table(
&mut self,
path: Vec<Key>,
decor: Decor,
span: std::ops::Range<usize>,
) -> Result<(), CustomError> {
debug_assert!(!path.is_empty());
debug_assert!(self.current_table.is_empty());
debug_assert!(self.current_table_path.is_empty());
// Look up the table on start to ensure the duplicate_key error points to the right line
let root = &mut self.root;
let parent_table = Self::descend_path(root, &path[..path.len() - 1], false)?;
let key = &path[path.len() - 1];
let entry = parent_table
.entry_format(key)
.or_insert(Item::ArrayOfTables(ArrayOfTables::new()));
entry
.as_array_of_tables()
.ok_or_else(|| CustomError::duplicate_key(&path, path.len() - 1))?;
self.current_table_position += 1;
self.current_table.decor = decor;
self.current_table.set_implicit(false);
self.current_table.set_dotted(false);
self.current_table.set_position(self.current_table_position);
self.current_table.span = Some(span);
self.current_is_array = true;
self.current_table_path = path;
Ok(())
}
pub(crate) fn start_table(
&mut self,
path: Vec<Key>,
decor: Decor,
span: std::ops::Range<usize>,
) -> Result<(), CustomError> {
debug_assert!(!path.is_empty());
debug_assert!(self.current_table.is_empty());
debug_assert!(self.current_table_path.is_empty());
// 1. Look up the table on start to ensure the duplicate_key error points to the right line
// 2. Ensure any child tables from an implicit table are preserved
let root = &mut self.root;
let parent_table = Self::descend_path(root, &path[..path.len() - 1], false)?;
let key = &path[path.len() - 1];
if let Some(entry) = parent_table.remove(key.get()) {
match entry {
Item::Table(t) if t.implicit && !t.is_dotted() => {
self.current_table = t;
}
// Since tables cannot be defined more than once, redefining such tables using a [table] header is not allowed. Likewise, using dotted keys to redefine tables already defined in [table] form is not allowed.
_ => return Err(CustomError::duplicate_key(&path, path.len() - 1)),
}
}
self.current_table_position += 1;
self.current_table.decor = decor;
self.current_table.set_implicit(false);
self.current_table.set_dotted(false);
self.current_table.set_position(self.current_table_position);
self.current_table.span = Some(span);
self.current_is_array = false;
self.current_table_path = path;
Ok(())
}
pub(crate) fn finalize_table(&mut self) -> Result<(), CustomError> {
let mut table = std::mem::take(&mut self.current_table);
let path = std::mem::take(&mut self.current_table_path);
let root = &mut self.root;
if path.is_empty() {
assert!(root.is_empty());
std::mem::swap(&mut table, root);
} else if self.current_is_array {
let parent_table = Self::descend_path(root, &path[..path.len() - 1], false)?;
let key = &path[path.len() - 1];
let entry = parent_table
.entry_format(key)
.or_insert(Item::ArrayOfTables(ArrayOfTables::new()));
let array = entry
.as_array_of_tables_mut()
.ok_or_else(|| CustomError::duplicate_key(&path, path.len() - 1))?;
array.push(table);
let span = if let (Some(first), Some(last)) = (
array.values.first().and_then(|t| t.span()),
array.values.last().and_then(|t| t.span()),
) {
Some((first.start)..(last.end))
} else {
None
};
array.span = span;
} else {
let parent_table = Self::descend_path(root, &path[..path.len() - 1], false)?;
let key = &path[path.len() - 1];
let entry = parent_table.entry_format(key);
match entry {
crate::Entry::Occupied(entry) => {
match entry.into_mut() {
// if [a.b.c] header preceded [a.b]
Item::Table(ref mut t) if t.implicit => {
std::mem::swap(t, &mut table);
}
_ => return Err(CustomError::duplicate_key(&path, path.len() - 1)),
}
}
crate::Entry::Vacant(entry) => {
let item = Item::Table(table);
entry.insert(item);
}
}
}
Ok(())
}
pub(crate) fn descend_path<'t>(
mut table: &'t mut Table,
path: &[Key],
dotted: bool,
) -> Result<&'t mut Table, CustomError> {
for (i, key) in path.iter().enumerate() {
table = match table.entry_format(key) {
crate::Entry::Vacant(entry) => {
let mut new_table = Table::new();
new_table.set_implicit(true);
new_table.set_dotted(dotted);
entry.insert(Item::Table(new_table)).as_table_mut().unwrap()
}
crate::Entry::Occupied(entry) => {
match entry.into_mut() {
Item::Value(ref v) => {
return Err(CustomError::extend_wrong_type(path, i, v.type_name()));
}
Item::ArrayOfTables(ref mut array) => {
debug_assert!(!array.is_empty());
let index = array.len() - 1;
let last_child = array.get_mut(index).unwrap();
last_child
}
Item::Table(ref mut sweet_child_of_mine) => {
// Since tables cannot be defined more than once, redefining such tables using a
// [table] header is not allowed. Likewise, using dotted keys to redefine tables
// already defined in [table] form is not allowed.
if dotted && !sweet_child_of_mine.is_implicit() {
return Err(CustomError::DuplicateKey {
key: key.get().into(),
table: None,
});
}
sweet_child_of_mine
}
Item::None => unreachable!(),
}
}
};
}
Ok(table)
}
pub(crate) fn on_std_header(
&mut self,
path: Vec<Key>,
trailing: std::ops::Range<usize>,
span: std::ops::Range<usize>,
) -> Result<(), CustomError> {
debug_assert!(!path.is_empty());
self.finalize_table()?;
let leading = self
.trailing
.take()
.map(RawString::with_span)
.unwrap_or_default();
self.start_table(
path,
Decor::new(leading, RawString::with_span(trailing)),
span,
)?;
Ok(())
}
pub(crate) fn on_array_header(
&mut self,
path: Vec<Key>,
trailing: std::ops::Range<usize>,
span: std::ops::Range<usize>,
) -> Result<(), CustomError> {
debug_assert!(!path.is_empty());
self.finalize_table()?;
let leading = self
.trailing
.take()
.map(RawString::with_span)
.unwrap_or_default();
self.start_array_table(
path,
Decor::new(leading, RawString::with_span(trailing)),
span,
)?;
Ok(())
}
}

View File

@@ -0,0 +1,479 @@
use std::borrow::Cow;
use std::char;
use std::ops::RangeInclusive;
use winnow::combinator::alt;
use winnow::combinator::cut_err;
use winnow::combinator::delimited;
use winnow::combinator::empty;
use winnow::combinator::fail;
use winnow::combinator::opt;
use winnow::combinator::peek;
use winnow::combinator::preceded;
use winnow::combinator::repeat;
use winnow::combinator::terminated;
use winnow::combinator::trace;
use winnow::prelude::*;
use winnow::stream::Stream;
use winnow::token::any;
use winnow::token::none_of;
use winnow::token::one_of;
use winnow::token::take_while;
use crate::parser::error::CustomError;
use crate::parser::numbers::HEXDIG;
use crate::parser::prelude::*;
use crate::parser::trivia::{from_utf8_unchecked, newline, ws, ws_newlines, NON_ASCII, WSCHAR};
// ;; String
// string = ml-basic-string / basic-string / ml-literal-string / literal-string
pub(crate) fn string<'i>(input: &mut Input<'i>) -> ModalResult<Cow<'i, str>> {
trace(
"string",
alt((
ml_basic_string,
basic_string,
ml_literal_string,
literal_string.map(Cow::Borrowed),
)),
)
.parse_next(input)
}
// ;; Basic String
// basic-string = quotation-mark *basic-char quotation-mark
pub(crate) fn basic_string<'i>(input: &mut Input<'i>) -> ModalResult<Cow<'i, str>> {
trace("basic-string", |input: &mut Input<'i>| {
let _ = one_of(QUOTATION_MARK).parse_next(input)?;
let mut c = Cow::Borrowed("");
if let Some(ci) = opt(basic_chars).parse_next(input)? {
c = ci;
}
while let Some(ci) = opt(basic_chars).parse_next(input)? {
c.to_mut().push_str(&ci);
}
let _ = cut_err(one_of(QUOTATION_MARK))
.context(StrContext::Label("basic string"))
.parse_next(input)?;
Ok(c)
})
.parse_next(input)
}
// quotation-mark = %x22 ; "
pub(crate) const QUOTATION_MARK: u8 = b'"';
// basic-char = basic-unescaped / escaped
fn basic_chars<'i>(input: &mut Input<'i>) -> ModalResult<Cow<'i, str>> {
alt((
// Deviate from the official grammar by batching the unescaped chars so we build a string a
// chunk at a time, rather than a `char` at a time.
take_while(1.., BASIC_UNESCAPED)
.try_map(std::str::from_utf8)
.map(Cow::Borrowed),
escaped.map(|c| Cow::Owned(String::from(c))),
))
.parse_next(input)
}
// basic-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii
const BASIC_UNESCAPED: (
(u8, u8),
u8,
RangeInclusive<u8>,
RangeInclusive<u8>,
RangeInclusive<u8>,
) = (WSCHAR, 0x21, 0x23..=0x5B, 0x5D..=0x7E, NON_ASCII);
// escaped = escape escape-seq-char
fn escaped(input: &mut Input<'_>) -> ModalResult<char> {
preceded(ESCAPE, escape_seq_char).parse_next(input)
}
// escape = %x5C ; \
const ESCAPE: u8 = b'\\';
// escape-seq-char = %x22 ; " quotation mark U+0022
// escape-seq-char =/ %x5C ; \ reverse solidus U+005C
// escape-seq-char =/ %x62 ; b backspace U+0008
// escape-seq-char =/ %x66 ; f form feed U+000C
// escape-seq-char =/ %x6E ; n line feed U+000A
// escape-seq-char =/ %x72 ; r carriage return U+000D
// escape-seq-char =/ %x74 ; t tab U+0009
// escape-seq-char =/ %x75 4HEXDIG ; uXXXX U+XXXX
// escape-seq-char =/ %x55 8HEXDIG ; UXXXXXXXX U+XXXXXXXX
fn escape_seq_char(input: &mut Input<'_>) -> ModalResult<char> {
dispatch! {any;
b'b' => empty.value('\u{8}'),
b'f' => empty.value('\u{c}'),
b'n' => empty.value('\n'),
b'r' => empty.value('\r'),
b't' => empty.value('\t'),
b'u' => cut_err(hexescape::<4>).context(StrContext::Label("unicode 4-digit hex code")),
b'U' => cut_err(hexescape::<8>).context(StrContext::Label("unicode 8-digit hex code")),
b'\\' => empty.value('\\'),
b'"' => empty.value('"'),
_ => {
cut_err(fail::<_, char, _>)
.context(StrContext::Label("escape sequence"))
.context(StrContext::Expected(StrContextValue::CharLiteral('b')))
.context(StrContext::Expected(StrContextValue::CharLiteral('f')))
.context(StrContext::Expected(StrContextValue::CharLiteral('n')))
.context(StrContext::Expected(StrContextValue::CharLiteral('r')))
.context(StrContext::Expected(StrContextValue::CharLiteral('t')))
.context(StrContext::Expected(StrContextValue::CharLiteral('u')))
.context(StrContext::Expected(StrContextValue::CharLiteral('U')))
.context(StrContext::Expected(StrContextValue::CharLiteral('\\')))
.context(StrContext::Expected(StrContextValue::CharLiteral('"')))
}
}
.parse_next(input)
}
fn hexescape<const N: usize>(input: &mut Input<'_>) -> ModalResult<char> {
take_while(0..=N, HEXDIG)
.verify(|b: &[u8]| b.len() == N)
.map(|b: &[u8]| unsafe { from_utf8_unchecked(b, "`is_ascii_digit` filters out on-ASCII") })
.verify_map(|s| u32::from_str_radix(s, 16).ok())
.try_map(|h| char::from_u32(h).ok_or(CustomError::OutOfRange))
.parse_next(input)
}
// ;; Multiline Basic String
// ml-basic-string = ml-basic-string-delim [ newline ] ml-basic-body
// ml-basic-string-delim
fn ml_basic_string<'i>(input: &mut Input<'i>) -> ModalResult<Cow<'i, str>> {
trace(
"ml-basic-string",
delimited(
ML_BASIC_STRING_DELIM,
preceded(opt(newline), cut_err(ml_basic_body))
.context(StrContext::Label("multiline basic string")),
cut_err(ML_BASIC_STRING_DELIM).context(StrContext::Label("multiline basic string")),
),
)
.parse_next(input)
}
// ml-basic-string-delim = 3quotation-mark
const ML_BASIC_STRING_DELIM: &[u8] = b"\"\"\"";
// ml-basic-body = *mlb-content *( mlb-quotes 1*mlb-content ) [ mlb-quotes ]
fn ml_basic_body<'i>(input: &mut Input<'i>) -> ModalResult<Cow<'i, str>> {
let mut c = Cow::Borrowed("");
if let Some(ci) = opt(mlb_content).parse_next(input)? {
c = ci;
}
while let Some(ci) = opt(mlb_content).parse_next(input)? {
c.to_mut().push_str(&ci);
}
while let Some(qi) = opt(mlb_quotes(none_of(b'\"').value(()))).parse_next(input)? {
if let Some(ci) = opt(mlb_content).parse_next(input)? {
c.to_mut().push_str(qi);
c.to_mut().push_str(&ci);
while let Some(ci) = opt(mlb_content).parse_next(input)? {
c.to_mut().push_str(&ci);
}
} else {
break;
}
}
if let Some(qi) = opt(mlb_quotes(ML_BASIC_STRING_DELIM.void())).parse_next(input)? {
c.to_mut().push_str(qi);
}
Ok(c)
}
// mlb-content = mlb-char / newline / mlb-escaped-nl
// mlb-char = mlb-unescaped / escaped
fn mlb_content<'i>(input: &mut Input<'i>) -> ModalResult<Cow<'i, str>> {
alt((
// Deviate from the official grammar by batching the unescaped chars so we build a string a
// chunk at a time, rather than a `char` at a time.
take_while(1.., MLB_UNESCAPED)
.try_map(std::str::from_utf8)
.map(Cow::Borrowed),
// Order changed fromg grammar so `escaped` can more easily `cut_err` on bad escape sequences
mlb_escaped_nl.map(|_| Cow::Borrowed("")),
escaped.map(|c| Cow::Owned(String::from(c))),
newline.map(|_| Cow::Borrowed("\n")),
))
.parse_next(input)
}
// mlb-quotes = 1*2quotation-mark
fn mlb_quotes<'i>(
mut term: impl ModalParser<Input<'i>, (), ContextError>,
) -> impl ModalParser<Input<'i>, &'i str, ContextError> {
move |input: &mut Input<'i>| {
let start = input.checkpoint();
let res = terminated(b"\"\"", peek(term.by_ref()))
.map(|b| unsafe { from_utf8_unchecked(b, "`bytes` out non-ASCII") })
.parse_next(input);
match res {
Err(winnow::error::ErrMode::Backtrack(_)) => {
input.reset(&start);
terminated(b"\"", peek(term.by_ref()))
.map(|b| unsafe { from_utf8_unchecked(b, "`bytes` out non-ASCII") })
.parse_next(input)
}
res => res,
}
}
}
// mlb-unescaped = wschar / %x21 / %x23-5B / %x5D-7E / non-ascii
const MLB_UNESCAPED: (
(u8, u8),
u8,
RangeInclusive<u8>,
RangeInclusive<u8>,
RangeInclusive<u8>,
) = (WSCHAR, 0x21, 0x23..=0x5B, 0x5D..=0x7E, NON_ASCII);
// mlb-escaped-nl = escape ws newline *( wschar / newline
// When the last non-whitespace character on a line is a \,
// it will be trimmed along with all whitespace
// (including newlines) up to the next non-whitespace
// character or closing delimiter.
fn mlb_escaped_nl(input: &mut Input<'_>) -> ModalResult<()> {
repeat(1.., (ESCAPE, ws, ws_newlines))
.map(|()| ())
.value(())
.parse_next(input)
}
// ;; Literal String
// literal-string = apostrophe *literal-char apostrophe
pub(crate) fn literal_string<'i>(input: &mut Input<'i>) -> ModalResult<&'i str> {
trace(
"literal-string",
delimited(
APOSTROPHE,
cut_err(take_while(0.., LITERAL_CHAR)),
cut_err(APOSTROPHE),
)
.try_map(std::str::from_utf8)
.context(StrContext::Label("literal string")),
)
.parse_next(input)
}
// apostrophe = %x27 ; ' apostrophe
pub(crate) const APOSTROPHE: u8 = b'\'';
// literal-char = %x09 / %x20-26 / %x28-7E / non-ascii
const LITERAL_CHAR: (
u8,
RangeInclusive<u8>,
RangeInclusive<u8>,
RangeInclusive<u8>,
) = (0x9, 0x20..=0x26, 0x28..=0x7E, NON_ASCII);
// ;; Multiline Literal String
// ml-literal-string = ml-literal-string-delim [ newline ] ml-literal-body
// ml-literal-string-delim
fn ml_literal_string<'i>(input: &mut Input<'i>) -> ModalResult<Cow<'i, str>> {
trace(
"ml-literal-string",
delimited(
(ML_LITERAL_STRING_DELIM, opt(newline)),
cut_err(ml_literal_body.map(|t| {
if t.contains("\r\n") {
Cow::Owned(t.replace("\r\n", "\n"))
} else {
Cow::Borrowed(t)
}
}))
.context(StrContext::Label("multiline literal string")),
cut_err(ML_LITERAL_STRING_DELIM).context(StrContext::Label("multiline literal string")),
),
)
.parse_next(input)
}
// ml-literal-string-delim = 3apostrophe
const ML_LITERAL_STRING_DELIM: &[u8] = b"'''";
// ml-literal-body = *mll-content *( mll-quotes 1*mll-content ) [ mll-quotes ]
fn ml_literal_body<'i>(input: &mut Input<'i>) -> ModalResult<&'i str> {
(
repeat(0.., mll_content).map(|()| ()),
repeat(
0..,
(
mll_quotes(none_of(APOSTROPHE).value(())),
repeat(1.., mll_content).map(|()| ()),
),
)
.map(|()| ()),
opt(mll_quotes(ML_LITERAL_STRING_DELIM.void())),
)
.take()
.try_map(std::str::from_utf8)
.parse_next(input)
}
// mll-content = mll-char / newline
fn mll_content(input: &mut Input<'_>) -> ModalResult<u8> {
alt((one_of(MLL_CHAR), newline.value(b'\n'))).parse_next(input)
}
// mll-char = %x09 / %x20-26 / %x28-7E / non-ascii
const MLL_CHAR: (
u8,
RangeInclusive<u8>,
RangeInclusive<u8>,
RangeInclusive<u8>,
) = (0x9, 0x20..=0x26, 0x28..=0x7E, NON_ASCII);
// mll-quotes = 1*2apostrophe
fn mll_quotes<'i>(
mut term: impl ModalParser<Input<'i>, (), ContextError>,
) -> impl ModalParser<Input<'i>, &'i str, ContextError> {
move |input: &mut Input<'i>| {
let start = input.checkpoint();
let res = terminated(b"''", peek(term.by_ref()))
.map(|b| unsafe { from_utf8_unchecked(b, "`bytes` out non-ASCII") })
.parse_next(input);
match res {
Err(winnow::error::ErrMode::Backtrack(_)) => {
input.reset(&start);
terminated(b"'", peek(term.by_ref()))
.map(|b| unsafe { from_utf8_unchecked(b, "`bytes` out non-ASCII") })
.parse_next(input)
}
res => res,
}
}
}
#[cfg(test)]
#[cfg(feature = "parse")]
#[cfg(feature = "display")]
mod test {
use super::*;
#[test]
fn basic_string() {
let input =
r#""I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF. \U0002070E""#;
let expected = "I\'m a string. \"You can quote me\". Name\tJosé\nLocation\tSF. \u{2070E}";
let parsed = string.parse(new_input(input));
assert_eq!(parsed.as_deref(), Ok(expected), "Parsing {input:?}");
}
#[test]
fn ml_basic_string() {
let cases = [
(
r#""""
Roses are red
Violets are blue""""#,
r#"Roses are red
Violets are blue"#,
),
(r#"""" \""" """"#, " \"\"\" "),
(r#"""" \\""""#, " \\"),
];
for &(input, expected) in &cases {
let parsed = string.parse(new_input(input));
assert_eq!(parsed.as_deref(), Ok(expected), "Parsing {input:?}");
}
let invalid_cases = [r#"""" """#, r#"""" \""""#];
for input in &invalid_cases {
let parsed = string.parse(new_input(input));
assert!(parsed.is_err());
}
}
#[test]
fn ml_basic_string_escape_ws() {
let inputs = [
r#""""
The quick brown \
fox jumps over \
the lazy dog.""""#,
r#""""\
The quick brown \
fox jumps over \
the lazy dog.\
""""#,
];
for input in &inputs {
let expected = "The quick brown fox jumps over the lazy dog.";
let parsed = string.parse(new_input(input));
assert_eq!(parsed.as_deref(), Ok(expected), "Parsing {input:?}");
}
let empties = [
r#""""\
""""#,
r#""""
\
\
""""#,
];
for input in &empties {
let expected = "";
let parsed = string.parse(new_input(input));
assert_eq!(parsed.as_deref(), Ok(expected), "Parsing {input:?}");
}
}
#[test]
fn literal_string() {
let inputs = [
r"'C:\Users\nodejs\templates'",
r"'\\ServerX\admin$\system32\'",
r#"'Tom "Dubs" Preston-Werner'"#,
r"'<\i\c*\s*>'",
];
for input in &inputs {
let expected = &input[1..input.len() - 1];
let parsed = string.parse(new_input(input));
assert_eq!(parsed.as_deref(), Ok(expected), "Parsing {input:?}");
}
}
#[test]
fn ml_literal_string() {
let inputs = [
r"'''I [dw]on't need \d{2} apples'''",
r#"''''one_quote''''"#,
];
for input in &inputs {
let expected = &input[3..input.len() - 3];
let parsed = string.parse(new_input(input));
assert_eq!(parsed.as_deref(), Ok(expected), "Parsing {input:?}");
}
let input = r#"'''
The first newline is
trimmed in raw strings.
All other whitespace
is preserved.
'''"#;
let expected = &input[4..input.len() - 3];
let parsed = string.parse(new_input(input));
assert_eq!(parsed.as_deref(), Ok(expected), "Parsing {input:?}");
}
}

View File

@@ -0,0 +1,89 @@
use std::cell::RefCell;
#[allow(unused_imports)]
use std::ops::DerefMut;
use winnow::combinator::cut_err;
use winnow::combinator::delimited;
use winnow::combinator::peek;
use winnow::token::take;
// https://github.com/rust-lang/rust/issues/41358
use crate::parser::key::key;
use crate::parser::prelude::*;
use crate::parser::state::ParseState;
use crate::parser::trivia::line_trailing;
// std-table-open = %x5B ws ; [ Left square bracket
pub(crate) const STD_TABLE_OPEN: u8 = b'[';
// std-table-close = ws %x5D ; ] Right square bracket
const STD_TABLE_CLOSE: u8 = b']';
// array-table-open = %x5B.5B ws ; [[ Double left square bracket
const ARRAY_TABLE_OPEN: &[u8] = b"[[";
// array-table-close = ws %x5D.5D ; ]] Double right square bracket
const ARRAY_TABLE_CLOSE: &[u8] = b"]]";
// ;; Standard Table
// std-table = std-table-open key *( table-key-sep key) std-table-close
fn std_table<'s, 'i>(
state: &'s RefCell<ParseState>,
) -> impl ModalParser<Input<'i>, (), ContextError> + 's {
move |i: &mut Input<'i>| {
(
delimited(
STD_TABLE_OPEN,
cut_err(key),
cut_err(STD_TABLE_CLOSE)
.context(StrContext::Expected(StrContextValue::CharLiteral('.')))
.context(StrContext::Expected(StrContextValue::StringLiteral("]"))),
)
.with_span(),
cut_err(line_trailing)
.context(StrContext::Expected(StrContextValue::CharLiteral('\n')))
.context(StrContext::Expected(StrContextValue::CharLiteral('#'))),
)
.try_map(|((h, span), t)| state.borrow_mut().deref_mut().on_std_header(h, t, span))
.parse_next(i)
}
}
// ;; Array Table
// array-table = array-table-open key *( table-key-sep key) array-table-close
fn array_table<'s, 'i>(
state: &'s RefCell<ParseState>,
) -> impl ModalParser<Input<'i>, (), ContextError> + 's {
move |i: &mut Input<'i>| {
(
delimited(
ARRAY_TABLE_OPEN,
cut_err(key),
cut_err(ARRAY_TABLE_CLOSE)
.context(StrContext::Expected(StrContextValue::CharLiteral('.')))
.context(StrContext::Expected(StrContextValue::StringLiteral("]]"))),
)
.with_span(),
cut_err(line_trailing)
.context(StrContext::Expected(StrContextValue::CharLiteral('\n')))
.context(StrContext::Expected(StrContextValue::CharLiteral('#'))),
)
.try_map(|((h, span), t)| state.borrow_mut().deref_mut().on_array_header(h, t, span))
.parse_next(i)
}
}
// ;; Table
// table = std-table / array-table
pub(crate) fn table<'s, 'i>(
state: &'s RefCell<ParseState>,
) -> impl ModalParser<Input<'i>, (), ContextError> + 's {
move |i: &mut Input<'i>| {
dispatch!(peek::<_, &[u8],_,_>(take(2usize));
b"[[" => array_table(state),
_ => std_table(state),
)
.context(StrContext::Label("table header"))
.parse_next(i)
}
}

View File

@@ -0,0 +1,166 @@
use std::ops::RangeInclusive;
use winnow::combinator::alt;
use winnow::combinator::empty;
use winnow::combinator::eof;
use winnow::combinator::fail;
use winnow::combinator::opt;
use winnow::combinator::peek;
use winnow::combinator::repeat;
use winnow::combinator::terminated;
use winnow::prelude::*;
use winnow::token::any;
use winnow::token::one_of;
use winnow::token::take_while;
use crate::parser::prelude::*;
pub(crate) unsafe fn from_utf8_unchecked<'b>(
bytes: &'b [u8],
safety_justification: &'static str,
) -> &'b str {
unsafe {
if cfg!(debug_assertions) {
// Catch problems more quickly when testing
std::str::from_utf8(bytes).expect(safety_justification)
} else {
std::str::from_utf8_unchecked(bytes)
}
}
}
// wschar = ( %x20 / ; Space
// %x09 ) ; Horizontal tab
pub(crate) const WSCHAR: (u8, u8) = (b' ', b'\t');
// ws = *wschar
pub(crate) fn ws<'i>(input: &mut Input<'i>) -> ModalResult<&'i str> {
take_while(0.., WSCHAR)
.map(|b| unsafe { from_utf8_unchecked(b, "`is_wschar` filters out on-ASCII") })
.parse_next(input)
}
// non-ascii = %x80-D7FF / %xE000-10FFFF
// - ASCII is 0xxxxxxx
// - First byte for UTF-8 is 11xxxxxx
// - Subsequent UTF-8 bytes are 10xxxxxx
pub(crate) const NON_ASCII: RangeInclusive<u8> = 0x80..=0xff;
// non-eol = %x09 / %x20-7E / non-ascii
pub(crate) const NON_EOL: (u8, RangeInclusive<u8>, RangeInclusive<u8>) =
(0x09, 0x20..=0x7E, NON_ASCII);
// comment-start-symbol = %x23 ; #
pub(crate) const COMMENT_START_SYMBOL: u8 = b'#';
// comment = comment-start-symbol *non-eol
pub(crate) fn comment(input: &mut Input<'_>) -> ModalResult<()> {
(COMMENT_START_SYMBOL, take_while(0.., NON_EOL))
.void()
.parse_next(input)
}
// newline = ( %x0A / ; LF
// %x0D.0A ) ; CRLF
pub(crate) fn newline(input: &mut Input<'_>) -> ModalResult<()> {
dispatch! {any;
b'\n' => empty,
b'\r' => one_of(LF).void(),
_ => fail,
}
.parse_next(input)
}
pub(crate) const LF: u8 = b'\n';
pub(crate) const CR: u8 = b'\r';
// ws-newline = *( wschar / newline )
pub(crate) fn ws_newline(input: &mut Input<'_>) -> ModalResult<()> {
repeat(
0..,
alt((newline.value(&b"\n"[..]), take_while(1.., WSCHAR))),
)
.map(|()| ())
.parse_next(input)
}
// ws-newlines = newline *( wschar / newline )
pub(crate) fn ws_newlines(input: &mut Input<'_>) -> ModalResult<()> {
(newline, ws_newline).void().parse_next(input)
}
// note: this rule is not present in the original grammar
// ws-comment-newline = *( ws-newline-nonempty / comment )
pub(crate) fn ws_comment_newline(input: &mut Input<'_>) -> ModalResult<()> {
let mut start = input.checkpoint();
loop {
let _ = ws.parse_next(input)?;
let next_token = opt(peek(any)).parse_next(input)?;
match next_token {
Some(b'#') => (comment, newline).void().parse_next(input)?,
Some(b'\n') => (newline).void().parse_next(input)?,
Some(b'\r') => (newline).void().parse_next(input)?,
_ => break,
}
let end = input.checkpoint();
if start == end {
break;
}
start = end;
}
Ok(())
}
// note: this rule is not present in the original grammar
// line-ending = newline / eof
pub(crate) fn line_ending(input: &mut Input<'_>) -> ModalResult<()> {
alt((newline.value("\n"), eof.value("")))
.void()
.parse_next(input)
}
// note: this rule is not present in the original grammar
// line-trailing = ws [comment] skip-line-ending
pub(crate) fn line_trailing(input: &mut Input<'_>) -> ModalResult<std::ops::Range<usize>> {
terminated((ws, opt(comment)).span(), line_ending).parse_next(input)
}
#[cfg(test)]
#[cfg(feature = "parse")]
#[cfg(feature = "display")]
mod test {
use super::*;
#[test]
fn trivia() {
let inputs = [
"",
r#" "#,
r#"
"#,
r#"
# comment
# comment2
"#,
r#"
"#,
r#"# comment
# comment2
"#,
];
for input in inputs {
dbg!(input);
let parsed = ws_comment_newline.take().parse(new_input(input));
assert!(parsed.is_ok(), "{parsed:?}");
let parsed = parsed.unwrap();
assert_eq!(parsed, input.as_bytes());
}
}
}

View File

@@ -0,0 +1,154 @@
use winnow::combinator::alt;
use winnow::combinator::fail;
use winnow::combinator::peek;
use winnow::token::any;
use crate::parser::array::array;
use crate::parser::datetime::date_time;
use crate::parser::inline_table::inline_table;
use crate::parser::numbers::{float, integer};
use crate::parser::prelude::*;
use crate::parser::strings::string;
use crate::repr::{Formatted, Repr};
use crate::RawString;
use crate::Value;
// val = string / boolean / array / inline-table / date-time / float / integer
pub(crate) fn value(input: &mut Input<'_>) -> ModalResult<Value> {
dispatch! {peek(any);
crate::parser::strings::QUOTATION_MARK |
crate::parser::strings::APOSTROPHE => string.map(|s| {
Value::String(Formatted::new(
s.into_owned()
))
}),
crate::parser::array::ARRAY_OPEN => check_recursion(array).map(Value::Array),
crate::parser::inline_table::INLINE_TABLE_OPEN => check_recursion(inline_table).map(Value::InlineTable),
// Date/number starts
b'+' | b'-' | b'0'..=b'9' => {
// Uncommon enough not to be worth optimizing at this time
alt((
date_time
.map(Value::from),
float
.map(Value::from),
integer
.map(Value::from),
))
},
// Report as if they were numbers because its most likely a typo
b'_' => {
integer
.map(Value::from)
.context(StrContext::Expected(StrContextValue::Description("leading digit")))
},
// Report as if they were numbers because its most likely a typo
b'.' => {
float
.map(Value::from)
.context(StrContext::Expected(StrContextValue::Description("leading digit")))
},
b't' => {
crate::parser::numbers::true_.map(Value::from)
.context(StrContext::Label("string"))
.context(StrContext::Expected(StrContextValue::CharLiteral('"')))
.context(StrContext::Expected(StrContextValue::CharLiteral('\'')))
},
b'f' => {
crate::parser::numbers::false_.map(Value::from)
.context(StrContext::Label("string"))
.context(StrContext::Expected(StrContextValue::CharLiteral('"')))
.context(StrContext::Expected(StrContextValue::CharLiteral('\'')))
},
b'i' => {
crate::parser::numbers::inf.map(Value::from)
.context(StrContext::Label("string"))
.context(StrContext::Expected(StrContextValue::CharLiteral('"')))
.context(StrContext::Expected(StrContextValue::CharLiteral('\'')))
},
b'n' => {
crate::parser::numbers::nan.map(Value::from)
.context(StrContext::Label("string"))
.context(StrContext::Expected(StrContextValue::CharLiteral('"')))
.context(StrContext::Expected(StrContextValue::CharLiteral('\'')))
},
_ => {
fail
.context(StrContext::Label("string"))
.context(StrContext::Expected(StrContextValue::CharLiteral('"')))
.context(StrContext::Expected(StrContextValue::CharLiteral('\'')))
},
}
.with_span()
.map(|(value, span)| apply_raw(value, span))
.parse_next(input)
}
fn apply_raw(mut val: Value, span: std::ops::Range<usize>) -> Value {
match val {
Value::String(ref mut f) => {
let raw = RawString::with_span(span);
f.set_repr_unchecked(Repr::new_unchecked(raw));
}
Value::Integer(ref mut f) => {
let raw = RawString::with_span(span);
f.set_repr_unchecked(Repr::new_unchecked(raw));
}
Value::Float(ref mut f) => {
let raw = RawString::with_span(span);
f.set_repr_unchecked(Repr::new_unchecked(raw));
}
Value::Boolean(ref mut f) => {
let raw = RawString::with_span(span);
f.set_repr_unchecked(Repr::new_unchecked(raw));
}
Value::Datetime(ref mut f) => {
let raw = RawString::with_span(span);
f.set_repr_unchecked(Repr::new_unchecked(raw));
}
Value::Array(ref mut arr) => {
arr.span = Some(span);
}
Value::InlineTable(ref mut table) => {
table.span = Some(span);
}
};
val.decorate("", "");
val
}
#[cfg(test)]
#[cfg(feature = "parse")]
#[cfg(feature = "display")]
mod test {
use super::*;
#[test]
fn values() {
let inputs = [
"1979-05-27T00:32:00.999999",
"-239",
"1e200",
"9_224_617.445_991_228_313",
r"'''I [dw]on't need \d{2} apples'''",
r#"'''
The first newline is
trimmed in raw strings.
All other whitespace
is preserved.
'''"#,
r#""Jos\u00E9\n""#,
r#""\\\"\b/\f\n\r\t\u00E9\U000A0000""#,
r#"{ hello = "world", a = 1}"#,
r#"[ { x = 1, a = "2" }, {a = "a",b = "b", c = "c"} ]"#,
];
for input in inputs {
dbg!(input);
let mut parsed = value.parse(new_input(input));
if let Ok(parsed) = &mut parsed {
parsed.despan(input);
}
assert_eq!(parsed.map(|a| a.to_string()), Ok(input.to_owned()));
}
}
}

View File

@@ -0,0 +1,188 @@
use crate::InternalString;
/// Opaque string storage for raw TOML; internal to `toml_edit`
#[derive(PartialEq, Eq, Clone, Hash)]
pub struct RawString(RawStringInner);
#[derive(PartialEq, Eq, Clone, Hash)]
enum RawStringInner {
Empty,
Explicit(InternalString),
Spanned(std::ops::Range<usize>),
}
impl RawString {
pub(crate) fn with_span(span: std::ops::Range<usize>) -> Self {
RawString(RawStringInner::Spanned(span))
}
/// Access the underlying string
///
/// This generally requires a [`DocumentMut`][crate::DocumentMut].
pub fn as_str(&self) -> Option<&str> {
match &self.0 {
RawStringInner::Empty => Some(""),
RawStringInner::Explicit(s) => Some(s.as_str()),
RawStringInner::Spanned(_) => None,
}
}
/// The location within the original document
///
/// This generally requires an [`ImDocument`][crate::ImDocument].
pub fn span(&self) -> Option<std::ops::Range<usize>> {
match &self.0 {
RawStringInner::Empty => None,
RawStringInner::Explicit(_) => None,
RawStringInner::Spanned(span) => Some(span.clone()),
}
}
pub(crate) fn to_str<'s>(&'s self, input: &'s str) -> &'s str {
match &self.0 {
RawStringInner::Empty => "",
RawStringInner::Explicit(s) => s.as_str(),
RawStringInner::Spanned(span) => input
.get(span.clone())
.unwrap_or_else(|| panic!("span {span:?} should be in input:\n```\n{input}\n```")),
}
}
pub(crate) fn to_str_with_default<'s>(
&'s self,
input: Option<&'s str>,
default: &'s str,
) -> &'s str {
match &self.0 {
RawStringInner::Empty => "",
RawStringInner::Explicit(s) => s.as_str(),
RawStringInner::Spanned(span) => {
if let Some(input) = input {
input.get(span.clone()).unwrap_or_else(|| {
panic!("span {span:?} should be in input:\n```\n{input}\n```")
})
} else {
default
}
}
}
}
pub(crate) fn despan(&mut self, input: &str) {
match &self.0 {
RawStringInner::Empty => {}
RawStringInner::Explicit(_) => {}
RawStringInner::Spanned(span) => {
if span.start == span.end {
*self = RawString(RawStringInner::Empty);
} else {
*self = Self::from(input.get(span.clone()).unwrap_or_else(|| {
panic!("span {span:?} should be in input:\n```\n{input}\n```")
}));
}
}
}
}
#[cfg(feature = "display")]
pub(crate) fn encode(&self, buf: &mut dyn std::fmt::Write, input: &str) -> std::fmt::Result {
let raw = self.to_str(input);
for part in raw.split('\r') {
write!(buf, "{part}")?;
}
Ok(())
}
#[cfg(feature = "display")]
pub(crate) fn encode_with_default(
&self,
buf: &mut dyn std::fmt::Write,
input: Option<&str>,
default: &str,
) -> std::fmt::Result {
let raw = self.to_str_with_default(input, default);
for part in raw.split('\r') {
write!(buf, "{part}")?;
}
Ok(())
}
}
impl Default for RawString {
fn default() -> Self {
Self(RawStringInner::Empty)
}
}
impl std::fmt::Debug for RawString {
#[inline]
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
match &self.0 {
RawStringInner::Empty => write!(formatter, "empty"),
RawStringInner::Explicit(s) => write!(formatter, "{s:?}"),
RawStringInner::Spanned(s) => write!(formatter, "{s:?}"),
}
}
}
impl From<&str> for RawString {
#[inline]
fn from(s: &str) -> Self {
if s.is_empty() {
Self(RawStringInner::Empty)
} else {
InternalString::from(s).into()
}
}
}
impl From<String> for RawString {
#[inline]
fn from(s: String) -> Self {
if s.is_empty() {
Self(RawStringInner::Empty)
} else {
InternalString::from(s).into()
}
}
}
impl From<&String> for RawString {
#[inline]
fn from(s: &String) -> Self {
if s.is_empty() {
Self(RawStringInner::Empty)
} else {
InternalString::from(s).into()
}
}
}
impl From<InternalString> for RawString {
#[inline]
fn from(inner: InternalString) -> Self {
Self(RawStringInner::Explicit(inner))
}
}
impl From<&InternalString> for RawString {
#[inline]
fn from(s: &InternalString) -> Self {
if s.is_empty() {
Self(RawStringInner::Empty)
} else {
InternalString::from(s).into()
}
}
}
impl From<Box<str>> for RawString {
#[inline]
fn from(s: Box<str>) -> Self {
if s.is_empty() {
Self(RawStringInner::Empty)
} else {
InternalString::from(s).into()
}
}
}

276
vendor/toml_edit-0.22.27/src/repr.rs vendored Normal file
View File

@@ -0,0 +1,276 @@
use std::borrow::Cow;
use crate::RawString;
/// A scalar TOML [`Value`][crate::Value]'s logical value and its representation in a `&str`
///
/// This includes the surrounding whitespace and comments.
#[derive(Eq, PartialEq, Clone, Hash)]
pub struct Formatted<T> {
value: T,
repr: Option<Repr>,
decor: Decor,
}
impl<T> Formatted<T>
where
T: ValueRepr,
{
/// Default-formatted value
pub fn new(value: T) -> Self {
Self {
value,
repr: None,
decor: Default::default(),
}
}
pub(crate) fn set_repr_unchecked(&mut self, repr: Repr) {
self.repr = Some(repr);
}
/// The wrapped value
pub fn value(&self) -> &T {
&self.value
}
/// The wrapped value
pub fn into_value(self) -> T {
self.value
}
/// Returns the raw representation, if available.
pub fn as_repr(&self) -> Option<&Repr> {
self.repr.as_ref()
}
/// Returns the default raw representation.
#[cfg(feature = "display")]
pub fn default_repr(&self) -> Repr {
self.value.to_repr()
}
/// Returns a raw representation.
#[cfg(feature = "display")]
pub fn display_repr(&self) -> Cow<'_, str> {
self.as_repr()
.and_then(|r| r.as_raw().as_str())
.map(Cow::Borrowed)
.unwrap_or_else(|| {
Cow::Owned(self.default_repr().as_raw().as_str().unwrap().to_owned())
})
}
/// The location within the original document
///
/// This generally requires an [`ImDocument`][crate::ImDocument].
pub fn span(&self) -> Option<std::ops::Range<usize>> {
self.repr.as_ref().and_then(|r| r.span())
}
pub(crate) fn despan(&mut self, input: &str) {
self.decor.despan(input);
if let Some(repr) = &mut self.repr {
repr.despan(input);
}
}
/// Returns the surrounding whitespace
pub fn decor_mut(&mut self) -> &mut Decor {
&mut self.decor
}
/// Returns the surrounding whitespace
pub fn decor(&self) -> &Decor {
&self.decor
}
/// Auto formats the value.
pub fn fmt(&mut self) {
self.repr = None;
}
}
impl<T> std::fmt::Debug for Formatted<T>
where
T: std::fmt::Debug,
{
#[inline]
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
let mut d = formatter.debug_struct("Formatted");
d.field("value", &self.value);
match &self.repr {
Some(r) => d.field("repr", r),
None => d.field("repr", &"default"),
};
d.field("decor", &self.decor);
d.finish()
}
}
#[cfg(feature = "display")]
impl<T> std::fmt::Display for Formatted<T>
where
T: ValueRepr,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
crate::encode::encode_formatted(self, f, None, ("", ""))
}
}
pub trait ValueRepr: crate::private::Sealed {
/// The TOML representation of the value
#[cfg(feature = "display")]
fn to_repr(&self) -> Repr;
}
#[cfg(not(feature = "display"))]
mod inner {
use super::ValueRepr;
impl ValueRepr for String {}
impl ValueRepr for i64 {}
impl ValueRepr for f64 {}
impl ValueRepr for bool {}
impl ValueRepr for toml_datetime::Datetime {}
}
/// A TOML [`Value`][crate::Value] encoded as a `&str`
#[derive(Eq, PartialEq, Clone, Hash)]
pub struct Repr {
raw_value: RawString,
}
impl Repr {
pub(crate) fn new_unchecked(raw: impl Into<RawString>) -> Self {
Repr {
raw_value: raw.into(),
}
}
/// Access the underlying value
pub fn as_raw(&self) -> &RawString {
&self.raw_value
}
/// The location within the original document
///
/// This generally requires an [`ImDocument`][crate::ImDocument].
pub fn span(&self) -> Option<std::ops::Range<usize>> {
self.raw_value.span()
}
pub(crate) fn despan(&mut self, input: &str) {
self.raw_value.despan(input);
}
#[cfg(feature = "display")]
pub(crate) fn encode(&self, buf: &mut dyn std::fmt::Write, input: &str) -> std::fmt::Result {
self.as_raw().encode(buf, input)
}
}
impl std::fmt::Debug for Repr {
#[inline]
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
self.raw_value.fmt(formatter)
}
}
/// A prefix and suffix,
///
/// Including comments, whitespaces and newlines.
#[derive(Eq, PartialEq, Clone, Default, Hash)]
pub struct Decor {
prefix: Option<RawString>,
suffix: Option<RawString>,
}
impl Decor {
/// Creates a new decor from the given prefix and suffix.
pub fn new(prefix: impl Into<RawString>, suffix: impl Into<RawString>) -> Self {
Self {
prefix: Some(prefix.into()),
suffix: Some(suffix.into()),
}
}
/// Go back to default decor
pub fn clear(&mut self) {
self.prefix = None;
self.suffix = None;
}
/// Get the prefix.
pub fn prefix(&self) -> Option<&RawString> {
self.prefix.as_ref()
}
#[cfg(feature = "display")]
pub(crate) fn prefix_encode(
&self,
buf: &mut dyn std::fmt::Write,
input: Option<&str>,
default: &str,
) -> std::fmt::Result {
if let Some(prefix) = self.prefix() {
prefix.encode_with_default(buf, input, default)
} else {
write!(buf, "{default}")
}
}
/// Set the prefix.
pub fn set_prefix(&mut self, prefix: impl Into<RawString>) {
self.prefix = Some(prefix.into());
}
/// Get the suffix.
pub fn suffix(&self) -> Option<&RawString> {
self.suffix.as_ref()
}
#[cfg(feature = "display")]
pub(crate) fn suffix_encode(
&self,
buf: &mut dyn std::fmt::Write,
input: Option<&str>,
default: &str,
) -> std::fmt::Result {
if let Some(suffix) = self.suffix() {
suffix.encode_with_default(buf, input, default)
} else {
write!(buf, "{default}")
}
}
/// Set the suffix.
pub fn set_suffix(&mut self, suffix: impl Into<RawString>) {
self.suffix = Some(suffix.into());
}
pub(crate) fn despan(&mut self, input: &str) {
if let Some(prefix) = &mut self.prefix {
prefix.despan(input);
}
if let Some(suffix) = &mut self.suffix {
suffix.despan(input);
}
}
}
impl std::fmt::Debug for Decor {
#[inline]
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
let mut d = formatter.debug_struct("Decor");
match &self.prefix {
Some(r) => d.field("prefix", r),
None => d.field("prefix", &"default"),
};
match &self.suffix {
Some(r) => d.field("suffix", r),
None => d.field("suffix", &"default"),
};
d.finish()
}
}

View File

@@ -0,0 +1,102 @@
use super::Error;
#[doc(hidden)]
pub struct SerializeValueArray {
values: Vec<crate::Item>,
}
impl SerializeValueArray {
pub(crate) fn seq(len: Option<usize>) -> Self {
let mut values = Vec::new();
if let Some(len) = len {
values.reserve(len);
}
Self { values }
}
}
impl serde::ser::SerializeSeq for SerializeValueArray {
type Ok = crate::Value;
type Error = Error;
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Error>
where
T: serde::ser::Serialize + ?Sized,
{
let value = value.serialize(super::ValueSerializer {})?;
self.values.push(crate::Item::Value(value));
Ok(())
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(crate::Value::Array(crate::Array::with_vec(self.values)))
}
}
impl serde::ser::SerializeTuple for SerializeValueArray {
type Ok = crate::Value;
type Error = Error;
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Error>
where
T: serde::ser::Serialize + ?Sized,
{
serde::ser::SerializeSeq::serialize_element(self, value)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
serde::ser::SerializeSeq::end(self)
}
}
impl serde::ser::SerializeTupleStruct for SerializeValueArray {
type Ok = crate::Value;
type Error = Error;
fn serialize_field<T>(&mut self, value: &T) -> Result<(), Error>
where
T: serde::ser::Serialize + ?Sized,
{
serde::ser::SerializeSeq::serialize_element(self, value)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
serde::ser::SerializeSeq::end(self)
}
}
pub struct SerializeTupleVariant {
variant: &'static str,
inner: SerializeValueArray,
}
impl SerializeTupleVariant {
pub(crate) fn tuple(variant: &'static str, len: usize) -> Self {
Self {
variant,
inner: SerializeValueArray::seq(Some(len)),
}
}
}
impl serde::ser::SerializeTupleVariant for SerializeTupleVariant {
type Ok = crate::Value;
type Error = Error;
fn serialize_field<T>(&mut self, value: &T) -> Result<(), Error>
where
T: serde::ser::Serialize + ?Sized,
{
serde::ser::SerializeSeq::serialize_element(&mut self.inner, value)
}
fn end(self) -> Result<Self::Ok, Self::Error> {
let inner = serde::ser::SerializeSeq::end(self.inner)?;
let mut items = crate::table::KeyValuePairs::new();
let value = crate::Item::Value(inner);
items.insert(crate::Key::new(self.variant), value);
Ok(crate::Value::InlineTable(crate::InlineTable::with_pairs(
items,
)))
}
}

173
vendor/toml_edit-0.22.27/src/ser/key.rs vendored Normal file
View File

@@ -0,0 +1,173 @@
use crate::Key;
use super::Error;
pub(crate) struct KeySerializer;
impl serde::ser::Serializer for KeySerializer {
type Ok = Key;
type Error = Error;
type SerializeSeq = serde::ser::Impossible<Self::Ok, Error>;
type SerializeTuple = serde::ser::Impossible<Self::Ok, Error>;
type SerializeTupleStruct = serde::ser::Impossible<Self::Ok, Error>;
type SerializeTupleVariant = serde::ser::Impossible<Self::Ok, Error>;
type SerializeMap = serde::ser::Impossible<Self::Ok, Error>;
type SerializeStruct = serde::ser::Impossible<Self::Ok, Error>;
type SerializeStructVariant = serde::ser::Impossible<Self::Ok, Error>;
fn serialize_bool(self, _v: bool) -> Result<Self::Ok, Self::Error> {
Err(Error::key_not_string())
}
fn serialize_i8(self, _v: i8) -> Result<Self::Ok, Self::Error> {
Err(Error::key_not_string())
}
fn serialize_i16(self, _v: i16) -> Result<Self::Ok, Self::Error> {
Err(Error::key_not_string())
}
fn serialize_i32(self, _v: i32) -> Result<Self::Ok, Self::Error> {
Err(Error::key_not_string())
}
fn serialize_i64(self, _v: i64) -> Result<Self::Ok, Self::Error> {
Err(Error::key_not_string())
}
fn serialize_u8(self, _v: u8) -> Result<Self::Ok, Self::Error> {
Err(Error::key_not_string())
}
fn serialize_u16(self, _v: u16) -> Result<Self::Ok, Self::Error> {
Err(Error::key_not_string())
}
fn serialize_u32(self, _v: u32) -> Result<Self::Ok, Self::Error> {
Err(Error::key_not_string())
}
fn serialize_u64(self, _v: u64) -> Result<Self::Ok, Self::Error> {
Err(Error::key_not_string())
}
fn serialize_f32(self, _v: f32) -> Result<Self::Ok, Self::Error> {
Err(Error::key_not_string())
}
fn serialize_f64(self, _v: f64) -> Result<Self::Ok, Self::Error> {
Err(Error::key_not_string())
}
fn serialize_char(self, _v: char) -> Result<Self::Ok, Self::Error> {
Err(Error::key_not_string())
}
fn serialize_str(self, value: &str) -> Result<Self::Ok, Self::Error> {
Ok(Key::new(value))
}
fn serialize_bytes(self, _value: &[u8]) -> Result<Self::Ok, Self::Error> {
Err(Error::key_not_string())
}
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
Err(Error::key_not_string())
}
fn serialize_some<T>(self, _value: &T) -> Result<Self::Ok, Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
Err(Error::key_not_string())
}
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
Err(Error::key_not_string())
}
fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
Err(Error::key_not_string())
}
fn serialize_unit_variant(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
) -> Result<Self::Ok, Self::Error> {
Ok(variant.into())
}
fn serialize_newtype_struct<T>(
self,
_name: &'static str,
value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
value.serialize(self)
}
fn serialize_newtype_variant<T>(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
Err(Error::key_not_string())
}
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
Err(Error::key_not_string())
}
fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
Err(Error::key_not_string())
}
fn serialize_tuple_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleStruct, Self::Error> {
Err(Error::key_not_string())
}
fn serialize_tuple_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> {
Err(Error::key_not_string())
}
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
Err(Error::key_not_string())
}
fn serialize_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeStruct, Self::Error> {
Err(Error::key_not_string())
}
fn serialize_struct_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> {
Err(Error::key_not_string())
}
}

608
vendor/toml_edit-0.22.27/src/ser/map.rs vendored Normal file
View File

@@ -0,0 +1,608 @@
use super::array::SerializeTupleVariant;
use super::array::SerializeValueArray;
use super::key::KeySerializer;
use super::value::ValueSerializer;
use super::Error;
#[doc(hidden)]
#[allow(clippy::large_enum_variant)]
pub enum SerializeMap {
Datetime(SerializeDatetime),
Table(SerializeInlineTable),
}
impl SerializeMap {
pub(crate) fn map(len: Option<usize>) -> Self {
Self::Table(SerializeInlineTable::map(len))
}
pub(crate) fn struct_(name: &'static str, len: Option<usize>) -> Self {
if name == toml_datetime::__unstable::NAME {
Self::Datetime(SerializeDatetime::new())
} else {
Self::map(len)
}
}
}
impl serde::ser::SerializeMap for SerializeMap {
type Ok = crate::Value;
type Error = Error;
fn serialize_key<T>(&mut self, input: &T) -> Result<(), Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
match self {
Self::Datetime(s) => s.serialize_key(input),
Self::Table(s) => s.serialize_key(input),
}
}
fn serialize_value<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
match self {
Self::Datetime(s) => s.serialize_value(value),
Self::Table(s) => s.serialize_value(value),
}
}
fn end(self) -> Result<Self::Ok, Self::Error> {
match self {
Self::Datetime(s) => s.end().map(|items| items.into()),
Self::Table(s) => s.end().map(|items| items.into()),
}
}
}
impl serde::ser::SerializeStruct for SerializeMap {
type Ok = crate::Value;
type Error = Error;
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
match self {
Self::Datetime(s) => s.serialize_field(key, value),
Self::Table(s) => s.serialize_field(key, value),
}
}
fn end(self) -> Result<Self::Ok, Self::Error> {
match self {
Self::Datetime(s) => s.end().map(|items| items.into()),
Self::Table(s) => s.end().map(|items| items.into()),
}
}
}
#[doc(hidden)]
pub struct SerializeDatetime {
value: Option<crate::Datetime>,
}
impl SerializeDatetime {
pub(crate) fn new() -> Self {
Self { value: None }
}
}
impl serde::ser::SerializeMap for SerializeDatetime {
type Ok = crate::Datetime;
type Error = Error;
fn serialize_key<T>(&mut self, _input: &T) -> Result<(), Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
unreachable!("datetimes should only be serialized as structs, not maps")
}
fn serialize_value<T>(&mut self, _value: &T) -> Result<(), Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
unreachable!("datetimes should only be serialized as structs, not maps")
}
fn end(self) -> Result<Self::Ok, Self::Error> {
unreachable!("datetimes should only be serialized as structs, not maps")
}
}
impl serde::ser::SerializeStruct for SerializeDatetime {
type Ok = crate::Datetime;
type Error = Error;
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
if key == toml_datetime::__unstable::FIELD {
self.value = Some(value.serialize(DatetimeFieldSerializer::default())?);
}
Ok(())
}
fn end(self) -> Result<Self::Ok, Self::Error> {
self.value.ok_or(Error::unsupported_none())
}
}
#[doc(hidden)]
pub struct SerializeInlineTable {
items: crate::table::KeyValuePairs,
key: Option<crate::Key>,
}
impl SerializeInlineTable {
pub(crate) fn map(len: Option<usize>) -> Self {
let mut items: crate::table::KeyValuePairs = Default::default();
let key = Default::default();
if let Some(len) = len {
items.reserve(len);
}
Self { items, key }
}
}
impl serde::ser::SerializeMap for SerializeInlineTable {
type Ok = crate::InlineTable;
type Error = Error;
fn serialize_key<T>(&mut self, input: &T) -> Result<(), Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
self.key = Some(input.serialize(KeySerializer)?);
Ok(())
}
fn serialize_value<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
let mut is_none = false;
let value_serializer = MapValueSerializer::new(&mut is_none);
let res = value.serialize(value_serializer);
match res {
Ok(item) => {
let key = self.key.take().unwrap();
let item = crate::Item::Value(item);
self.items.insert(key, item);
}
Err(e) => {
if !(e == Error::unsupported_none() && is_none) {
return Err(e);
}
}
}
Ok(())
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(crate::InlineTable::with_pairs(self.items))
}
}
impl serde::ser::SerializeStruct for SerializeInlineTable {
type Ok = crate::InlineTable;
type Error = Error;
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
let mut is_none = false;
let value_serializer = MapValueSerializer::new(&mut is_none);
let res = value.serialize(value_serializer);
match res {
Ok(item) => {
let item = crate::Item::Value(item);
self.items.insert(crate::Key::new(key), item);
}
Err(e) => {
if !(e == Error::unsupported_none() && is_none) {
return Err(e);
}
}
};
Ok(())
}
fn end(self) -> Result<Self::Ok, Self::Error> {
Ok(crate::InlineTable::with_pairs(self.items))
}
}
#[derive(Default)]
struct DatetimeFieldSerializer {}
impl serde::ser::Serializer for DatetimeFieldSerializer {
type Ok = toml_datetime::Datetime;
type Error = Error;
type SerializeSeq = serde::ser::Impossible<Self::Ok, Self::Error>;
type SerializeTuple = serde::ser::Impossible<Self::Ok, Self::Error>;
type SerializeTupleStruct = serde::ser::Impossible<Self::Ok, Self::Error>;
type SerializeTupleVariant = serde::ser::Impossible<Self::Ok, Self::Error>;
type SerializeMap = serde::ser::Impossible<Self::Ok, Self::Error>;
type SerializeStruct = serde::ser::Impossible<Self::Ok, Self::Error>;
type SerializeStructVariant = serde::ser::Impossible<Self::Ok, Self::Error>;
fn serialize_bool(self, _value: bool) -> Result<Self::Ok, Self::Error> {
Err(Error::date_invalid())
}
fn serialize_i8(self, _value: i8) -> Result<Self::Ok, Self::Error> {
Err(Error::date_invalid())
}
fn serialize_i16(self, _value: i16) -> Result<Self::Ok, Self::Error> {
Err(Error::date_invalid())
}
fn serialize_i32(self, _value: i32) -> Result<Self::Ok, Self::Error> {
Err(Error::date_invalid())
}
fn serialize_i64(self, _value: i64) -> Result<Self::Ok, Self::Error> {
Err(Error::date_invalid())
}
fn serialize_u8(self, _value: u8) -> Result<Self::Ok, Self::Error> {
Err(Error::date_invalid())
}
fn serialize_u16(self, _value: u16) -> Result<Self::Ok, Self::Error> {
Err(Error::date_invalid())
}
fn serialize_u32(self, _value: u32) -> Result<Self::Ok, Self::Error> {
Err(Error::date_invalid())
}
fn serialize_u64(self, _value: u64) -> Result<Self::Ok, Self::Error> {
Err(Error::date_invalid())
}
fn serialize_f32(self, _value: f32) -> Result<Self::Ok, Self::Error> {
Err(Error::date_invalid())
}
fn serialize_f64(self, _value: f64) -> Result<Self::Ok, Self::Error> {
Err(Error::date_invalid())
}
fn serialize_char(self, _value: char) -> Result<Self::Ok, Self::Error> {
Err(Error::date_invalid())
}
fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
v.parse::<toml_datetime::Datetime>().map_err(Error::custom)
}
fn serialize_bytes(self, _value: &[u8]) -> Result<Self::Ok, Self::Error> {
Err(Error::date_invalid())
}
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
Err(Error::date_invalid())
}
fn serialize_some<T>(self, _value: &T) -> Result<Self::Ok, Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
Err(Error::date_invalid())
}
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
Err(Error::date_invalid())
}
fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok, Self::Error> {
Err(Error::date_invalid())
}
fn serialize_unit_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
) -> Result<Self::Ok, Self::Error> {
Err(Error::date_invalid())
}
fn serialize_newtype_struct<T>(
self,
_name: &'static str,
_value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
Err(Error::date_invalid())
}
fn serialize_newtype_variant<T>(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
Err(Error::date_invalid())
}
fn serialize_seq(self, _len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
Err(Error::date_invalid())
}
fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple, Self::Error> {
Err(Error::date_invalid())
}
fn serialize_tuple_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleStruct, Self::Error> {
Err(Error::date_invalid())
}
fn serialize_tuple_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> {
Err(Error::date_invalid())
}
fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
Err(Error::date_invalid())
}
fn serialize_struct(
self,
_name: &'static str,
_len: usize,
) -> Result<Self::SerializeStruct, Self::Error> {
Err(Error::date_invalid())
}
fn serialize_struct_variant(
self,
_name: &'static str,
_variant_index: u32,
_variant: &'static str,
_len: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> {
Err(Error::date_invalid())
}
}
struct MapValueSerializer<'d> {
is_none: &'d mut bool,
}
impl<'d> MapValueSerializer<'d> {
fn new(is_none: &'d mut bool) -> Self {
Self { is_none }
}
}
impl serde::ser::Serializer for MapValueSerializer<'_> {
type Ok = crate::Value;
type Error = Error;
type SerializeSeq = SerializeValueArray;
type SerializeTuple = SerializeValueArray;
type SerializeTupleStruct = SerializeValueArray;
type SerializeTupleVariant = SerializeTupleVariant;
type SerializeMap = SerializeMap;
type SerializeStruct = SerializeMap;
type SerializeStructVariant = SerializeStructVariant;
fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
ValueSerializer::new().serialize_bool(v)
}
fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> {
ValueSerializer::new().serialize_i8(v)
}
fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> {
ValueSerializer::new().serialize_i16(v)
}
fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> {
ValueSerializer::new().serialize_i32(v)
}
fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {
ValueSerializer::new().serialize_i64(v)
}
fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
ValueSerializer::new().serialize_u8(v)
}
fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {
ValueSerializer::new().serialize_u16(v)
}
fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
ValueSerializer::new().serialize_u32(v)
}
fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
ValueSerializer::new().serialize_u64(v)
}
fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> {
ValueSerializer::new().serialize_f32(v)
}
fn serialize_f64(self, v: f64) -> Result<Self::Ok, Self::Error> {
ValueSerializer::new().serialize_f64(v)
}
fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
ValueSerializer::new().serialize_char(v)
}
fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
ValueSerializer::new().serialize_str(v)
}
fn serialize_bytes(self, value: &[u8]) -> Result<Self::Ok, Self::Error> {
ValueSerializer::new().serialize_bytes(value)
}
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
*self.is_none = true;
Err(Error::unsupported_none())
}
fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
ValueSerializer::new().serialize_some(value)
}
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
ValueSerializer::new().serialize_unit()
}
fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, Self::Error> {
ValueSerializer::new().serialize_unit_struct(name)
}
fn serialize_unit_variant(
self,
name: &'static str,
variant_index: u32,
variant: &'static str,
) -> Result<Self::Ok, Self::Error> {
ValueSerializer::new().serialize_unit_variant(name, variant_index, variant)
}
fn serialize_newtype_struct<T>(
self,
_name: &'static str,
value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
value.serialize(self)
}
fn serialize_newtype_variant<T>(
self,
name: &'static str,
variant_index: u32,
variant: &'static str,
value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
ValueSerializer::new().serialize_newtype_variant(name, variant_index, variant, value)
}
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
ValueSerializer::new().serialize_seq(len)
}
fn serialize_tuple(self, len: usize) -> Result<Self::SerializeTuple, Self::Error> {
ValueSerializer::new().serialize_tuple(len)
}
fn serialize_tuple_struct(
self,
name: &'static str,
len: usize,
) -> Result<Self::SerializeTupleStruct, Self::Error> {
ValueSerializer::new().serialize_tuple_struct(name, len)
}
fn serialize_tuple_variant(
self,
name: &'static str,
variant_index: u32,
variant: &'static str,
len: usize,
) -> Result<Self::SerializeTupleVariant, Self::Error> {
ValueSerializer::new().serialize_tuple_variant(name, variant_index, variant, len)
}
fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
ValueSerializer::new().serialize_map(len)
}
fn serialize_struct(
self,
name: &'static str,
len: usize,
) -> Result<Self::SerializeStruct, Self::Error> {
ValueSerializer::new().serialize_struct(name, len)
}
fn serialize_struct_variant(
self,
name: &'static str,
variant_index: u32,
variant: &'static str,
len: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> {
ValueSerializer::new().serialize_struct_variant(name, variant_index, variant, len)
}
}
pub struct SerializeStructVariant {
variant: &'static str,
inner: SerializeInlineTable,
}
impl SerializeStructVariant {
pub(crate) fn struct_(variant: &'static str, len: usize) -> Self {
Self {
variant,
inner: SerializeInlineTable::map(Some(len)),
}
}
}
impl serde::ser::SerializeStructVariant for SerializeStructVariant {
type Ok = crate::Value;
type Error = Error;
#[inline]
fn serialize_field<T>(&mut self, key: &'static str, value: &T) -> Result<(), Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
serde::ser::SerializeStruct::serialize_field(&mut self.inner, key, value)
}
#[inline]
fn end(self) -> Result<Self::Ok, Self::Error> {
let inner = serde::ser::SerializeStruct::end(self.inner)?.into();
let mut items = crate::table::KeyValuePairs::new();
let value = crate::Item::Value(inner);
items.insert(crate::Key::new(self.variant), value);
Ok(crate::Value::InlineTable(crate::InlineTable::with_pairs(
items,
)))
}
}

188
vendor/toml_edit-0.22.27/src/ser/mod.rs vendored Normal file
View File

@@ -0,0 +1,188 @@
//! Serializing Rust structures into TOML.
//!
//! This module contains all the Serde support for serializing Rust structures into TOML.
mod array;
mod key;
mod map;
mod pretty;
mod value;
use crate::visit_mut::VisitMut as _;
#[allow(clippy::wildcard_imports)]
use array::*;
#[allow(clippy::wildcard_imports)]
use map::*;
pub use value::ValueSerializer;
/// 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.
#[cfg(feature = "display")]
pub fn to_vec<T>(value: &T) -> Result<Vec<u8>, Error>
where
T: serde::ser::Serialize + ?Sized,
{
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::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_edit::ser::to_string(&config).unwrap();
/// println!("{}", toml)
/// ```
#[cfg(feature = "display")]
pub fn to_string<T>(value: &T) -> Result<String, Error>
where
T: serde::ser::Serialize + ?Sized,
{
to_document(value).map(|e| e.to_string())
}
/// 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 `ValueSerializer::pretty` for more details.
#[cfg(feature = "display")]
pub fn to_string_pretty<T>(value: &T) -> Result<String, Error>
where
T: serde::ser::Serialize + ?Sized,
{
let mut document = to_document(value)?;
pretty::Pretty::new().visit_document_mut(&mut document);
Ok(document.to_string())
}
/// Serialize the given data structure into a TOML document.
///
/// This would allow custom formatting to be applied, mixing with format preserving edits, etc.
pub fn to_document<T>(value: &T) -> Result<crate::DocumentMut, Error>
where
T: serde::ser::Serialize + ?Sized,
{
let value = value.serialize(ValueSerializer::new())?;
let item = crate::Item::Value(value);
let root = item
.into_table()
.map_err(|_| Error::UnsupportedType(None))?;
Ok(root.into())
}
/// Errors that can occur when deserializing a type.
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum Error {
/// Type could not be serialized to TOML
UnsupportedType(Option<&'static str>),
/// Value was out of range for the given type
OutOfRange(Option<&'static str>),
/// `None` could not be serialized to TOML
UnsupportedNone,
/// Key was not convertible to `String` for serializing to TOML
KeyNotString,
/// A serialized date was invalid
DateInvalid,
/// Other serialization error
Custom(String),
}
impl Error {
pub(crate) fn custom<T>(msg: T) -> Self
where
T: std::fmt::Display,
{
Error::Custom(msg.to_string())
}
pub(crate) fn unsupported_type(t: Option<&'static str>) -> Self {
Error::UnsupportedType(t)
}
fn out_of_range(t: Option<&'static str>) -> Self {
Error::OutOfRange(t)
}
pub(crate) fn unsupported_none() -> Self {
Error::UnsupportedNone
}
pub(crate) fn key_not_string() -> Self {
Error::KeyNotString
}
fn date_invalid() -> Self {
Error::DateInvalid
}
}
impl serde::ser::Error for Error {
fn custom<T>(msg: T) -> Self
where
T: std::fmt::Display,
{
Self::custom(msg)
}
}
impl std::fmt::Display for Error {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::UnsupportedType(Some(t)) => write!(formatter, "unsupported {t} type"),
Self::UnsupportedType(None) => write!(formatter, "unsupported rust type"),
Self::OutOfRange(Some(t)) => write!(formatter, "out-of-range value for {t} type"),
Self::OutOfRange(None) => write!(formatter, "out-of-range value"),
Self::UnsupportedNone => "unsupported None value".fmt(formatter),
Self::KeyNotString => "map key was not a string".fmt(formatter),
Self::DateInvalid => "a serialized date was invalid".fmt(formatter),
Self::Custom(s) => s.fmt(formatter),
}
}
}
impl From<crate::TomlError> for Error {
fn from(e: crate::TomlError) -> Error {
Self::custom(e)
}
}
impl From<Error> for crate::TomlError {
fn from(e: Error) -> crate::TomlError {
Self::custom(e.to_string(), None)
}
}
impl std::error::Error for Error {}

View File

@@ -0,0 +1,58 @@
pub(crate) struct Pretty {
in_value: bool,
}
impl Pretty {
pub(crate) fn new() -> Self {
Self { in_value: false }
}
}
impl crate::visit_mut::VisitMut for Pretty {
fn visit_document_mut(&mut self, node: &mut crate::DocumentMut) {
crate::visit_mut::visit_document_mut(self, node);
}
fn visit_item_mut(&mut self, node: &mut crate::Item) {
if !self.in_value {
node.make_item();
}
crate::visit_mut::visit_item_mut(self, node);
}
fn visit_table_mut(&mut self, node: &mut crate::Table) {
node.decor_mut().clear();
// Empty tables could be semantically meaningful, so make sure they are not implicit
if !node.is_empty() {
node.set_implicit(true);
}
crate::visit_mut::visit_table_mut(self, node);
}
fn visit_value_mut(&mut self, node: &mut crate::Value) {
node.decor_mut().clear();
let old_in_value = self.in_value;
self.in_value = true;
crate::visit_mut::visit_value_mut(self, node);
self.in_value = old_in_value;
}
fn visit_array_mut(&mut self, node: &mut crate::Array) {
crate::visit_mut::visit_array_mut(self, node);
if (0..=1).contains(&node.len()) {
node.set_trailing("");
node.set_trailing_comma(false);
} else {
for item in node.iter_mut() {
item.decor_mut().set_prefix("\n ");
}
node.set_trailing("\n");
node.set_trailing_comma(true);
}
}
}

View File

@@ -0,0 +1,249 @@
use super::Error;
use super::SerializeMap;
use super::SerializeStructVariant;
use super::SerializeTupleVariant;
use super::SerializeValueArray;
/// Serialization for TOML [values][crate::Value].
///
/// 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.
///
/// # Examples
///
/// ```
/// # #[cfg(feature = "parse")] {
/// # #[cfg(feature = "display")] {
/// use serde::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 value = serde::Serialize::serialize(
/// &config,
/// toml_edit::ser::ValueSerializer::new()
/// ).unwrap();
/// println!("{}", value)
/// # }
/// # }
/// ```
#[derive(Default)]
#[non_exhaustive]
pub struct ValueSerializer {}
impl ValueSerializer {
/// Creates a new serializer generate a TOML document.
pub fn new() -> Self {
Self {}
}
}
impl serde::ser::Serializer for ValueSerializer {
type Ok = crate::Value;
type Error = Error;
type SerializeSeq = SerializeValueArray;
type SerializeTuple = SerializeValueArray;
type SerializeTupleStruct = SerializeValueArray;
type SerializeTupleVariant = SerializeTupleVariant;
type SerializeMap = SerializeMap;
type SerializeStruct = SerializeMap;
type SerializeStructVariant = SerializeStructVariant;
fn serialize_bool(self, v: bool) -> Result<Self::Ok, Self::Error> {
Ok(v.into())
}
fn serialize_i8(self, v: i8) -> Result<Self::Ok, Self::Error> {
self.serialize_i64(v as i64)
}
fn serialize_i16(self, v: i16) -> Result<Self::Ok, Self::Error> {
self.serialize_i64(v as i64)
}
fn serialize_i32(self, v: i32) -> Result<Self::Ok, Self::Error> {
self.serialize_i64(v as i64)
}
fn serialize_i64(self, v: i64) -> Result<Self::Ok, Self::Error> {
Ok(v.into())
}
fn serialize_u8(self, v: u8) -> Result<Self::Ok, Self::Error> {
self.serialize_i64(v as i64)
}
fn serialize_u16(self, v: u16) -> Result<Self::Ok, Self::Error> {
self.serialize_i64(v as i64)
}
fn serialize_u32(self, v: u32) -> Result<Self::Ok, Self::Error> {
self.serialize_i64(v as i64)
}
fn serialize_u64(self, v: u64) -> Result<Self::Ok, Self::Error> {
let v: i64 = v
.try_into()
.map_err(|_err| Error::out_of_range(Some("u64")))?;
self.serialize_i64(v)
}
fn serialize_f32(self, v: f32) -> Result<Self::Ok, Self::Error> {
self.serialize_f64(v as f64)
}
fn serialize_f64(self, mut v: f64) -> Result<Self::Ok, Self::Error> {
// Discard sign of NaN when serialized using Serde.
//
// In all likelihood the sign of NaNs is not meaningful in the user's
// program. Ending up with `-nan` in the TOML document would usually be
// surprising and undesirable, when the sign of the NaN was not
// intentionally controlled by the caller, or may even be
// nondeterministic if it comes from arithmetic operations or a cast.
if v.is_nan() {
v = v.copysign(1.0);
}
Ok(v.into())
}
fn serialize_char(self, v: char) -> Result<Self::Ok, Self::Error> {
let mut buf = [0; 4];
self.serialize_str(v.encode_utf8(&mut buf))
}
fn serialize_str(self, v: &str) -> Result<Self::Ok, Self::Error> {
Ok(v.into())
}
fn serialize_bytes(self, value: &[u8]) -> Result<Self::Ok, Self::Error> {
use serde::ser::Serialize;
value.serialize(self)
}
fn serialize_none(self) -> Result<Self::Ok, Self::Error> {
Err(Error::unsupported_none())
}
fn serialize_some<T>(self, value: &T) -> Result<Self::Ok, Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
value.serialize(self)
}
fn serialize_unit(self) -> Result<Self::Ok, Self::Error> {
Err(Error::unsupported_type(Some("unit")))
}
fn serialize_unit_struct(self, name: &'static str) -> Result<Self::Ok, Self::Error> {
Err(Error::unsupported_type(Some(name)))
}
fn serialize_unit_variant(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
) -> Result<Self::Ok, Self::Error> {
self.serialize_str(variant)
}
fn serialize_newtype_struct<T>(
self,
_name: &'static str,
value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
value.serialize(self)
}
fn serialize_newtype_variant<T>(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
value: &T,
) -> Result<Self::Ok, Self::Error>
where
T: serde::ser::Serialize + ?Sized,
{
let value = value.serialize(self)?;
let mut table = crate::InlineTable::new();
table.insert(variant, value);
Ok(table.into())
}
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
Ok(SerializeValueArray::seq(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> {
Ok(SerializeTupleVariant::tuple(variant, len))
}
fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap, Self::Error> {
Ok(SerializeMap::map(len))
}
fn serialize_struct(
self,
name: &'static str,
len: usize,
) -> Result<Self::SerializeStruct, Self::Error> {
Ok(SerializeMap::struct_(name, Some(len)))
}
fn serialize_struct_variant(
self,
_name: &'static str,
_variant_index: u32,
variant: &'static str,
len: usize,
) -> Result<Self::SerializeStructVariant, Self::Error> {
Ok(SerializeStructVariant::struct_(variant, len))
}
}

818
vendor/toml_edit-0.22.27/src/table.rs vendored Normal file
View File

@@ -0,0 +1,818 @@
use std::iter::FromIterator;
use indexmap::map::IndexMap;
use crate::key::Key;
use crate::repr::Decor;
use crate::value::DEFAULT_VALUE_DECOR;
use crate::{InlineTable, InternalString, Item, KeyMut, Value};
/// A TOML table, a top-level collection of key/[`Value`] pairs under a header and logical
/// sub-tables
#[derive(Clone, Debug, Default)]
pub struct Table {
// Comments/spaces before and after the header
pub(crate) decor: Decor,
// Whether to hide an empty table
pub(crate) implicit: bool,
// Whether this is a proxy for dotted keys
pub(crate) dotted: bool,
// Used for putting tables back in their original order when serialising.
//
// `None` for user created tables (can be overridden with `set_position`)
doc_position: Option<usize>,
pub(crate) span: Option<std::ops::Range<usize>>,
pub(crate) items: KeyValuePairs,
}
/// Constructors
///
/// See also `FromIterator`
impl Table {
/// Creates an empty table.
pub fn new() -> Self {
Default::default()
}
pub(crate) fn with_pos(doc_position: Option<usize>) -> Self {
Self {
doc_position,
..Default::default()
}
}
pub(crate) fn with_pairs(items: KeyValuePairs) -> Self {
Self {
items,
..Default::default()
}
}
/// Convert to an inline table
pub fn into_inline_table(mut self) -> InlineTable {
for (_, value) in self.items.iter_mut() {
value.make_value();
}
let mut t = InlineTable::with_pairs(self.items);
t.fmt();
t
}
}
/// Formatting
impl Table {
/// Get key/values for values that are visually children of this table
///
/// For example, this will return dotted keys
pub fn get_values(&self) -> Vec<(Vec<&Key>, &Value)> {
let mut values = Vec::new();
let root = Vec::new();
self.append_values(&root, &mut values);
values
}
fn append_values<'s>(
&'s self,
parent: &[&'s Key],
values: &mut Vec<(Vec<&'s Key>, &'s Value)>,
) {
for (key, value) in self.items.iter() {
let mut path = parent.to_vec();
path.push(key);
match value {
Item::Table(table) if table.is_dotted() => {
table.append_values(&path, values);
}
Item::Value(value) => {
if let Some(table) = value.as_inline_table() {
if table.is_dotted() {
table.append_values(&path, values);
} else {
values.push((path, value));
}
} else {
values.push((path, value));
}
}
_ => {}
}
}
}
/// Auto formats the table.
pub fn fmt(&mut self) {
decorate_table(self);
}
/// Sorts [Key]/[Value]-pairs of the table
///
/// <div class="warning">
///
/// This sorts the syntactic table (everything under the `[header]`) and not the logical map of
/// key-value pairs.
/// This does not affect the order of [sub-tables][Table] or [sub-arrays][crate::ArrayOfTables].
/// This is not recursive.
///
/// </div>
pub fn sort_values(&mut self) {
// Assuming standard tables have their doc_position set and this won't negatively impact them
self.items.sort_keys();
for value in self.items.values_mut() {
match value {
Item::Table(table) if table.is_dotted() => {
table.sort_values();
}
_ => {}
}
}
}
/// Sort [Key]/[Value]-pairs of the table using the using the comparison function `compare`
///
/// The comparison function receives two key and value pairs to compare (you can sort by keys or
/// values or their combination as needed).
///
/// <div class="warning">
///
/// This sorts the syntactic table (everything under the `[header]`) and not the logical map of
/// key-value pairs.
/// This does not affect the order of [sub-tables][Table] or [sub-arrays][crate::ArrayOfTables].
/// This is not recursive.
///
/// </div>
pub fn sort_values_by<F>(&mut self, mut compare: F)
where
F: FnMut(&Key, &Item, &Key, &Item) -> std::cmp::Ordering,
{
self.sort_values_by_internal(&mut compare);
}
fn sort_values_by_internal<F>(&mut self, compare: &mut F)
where
F: FnMut(&Key, &Item, &Key, &Item) -> std::cmp::Ordering,
{
let modified_cmp =
|key1: &Key, val1: &Item, key2: &Key, val2: &Item| -> std::cmp::Ordering {
compare(key1, val1, key2, val2)
};
self.items.sort_by(modified_cmp);
for value in self.items.values_mut() {
match value {
Item::Table(table) if table.is_dotted() => {
table.sort_values_by_internal(compare);
}
_ => {}
}
}
}
/// If a table has no key/value pairs and implicit, it will not be displayed.
///
/// # Examples
///
/// ```notrust
/// [target."x86_64/windows.json".dependencies]
/// ```
///
/// In the document above, tables `target` and `target."x86_64/windows.json"` are implicit.
///
/// ```
/// # #[cfg(feature = "parse")] {
/// # #[cfg(feature = "display")] {
/// use toml_edit::DocumentMut;
/// let mut doc = "[a]\n[a.b]\n".parse::<DocumentMut>().expect("invalid toml");
///
/// doc["a"].as_table_mut().unwrap().set_implicit(true);
/// assert_eq!(doc.to_string(), "[a.b]\n");
/// # }
/// # }
/// ```
pub fn set_implicit(&mut self, implicit: bool) {
self.implicit = implicit;
}
/// If a table has no key/value pairs and implicit, it will not be displayed.
pub fn is_implicit(&self) -> bool {
self.implicit
}
/// Change this table's dotted status
pub fn set_dotted(&mut self, yes: bool) {
self.dotted = yes;
}
/// Check if this is a wrapper for dotted keys, rather than a standard table
pub fn is_dotted(&self) -> bool {
self.dotted
}
/// Sets the position of the `Table` within the [`DocumentMut`][crate::DocumentMut].
pub fn set_position(&mut self, doc_position: usize) {
self.doc_position = Some(doc_position);
}
/// The position of the `Table` within the [`DocumentMut`][crate::DocumentMut].
///
/// Returns `None` if the `Table` was created manually (i.e. not via parsing)
/// in which case its position is set automatically. This can be overridden with
/// [`Table::set_position`].
pub fn position(&self) -> Option<usize> {
self.doc_position
}
/// Returns the surrounding whitespace
pub fn decor_mut(&mut self) -> &mut Decor {
&mut self.decor
}
/// Returns the decor associated with a given key of the table.
pub fn decor(&self) -> &Decor {
&self.decor
}
/// Returns an accessor to a key's formatting
pub fn key(&self, key: &str) -> Option<&'_ Key> {
self.items.get_full(key).map(|(_, key, _)| key)
}
/// Returns an accessor to a key's formatting
pub fn key_mut(&mut self, key: &str) -> Option<KeyMut<'_>> {
use indexmap::map::MutableKeys;
self.items
.get_full_mut2(key)
.map(|(_, key, _)| key.as_mut())
}
/// Returns the decor associated with a given key of the table.
#[deprecated(since = "0.21.1", note = "Replaced with `key_mut`")]
pub fn key_decor_mut(&mut self, key: &str) -> Option<&mut Decor> {
#![allow(deprecated)]
use indexmap::map::MutableKeys;
self.items
.get_full_mut2(key)
.map(|(_, key, _)| key.leaf_decor_mut())
}
/// Returns the decor associated with a given key of the table.
#[deprecated(since = "0.21.1", note = "Replaced with `key_mut`")]
pub fn key_decor(&self, key: &str) -> Option<&Decor> {
#![allow(deprecated)]
self.items.get_full(key).map(|(_, key, _)| key.leaf_decor())
}
/// The location within the original document
///
/// This generally requires an [`ImDocument`][crate::ImDocument].
pub fn span(&self) -> Option<std::ops::Range<usize>> {
self.span.clone()
}
pub(crate) fn despan(&mut self, input: &str) {
use indexmap::map::MutableKeys;
self.span = None;
self.decor.despan(input);
for (key, value) in self.items.iter_mut2() {
key.despan(input);
value.despan(input);
}
}
}
impl Table {
/// Returns an iterator over all key/value pairs, including empty.
pub fn iter(&self) -> Iter<'_> {
Box::new(
self.items
.iter()
.filter(|(_, value)| !value.is_none())
.map(|(key, value)| (key.get(), value)),
)
}
/// Returns an mutable iterator over all key/value pairs, including empty.
pub fn iter_mut(&mut self) -> IterMut<'_> {
use indexmap::map::MutableKeys;
Box::new(
self.items
.iter_mut2()
.filter(|(_, value)| !value.is_none())
.map(|(key, value)| (key.as_mut(), value)),
)
}
/// Returns the number of non-empty items in the table.
pub fn len(&self) -> usize {
self.iter().count()
}
/// Returns true if the table is empty.
pub fn is_empty(&self) -> bool {
self.len() == 0
}
/// Clears the table, removing all key-value pairs. Keeps the allocated memory for reuse.
pub fn clear(&mut self) {
self.items.clear();
}
/// Gets the given key's corresponding entry in the Table for in-place manipulation.
pub fn entry<'a>(&'a mut self, key: &str) -> Entry<'a> {
// Accept a `&str` rather than an owned type to keep `InternalString`, well, internal
match self.items.entry(key.into()) {
indexmap::map::Entry::Occupied(entry) => Entry::Occupied(OccupiedEntry { entry }),
indexmap::map::Entry::Vacant(entry) => Entry::Vacant(VacantEntry { entry }),
}
}
/// Gets the given key's corresponding entry in the Table for in-place manipulation.
pub fn entry_format<'a>(&'a mut self, key: &Key) -> Entry<'a> {
// Accept a `&Key` to be consistent with `entry`
match self.items.entry(key.clone()) {
indexmap::map::Entry::Occupied(entry) => Entry::Occupied(OccupiedEntry { entry }),
indexmap::map::Entry::Vacant(entry) => Entry::Vacant(VacantEntry { entry }),
}
}
/// Returns an optional reference to an item given the key.
pub fn get<'a>(&'a self, key: &str) -> Option<&'a Item> {
self.items
.get(key)
.and_then(|value| if !value.is_none() { Some(value) } else { None })
}
/// Returns an optional mutable reference to an item given the key.
pub fn get_mut<'a>(&'a mut self, key: &str) -> Option<&'a mut Item> {
self.items
.get_mut(key)
.and_then(|value| if !value.is_none() { Some(value) } else { None })
}
/// Return references to the key-value pair stored for key, if it is present, else None.
pub fn get_key_value<'a>(&'a self, key: &str) -> Option<(&'a Key, &'a Item)> {
self.items.get_full(key).and_then(|(_, key, value)| {
if !value.is_none() {
Some((key, value))
} else {
None
}
})
}
/// Return mutable references to the key-value pair stored for key, if it is present, else None.
pub fn get_key_value_mut<'a>(&'a mut self, key: &str) -> Option<(KeyMut<'a>, &'a mut Item)> {
use indexmap::map::MutableKeys;
self.items.get_full_mut2(key).and_then(|(_, key, value)| {
if !value.is_none() {
Some((key.as_mut(), value))
} else {
None
}
})
}
/// Returns true if the table contains an item with the given key.
pub fn contains_key(&self, key: &str) -> bool {
if let Some(value) = self.items.get(key) {
!value.is_none()
} else {
false
}
}
/// Returns true if the table contains a table with the given key.
pub fn contains_table(&self, key: &str) -> bool {
if let Some(value) = self.items.get(key) {
value.is_table()
} else {
false
}
}
/// Returns true if the table contains a value with the given key.
pub fn contains_value(&self, key: &str) -> bool {
if let Some(value) = self.items.get(key) {
value.is_value()
} else {
false
}
}
/// Returns true if the table contains an array of tables with the given key.
pub fn contains_array_of_tables(&self, key: &str) -> bool {
if let Some(value) = self.items.get(key) {
value.is_array_of_tables()
} else {
false
}
}
/// Inserts a key-value pair into the map.
pub fn insert(&mut self, key: &str, item: Item) -> Option<Item> {
use indexmap::map::MutableEntryKey;
let key = Key::new(key);
match self.items.entry(key.clone()) {
indexmap::map::Entry::Occupied(mut entry) => {
entry.key_mut().fmt();
let old = std::mem::replace(entry.get_mut(), item);
Some(old)
}
indexmap::map::Entry::Vacant(entry) => {
entry.insert(item);
None
}
}
}
/// Inserts a key-value pair into the map.
pub fn insert_formatted(&mut self, key: &Key, item: Item) -> Option<Item> {
use indexmap::map::MutableEntryKey;
match self.items.entry(key.clone()) {
indexmap::map::Entry::Occupied(mut entry) => {
*entry.key_mut() = key.clone();
let old = std::mem::replace(entry.get_mut(), item);
Some(old)
}
indexmap::map::Entry::Vacant(entry) => {
entry.insert(item);
None
}
}
}
/// Removes an item given the key.
pub fn remove(&mut self, key: &str) -> Option<Item> {
self.items.shift_remove(key)
}
/// Removes a key from the map, returning the stored key and value if the key was previously in the map.
pub fn remove_entry(&mut self, key: &str) -> Option<(Key, Item)> {
self.items.shift_remove_entry(key)
}
/// Retains only the elements specified by the `keep` predicate.
///
/// In other words, remove all pairs `(key, item)` for which
/// `keep(&key, &mut item)` returns `false`.
///
/// The elements are visited in iteration order.
pub fn retain<F>(&mut self, mut keep: F)
where
F: FnMut(&str, &mut Item) -> bool,
{
self.items.retain(|key, value| keep(key, value));
}
}
#[cfg(feature = "display")]
impl std::fmt::Display for Table {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let children = self.get_values();
// print table body
for (key_path, value) in children {
crate::encode::encode_key_path_ref(&key_path, f, None, DEFAULT_KEY_DECOR)?;
write!(f, "=")?;
crate::encode::encode_value(value, f, None, DEFAULT_VALUE_DECOR)?;
writeln!(f)?;
}
Ok(())
}
}
impl<K: Into<Key>, V: Into<Item>> Extend<(K, V)> for Table {
fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) {
for (key, value) in iter {
let key = key.into();
let value = value.into();
self.items.insert(key, value);
}
}
}
impl<K: Into<Key>, V: Into<Item>> FromIterator<(K, V)> for Table {
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = (K, V)>,
{
let mut table = Table::new();
table.extend(iter);
table
}
}
impl IntoIterator for Table {
type Item = (InternalString, Item);
type IntoIter = IntoIter;
fn into_iter(self) -> Self::IntoIter {
Box::new(self.items.into_iter().map(|(k, value)| (k.into(), value)))
}
}
impl<'s> IntoIterator for &'s Table {
type Item = (&'s str, &'s Item);
type IntoIter = Iter<'s>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
pub(crate) type KeyValuePairs = IndexMap<Key, Item>;
fn decorate_table(table: &mut Table) {
use indexmap::map::MutableKeys;
for (mut key, value) in table
.items
.iter_mut2()
.filter(|(_, value)| value.is_value())
.map(|(key, value)| (key.as_mut(), value.as_value_mut().unwrap()))
{
key.leaf_decor_mut().clear();
key.dotted_decor_mut().clear();
value.decor_mut().clear();
}
}
// `key1 = value1`
pub(crate) const DEFAULT_ROOT_DECOR: (&str, &str) = ("", "");
pub(crate) const DEFAULT_KEY_DECOR: (&str, &str) = ("", " ");
pub(crate) const DEFAULT_TABLE_DECOR: (&str, &str) = ("\n", "");
pub(crate) const DEFAULT_KEY_PATH_DECOR: (&str, &str) = ("", "");
/// An owned iterator type over [`Table`]'s [`Key`]/[`Item`] pairs
pub type IntoIter = Box<dyn Iterator<Item = (InternalString, Item)>>;
/// An iterator type over [`Table`]'s [`Key`]/[`Item`] pairs
pub type Iter<'a> = Box<dyn Iterator<Item = (&'a str, &'a Item)> + 'a>;
/// A mutable iterator type over [`Table`]'s [`Key`]/[`Item`] pairs
pub type IterMut<'a> = Box<dyn Iterator<Item = (KeyMut<'a>, &'a mut Item)> + 'a>;
/// This trait represents either a `Table`, or an `InlineTable`.
pub trait TableLike: crate::private::Sealed {
/// Returns an iterator over key/value pairs.
fn iter(&self) -> Iter<'_>;
/// Returns an mutable iterator over all key/value pairs, including empty.
fn iter_mut(&mut self) -> IterMut<'_>;
/// Returns the number of nonempty items.
fn len(&self) -> usize {
self.iter().filter(|&(_, v)| !v.is_none()).count()
}
/// Returns true if the table is empty.
fn is_empty(&self) -> bool {
self.len() == 0
}
/// Clears the table, removing all key-value pairs. Keeps the allocated memory for reuse.
fn clear(&mut self);
/// Gets the given key's corresponding entry in the Table for in-place manipulation.
fn entry<'a>(&'a mut self, key: &str) -> Entry<'a>;
/// Gets the given key's corresponding entry in the Table for in-place manipulation.
fn entry_format<'a>(&'a mut self, key: &Key) -> Entry<'a>;
/// Returns an optional reference to an item given the key.
fn get<'s>(&'s self, key: &str) -> Option<&'s Item>;
/// Returns an optional mutable reference to an item given the key.
fn get_mut<'s>(&'s mut self, key: &str) -> Option<&'s mut Item>;
/// Return references to the key-value pair stored for key, if it is present, else None.
fn get_key_value<'a>(&'a self, key: &str) -> Option<(&'a Key, &'a Item)>;
/// Return mutable references to the key-value pair stored for key, if it is present, else None.
fn get_key_value_mut<'a>(&'a mut self, key: &str) -> Option<(KeyMut<'a>, &'a mut Item)>;
/// Returns true if the table contains an item with the given key.
fn contains_key(&self, key: &str) -> bool;
/// Inserts a key-value pair into the map.
fn insert(&mut self, key: &str, value: Item) -> Option<Item>;
/// Removes an item given the key.
fn remove(&mut self, key: &str) -> Option<Item>;
/// Get key/values for values that are visually children of this table
///
/// For example, this will return dotted keys
fn get_values(&self) -> Vec<(Vec<&Key>, &Value)>;
/// Auto formats the table.
fn fmt(&mut self);
/// Sorts [Key]/[Value]-pairs of the table
///
/// <div class="warning">
///
/// This sorts the syntactic table (everything under the `[header]`) and not the logical map of
/// key-value pairs.
/// This does not affect the order of [sub-tables][Table] or [sub-arrays][crate::ArrayOfTables].
/// This is not recursive.
///
/// </div>
fn sort_values(&mut self);
/// Change this table's dotted status
fn set_dotted(&mut self, yes: bool);
/// Check if this is a wrapper for dotted keys, rather than a standard table
fn is_dotted(&self) -> bool;
/// Returns an accessor to a key's formatting
fn key(&self, key: &str) -> Option<&'_ Key>;
/// Returns an accessor to a key's formatting
fn key_mut(&mut self, key: &str) -> Option<KeyMut<'_>>;
/// Returns the decor associated with a given key of the table.
#[deprecated(since = "0.21.1", note = "Replaced with `key_mut`")]
fn key_decor_mut(&mut self, key: &str) -> Option<&mut Decor>;
/// Returns the decor associated with a given key of the table.
#[deprecated(since = "0.21.1", note = "Replaced with `key_mut`")]
fn key_decor(&self, key: &str) -> Option<&Decor>;
}
impl TableLike for Table {
fn iter(&self) -> Iter<'_> {
self.iter()
}
fn iter_mut(&mut self) -> IterMut<'_> {
self.iter_mut()
}
fn clear(&mut self) {
self.clear();
}
fn entry<'a>(&'a mut self, key: &str) -> Entry<'a> {
self.entry(key)
}
fn entry_format<'a>(&'a mut self, key: &Key) -> Entry<'a> {
self.entry_format(key)
}
fn get<'s>(&'s self, key: &str) -> Option<&'s Item> {
self.get(key)
}
fn get_mut<'s>(&'s mut self, key: &str) -> Option<&'s mut Item> {
self.get_mut(key)
}
fn get_key_value<'a>(&'a self, key: &str) -> Option<(&'a Key, &'a Item)> {
self.get_key_value(key)
}
fn get_key_value_mut<'a>(&'a mut self, key: &str) -> Option<(KeyMut<'a>, &'a mut Item)> {
self.get_key_value_mut(key)
}
fn contains_key(&self, key: &str) -> bool {
self.contains_key(key)
}
fn insert(&mut self, key: &str, value: Item) -> Option<Item> {
self.insert(key, value)
}
fn remove(&mut self, key: &str) -> Option<Item> {
self.remove(key)
}
fn get_values(&self) -> Vec<(Vec<&Key>, &Value)> {
self.get_values()
}
fn fmt(&mut self) {
self.fmt();
}
fn sort_values(&mut self) {
self.sort_values();
}
fn is_dotted(&self) -> bool {
self.is_dotted()
}
fn set_dotted(&mut self, yes: bool) {
self.set_dotted(yes);
}
fn key(&self, key: &str) -> Option<&'_ Key> {
self.key(key)
}
fn key_mut(&mut self, key: &str) -> Option<KeyMut<'_>> {
self.key_mut(key)
}
fn key_decor_mut(&mut self, key: &str) -> Option<&mut Decor> {
#![allow(deprecated)]
self.key_decor_mut(key)
}
fn key_decor(&self, key: &str) -> Option<&Decor> {
#![allow(deprecated)]
self.key_decor(key)
}
}
/// A view into a single location in a [`Table`], which may be vacant or occupied.
pub enum Entry<'a> {
/// An occupied Entry.
Occupied(OccupiedEntry<'a>),
/// A vacant Entry.
Vacant(VacantEntry<'a>),
}
impl<'a> Entry<'a> {
/// Returns the entry key
///
/// # Examples
///
/// ```
/// use toml_edit::Table;
///
/// let mut map = Table::new();
///
/// assert_eq!("hello", map.entry("hello").key());
/// ```
pub fn key(&self) -> &str {
match self {
Entry::Occupied(e) => e.key(),
Entry::Vacant(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: Item) -> &'a mut Item {
match self {
Entry::Occupied(entry) => entry.into_mut(),
Entry::Vacant(entry) => entry.insert(default),
}
}
/// 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: FnOnce() -> Item>(self, default: F) -> &'a mut Item {
match self {
Entry::Occupied(entry) => entry.into_mut(),
Entry::Vacant(entry) => entry.insert(default()),
}
}
}
/// A view into a single occupied location in a [`Table`].
pub struct OccupiedEntry<'a> {
pub(crate) entry: indexmap::map::OccupiedEntry<'a, Key, Item>,
}
impl<'a> OccupiedEntry<'a> {
/// Gets a reference to the entry key
///
/// # Examples
///
/// ```
/// use toml_edit::Table;
///
/// let mut map = Table::new();
///
/// assert_eq!("foo", map.entry("foo").key());
/// ```
pub fn key(&self) -> &str {
self.entry.key().get()
}
/// Gets a mutable reference to the entry key
pub fn key_mut(&mut self) -> KeyMut<'_> {
use indexmap::map::MutableEntryKey;
self.entry.key_mut().as_mut()
}
/// Gets a reference to the value in the entry.
pub fn get(&self) -> &Item {
self.entry.get()
}
/// Gets a mutable reference to the value in the entry.
pub fn get_mut(&mut self) -> &mut Item {
self.entry.get_mut()
}
/// Converts the `OccupiedEntry` into a mutable reference to the value in the entry
/// with a lifetime bound to the map itself
pub fn into_mut(self) -> &'a mut Item {
self.entry.into_mut()
}
/// Sets the value of the entry, and returns the entry's old value
pub fn insert(&mut self, value: Item) -> Item {
self.entry.insert(value)
}
/// Takes the value out of the entry, and returns it
pub fn remove(self) -> Item {
self.entry.shift_remove()
}
}
/// A view into a single empty location in a [`Table`].
pub struct VacantEntry<'a> {
pub(crate) entry: indexmap::map::VacantEntry<'a, Key, Item>,
}
impl<'a> VacantEntry<'a> {
/// Gets a reference to the entry key
///
/// # Examples
///
/// ```
/// use toml_edit::Table;
///
/// let mut map = Table::new();
///
/// assert_eq!("foo", map.entry("foo").key());
/// ```
pub fn key(&self) -> &str {
self.entry.key().get()
}
/// Sets the value of the entry with the `VacantEntry`'s key,
/// and returns a mutable reference to it
pub fn insert(self, value: Item) -> &'a mut Item {
let entry = self.entry;
entry.insert(value)
}
}

392
vendor/toml_edit-0.22.27/src/value.rs vendored Normal file
View File

@@ -0,0 +1,392 @@
use std::iter::FromIterator;
use std::str::FromStr;
use toml_datetime::{Date, Datetime, Time};
use crate::key::Key;
use crate::repr::{Decor, Formatted};
use crate::{Array, InlineTable, InternalString, RawString};
/// For [`Key`]/Value pairs under a [`Table`][crate::Table] header or inside another
/// Value
#[derive(Debug, Clone)]
pub enum Value {
/// A string value.
String(Formatted<String>),
/// A 64-bit integer value.
Integer(Formatted<i64>),
/// A 64-bit float value.
Float(Formatted<f64>),
/// A boolean value.
Boolean(Formatted<bool>),
/// An RFC 3339 formatted date-time with offset.
Datetime(Formatted<Datetime>),
/// An inline array of values.
Array(Array),
/// An inline table of key/value pairs.
InlineTable(InlineTable),
}
/// Downcasting
impl Value {
/// Text description of value type
pub fn type_name(&self) -> &'static str {
match self {
Value::String(..) => "string",
Value::Integer(..) => "integer",
Value::Float(..) => "float",
Value::Boolean(..) => "boolean",
Value::Datetime(..) => "datetime",
Value::Array(..) => "array",
Value::InlineTable(..) => "inline table",
}
}
/// Casts `self` to str.
pub fn as_str(&self) -> Option<&str> {
match *self {
Value::String(ref value) => Some(value.value()),
_ => None,
}
}
/// Returns true if `self` is a string.
pub fn is_str(&self) -> bool {
self.as_str().is_some()
}
/// Casts `self` to integer.
pub fn as_integer(&self) -> Option<i64> {
match *self {
Value::Integer(ref value) => Some(*value.value()),
_ => None,
}
}
/// Returns true if `self` is an integer.
pub fn is_integer(&self) -> bool {
self.as_integer().is_some()
}
/// Casts `self` to float.
pub fn as_float(&self) -> Option<f64> {
match *self {
Value::Float(ref value) => Some(*value.value()),
_ => None,
}
}
/// Returns true if `self` is a float.
pub fn is_float(&self) -> bool {
self.as_float().is_some()
}
/// Casts `self` to boolean.
pub fn as_bool(&self) -> Option<bool> {
match *self {
Value::Boolean(ref value) => Some(*value.value()),
_ => None,
}
}
/// Returns true if `self` is a boolean.
pub fn is_bool(&self) -> bool {
self.as_bool().is_some()
}
/// Casts `self` to date-time.
pub fn as_datetime(&self) -> Option<&Datetime> {
match *self {
Value::Datetime(ref value) => Some(value.value()),
_ => None,
}
}
/// Returns true if `self` is a date-time.
pub fn is_datetime(&self) -> bool {
self.as_datetime().is_some()
}
/// Casts `self` to array.
pub fn as_array(&self) -> Option<&Array> {
match *self {
Value::Array(ref value) => Some(value),
_ => None,
}
}
/// Casts `self` to mutable array.
pub fn as_array_mut(&mut self) -> Option<&mut Array> {
match *self {
Value::Array(ref mut value) => Some(value),
_ => None,
}
}
/// Returns true if `self` is an array.
pub fn is_array(&self) -> bool {
self.as_array().is_some()
}
/// Casts `self` to inline table.
pub fn as_inline_table(&self) -> Option<&InlineTable> {
match *self {
Value::InlineTable(ref value) => Some(value),
_ => None,
}
}
/// Casts `self` to mutable inline table.
pub fn as_inline_table_mut(&mut self) -> Option<&mut InlineTable> {
match *self {
Value::InlineTable(ref mut value) => Some(value),
_ => None,
}
}
/// Returns true if `self` is an inline table.
pub fn is_inline_table(&self) -> bool {
self.as_inline_table().is_some()
}
}
impl Value {
/// Get the decoration of the value.
/// # Example
/// ```rust
/// let v = toml_edit::Value::from(true);
/// assert_eq!(v.decor().suffix(), None);
///```
pub fn decor_mut(&mut self) -> &mut Decor {
match self {
Value::String(f) => f.decor_mut(),
Value::Integer(f) => f.decor_mut(),
Value::Float(f) => f.decor_mut(),
Value::Boolean(f) => f.decor_mut(),
Value::Datetime(f) => f.decor_mut(),
Value::Array(a) => a.decor_mut(),
Value::InlineTable(t) => t.decor_mut(),
}
}
/// Get the decoration of the value.
/// # Example
/// ```rust
/// let v = toml_edit::Value::from(true);
/// assert_eq!(v.decor().suffix(), None);
///```
pub fn decor(&self) -> &Decor {
match *self {
Value::String(ref f) => f.decor(),
Value::Integer(ref f) => f.decor(),
Value::Float(ref f) => f.decor(),
Value::Boolean(ref f) => f.decor(),
Value::Datetime(ref f) => f.decor(),
Value::Array(ref a) => a.decor(),
Value::InlineTable(ref t) => t.decor(),
}
}
/// Sets the prefix and the suffix for value.
/// # Example
/// ```rust
/// # #[cfg(feature = "display")] {
/// let mut v = toml_edit::Value::from(42);
/// assert_eq!(&v.to_string(), "42");
/// let d = v.decorated(" ", " ");
/// assert_eq!(&d.to_string(), " 42 ");
/// # }
/// ```
pub fn decorated(mut self, prefix: impl Into<RawString>, suffix: impl Into<RawString>) -> Self {
self.decorate(prefix, suffix);
self
}
pub(crate) fn decorate(&mut self, prefix: impl Into<RawString>, suffix: impl Into<RawString>) {
let decor = self.decor_mut();
*decor = Decor::new(prefix, suffix);
}
/// The location within the original document
///
/// This generally requires an [`ImDocument`][crate::ImDocument].
pub fn span(&self) -> Option<std::ops::Range<usize>> {
match self {
Value::String(f) => f.span(),
Value::Integer(f) => f.span(),
Value::Float(f) => f.span(),
Value::Boolean(f) => f.span(),
Value::Datetime(f) => f.span(),
Value::Array(a) => a.span(),
Value::InlineTable(t) => t.span(),
}
}
pub(crate) fn despan(&mut self, input: &str) {
match self {
Value::String(f) => f.despan(input),
Value::Integer(f) => f.despan(input),
Value::Float(f) => f.despan(input),
Value::Boolean(f) => f.despan(input),
Value::Datetime(f) => f.despan(input),
Value::Array(a) => a.despan(input),
Value::InlineTable(t) => t.despan(input),
}
}
}
#[cfg(feature = "parse")]
impl FromStr for Value {
type Err = crate::TomlError;
/// Parses a value from a &str
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut value = crate::parser::parse_value(s)?;
// Only take the repr and not decor, as its probably not intended
value.decor_mut().clear();
value.despan(s);
Ok(value)
}
}
impl<'b> From<&'b Value> for Value {
fn from(s: &'b Value) -> Self {
s.clone()
}
}
impl<'b> From<&'b str> for Value {
fn from(s: &'b str) -> Self {
s.to_owned().into()
}
}
impl<'b> From<&'b String> for Value {
fn from(s: &'b String) -> Self {
s.to_owned().into()
}
}
impl From<String> for Value {
fn from(s: String) -> Self {
Value::String(Formatted::new(s))
}
}
impl<'b> From<&'b InternalString> for Value {
fn from(s: &'b InternalString) -> Self {
s.as_str().into()
}
}
impl From<InternalString> for Value {
fn from(s: InternalString) -> Self {
s.as_str().into()
}
}
impl From<i64> for Value {
fn from(i: i64) -> Self {
Value::Integer(Formatted::new(i))
}
}
impl From<f64> for Value {
fn from(f: f64) -> Self {
// Preserve sign of NaN. It may get written to TOML as `-nan`.
Value::Float(Formatted::new(f))
}
}
impl From<bool> for Value {
fn from(b: bool) -> Self {
Value::Boolean(Formatted::new(b))
}
}
impl From<Datetime> for Value {
fn from(d: Datetime) -> Self {
Value::Datetime(Formatted::new(d))
}
}
impl From<Date> for Value {
fn from(d: Date) -> Self {
let d: Datetime = d.into();
d.into()
}
}
impl From<Time> for Value {
fn from(d: Time) -> Self {
let d: Datetime = d.into();
d.into()
}
}
impl From<Array> for Value {
fn from(array: Array) -> Self {
Value::Array(array)
}
}
impl From<InlineTable> for Value {
fn from(table: InlineTable) -> Self {
Value::InlineTable(table)
}
}
impl<V: Into<Value>> FromIterator<V> for Value {
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = V>,
{
let array: Array = iter.into_iter().collect();
Value::Array(array)
}
}
impl<K: Into<Key>, V: Into<Value>> FromIterator<(K, V)> for Value {
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = (K, V)>,
{
let table: InlineTable = iter.into_iter().collect();
Value::InlineTable(table)
}
}
#[cfg(feature = "display")]
impl std::fmt::Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
crate::encode::encode_value(self, f, None, ("", ""))
}
}
// `key1 = value1`
pub(crate) const DEFAULT_VALUE_DECOR: (&str, &str) = (" ", "");
// `{ key = value }`
pub(crate) const DEFAULT_TRAILING_VALUE_DECOR: (&str, &str) = (" ", " ");
// `[value1, value2]`
pub(crate) const DEFAULT_LEADING_VALUE_DECOR: (&str, &str) = ("", "");
#[cfg(test)]
#[cfg(feature = "parse")]
#[cfg(feature = "display")]
mod tests {
use super::*;
#[test]
fn from_iter_formatting() {
let features = ["node".to_owned(), "mouth".to_owned()];
let features: Value = features.iter().cloned().collect();
assert_eq!(features.to_string(), r#"["node", "mouth"]"#);
}
}
#[test]
#[cfg(feature = "parse")]
#[cfg(feature = "display")]
fn string_roundtrip() {
Value::from("hello").to_string().parse::<Value>().unwrap();
}

239
vendor/toml_edit-0.22.27/src/visit.rs vendored Normal file
View File

@@ -0,0 +1,239 @@
#![allow(missing_docs)]
//! Document tree traversal to walk a shared borrow of a document tree.
//!
//! Each method of the [`Visit`] trait is a hook that can be overridden
//! to customize the behavior when mutating the corresponding type of node.
//! By default, every method recursively visits the substructure of the
//! input by invoking the right visitor method of each of its fields.
//!
//! ```
//! # use toml_edit::{Item, ArrayOfTables, Table, Value};
//!
//! pub trait Visit<'doc> {
//! /* ... */
//!
//! fn visit_item(&mut self, i: &'doc Item) {
//! visit_item(self, i);
//! }
//!
//! /* ... */
//! # fn visit_value(&mut self, i: &'doc Value);
//! # fn visit_table(&mut self, i: &'doc Table);
//! # fn visit_array_of_tables(&mut self, i: &'doc ArrayOfTables);
//! }
//!
//! pub fn visit_item<'doc, V>(v: &mut V, node: &'doc Item)
//! where
//! V: Visit<'doc> + ?Sized,
//! {
//! match node {
//! Item::None => {}
//! Item::Value(value) => v.visit_value(value),
//! Item::Table(table) => v.visit_table(table),
//! Item::ArrayOfTables(array) => v.visit_array_of_tables(array),
//! }
//! }
//! ```
//!
//! The API is modeled after [`syn::visit`](https://docs.rs/syn/1/syn/visit).
//!
//! # Examples
//!
//! This visitor stores every string in the document.
//!
//! ```
//! # #[cfg(feature = "parse")] {
//! # use toml_edit::*;
//! use toml_edit::visit::*;
//!
//! #[derive(Default)]
//! struct StringCollector<'doc> {
//! strings: Vec<&'doc str>,
//! }
//!
//! impl<'doc> Visit<'doc> for StringCollector<'doc> {
//! fn visit_string(&mut self, node: &'doc Formatted<String>) {
//! self.strings.push(node.value().as_str());
//! }
//! }
//!
//! let input = r#"
//! laputa = "sky-castle"
//! the-force = { value = "surrounds-you" }
//! "#;
//!
//! let mut document: DocumentMut = input.parse().unwrap();
//! let mut visitor = StringCollector::default();
//! visitor.visit_document(&document);
//!
//! assert_eq!(visitor.strings, vec!["sky-castle", "surrounds-you"]);
//! # }
//! ```
//!
//! For a more complex example where the visitor has internal state, see `examples/visit.rs`
//! [on GitHub](https://github.com/toml-rs/toml/blob/main/crates/toml_edit/examples/visit.rs).
use crate::{
Array, ArrayOfTables, Datetime, DocumentMut, Formatted, InlineTable, Item, Table, TableLike,
Value,
};
/// Document tree traversal to mutate an exclusive borrow of a document tree in-place.
///
/// See the [module documentation](self) for details.
pub trait Visit<'doc> {
fn visit_document(&mut self, node: &'doc DocumentMut) {
visit_document(self, node);
}
fn visit_item(&mut self, node: &'doc Item) {
visit_item(self, node);
}
fn visit_table(&mut self, node: &'doc Table) {
visit_table(self, node);
}
fn visit_inline_table(&mut self, node: &'doc InlineTable) {
visit_inline_table(self, node);
}
fn visit_table_like(&mut self, node: &'doc dyn TableLike) {
visit_table_like(self, node);
}
fn visit_table_like_kv(&mut self, key: &'doc str, node: &'doc Item) {
visit_table_like_kv(self, key, node);
}
fn visit_array(&mut self, node: &'doc Array) {
visit_array(self, node);
}
fn visit_array_of_tables(&mut self, node: &'doc ArrayOfTables) {
visit_array_of_tables(self, node);
}
fn visit_value(&mut self, node: &'doc Value) {
visit_value(self, node);
}
fn visit_boolean(&mut self, node: &'doc Formatted<bool>) {
visit_boolean(self, node);
}
fn visit_datetime(&mut self, node: &'doc Formatted<Datetime>) {
visit_datetime(self, node);
}
fn visit_float(&mut self, node: &'doc Formatted<f64>) {
visit_float(self, node);
}
fn visit_integer(&mut self, node: &'doc Formatted<i64>) {
visit_integer(self, node);
}
fn visit_string(&mut self, node: &'doc Formatted<String>) {
visit_string(self, node);
}
}
pub fn visit_document<'doc, V>(v: &mut V, node: &'doc DocumentMut)
where
V: Visit<'doc> + ?Sized,
{
v.visit_table(node.as_table());
}
pub fn visit_item<'doc, V>(v: &mut V, node: &'doc Item)
where
V: Visit<'doc> + ?Sized,
{
match node {
Item::None => {}
Item::Value(value) => v.visit_value(value),
Item::Table(table) => v.visit_table(table),
Item::ArrayOfTables(array) => v.visit_array_of_tables(array),
}
}
pub fn visit_table<'doc, V>(v: &mut V, node: &'doc Table)
where
V: Visit<'doc> + ?Sized,
{
v.visit_table_like(node);
}
pub fn visit_inline_table<'doc, V>(v: &mut V, node: &'doc InlineTable)
where
V: Visit<'doc> + ?Sized,
{
v.visit_table_like(node);
}
pub fn visit_table_like<'doc, V>(v: &mut V, node: &'doc dyn TableLike)
where
V: Visit<'doc> + ?Sized,
{
for (key, item) in node.iter() {
v.visit_table_like_kv(key, item);
}
}
pub fn visit_table_like_kv<'doc, V>(v: &mut V, _key: &'doc str, node: &'doc Item)
where
V: Visit<'doc> + ?Sized,
{
v.visit_item(node);
}
pub fn visit_array<'doc, V>(v: &mut V, node: &'doc Array)
where
V: Visit<'doc> + ?Sized,
{
for value in node.iter() {
v.visit_value(value);
}
}
pub fn visit_array_of_tables<'doc, V>(v: &mut V, node: &'doc ArrayOfTables)
where
V: Visit<'doc> + ?Sized,
{
for table in node.iter() {
v.visit_table(table);
}
}
pub fn visit_value<'doc, V>(v: &mut V, node: &'doc Value)
where
V: Visit<'doc> + ?Sized,
{
match node {
Value::String(s) => v.visit_string(s),
Value::Integer(i) => v.visit_integer(i),
Value::Float(f) => v.visit_float(f),
Value::Boolean(b) => v.visit_boolean(b),
Value::Datetime(dt) => v.visit_datetime(dt),
Value::Array(array) => v.visit_array(array),
Value::InlineTable(table) => v.visit_inline_table(table),
}
}
macro_rules! empty_visit {
($name: ident, $t: ty) => {
fn $name<'doc, V>(_v: &mut V, _node: &'doc $t)
where
V: Visit<'doc> + ?Sized,
{
}
};
}
empty_visit!(visit_boolean, Formatted<bool>);
empty_visit!(visit_datetime, Formatted<Datetime>);
empty_visit!(visit_float, Formatted<f64>);
empty_visit!(visit_integer, Formatted<i64>);
empty_visit!(visit_string, Formatted<String>);

View File

@@ -0,0 +1,256 @@
#![allow(missing_docs)]
//! Document tree traversal to mutate an exclusive borrow of a document tree in place.
//!
//!
//! Each method of the [`VisitMut`] trait is a hook that can be overridden
//! to customize the behavior when mutating the corresponding type of node.
//! By default, every method recursively visits the substructure of the
//! input by invoking the right visitor method of each of its fields.
//!
//! ```
//! # use toml_edit::{Item, ArrayOfTables, Table, Value};
//!
//! pub trait VisitMut {
//! /* ... */
//!
//! fn visit_item_mut(&mut self, i: &mut Item) {
//! visit_item_mut(self, i);
//! }
//!
//! /* ... */
//! # fn visit_value_mut(&mut self, i: &mut Value);
//! # fn visit_table_mut(&mut self, i: &mut Table);
//! # fn visit_array_of_tables_mut(&mut self, i: &mut ArrayOfTables);
//! }
//!
//! pub fn visit_item_mut<V>(v: &mut V, node: &mut Item)
//! where
//! V: VisitMut + ?Sized,
//! {
//! match node {
//! Item::None => {}
//! Item::Value(value) => v.visit_value_mut(value),
//! Item::Table(table) => v.visit_table_mut(table),
//! Item::ArrayOfTables(array) => v.visit_array_of_tables_mut(array),
//! }
//! }
//! ```
//!
//! The API is modeled after [`syn::visit_mut`](https://docs.rs/syn/1/syn/visit_mut).
//!
//! # Examples
//!
//! This visitor replaces every floating point value with its decimal string representation, to
//! 2 decimal points.
//!
//! ```
//! # #[cfg(feature = "parse")] {
//! # #[cfg(feature = "display")] {
//! # use toml_edit::*;
//! use toml_edit::visit_mut::*;
//!
//! struct FloatToString;
//!
//! impl VisitMut for FloatToString {
//! fn visit_value_mut(&mut self, node: &mut Value) {
//! if let Value::Float(f) = node {
//! // Convert the float to a string.
//! let mut s = Formatted::new(format!("{:.2}", f.value()));
//! // Copy over the formatting.
//! std::mem::swap(s.decor_mut(), f.decor_mut());
//! *node = Value::String(s);
//! }
//! // Most of the time, you will also need to call the default implementation to recurse
//! // further down the document tree.
//! visit_value_mut(self, node);
//! }
//! }
//!
//! let input = r#"
//! banana = 3.26
//! table = { apple = 4.5 }
//! "#;
//!
//! let mut document: DocumentMut = input.parse().unwrap();
//! let mut visitor = FloatToString;
//! visitor.visit_document_mut(&mut document);
//!
//! let output = r#"
//! banana = "3.26"
//! table = { apple = "4.50" }
//! "#;
//!
//! assert_eq!(format!("{}", document), output);
//! # }
//! # }
//! ```
//!
//! For a more complex example where the visitor has internal state, see `examples/visit.rs`
//! [on GitHub](https://github.com/toml-rs/toml/blob/main/crates/toml_edit/examples/visit.rs).
use crate::{
Array, ArrayOfTables, Datetime, DocumentMut, Formatted, InlineTable, Item, KeyMut, Table,
TableLike, Value,
};
/// Document tree traversal to mutate an exclusive borrow of a document tree in-place.
///
/// See the [module documentation](self) for details.
pub trait VisitMut {
fn visit_document_mut(&mut self, node: &mut DocumentMut) {
visit_document_mut(self, node);
}
fn visit_item_mut(&mut self, node: &mut Item) {
visit_item_mut(self, node);
}
fn visit_table_mut(&mut self, node: &mut Table) {
visit_table_mut(self, node);
}
fn visit_inline_table_mut(&mut self, node: &mut InlineTable) {
visit_inline_table_mut(self, node);
}
/// [`visit_table_mut`](Self::visit_table_mut) and
/// [`visit_inline_table_mut`](Self::visit_inline_table_mut) both recurse into this method.
fn visit_table_like_mut(&mut self, node: &mut dyn TableLike) {
visit_table_like_mut(self, node);
}
fn visit_table_like_kv_mut(&mut self, key: KeyMut<'_>, node: &mut Item) {
visit_table_like_kv_mut(self, key, node);
}
fn visit_array_mut(&mut self, node: &mut Array) {
visit_array_mut(self, node);
}
fn visit_array_of_tables_mut(&mut self, node: &mut ArrayOfTables) {
visit_array_of_tables_mut(self, node);
}
fn visit_value_mut(&mut self, node: &mut Value) {
visit_value_mut(self, node);
}
fn visit_boolean_mut(&mut self, node: &mut Formatted<bool>) {
visit_boolean_mut(self, node);
}
fn visit_datetime_mut(&mut self, node: &mut Formatted<Datetime>) {
visit_datetime_mut(self, node);
}
fn visit_float_mut(&mut self, node: &mut Formatted<f64>) {
visit_float_mut(self, node);
}
fn visit_integer_mut(&mut self, node: &mut Formatted<i64>) {
visit_integer_mut(self, node);
}
fn visit_string_mut(&mut self, node: &mut Formatted<String>) {
visit_string_mut(self, node);
}
}
pub fn visit_document_mut<V>(v: &mut V, node: &mut DocumentMut)
where
V: VisitMut + ?Sized,
{
v.visit_table_mut(node.as_table_mut());
}
pub fn visit_item_mut<V>(v: &mut V, node: &mut Item)
where
V: VisitMut + ?Sized,
{
match node {
Item::None => {}
Item::Value(value) => v.visit_value_mut(value),
Item::Table(table) => v.visit_table_mut(table),
Item::ArrayOfTables(array) => v.visit_array_of_tables_mut(array),
}
}
pub fn visit_table_mut<V>(v: &mut V, node: &mut Table)
where
V: VisitMut + ?Sized,
{
v.visit_table_like_mut(node);
}
pub fn visit_inline_table_mut<V>(v: &mut V, node: &mut InlineTable)
where
V: VisitMut + ?Sized,
{
v.visit_table_like_mut(node);
}
pub fn visit_table_like_mut<V>(v: &mut V, node: &mut dyn TableLike)
where
V: VisitMut + ?Sized,
{
for (key, item) in node.iter_mut() {
v.visit_table_like_kv_mut(key, item);
}
}
pub fn visit_table_like_kv_mut<V>(v: &mut V, _key: KeyMut<'_>, node: &mut Item)
where
V: VisitMut + ?Sized,
{
v.visit_item_mut(node);
}
pub fn visit_array_mut<V>(v: &mut V, node: &mut Array)
where
V: VisitMut + ?Sized,
{
for value in node.iter_mut() {
v.visit_value_mut(value);
}
}
pub fn visit_array_of_tables_mut<V>(v: &mut V, node: &mut ArrayOfTables)
where
V: VisitMut + ?Sized,
{
for table in node.iter_mut() {
v.visit_table_mut(table);
}
}
pub fn visit_value_mut<V>(v: &mut V, node: &mut Value)
where
V: VisitMut + ?Sized,
{
match node {
Value::String(s) => v.visit_string_mut(s),
Value::Integer(i) => v.visit_integer_mut(i),
Value::Float(f) => v.visit_float_mut(f),
Value::Boolean(b) => v.visit_boolean_mut(b),
Value::Datetime(dt) => v.visit_datetime_mut(dt),
Value::Array(array) => v.visit_array_mut(array),
Value::InlineTable(table) => v.visit_inline_table_mut(table),
}
}
macro_rules! empty_visit_mut {
($name: ident, $t: ty) => {
fn $name<V>(_v: &mut V, _node: &mut $t)
where
V: VisitMut + ?Sized,
{
}
};
}
empty_visit_mut!(visit_boolean_mut, Formatted<bool>);
empty_visit_mut!(visit_datetime_mut, Formatted<Datetime>);
empty_visit_mut!(visit_float_mut, Formatted<f64>);
empty_visit_mut!(visit_integer_mut, Formatted<i64>);
empty_visit_mut!(visit_string_mut, Formatted<String>);