shine_core/install/line_endings.rs
1//! Line-ending-agnostic comparison helpers.
2//!
3//! Preset templates are embedded and written LF (see the repo `.gitattributes`),
4//! but a user's on-disk copy of an installed file may be CRLF — e.g. a Windows
5//! editor re-saving a PowerShell profile. Comparing those byte-exact would treat
6//! a pure CRLF↔LF difference as a real change, producing spurious "update"
7//! reports (and silent whole-file rewrites) on re-install. These helpers let the
8//! reconciliation logic compare content while ignoring line-ending style.
9
10/// Returns a copy of `bytes` with every `\r\n` and lone `\r` reduced to `\n`.
11///
12/// For input that is already LF-only this returns an identical byte sequence, so
13/// callers that compare via [`eol_eq`] see no behavior change for LF content.
14pub fn normalize_eol(bytes: &[u8]) -> Vec<u8> {
15 let mut out = Vec::with_capacity(bytes.len());
16 let mut i = 0;
17 while i < bytes.len() {
18 if bytes[i] == b'\r' {
19 out.push(b'\n');
20 // Collapse a `\r\n` pair into the single `\n` just pushed.
21 if bytes.get(i + 1) == Some(&b'\n') {
22 i += 1;
23 }
24 } else {
25 out.push(bytes[i]);
26 }
27 i += 1;
28 }
29 out
30}
31
32/// Compares two byte slices for equality, ignoring line-ending style
33/// (`\r\n`, lone `\r`, and `\n` are all treated as equivalent line breaks).
34pub fn eol_eq(a: &[u8], b: &[u8]) -> bool {
35 normalize_eol(a) == normalize_eol(b)
36}
37
38#[cfg(test)]
39mod tests {
40 use super::*;
41
42 #[test]
43 fn normalize_eol_converts_crlf_to_lf() {
44 assert_eq!(normalize_eol(b"a\r\nb\r\n"), b"a\nb\n");
45 }
46
47 #[test]
48 fn normalize_eol_converts_lone_cr_to_lf() {
49 assert_eq!(normalize_eol(b"a\rb\r"), b"a\nb\n");
50 }
51
52 #[test]
53 fn normalize_eol_handles_mixed_endings() {
54 assert_eq!(normalize_eol(b"a\r\nb\rc\nd"), b"a\nb\nc\nd");
55 }
56
57 #[test]
58 fn normalize_eol_is_noop_for_lf_only() {
59 let input = b"a\nb\nc";
60 assert_eq!(normalize_eol(input), input);
61 }
62
63 #[test]
64 fn eol_eq_ignores_line_ending_style() {
65 assert!(eol_eq(b"a\r\nb\r\n", b"a\nb\n"));
66 assert!(eol_eq(b"a\rb", b"a\nb"));
67 assert!(!eol_eq(b"a\r\nb", b"a\r\nc"));
68 }
69}