From 791460f64582450b8ca7181c897ca2bb242c0769 Mon Sep 17 00:00:00 2001
From: ranger-ross <rosssullivan101@gmail.com>
Date: Thu, 26 Dec 2024 15:23:28 +0900
Subject: [PATCH] Added logic to preserve carriage returns when updating the
 manifest file

---
 src/cargo/util/toml_mut/manifest.rs           | 33 ++++++++++++++++-
 tests/testsuite/cargo_add/mod.rs              |  1 +
 .../preserve_carriage_returns/in/Cargo.toml   |  6 ++++
 .../preserve_carriage_returns/in/src/lib.rs   |  0
 .../preserve_carriage_returns/mod.rs          | 36 +++++++++++++++++++
 .../preserve_carriage_returns/out/Cargo.toml  |  9 +++++
 .../preserve_carriage_returns/stderr.term.svg | 31 ++++++++++++++++
 tests/testsuite/cargo_remove/mod.rs           |  1 +
 .../preserve_carriage_returns/in/Cargo.toml   |  9 +++++
 .../preserve_carriage_returns/in/src/lib.rs   |  0
 .../preserve_carriage_returns/mod.rs          | 36 +++++++++++++++++++
 .../preserve_carriage_returns/out/Cargo.toml  |  6 ++++
 .../preserve_carriage_returns/stderr.term.svg | 27 ++++++++++++++
 13 files changed, 194 insertions(+), 1 deletion(-)
 create mode 100644 tests/testsuite/cargo_add/preserve_carriage_returns/in/Cargo.toml
 create mode 100644 tests/testsuite/cargo_add/preserve_carriage_returns/in/src/lib.rs
 create mode 100644 tests/testsuite/cargo_add/preserve_carriage_returns/mod.rs
 create mode 100644 tests/testsuite/cargo_add/preserve_carriage_returns/out/Cargo.toml
 create mode 100644 tests/testsuite/cargo_add/preserve_carriage_returns/stderr.term.svg
 create mode 100644 tests/testsuite/cargo_remove/preserve_carriage_returns/in/Cargo.toml
 create mode 100644 tests/testsuite/cargo_remove/preserve_carriage_returns/in/src/lib.rs
 create mode 100644 tests/testsuite/cargo_remove/preserve_carriage_returns/mod.rs
 create mode 100644 tests/testsuite/cargo_remove/preserve_carriage_returns/out/Cargo.toml
 create mode 100644 tests/testsuite/cargo_remove/preserve_carriage_returns/stderr.term.svg

diff --git a/src/cargo/util/toml_mut/manifest.rs b/src/cargo/util/toml_mut/manifest.rs
index 79fd24029de..03f43afe1d4 100644
--- a/src/cargo/util/toml_mut/manifest.rs
+++ b/src/cargo/util/toml_mut/manifest.rs
@@ -300,7 +300,7 @@ impl LocalManifest {
     /// Write changes back to the file.
     pub fn write(&self) -> CargoResult<()> {
         let mut manifest = self.manifest.data.to_string();
-        let raw = match self.embedded.as_ref() {
+        let mut raw = match self.embedded.as_ref() {
             Some(Embedded::Implicit(start)) => {
                 if !manifest.ends_with("\n") {
                     manifest.push_str("\n");
@@ -321,6 +321,11 @@ impl LocalManifest {
             }
             None => manifest,
         };
+
+        if self.is_crlf() {
+            raw = to_crlf_line_ending(&raw);
+        }
+
         let new_contents_bytes = raw.as_bytes();
 
         cargo_util::paths::write_atomic(&self.path, new_contents_bytes)
@@ -566,6 +571,10 @@ impl LocalManifest {
         }
         status
     }
+
+    fn is_crlf(&self) -> bool {
+        return self.raw.contains("\r\n");
+    }
 }
 
 impl std::fmt::Display for LocalManifest {
@@ -753,3 +762,25 @@ fn remove_array_index(array: &mut toml_edit::Array, index: usize) {
         array.set_trailing(merged_lines);
     }
 }
+
+fn to_crlf_line_ending(input: &str) -> String {
+    let mut result = String::with_capacity(input.len());
+    let mut chars = input.chars().peekable();
+
+    while let Some(c) = chars.next() {
+        match c {
+            '\r' if chars.peek() == Some(&'\n') => {
+                chars.next();
+                result.push('\r');
+                result.push('\n');
+            }
+            '\n' => {
+                result.push('\r');
+                result.push('\n');
+            }
+            _ => result.push(c),
+        }
+    }
+
+    result
+}
diff --git a/tests/testsuite/cargo_add/mod.rs b/tests/testsuite/cargo_add/mod.rs
index 35ab71346c6..28ba890bc9c 100644
--- a/tests/testsuite/cargo_add/mod.rs
+++ b/tests/testsuite/cargo_add/mod.rs
@@ -120,6 +120,7 @@ mod path_base_unstable;
 mod path_dev;
 mod path_inferred_name;
 mod path_inferred_name_conflicts_full_feature;
+mod preserve_carriage_returns;
 mod preserve_dep_std_table;
 mod preserve_features_sorted;
 mod preserve_features_table;
diff --git a/tests/testsuite/cargo_add/preserve_carriage_returns/in/Cargo.toml b/tests/testsuite/cargo_add/preserve_carriage_returns/in/Cargo.toml
new file mode 100644
index 00000000000..48c5359a5ba
--- /dev/null
+++ b/tests/testsuite/cargo_add/preserve_carriage_returns/in/Cargo.toml
@@ -0,0 +1,6 @@
+[workspace]
+
+[package]
+name = "cargo-list-test-fixture"
+version = "0.0.0"
+edition = "2015"
diff --git a/tests/testsuite/cargo_add/preserve_carriage_returns/in/src/lib.rs b/tests/testsuite/cargo_add/preserve_carriage_returns/in/src/lib.rs
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/testsuite/cargo_add/preserve_carriage_returns/mod.rs b/tests/testsuite/cargo_add/preserve_carriage_returns/mod.rs
new file mode 100644
index 00000000000..59e64083de0
--- /dev/null
+++ b/tests/testsuite/cargo_add/preserve_carriage_returns/mod.rs
@@ -0,0 +1,36 @@
+use cargo_test_support::compare::assert_ui;
+use cargo_test_support::current_dir;
+use cargo_test_support::file;
+use cargo_test_support::prelude::*;
+use cargo_test_support::str;
+use cargo_test_support::Project;
+
+#[cargo_test]
+fn case() {
+    cargo_test_support::registry::init();
+    cargo_test_support::registry::Package::new("my-package", "0.1.0").publish();
+
+    let project = Project::from_template(current_dir!().join("in"));
+    let project_root = project.root();
+    let cwd = &project_root;
+
+    snapbox::cmd::Command::cargo_ui()
+        .arg("add")
+        .arg_line("my-package")
+        .current_dir(cwd)
+        .assert()
+        .success()
+        .stdout_eq(str![""])
+        .stderr_eq(file!["stderr.term.svg"]);
+
+    // Verify the content matches first (for nicer error output)
+    assert_ui().subset_matches(current_dir!().join("out"), &project_root);
+
+    // Snapbox normalizes lines so we also need to do a string comparision to verify line endings
+    let expected = current_dir!().join("out/Cargo.toml");
+    let actual = project_root.join("Cargo.toml");
+    assert_eq!(
+        std::fs::read_to_string(expected).unwrap(),
+        std::fs::read_to_string(actual).unwrap()
+    );
+}
diff --git a/tests/testsuite/cargo_add/preserve_carriage_returns/out/Cargo.toml b/tests/testsuite/cargo_add/preserve_carriage_returns/out/Cargo.toml
new file mode 100644
index 00000000000..f9c0a3d7665
--- /dev/null
+++ b/tests/testsuite/cargo_add/preserve_carriage_returns/out/Cargo.toml
@@ -0,0 +1,9 @@
+[workspace]
+
+[package]
+name = "cargo-list-test-fixture"
+version = "0.0.0"
+edition = "2015"
+
+[dependencies]
+my-package = "0.1.0"
diff --git a/tests/testsuite/cargo_add/preserve_carriage_returns/stderr.term.svg b/tests/testsuite/cargo_add/preserve_carriage_returns/stderr.term.svg
new file mode 100644
index 00000000000..eaa9c860056
--- /dev/null
+++ b/tests/testsuite/cargo_add/preserve_carriage_returns/stderr.term.svg
@@ -0,0 +1,31 @@
+<svg width="740px" height="92px" xmlns="http://www.w3.org/2000/svg">
+  <style>
+    .fg { fill: #AAAAAA }
+    .bg { background: #000000 }
+    .fg-green { fill: #00AA00 }
+    .container {
+      padding: 0 10px;
+      line-height: 18px;
+    }
+    .bold { font-weight: bold; }
+    tspan {
+      font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
+      white-space: pre;
+      line-height: 18px;
+    }
+  </style>
+
+  <rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
+
+  <text xml:space="preserve" class="container fg">
+    <tspan x="10px" y="28px"><tspan class="fg-green bold">    Updating</tspan><tspan> `dummy-registry` index</tspan>
+</tspan>
+    <tspan x="10px" y="46px"><tspan class="fg-green bold">      Adding</tspan><tspan> my-package v0.1.0 to dependencies</tspan>
+</tspan>
+    <tspan x="10px" y="64px"><tspan class="fg-green bold">     Locking</tspan><tspan> 1 package to latest compatible version</tspan>
+</tspan>
+    <tspan x="10px" y="82px">
+</tspan>
+  </text>
+
+</svg>
diff --git a/tests/testsuite/cargo_remove/mod.rs b/tests/testsuite/cargo_remove/mod.rs
index 3510ece4b40..d9d77a3994f 100644
--- a/tests/testsuite/cargo_remove/mod.rs
+++ b/tests/testsuite/cargo_remove/mod.rs
@@ -24,6 +24,7 @@ mod optional_dep_feature;
 mod optional_dep_feature_formatting;
 mod optional_feature;
 mod package;
+mod preserve_carriage_returns;
 mod remove_basic;
 mod script;
 mod script_last;
diff --git a/tests/testsuite/cargo_remove/preserve_carriage_returns/in/Cargo.toml b/tests/testsuite/cargo_remove/preserve_carriage_returns/in/Cargo.toml
new file mode 100644
index 00000000000..f9c0a3d7665
--- /dev/null
+++ b/tests/testsuite/cargo_remove/preserve_carriage_returns/in/Cargo.toml
@@ -0,0 +1,9 @@
+[workspace]
+
+[package]
+name = "cargo-list-test-fixture"
+version = "0.0.0"
+edition = "2015"
+
+[dependencies]
+my-package = "0.1.0"
diff --git a/tests/testsuite/cargo_remove/preserve_carriage_returns/in/src/lib.rs b/tests/testsuite/cargo_remove/preserve_carriage_returns/in/src/lib.rs
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/testsuite/cargo_remove/preserve_carriage_returns/mod.rs b/tests/testsuite/cargo_remove/preserve_carriage_returns/mod.rs
new file mode 100644
index 00000000000..08b812822af
--- /dev/null
+++ b/tests/testsuite/cargo_remove/preserve_carriage_returns/mod.rs
@@ -0,0 +1,36 @@
+use cargo_test_support::compare::assert_ui;
+use cargo_test_support::current_dir;
+use cargo_test_support::file;
+use cargo_test_support::prelude::*;
+use cargo_test_support::str;
+use cargo_test_support::Project;
+
+#[cargo_test]
+fn case() {
+    cargo_test_support::registry::init();
+    cargo_test_support::registry::Package::new("my-package", "0.1.0").publish();
+
+    let project = Project::from_template(current_dir!().join("in"));
+    let project_root = project.root();
+    let cwd = &project_root;
+
+    snapbox::cmd::Command::cargo_ui()
+        .arg("remove")
+        .arg_line("my-package")
+        .current_dir(cwd)
+        .assert()
+        .success()
+        .stdout_eq(str![""])
+        .stderr_eq(file!["stderr.term.svg"]);
+
+    // Verify the content matches first (for nicer error output)
+    assert_ui().subset_matches(current_dir!().join("out"), &project_root);
+
+    // Snapbox normalizes lines so we also need to do a string comparision to verify line endings
+    let expected = current_dir!().join("out/Cargo.toml");
+    let actual = project_root.join("Cargo.toml");
+    assert_eq!(
+        std::fs::read_to_string(expected).unwrap(),
+        std::fs::read_to_string(actual).unwrap()
+    );
+}
diff --git a/tests/testsuite/cargo_remove/preserve_carriage_returns/out/Cargo.toml b/tests/testsuite/cargo_remove/preserve_carriage_returns/out/Cargo.toml
new file mode 100644
index 00000000000..48c5359a5ba
--- /dev/null
+++ b/tests/testsuite/cargo_remove/preserve_carriage_returns/out/Cargo.toml
@@ -0,0 +1,6 @@
+[workspace]
+
+[package]
+name = "cargo-list-test-fixture"
+version = "0.0.0"
+edition = "2015"
diff --git a/tests/testsuite/cargo_remove/preserve_carriage_returns/stderr.term.svg b/tests/testsuite/cargo_remove/preserve_carriage_returns/stderr.term.svg
new file mode 100644
index 00000000000..583ddb69f38
--- /dev/null
+++ b/tests/testsuite/cargo_remove/preserve_carriage_returns/stderr.term.svg
@@ -0,0 +1,27 @@
+<svg width="740px" height="92px" xmlns="http://www.w3.org/2000/svg">
+  <style>
+    .fg { fill: #AAAAAA }
+    .bg { background: #000000 }
+    .fg-green { fill: #00AA00 }
+    .container {
+      padding: 0 10px;
+      line-height: 18px;
+    }
+    .bold { font-weight: bold; }
+    tspan {
+      font: 14px SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
+      white-space: pre;
+      line-height: 18px;
+    }
+  </style>
+
+  <rect width="100%" height="100%" y="0" rx="4.5" class="bg" />
+
+  <text xml:space="preserve" class="container fg">
+    <tspan x="10px" y="28px"><tspan class="fg-green bold">    Removing</tspan><tspan> my-package from dependencies</tspan>
+</tspan>
+    <tspan x="10px" y="46px">
+</tspan>
+  </text>
+
+</svg>