Skip to content

Commit bb96b3a

Browse files
committed
Auto merge of #10086 - epage:toml, r=ehuss
Port cargo from toml-rs to toml_edit Benefits: - A TOML 1.0 compliant parser - Unblock future work - Have `cargo init` add the current crate to the workspace, rather than error - #5586: Upstream `cargo-add` TODO - [x] Analyze performance and address regressions - [x] Identify and resolve incompatibiies - [x] Resolve remaining test failures, see https://github.com/ordian/toml_edit/labels/cargo - [x] ~~Switch the code from #10176 to only parse once~~ (this PR is being merged first)
2 parents 7e89966 + 320c279 commit bb96b3a

34 files changed

+176
-38
lines changed

Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ strip-ansi-escapes = "0.1.0"
5757
tar = { version = "0.4.36", default-features = false }
5858
tempfile = "3.0"
5959
termcolor = "1.1"
60-
toml = "0.5.7"
60+
toml_edit = { version = "0.13", features = ["serde", "easy"] }
6161
unicode-xid = "0.2.0"
6262
url = "2.2.2"
6363
walkdir = "2.2"

benches/capture/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ description = "Tool for capturing a real-world workspace for benchmarking."
99
cargo_metadata = "0.14.0"
1010
flate2 = { version = "1.0.3", default-features = false, features = ["zlib"] }
1111
tar = { version = "0.4.35", default-features = false }
12-
toml = "0.5.8"
12+
toml_edit = { version = "0.9.1", features = ["easy"] }

benches/capture/src/main.rs

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use flate2::{Compression, GzBuilder};
88
use std::fs;
99
use std::path::{Path, PathBuf};
1010
use std::process::Command;
11+
use toml_edit::easy as toml;
1112

1213
fn main() {
1314
let force = std::env::args().any(|arg| arg == "-f");

crates/cargo-test-support/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ remove_dir_all = "0.5"
2121
serde_json = "1.0"
2222
tar = { version = "0.4.18", default-features = false }
2323
termcolor = "1.1.2"
24-
toml = "0.5.7"
24+
toml_edit = { version = "0.9.1", features = ["serde", "easy"] }
2525
url = "2.2.2"
2626

2727
[features]

crates/cargo-test-support/src/registry.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -709,7 +709,7 @@ impl Package {
709709
if !self.cargo_features.is_empty() {
710710
manifest.push_str(&format!(
711711
"cargo-features = {}\n\n",
712-
toml::to_string(&self.cargo_features).unwrap()
712+
toml_edit::ser::to_item(&self.cargo_features).unwrap()
713713
));
714714
}
715715

src/cargo/core/manifest.rs

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use anyhow::Context as _;
99
use semver::Version;
1010
use serde::ser;
1111
use serde::Serialize;
12+
use toml_edit::easy as toml;
1213
use url::Url;
1314

1415
use crate::core::compiler::{CompileKind, CrateType};

src/cargo/core/package.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use lazycell::LazyCell;
1616
use log::{debug, warn};
1717
use semver::Version;
1818
use serde::Serialize;
19+
use toml_edit::easy as toml;
1920

2021
use crate::core::compiler::{CompileKind, RustcTargetData};
2122
use crate::core::dependency::DepKind;
@@ -199,7 +200,7 @@ impl Package {
199200
.manifest()
200201
.original()
201202
.prepare_for_publish(ws, self.root())?;
202-
let toml = toml::to_string(&manifest)?;
203+
let toml = toml::to_string_pretty(&manifest)?;
203204
Ok(format!("{}\n{}", MANIFEST_PREAMBLE, toml))
204205
}
205206

src/cargo/core/workspace.rs

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use anyhow::{bail, Context as _};
88
use glob::glob;
99
use itertools::Itertools;
1010
use log::debug;
11+
use toml_edit::easy as toml;
1112
use url::Url;
1213

1314
use crate::core::features::Features;

src/cargo/ops/cargo_config.rs

+9-4
Original file line numberDiff line numberDiff line change
@@ -127,19 +127,24 @@ fn print_toml(config: &Config, opts: &GetOptions<'_>, key: &ConfigKey, cv: &CV)
127127
config,
128128
"{} = {}{}",
129129
key,
130-
toml::to_string(&val).unwrap(),
130+
toml_edit::Value::from(val),
131131
origin(def)
132132
),
133133
CV::List(vals, _def) => {
134134
if opts.show_origin {
135135
drop_println!(config, "{} = [", key);
136136
for (val, def) in vals {
137-
drop_println!(config, " {}, # {}", toml::to_string(&val).unwrap(), def);
137+
drop_println!(
138+
config,
139+
" {}, # {}",
140+
toml_edit::ser::to_item(&val).unwrap(),
141+
def
142+
);
138143
}
139144
drop_println!(config, "]");
140145
} else {
141-
let vals: Vec<&String> = vals.iter().map(|x| &x.0).collect();
142-
drop_println!(config, "{} = {}", key, toml::to_string(&vals).unwrap());
146+
let vals: toml_edit::Array = vals.iter().map(|x| &x.0).collect();
147+
drop_println!(config, "{} = {}", key, vals);
143148
}
144149
}
145150
CV::Table(table, _def) => {

src/cargo/ops/cargo_new.rs

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use std::io::{BufRead, BufReader, ErrorKind};
1212
use std::path::{Path, PathBuf};
1313
use std::process::Command;
1414
use std::str::{from_utf8, FromStr};
15+
use toml_edit::easy as toml;
1516

1617
#[derive(Clone, Copy, Debug, PartialEq)]
1718
pub enum VersionControl {

src/cargo/ops/cargo_output_metadata.rs

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use cargo_platform::Platform;
1010
use serde::Serialize;
1111
use std::collections::BTreeMap;
1212
use std::path::PathBuf;
13+
use toml_edit::easy as toml;
1314

1415
const VERSION: u32 = 1;
1516

src/cargo/ops/common_for_install_and_uninstall.rs

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use std::rc::Rc;
77

88
use anyhow::{bail, format_err, Context as _};
99
use serde::{Deserialize, Serialize};
10+
use toml_edit::easy as toml;
1011

1112
use crate::core::compiler::Freshness;
1213
use crate::core::{Dependency, FeatureValue, Package, PackageId, Source, SourceId};

src/cargo/ops/lockfile.rs

+16-6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::util::toml as cargo_toml;
66
use crate::util::Filesystem;
77

88
use anyhow::Context as _;
9+
use toml_edit::easy as toml;
910

1011
pub fn load_pkg_lockfile(ws: &Workspace<'_>) -> CargoResult<Option<Resolve>> {
1112
if !ws.root().join("Cargo.lock").exists() {
@@ -100,7 +101,7 @@ fn resolve_to_string_orig(
100101
}
101102

102103
fn serialize_resolve(resolve: &Resolve, orig: Option<&str>) -> String {
103-
let toml = toml::Value::try_from(resolve).unwrap();
104+
let toml = toml_edit::ser::to_item(resolve).unwrap();
104105

105106
let mut out = String::new();
106107

@@ -139,7 +140,7 @@ fn serialize_resolve(resolve: &Resolve, orig: Option<&str>) -> String {
139140

140141
let deps = toml["package"].as_array().unwrap();
141142
for dep in deps {
142-
let dep = dep.as_table().unwrap();
143+
let dep = dep.as_inline_table().unwrap();
143144

144145
out.push_str("[[package]]\n");
145146
emit_package(dep, &mut out);
@@ -149,14 +150,23 @@ fn serialize_resolve(resolve: &Resolve, orig: Option<&str>) -> String {
149150
let list = patch["unused"].as_array().unwrap();
150151
for entry in list {
151152
out.push_str("[[patch.unused]]\n");
152-
emit_package(entry.as_table().unwrap(), &mut out);
153+
emit_package(entry.as_inline_table().unwrap(), &mut out);
153154
out.push('\n');
154155
}
155156
}
156157

157158
if let Some(meta) = toml.get("metadata") {
158-
out.push_str("[metadata]\n");
159-
out.push_str(&meta.to_string());
159+
// 1. We need to ensure we print the entire tree, not just the direct members of `metadata`
160+
// (which `toml_edit::Table::to_string` only shows)
161+
// 2. We need to ensure all children tables have `metadata.` prefix
162+
let meta_table = meta
163+
.clone()
164+
.into_table()
165+
.expect("validation ensures this is a table");
166+
let mut meta_doc = toml_edit::Document::new();
167+
meta_doc["metadata"] = toml_edit::Item::Table(meta_table);
168+
169+
out.push_str(&meta_doc.to_string());
160170
}
161171

162172
// Historical versions of Cargo in the old format accidentally left trailing
@@ -190,7 +200,7 @@ fn are_equal_lockfiles(orig: &str, current: &str, ws: &Workspace<'_>) -> bool {
190200
orig.lines().eq(current.lines())
191201
}
192202

193-
fn emit_package(dep: &toml::value::Table, out: &mut String) {
203+
fn emit_package(dep: &toml_edit::InlineTable, out: &mut String) {
194204
out.push_str(&format!("name = {}\n", &dep["name"]));
195205
out.push_str(&format!("version = {}\n", &dep["version"]));
196206

src/cargo/ops/vendor.rs

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use std::collections::{BTreeMap, BTreeSet, HashMap};
1212
use std::fs::{self, File, OpenOptions};
1313
use std::io::{Read, Write};
1414
use std::path::{Path, PathBuf};
15+
use toml_edit::easy as toml;
1516

1617
pub struct VendorOptions<'a> {
1718
pub no_delete: bool,

src/cargo/util/config/key.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,6 @@ fn escape_key_part<'a>(part: &'a str) -> Cow<'a, str> {
111111
Cow::Borrowed(part)
112112
} else {
113113
// This is a bit messy, but toml doesn't expose a function to do this.
114-
Cow::Owned(toml::to_string(&toml::Value::String(part.to_string())).unwrap())
114+
Cow::Owned(toml_edit::Value::from(part).to_string())
115115
}
116116
}

src/cargo/util/config/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ use cargo_util::paths;
7979
use curl::easy::Easy;
8080
use lazycell::LazyCell;
8181
use serde::Deserialize;
82+
use toml_edit::easy as toml;
8283
use url::Url;
8384

8485
mod de;

src/cargo/util/config/target.rs

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use crate::util::CargoResult;
44
use serde::Deserialize;
55
use std::collections::{BTreeMap, HashMap};
66
use std::path::PathBuf;
7+
use toml_edit::easy as toml;
78

89
/// Config definition of a `[target.'cfg(…)']` table.
910
///

src/cargo/util/toml/mod.rs

+18-2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use semver::{self, VersionReq};
1313
use serde::de;
1414
use serde::ser;
1515
use serde::{Deserialize, Serialize};
16+
use toml_edit::easy as toml;
1617
use url::Url;
1718

1819
use crate::core::compiler::{CompileKind, CompileTarget};
@@ -77,16 +78,21 @@ pub fn read_manifest_from_str(
7778
let pretty_filename = manifest_file
7879
.strip_prefix(config.cwd())
7980
.unwrap_or(manifest_file);
80-
parse(contents, pretty_filename, config)?
81+
parse_document(contents, pretty_filename, config)?
8182
};
8283

8384
// Provide a helpful error message for a common user error.
8485
if let Some(package) = toml.get("package").or_else(|| toml.get("project")) {
8586
if let Some(feats) = package.get("cargo-features") {
87+
let mut feats = feats.clone();
88+
if let Some(value) = feats.as_value_mut() {
89+
// Only keep formatting inside of the `[]` and not formatting around it
90+
value.decor_mut().clear();
91+
}
8692
bail!(
8793
"cargo-features = {} was found in the wrong location: it \
8894
should be set at the top of Cargo.toml before any tables",
89-
toml::to_string(feats).unwrap()
95+
feats.to_string()
9096
);
9197
}
9298
}
@@ -164,6 +170,16 @@ pub fn parse(toml: &str, _file: &Path, _config: &Config) -> CargoResult<toml::Va
164170
.map_err(|e| anyhow::Error::from(e).context("could not parse input as TOML"))
165171
}
166172

173+
pub fn parse_document(
174+
toml: &str,
175+
_file: &Path,
176+
_config: &Config,
177+
) -> CargoResult<toml_edit::Document> {
178+
// At the moment, no compatibility checks are needed.
179+
toml.parse()
180+
.map_err(|e| anyhow::Error::from(e).context("could not parse input as TOML"))
181+
}
182+
167183
type TomlLibTarget = TomlTarget;
168184
type TomlBinTarget = TomlTarget;
169185
type TomlExampleTarget = TomlTarget;

tests/testsuite/bad_config.rs

+33-3
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,12 @@ Caused by:
196196
could not parse input as TOML
197197
198198
Caused by:
199-
expected an equals, found eof at line 1 column 2
199+
TOML parse error at line 1, column 2
200+
|
201+
1 | 4
202+
| ^
203+
Unexpected end of input
204+
Expected `.` or `=`
200205
",
201206
)
202207
.run();
@@ -442,7 +447,13 @@ Caused by:
442447
could not parse input as TOML
443448
444449
Caused by:
445-
expected a table key, found a newline at line 8 column 27
450+
TOML parse error at line 8, column 27
451+
|
452+
8 | native = {
453+
| ^
454+
Unexpected `
455+
`
456+
Expected key
446457
",
447458
)
448459
.run();
@@ -781,7 +792,26 @@ fn invalid_toml_historically_allowed_fails() {
781792

782793
p.cargo("build")
783794
.with_status(101)
784-
.with_stderr_contains(" expected newline, found an identifier at line 1 column 7")
795+
.with_stderr(
796+
"\
797+
error: could not load Cargo configuration
798+
799+
Caused by:
800+
could not parse TOML configuration in `[..]`
801+
802+
Caused by:
803+
could not parse input as TOML
804+
805+
Caused by:
806+
TOML parse error at line 1, column 7
807+
|
808+
1 | [bar] baz = 2
809+
| ^
810+
Unexpected `b`
811+
Expected newline or end of input
812+
While parsing a Table Header
813+
",
814+
)
785815
.run();
786816
}
787817

tests/testsuite/build.rs

+18-3
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,12 @@ Caused by:
198198
could not parse input as TOML
199199
200200
Caused by:
201-
invalid TOML value, did you mean to use a quoted string? at line 3 column 23
201+
TOML parse error at line 3, column 23
202+
|
203+
3 | foo = bar
204+
| ^
205+
Unexpected `b`
206+
Expected quoted string
202207
",
203208
)
204209
.run();
@@ -218,7 +223,12 @@ Caused by:
218223
could not parse input as TOML
219224
220225
Caused by:
221-
invalid TOML value, did you mean to use a quoted string? at line 1 column 5
226+
TOML parse error at line 1, column 5
227+
|
228+
1 | a = bar
229+
| ^
230+
Unexpected `b`
231+
Expected quoted string
222232
",
223233
)
224234
.run();
@@ -2773,7 +2783,12 @@ Caused by:
27732783
could not parse input as TOML
27742784
27752785
Caused by:
2776-
expected an equals, found an identifier at line 1 column 6
2786+
TOML parse error at line 1, column 6
2787+
|
2788+
1 | this is not valid toml
2789+
| ^
2790+
Unexpected `i`
2791+
Expected `.` or `=`
27772792
",
27782793
)
27792794
.run();

0 commit comments

Comments
 (0)