diff --git a/src/cargo/ops/cargo_add/dependency.rs b/src/cargo/ops/cargo_add/dependency.rs index e86f12e033c..d70dd29cd78 100644 --- a/src/cargo/ops/cargo_add/dependency.rs +++ b/src/cargo/ops/cargo_add/dependency.rs @@ -490,7 +490,7 @@ impl Dependency { } else if let Some(table) = item.as_table_like_mut() { match &self.source { Some(Source::Registry(src)) => { - table.insert("version", toml_edit::value(src.version.as_str())); + overwrite_value(table, "version", src.version.as_str()); for key in ["path", "git", "branch", "tag", "rev", "workspace"] { table.remove(key); @@ -498,9 +498,9 @@ impl Dependency { } Some(Source::Path(src)) => { let relpath = path_field(crate_root, &src.path); - table.insert("path", toml_edit::value(relpath)); + overwrite_value(table, "path", relpath); if let Some(r) = src.version.as_deref() { - table.insert("version", toml_edit::value(r)); + overwrite_value(table, "version", r); } else { table.remove("version"); } @@ -510,24 +510,24 @@ impl Dependency { } } Some(Source::Git(src)) => { - table.insert("git", toml_edit::value(src.git.as_str())); + overwrite_value(table, "git", src.git.as_str()); if let Some(branch) = src.branch.as_deref() { - table.insert("branch", toml_edit::value(branch)); + overwrite_value(table, "branch", branch); } else { table.remove("branch"); } if let Some(tag) = src.tag.as_deref() { - table.insert("tag", toml_edit::value(tag)); + overwrite_value(table, "tag", tag); } else { table.remove("tag"); } if let Some(rev) = src.rev.as_deref() { - table.insert("rev", toml_edit::value(rev)); + overwrite_value(table, "rev", rev); } else { table.remove("rev"); } if let Some(r) = src.version.as_deref() { - table.insert("version", toml_edit::value(r)); + overwrite_value(table, "version", r); } else { table.remove("version"); } @@ -537,7 +537,7 @@ impl Dependency { } } Some(Source::Workspace(_)) => { - table.insert("workspace", toml_edit::value(true)); + overwrite_value(table, "workspace", true); table.set_dotted(true); key.fmt(); for key in [ @@ -559,7 +559,7 @@ impl Dependency { } if table.contains_key("version") { if let Some(r) = self.registry.as_deref() { - table.insert("registry", toml_edit::value(r)); + overwrite_value(table, "registry", r); } else { table.remove("registry"); } @@ -568,11 +568,11 @@ impl Dependency { } if self.rename.is_some() { - table.insert("package", toml_edit::value(self.name.as_str())); + overwrite_value(table, "package", self.name.as_str()); } match self.default_features { Some(v) => { - table.insert("default-features", toml_edit::value(v)); + overwrite_value(table, "default-features", v); } None => { table.remove("default-features"); @@ -590,29 +590,46 @@ impl Dependency { }) .unwrap_or_default(); features.extend(new_features.iter().map(|s| s.as_str())); - let features = toml_edit::value(features.into_iter().collect::()); + let features = features.into_iter().collect::(); table.set_dotted(false); - table.insert("features", features); + overwrite_value(table, "features", features); } else { table.remove("features"); } match self.optional { Some(v) => { table.set_dotted(false); - table.insert("optional", toml_edit::value(v)); + overwrite_value(table, "optional", v); } None => { table.remove("optional"); } } - - table.fmt(); } else { unreachable!("Invalid dependency type: {}", item.type_name()); } } } +/// Overwrite a value while preserving the original formatting +fn overwrite_value( + table: &mut dyn toml_edit::TableLike, + key: &str, + value: impl Into, +) { + let mut value = value.into(); + + let existing = table.entry(key).or_insert(toml_edit::Item::None); + let existing_decor = existing + .as_value() + .map(|v| v.decor().clone()) + .unwrap_or_default(); + + *value.decor_mut() = existing_decor; + + *existing = toml_edit::Item::Value(value); +} + fn invalid_type(dep: &str, key: &str, actual: &str, expected: &str) -> anyhow::Error { anyhow::format_err!("Found {actual} for {key} when {expected} was expected for {dep}") } diff --git a/tests/testsuite/cargo_add/mod.rs b/tests/testsuite/cargo_add/mod.rs index 1eb682ef2ed..2287f8d95e1 100644 --- a/tests/testsuite/cargo_add/mod.rs +++ b/tests/testsuite/cargo_add/mod.rs @@ -77,6 +77,7 @@ mod overwrite_optional_with_no_optional; mod overwrite_path_noop; mod overwrite_path_with_version; mod overwrite_preserves_inline_table; +mod overwrite_preserves_std_table; mod overwrite_rename_with_no_rename; mod overwrite_rename_with_rename; mod overwrite_rename_with_rename_noop; diff --git a/tests/testsuite/cargo_add/overwrite_preserves_std_table/in/Cargo.toml b/tests/testsuite/cargo_add/overwrite_preserves_std_table/in/Cargo.toml new file mode 100644 index 00000000000..e84097b9790 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_preserves_std_table/in/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies.your-face] +version="99999.0.0" # Hello world +features=["eyes"] # Goodbye moon diff --git a/tests/testsuite/cargo_add/overwrite_preserves_std_table/in/src/lib.rs b/tests/testsuite/cargo_add/overwrite_preserves_std_table/in/src/lib.rs new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/testsuite/cargo_add/overwrite_preserves_std_table/mod.rs b/tests/testsuite/cargo_add/overwrite_preserves_std_table/mod.rs new file mode 100644 index 00000000000..f27143d6ae0 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_preserves_std_table/mod.rs @@ -0,0 +1,25 @@ +use cargo_test_support::compare::assert_ui; +use cargo_test_support::prelude::*; +use cargo_test_support::Project; + +use crate::cargo_add::init_registry; +use cargo_test_support::curr_dir; + +#[cargo_test] +fn overwrite_preserves_std_table() { + init_registry(); + let project = Project::from_template(curr_dir!().join("in")); + let project_root = project.root(); + let cwd = &project_root; + + snapbox::cmd::Command::cargo_ui() + .arg("add") + .arg_line("your-face --features nose") + .current_dir(cwd) + .assert() + .success() + .stdout_matches_path(curr_dir!().join("stdout.log")) + .stderr_matches_path(curr_dir!().join("stderr.log")); + + assert_ui().subset_matches(curr_dir!().join("out"), &project_root); +} diff --git a/tests/testsuite/cargo_add/overwrite_preserves_std_table/out/Cargo.toml b/tests/testsuite/cargo_add/overwrite_preserves_std_table/out/Cargo.toml new file mode 100644 index 00000000000..76b898bafe0 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_preserves_std_table/out/Cargo.toml @@ -0,0 +1,9 @@ +[workspace] + +[package] +name = "cargo-list-test-fixture" +version = "0.0.0" + +[dependencies.your-face] +version="99999.0.0" # Hello world +features=["eyes", "nose"] # Goodbye moon diff --git a/tests/testsuite/cargo_add/overwrite_preserves_std_table/stderr.log b/tests/testsuite/cargo_add/overwrite_preserves_std_table/stderr.log new file mode 100644 index 00000000000..615459052e4 --- /dev/null +++ b/tests/testsuite/cargo_add/overwrite_preserves_std_table/stderr.log @@ -0,0 +1,7 @@ + Updating `dummy-registry` index + Adding your-face v99999.0.0 to dependencies. + Features: + + eyes + + nose + - ears + - mouth diff --git a/tests/testsuite/cargo_add/overwrite_preserves_std_table/stdout.log b/tests/testsuite/cargo_add/overwrite_preserves_std_table/stdout.log new file mode 100644 index 00000000000..e69de29bb2d