zagens_runtime_adapters/tools/
diff_format.rs1use similar::TextDiff;
10
11#[must_use]
18pub fn make_unified_diff(path: &str, old: &str, new: &str) -> String {
19 if old == new {
20 return String::new();
21 }
22 let a = format!("a/{path}");
23 let b = format!("b/{path}");
24 let diff = TextDiff::from_lines(old, new);
25 diff.unified_diff()
26 .context_radius(3)
27 .header(&a, &b)
28 .to_string()
29}
30
31#[cfg(test)]
32mod tests {
33 use super::*;
34
35 #[test]
36 fn identical_inputs_emit_empty_diff() {
37 let s = "hello\nworld\n";
38 assert!(make_unified_diff("foo.txt", s, s).is_empty());
39 }
40
41 #[test]
42 fn replacement_emits_minus_plus_pair() {
43 let old = "alpha\nbeta\ngamma\n";
44 let new = "alpha\nBETA\ngamma\n";
45 let diff = make_unified_diff("foo.txt", old, new);
46 assert!(diff.contains("--- a/foo.txt"), "{diff}");
47 assert!(diff.contains("+++ b/foo.txt"), "{diff}");
48 assert!(diff.contains("-beta"), "{diff}");
49 assert!(diff.contains("+BETA"), "{diff}");
50 }
51
52 #[test]
53 fn new_file_renders_against_empty_old() {
54 let new = "first line\nsecond line\n";
55 let diff = make_unified_diff("new.txt", "", new);
56 assert!(diff.contains("--- a/new.txt"), "{diff}");
57 assert!(diff.contains("+++ b/new.txt"), "{diff}");
58 assert!(diff.contains("+first line"), "{diff}");
59 assert!(diff.contains("+second line"), "{diff}");
60 }
61
62 #[test]
63 fn diff_contains_hunk_header_so_tui_renders_it() {
64 let diff = make_unified_diff("foo.txt", "a\n", "b\n");
65 let head: Vec<&str> = diff.lines().take(5).collect();
66 assert!(
67 head.iter().any(|line| line.starts_with("@@")),
68 "expected hunk header in first 5 lines; got {head:?}"
69 );
70 }
71}