From ac1dff6053ed41c2b2c8932cfeb6b1d3311f963e Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 9 Oct 2026 12:00:20 +0000 Subject: [PATCH 1/2] Start refactor for #815 Assisted-by: Claude Code:claude-opus-5-5 From 117c5e3abf8885ad77e65c7fa6f26e8ee49b676d Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 9 Oct 2026 12:23:49 +0000 Subject: [PATCH 2/2] Pick JVM line terminators via line_endings Vendored Gradle, hosted Gradle and the Maven reactor chose the line break for the lines they insert with their own rules: Gradle took the first line's break (gradle::eol::newline_of / sniff_crlf), the Maven reactor treated any CRLF as CRLF. Every other line-inserting writer uses line_endings::terminator (the majority on a mixed file), so the same mixed file got different inserted lines depending on the ecosystem. The Gradle module's line-ending helpers now live in utils::line_endings (eol_eq moves, apply_eol becomes respell keyed by the terminator), and gradle/eol.rs is deleted. Gradle's forward append and its revert lookups switch to terminator together, so a mixed settings file still reverts byte for byte. Behavior: unchanged for LF-only and CRLF-only files. On a mixed file the lines vendored Gradle, hosted Gradle and .mvn/maven.config insert take the file's majority terminator instead of the first line's (or CRLF whenever any line has it). Refs #815 Assisted-by: Claude Code:claude-opus-5-5 --- crates/socket-patch-core/src/gradle/eol.rs | 97 ------------------- crates/socket-patch-core/src/gradle/mod.rs | 3 - .../src/patch/redirect/gradle.rs | 8 +- .../src/patch/redirect/upstream/gradle.rs | 2 +- .../src/utils/line_endings.rs | 70 +++++++++++++ .../src/vendor/jvm/gradle.rs | 70 ++++++++----- .../src/vendor/jvm/maven_reactor.rs | 10 +- .../socket-patch-core/src/vendor/jvm/mod.rs | 4 +- .../src/vendor/redownload.rs | 2 +- .../src/vex/discover/gradle.rs | 2 +- 10 files changed, 134 insertions(+), 134 deletions(-) delete mode 100644 crates/socket-patch-core/src/gradle/eol.rs diff --git a/crates/socket-patch-core/src/gradle/eol.rs b/crates/socket-patch-core/src/gradle/eol.rs deleted file mode 100644 index e4d37210d..000000000 --- a/crates/socket-patch-core/src/gradle/eol.rs +++ /dev/null @@ -1,97 +0,0 @@ -//! Line endings of the text files socket-patch owns or edits in a Gradle -//! build. A clone with `core.autocrlf=true` checks those files out with -//! CRLF, so "is this still our file" compares must not see the line-ending -//! difference, and edits must hand the file back in the style they found. - -use std::borrow::Cow; - -/// Whether `a` and `b` are equal once every `\r\n` is read as `\n`. -/// Nothing else is normalised: a lone `\r`, trailing whitespace or a -/// missing final newline still differ. -pub fn eol_eq(a: &[u8], b: &[u8]) -> bool { - to_lf(a) == to_lf(b) -} - -/// Whether the file uses CRLF: its first line break is `\r\n`. A file -/// without any line break is not CRLF. -pub fn sniff_crlf(text: &[u8]) -> bool { - match text.iter().position(|&b| b == b'\n') { - Some(i) => i > 0 && text[i - 1] == b'\r', - None => false, - } -} - -/// `text` with every line break spelled `\r\n` when `crlf`, else `\n`. -/// Existing `\r\n` pairs are folded first, so the result never holds -/// `\r\r\n`. -pub fn apply_eol(text: &str, crlf: bool) -> String { - let lf = text.replace("\r\n", "\n"); - if crlf { - lf.replace('\n', "\r\n") - } else { - lf - } -} - -/// The line break [`sniff_crlf`] found in `text`. -pub fn newline_of(text: &str) -> &'static str { - if sniff_crlf(text.as_bytes()) { - "\r\n" - } else { - "\n" - } -} - -/// `bytes` with every `\r\n` folded to `\n` (borrowed when there is none). -pub fn to_lf(bytes: &[u8]) -> Cow<'_, [u8]> { - if !bytes.windows(2).any(|w| w == b"\r\n") { - return Cow::Borrowed(bytes); - } - let mut out = Vec::with_capacity(bytes.len()); - let mut i = 0; - while i < bytes.len() { - if bytes[i] == b'\r' && bytes.get(i + 1) == Some(&b'\n') { - i += 1; - continue; - } - out.push(bytes[i]); - i += 1; - } - Cow::Owned(out) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn eol_eq_ignores_only_crlf() { - assert!(eol_eq(b"a\nb\n", b"a\r\nb\r\n")); - assert!(eol_eq(b"a\r\nb", b"a\nb")); - assert!(eol_eq(b"", b"")); - assert!(!eol_eq(b"a\n", b"a")); - assert!(!eol_eq(b"a\rb", b"a\nb")); - assert!(!eol_eq(b"a \n", b"a\n")); - } - - #[test] - fn sniff_reads_the_first_break() { - assert!(sniff_crlf(b"a\r\nb\n")); - assert!(!sniff_crlf(b"a\nb\r\n")); - assert!(!sniff_crlf(b"abc")); - assert!(!sniff_crlf(b"\n")); - assert!(sniff_crlf(b"\r\n")); - assert_eq!(newline_of("x\r\ny"), "\r\n"); - assert_eq!(newline_of("x"), "\n"); - } - - #[test] - fn apply_eol_round_trips() { - assert_eq!(apply_eol("a\nb\n", true), "a\r\nb\r\n"); - assert_eq!(apply_eol("a\r\nb\n", true), "a\r\nb\r\n"); - assert_eq!(apply_eol("a\r\nb\r\n", false), "a\nb\n"); - assert_eq!(apply_eol("a", true), "a"); - let crlf = "x\r\ny\r\n"; - assert_eq!(apply_eol(&apply_eol(crlf, false), true), crlf); - } -} diff --git a/crates/socket-patch-core/src/gradle/mod.rs b/crates/socket-patch-core/src/gradle/mod.rs index 32add2875..2202b2666 100644 --- a/crates/socket-patch-core/src/gradle/mod.rs +++ b/crates/socket-patch-core/src/gradle/mod.rs @@ -15,11 +15,9 @@ //! - [`locks`]: dependency-lock files: where they are, what they hold and a //! one-entry rewrite. //! - [`home`]: the Gradle user home and the caches inside it. -//! - [`eol`]: line-ending sniffing and line-ending-blind comparison. //! - [`selector`]: Gradle version ordering and version selectors. pub mod dsl; -pub mod eol; pub mod graph; pub mod home; pub mod locks; @@ -203,7 +201,6 @@ mod tests { let sources = [ ("mod.rs", include_str!("mod.rs")), ("dsl.rs", include_str!("dsl.rs")), - ("eol.rs", include_str!("eol.rs")), ("graph.rs", include_str!("graph.rs")), ("home.rs", include_str!("home.rs")), ("locks.rs", include_str!("locks.rs")), diff --git a/crates/socket-patch-core/src/patch/redirect/gradle.rs b/crates/socket-patch-core/src/patch/redirect/gradle.rs index 2fabd0d18..9f35108e0 100644 --- a/crates/socket-patch-core/src/patch/redirect/gradle.rs +++ b/crates/socket-patch-core/src/patch/redirect/gradle.rs @@ -60,13 +60,13 @@ use super::{ }; use crate::formats::text::{split_bom, strip_bom}; use crate::gradle::dsl::{self, is_ident, is_punct, Dsl, Tok, Token}; -use crate::gradle::eol::{eol_eq, newline_of, to_lf}; use crate::gradle::graph::{ filter_claims_group, wrapper_version, BuildKind, DeclKind, ScriptGraph, Site, }; use crate::gradle::locks; use crate::gradle::selector::{admits, gradle_version_cmp, parse_selector, Selector}; use crate::patch::path_safety::is_canonical_uuid; +use crate::utils::line_endings::{eol_eq, terminator, to_lf}; use crate::vendor::jvm::gradle as vendored; use crate::vendor::jvm::layout::GRADLE_ROOT_FILES; @@ -196,7 +196,7 @@ pub fn suffixed_version(base: &str, uuid: &str) -> String { /// The rows of an index text (LF or CRLF). `Err` names the first line the /// script would refuse (an unknown header, a malformed or duplicate row). pub fn parse_index(text: &str) -> Result, String> { - let lf = String::from_utf8_lossy(&to_lf(text.as_bytes())).into_owned(); + let lf = to_lf(text); let mut lines = lf.split('\n'); if lines.next() != Some(HOSTED_INDEX_HEADER) { return Err(format!("{HOSTED_INDEX_REL} has an unknown header")); @@ -229,7 +229,7 @@ pub fn render_index(rows: &[HostedRow]) -> String { /// The apply line's digest of an index text: the first 16 hex of the /// sha256 of its LF form. pub fn index_digest(text: &str) -> String { - let digest = Sha256::digest(to_lf(text.as_bytes())); + let digest = Sha256::digest(to_lf(text).as_bytes()); hex::encode(digest)[..16].to_string() } @@ -380,7 +380,7 @@ pub fn with_apply_line( return Some(format!("{}{line}{}", &text[..start], &text[end..])); } let line = apply_line(dsl, prefix, digest, created); - let nl = newline_of(text); + let nl = terminator(text); let mut out = text.to_string(); if !strip_bom(&out).is_empty() && !out.ends_with('\n') { out.push_str(nl); diff --git a/crates/socket-patch-core/src/patch/redirect/upstream/gradle.rs b/crates/socket-patch-core/src/patch/redirect/upstream/gradle.rs index cfe538933..a22eae7e9 100644 --- a/crates/socket-patch-core/src/patch/redirect/upstream/gradle.rs +++ b/crates/socket-patch-core/src/patch/redirect/upstream/gradle.rs @@ -21,7 +21,6 @@ use std::collections::BTreeMap; use super::{Ctx, FormatResult, HostedPin, View}; -use crate::gradle::eol::eol_eq; use crate::gradle::locks; use crate::patch::redirect::gradle::{ apply_line_created, apply_line_span, graph_of, has_component, index_digest, lockfile_paths, @@ -29,6 +28,7 @@ use crate::patch::redirect::gradle::{ without_component, GradleFiles, HostedRow, GITATTRIBUTES, GITATTRIBUTES_REL, HOSTED_INDEX_REL, HOSTED_SCRIPT, HOSTED_SCRIPT_REL, MAX_ROUNDS, }; +use crate::utils::line_endings::eol_eq; use crate::vendor::jvm::gradle as vendored; const VERIFICATION_REL: &str = vendored::VERIFICATION_REL; diff --git a/crates/socket-patch-core/src/utils/line_endings.rs b/crates/socket-patch-core/src/utils/line_endings.rs index ec9f77713..aa797517c 100644 --- a/crates/socket-patch-core/src/utils/line_endings.rs +++ b/crates/socket-patch-core/src/utils/line_endings.rs @@ -6,6 +6,11 @@ //! refuse [`LineEndings::Mixed`] (there is no single style to restore), //! operate on the LF-normalized text ([`to_lf`]), and re-expand whatever is //! written or recorded with [`LineEndings::restore`]. +//! +//! Writers that insert lines pick their terminator with [`terminator`] and +//! look for what they wrote with [`respell`]; "is this still our file" +//! checks, which a `core.autocrlf` checkout must not defeat, compare with +//! [`eol_eq`]. use std::borrow::Cow; @@ -94,6 +99,45 @@ pub(crate) fn terminator(text: &str) -> &'static str { } } +/// `text` with every line break spelled `nl` (`"\r\n"` or `"\n"`): +/// existing `\r\n` pairs are folded first, so the result never holds +/// `\r\r\n`. How a writer spells a recorded fragment in the +/// [`terminator`] of the file it is looking for that fragment in. +pub(crate) fn respell(text: &str, nl: &str) -> String { + let lf = to_lf(text); + if nl == "\r\n" { + lf.replace('\n', "\r\n") + } else { + lf.into_owned() + } +} + +/// Whether `a` and `b` are equal once every `\r\n` is read as `\n`. +/// Nothing else is normalised: a lone `\r`, trailing whitespace or a +/// missing final newline still differ. Bytes, not text: the files compared +/// need not be UTF-8. +pub(crate) fn eol_eq(a: &[u8], b: &[u8]) -> bool { + to_lf_bytes(a) == to_lf_bytes(b) +} + +/// [`to_lf`] for bytes not known to be UTF-8. +fn to_lf_bytes(bytes: &[u8]) -> Cow<'_, [u8]> { + if !bytes.windows(2).any(|w| w == b"\r\n") { + return Cow::Borrowed(bytes); + } + let mut out = Vec::with_capacity(bytes.len()); + let mut i = 0; + while i < bytes.len() { + if bytes[i] == b'\r' && bytes.get(i + 1) == Some(&b'\n') { + i += 1; + continue; + } + out.push(bytes[i]); + i += 1; + } + Cow::Owned(out) +} + #[cfg(test)] mod tests { use super::*; @@ -146,4 +190,30 @@ mod tests { // A bare CR makes the file mixed; it is not counted as a break. assert_eq!(terminator("a\rb\r\n"), "\r\n"); } + + #[test] + fn eol_eq_ignores_only_crlf() { + assert!(eol_eq(b"a\nb\n", b"a\r\nb\r\n")); + assert!(eol_eq(b"a\r\nb", b"a\nb")); + assert!(eol_eq(b"", b"")); + assert!(!eol_eq(b"a\n", b"a")); + assert!(!eol_eq(b"a\rb", b"a\nb")); + assert!(!eol_eq(b"a \n", b"a\n")); + // Not UTF-8: still compared byte for byte past the folding. + assert!(eol_eq(b"\xff\r\n", b"\xff\n")); + assert!(matches!(to_lf_bytes(b"a\nb"), Cow::Borrowed(_))); + for text in ["a\r\nb\n", "a\rb", "\r\n\r\n", "x"] { + assert_eq!(to_lf_bytes(text.as_bytes()), to_lf(text).as_bytes()); + } + } + + #[test] + fn respell_round_trips() { + assert_eq!(respell("a\nb\n", "\r\n"), "a\r\nb\r\n"); + assert_eq!(respell("a\r\nb\n", "\r\n"), "a\r\nb\r\n"); + assert_eq!(respell("a\r\nb\r\n", "\n"), "a\nb\n"); + assert_eq!(respell("a", "\r\n"), "a"); + let crlf = "x\r\ny\r\n"; + assert_eq!(respell(&respell(crlf, "\n"), terminator(crlf)), crlf); + } } diff --git a/crates/socket-patch-core/src/vendor/jvm/gradle.rs b/crates/socket-patch-core/src/vendor/jvm/gradle.rs index c6f3df725..3ccb053ac 100644 --- a/crates/socket-patch-core/src/vendor/jvm/gradle.rs +++ b/crates/socket-patch-core/src/vendor/jvm/gradle.rs @@ -36,6 +36,7 @@ use super::{ use super::layout::{self, safe_coordinates}; use crate::formats::text::{split_bom, strip_bom}; use crate::formats::xml::{self, Element}; +use crate::utils::line_endings; /// The owned settings script. Its bytes change only with a CLI release. pub const SCRIPT: &str = include_str!("socket-patch.settings.gradle"); @@ -549,7 +550,7 @@ pub fn plan( Some(&text[start..end]).filter(|f| !f.contains("origin=\"socket-patch\"")); let mut replacement = replacement; if text[start..end].starts_with("") { - let nl = newline_of(&text); + let nl = line_endings::terminator(&text); let indent = line_indent(&text, start); let shell = format!("{nl}{indent}"); records.pop(); @@ -963,11 +964,7 @@ pub fn unplan(read: ReadFn<'_>, c: &Coords<'_>, records: &[WiringRecord]) -> Jvm // elements span lines in the file's vendor-time newline, and a // `core.autocrlf` checkout converts the whole file. let holds_to = |t: &str| { - t.contains(to) - || t.contains(&crate::gradle::eol::apply_eol( - to, - crate::gradle::eol::sniff_crlf(t.as_bytes()), - )) + t.contains(to) || t.contains(&line_endings::respell(to, line_endings::terminator(t))) }; let Some(from) = op_str(w, "from") else { if holds_to(t) { @@ -1047,8 +1044,7 @@ pub fn unplan(read: ReadFn<'_>, c: &Coords<'_>, records: &[WiringRecord]) -> Jvm { let Some(t) = text.as_deref() else { break }; let written = op_str(w, "text").unwrap_or_default(); - let crlf = crate::gradle::eol::sniff_crlf(t.as_bytes()); - let written_here = crate::gradle::eol::apply_eol(written, crlf); + let written_here = line_endings::respell(written, line_endings::terminator(t)); *text = match op_of(w) { "create" if is_ours(Some(t.as_bytes()), written) => None, "create" => { @@ -1108,8 +1104,7 @@ pub fn unplan(read: ReadFn<'_>, c: &Coords<'_>, records: &[WiringRecord]) -> Jvm }, ("line", Some(t)) => { let written = op_str(w, "text").unwrap_or_default(); - let crlf = crate::gradle::eol::sniff_crlf(t.as_bytes()); - let here = crate::gradle::eol::apply_eol(written, crlf); + let here = line_endings::respell(written, line_endings::terminator(t)); Some( match t .strip_suffix(here.as_str()) @@ -1158,10 +1153,10 @@ pub fn unplan(read: ReadFn<'_>, c: &Coords<'_>, records: &[WiringRecord]) -> Jvm /// `core.autocrlf` checkout of a settings file vendor edited with LF). fn undo_replace_eol(text: &str, from: &str, to: &str) -> Option { undo_replace(text, from, to).or_else(|| { - let crlf = crate::gradle::eol::sniff_crlf(text.as_bytes()); + let nl = line_endings::terminator(text); let (from, to) = ( - crate::gradle::eol::apply_eol(from, crlf), - crate::gradle::eol::apply_eol(to, crlf), + line_endings::respell(from, nl), + line_endings::respell(to, nl), ); undo_replace(text, &from, &to) }) @@ -1505,13 +1500,8 @@ pub(crate) fn remove_apply_line( Some(format!("{}{}", &text[..start], &text[end..])) } -/// The newline the file already uses (CRLF when its first line ends so). -fn newline_of(text: &str) -> &'static str { - crate::gradle::eol::newline_of(text) -} - fn append_line(text: &str, line: &str) -> String { - let nl = newline_of(text); + let nl = line_endings::terminator(text); let mut out = text.to_string(); if !strip_bom(&out).is_empty() && !out.ends_with('\n') { out.push_str(nl); @@ -1718,7 +1708,7 @@ fn scoped_in_block_entry( return InBlock::Unneeded; } let entry = in_block_line(kotlin, prefix, patch); - let nl = newline_of(text); + let nl = line_endings::terminator(text); let unit = indent_unit(text); let Some((pm_name, pm_open, pm_close)) = find_block(&toks, 0, toks.len(), scope) else { if scope == "buildscript" { @@ -2044,7 +2034,7 @@ fn has_checksum(masked: &str, art: &Element) -> bool { fn with_sha256(text: &str, masked: &str, art: &Element, sha: &str) -> String { const UNIT: &str = " "; let (start, tag_end, end) = (art.start, art.inner_start, art.end); - let nl = newline_of(text); + let nl = line_endings::terminator(text); let indent = line_indent(text, start); let name = xml::attr(art.open_tag(masked), "name").unwrap_or_default(); if tag_end == end { @@ -2206,7 +2196,7 @@ fn verification_artifact_edit( ) }; let masked = xml::blank_non_markup(text).map_err(|why| unparseable(&why))?; - let nl = newline_of(text); + let nl = line_endings::terminator(text); const UNIT: &str = " "; let (a, v) = (patch.artifact_id, patch.version); let jar_name = file_name.to_string(); @@ -3922,7 +3912,7 @@ mod tests { let text = String::from_utf8(out[*rel].clone()).unwrap(); out.insert( rel.to_string(), - crate::gradle::eol::apply_eol(&text, true).into_bytes(), + line_endings::respell(&text, "\r\n").into_bytes(), ); } out @@ -4005,6 +3995,40 @@ mod tests { ); } + /// #815: a mixed CRLF/LF settings file gets its inserted lines in the + /// majority terminator (`line_endings::terminator`, the rule every + /// other line-inserting writer uses) — not the first line's — whichever + /// way the first line and the majority disagree, and the revert finds + /// what the forward pass wrote and restores the file byte for byte. + #[test] + fn mixed_settings_take_the_majority_terminator_and_revert_byte_exact() { + for (src, nl) in [ + ( + "rootProject.name = 'x'\ninclude 'a'\r\ninclude 'b'\r\n", + "\r\n", + ), + ("rootProject.name = 'x'\r\ninclude 'a'\ninclude 'b'\n", "\n"), + ] { + let files = fs(&[("settings.gradle", src)]); + let p = patch(); + let plan = run(&files, &p).unwrap(); + assert_eq!( + text_of(&plan, "settings.gradle"), + format!("{src}{}{nl}", vendored_line(false, "")), + "{src:?}" + ); + let after = applied(&files, &plan); + assert!(run(&after, &p).unwrap().writes.is_empty(), "{src:?}"); + let undo = unplan(&|rel| after.get(rel).cloned(), &p.coords(), &plan.records); + assert!(undo.drifted.is_empty(), "{src:?}: {:?}", undo.drifted); + assert_eq!( + reverted(&after, &undo)["settings.gradle"], + src.as_bytes(), + "{src:?}" + ); + } + } + /// A settings file vendor created is deleted once only whitespace is /// left of it (a CRLF clone, or a blank line someone added). #[test] diff --git a/crates/socket-patch-core/src/vendor/jvm/maven_reactor.rs b/crates/socket-patch-core/src/vendor/jvm/maven_reactor.rs index 481fa789e..e1e2a0e2d 100644 --- a/crates/socket-patch-core/src/vendor/jvm/maven_reactor.rs +++ b/crates/socket-patch-core/src/vendor/jvm/maven_reactor.rs @@ -1501,7 +1501,7 @@ fn repository_edit(doc: &Doc) -> Edit { /// directory appended to its list, the last protocol list gets `file`. fn merge_maven_config(config: Option<&[u8]>) -> (Vec, Value) { let text = config.map(String::from_utf8_lossy).unwrap_or_default(); - let nl = if text.contains("\r\n") { "\r\n" } else { "\n" }; + let nl = crate::utils::line_endings::terminator(&text); let lines: Vec<&str> = text.split_inclusive('\n').collect(); let arg_of = |line: &str| line.trim().to_string(); let last = |key: &str| lines.iter().rposition(|l| arg_of(l).starts_with(key)); @@ -1780,7 +1780,7 @@ impl Doc { _ => return Err("no single element".to_string()), }; let unit = indent_unit(&masked); - let nl = if text.contains("\r\n") { "\r\n" } else { "\n" }; + let nl = crate::utils::line_endings::terminator(&text); Ok(Doc { text, masked, @@ -3205,6 +3205,12 @@ mod tests { config("-T4\r\n"), format!("-T4\r\n{OFFLINE_LINE}\r\n{TAIL_KEY}{TAIL_DIR}\r\n") ); + // #815: a mixed file's added lines take its majority terminator + // (`line_endings::terminator`), not "any CRLF means CRLF". + assert_eq!( + config("-T4\r\n-ntp\n-B\n"), + format!("-T4\r\n-ntp\n-B\n{OFFLINE_LINE}\n{TAIL_KEY}{TAIL_DIR}\n") + ); assert_eq!( config(&format!("{TAIL_KEY}/opt/repo\n-T4\n{OFFLINE_LINE}\n")), format!("{TAIL_KEY}/opt/repo,{TAIL_DIR}\n-T4\n{OFFLINE_LINE}\n") diff --git a/crates/socket-patch-core/src/vendor/jvm/mod.rs b/crates/socket-patch-core/src/vendor/jvm/mod.rs index f6eb22b17..7717b7c6b 100644 --- a/crates/socket-patch-core/src/vendor/jvm/mod.rs +++ b/crates/socket-patch-core/src/vendor/jvm/mod.rs @@ -557,7 +557,7 @@ pub(crate) fn finish_writes(read: ReadFn<'_>, writes: Vec) -> Vec true, Some(cur) if !w.tree && eol_blind(&w.rel) => { - !crate::gradle::eol::eol_eq(&cur, &w.bytes) + !crate::utils::line_endings::eol_eq(&cur, &w.bytes) } Some(cur) => cur != w.bytes, }) @@ -607,7 +607,7 @@ pub(crate) const TREE_GITATTRIBUTES: &str = "* -text\n"; /// Whether `current` is still the owned text `ours` (line endings aside). pub(crate) fn is_ours(current: Option<&[u8]>, ours: &str) -> bool { - current.is_some_and(|c| crate::gradle::eol::eol_eq(c, ours.as_bytes())) + current.is_some_and(|c| crate::utils::line_endings::eol_eq(c, ours.as_bytes())) } /// A disk round-trip harness for the planners' tests: vendor and revert diff --git a/crates/socket-patch-core/src/vendor/redownload.rs b/crates/socket-patch-core/src/vendor/redownload.rs index 04e3a3b96..92d5172cd 100644 --- a/crates/socket-patch-core/src/vendor/redownload.rs +++ b/crates/socket-patch-core/src/vendor/redownload.rs @@ -585,7 +585,7 @@ pub async fn restore_jvm_owned_files(root: &Path, entry: &VendorEntry) -> Result let current = read(&rel); if current .as_deref() - .is_some_and(|c| crate::gradle::eol::eol_eq(c, text.as_bytes())) + .is_some_and(|c| crate::utils::line_endings::eol_eq(c, text.as_bytes())) { continue; } diff --git a/crates/socket-patch-core/src/vex/discover/gradle.rs b/crates/socket-patch-core/src/vex/discover/gradle.rs index 8525197d2..0ec3c6c7e 100644 --- a/crates/socket-patch-core/src/vex/discover/gradle.rs +++ b/crates/socket-patch-core/src/vex/discover/gradle.rs @@ -53,13 +53,13 @@ use super::{ maven_purl, DiscoverCtx, Discovery, PatchedRef, UnattestedKind, DIAG_LOCKFILE_UNPARSEABLE, DIAG_REF_INVALID, }; -use crate::gradle::eol::eol_eq; use crate::gradle::locks; use crate::patch::redirect::gradle::{ apply_line_digest, graph_of, index_digest, is_settings_lock, lockfile_paths, parse_index, settings_targets, GradleFiles, PinnedRowChecks, HOSTED_INDEX_REL, HOSTED_SCRIPT, HOSTED_SCRIPT_REL, MAX_ROUNDS, }; +use crate::utils::line_endings::eol_eq; pub(crate) async fn extract(ctx: &DiscoverCtx<'_>, out: &mut Discovery) { let Some(index) = ctx.read_text(HOSTED_INDEX_REL, out).await else {