pub const TRANSFORM_ID: &str = "diff_compaction";
pub const TRANSFORM_VERSION: &str = "1.0.0";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum LineKind {
Structural,
ChangeBody,
Context,
}
fn classify_line(line: &str) -> LineKind {
if line.starts_with("diff --git")
|| line.starts_with("index ")
|| line.starts_with("--- ")
|| line.starts_with("+++ ")
|| line.starts_with("@@")
{
LineKind::Structural
} else if line.starts_with('+') || line.starts_with('-') {
LineKind::ChangeBody
} else {
LineKind::Context
}
}
fn flush_dropped_run(out: &mut Vec<String>, dropped_run: &mut usize, marker_noun: &str) {
if *dropped_run > 0 {
out.push(format!("[{dropped_run} {marker_noun} dropped]"));
*dropped_run = 0;
}
}
pub fn compact_diff(input: &str, keep_line_bodies: bool) -> String {
let marker_noun = if keep_line_bodies {
"context lines"
} else {
"lines"
};
let mut out: Vec<String> = Vec::new();
let mut dropped_run: usize = 0;
for line in input.lines() {
match classify_line(line) {
LineKind::Structural => {
flush_dropped_run(&mut out, &mut dropped_run, marker_noun);
out.push(line.to_string());
}
LineKind::ChangeBody => {
if keep_line_bodies {
flush_dropped_run(&mut out, &mut dropped_run, marker_noun);
out.push(line.to_string());
} else {
dropped_run += 1;
}
}
LineKind::Context => {
dropped_run += 1;
}
}
}
flush_dropped_run(&mut out, &mut dropped_run, marker_noun);
out.join("\n")
}
#[cfg(test)]
mod tests {
use super::*;
const SAMPLE_DIFF: &str = "diff --git a/f.rs b/f.rs\n\
index 1234567..89abcde 100644\n\
--- a/f.rs\n\
+++ b/f.rs\n\
@@ -1,7 +1,7 @@\n\
\x20fn main() {\n\
\x20 let x = 1;\n\
\x20 let y = 2;\n\
- println!(\"{}\", x);\n\
+ println!(\"{} {}\", x, y);\n\
\x20 let z = 3;\n\
\x20 let w = 4;\n\
\x20 println!(\"done\");";
#[test]
fn hunk_headers_are_preserved() {
let out = compact_diff(SAMPLE_DIFF, true);
assert!(out.lines().any(|l| l == "@@ -1,7 +1,7 @@"));
}
#[test]
fn change_body_lines_are_preserved_when_keep_line_bodies_true() {
let out = compact_diff(SAMPLE_DIFF, true);
assert!(out.lines().any(|l| l == "- println!(\"{}\", x);"));
assert!(out.lines().any(|l| l == "+ println!(\"{} {}\", x, y);"));
}
#[test]
fn file_names_and_diff_git_header_are_preserved() {
let out = compact_diff(SAMPLE_DIFF, true);
let lines: Vec<&str> = out.lines().collect();
assert!(lines.contains(&"diff --git a/f.rs b/f.rs"));
assert!(lines.contains(&"index 1234567..89abcde 100644"));
assert!(lines.contains(&"--- a/f.rs"));
assert!(lines.contains(&"+++ b/f.rs"));
}
#[test]
fn change_body_lines_are_dropped_when_keep_line_bodies_false_but_structural_survives() {
let out = compact_diff(SAMPLE_DIFF, false);
let lines: Vec<&str> = out.lines().collect();
assert!(lines.contains(&"diff --git a/f.rs b/f.rs"));
assert!(lines.contains(&"index 1234567..89abcde 100644"));
assert!(lines.contains(&"--- a/f.rs"));
assert!(lines.contains(&"+++ b/f.rs"));
assert!(lines.contains(&"@@ -1,7 +1,7 @@"));
assert!(
!lines
.iter()
.any(|l| l.starts_with('+') && !l.starts_with("+++ "))
);
assert!(
!lines
.iter()
.any(|l| l.starts_with('-') && !l.starts_with("--- "))
);
assert!(lines.iter().any(|l| l.ends_with("lines dropped]")));
}
#[test]
fn evidence_marker_counts_consecutive_dropped_context_lines() {
let out = compact_diff(SAMPLE_DIFF, true);
let lines: Vec<&str> = out.lines().collect();
let markers: Vec<&&str> = lines.iter().filter(|l| l.ends_with("dropped]")).collect();
assert_eq!(markers.len(), 2);
for marker in markers {
assert_eq!(*marker, "[3 context lines dropped]");
}
}
#[test]
fn relative_ordering_of_surviving_lines_matches_input() {
let out = compact_diff(SAMPLE_DIFF, true);
let lines: Vec<&str> = out.lines().collect();
assert_eq!(
lines,
vec![
"diff --git a/f.rs b/f.rs",
"index 1234567..89abcde 100644",
"--- a/f.rs",
"+++ b/f.rs",
"@@ -1,7 +1,7 @@",
"[3 context lines dropped]",
"- println!(\"{}\", x);",
"+ println!(\"{} {}\", x, y);",
"[3 context lines dropped]",
]
);
}
#[test]
fn empty_input_returns_empty_output() {
assert_eq!(compact_diff("", true), "");
assert_eq!(compact_diff("", false), "");
}
#[test]
fn pure_context_input_collapses_to_a_single_marker() {
let input = " line one\n line two\n line three";
let out = compact_diff(input, true);
assert_eq!(out, "[3 context lines dropped]");
}
#[test]
fn header_only_form_marker_wording_covers_dropped_bodies() {
let out = compact_diff(SAMPLE_DIFF, false);
assert_eq!(
out,
"diff --git a/f.rs b/f.rs\n\
index 1234567..89abcde 100644\n\
--- a/f.rs\n\
+++ b/f.rs\n\
@@ -1,7 +1,7 @@\n\
[8 lines dropped]"
);
}
}