1use similar::{ChangeTag, TextDiff};
2
3pub struct UnifiedDiffArgs<'a> {
4 pub old_path: &'a str,
5 pub new_path: &'a str,
6 pub old_content: &'a str,
7 pub new_content: &'a str,
8}
9
10pub fn unified_diff(args: UnifiedDiffArgs<'_>) -> String {
13 let diff = TextDiff::from_lines(args.old_content, args.new_content);
14 let mut out = String::new();
15 out.push_str(&format!("--- {}\n", args.old_path));
16 out.push_str(&format!("+++ {}\n", args.new_path));
17
18 for (idx, group) in diff.grouped_ops(3).iter().enumerate() {
19 if idx > 0 {
20 out.push('\n');
21 }
22 let first = &group[0];
23 let last = &group[group.len() - 1];
24 let old_start = first.old_range().start + 1;
25 let old_len = last.old_range().end - first.old_range().start;
26 let new_start = first.new_range().start + 1;
27 let new_len = last.new_range().end - first.new_range().start;
28 out.push_str(&format!(
29 "@@ -{},{} +{},{} @@\n",
30 old_start, old_len, new_start, new_len
31 ));
32 for op in group {
33 for change in diff.iter_changes(op) {
34 let sign = match change.tag() {
35 ChangeTag::Equal => ' ',
36 ChangeTag::Delete => '-',
37 ChangeTag::Insert => '+',
38 };
39 out.push(sign);
40 out.push_str(change.value());
41 if !out.ends_with('\n') {
42 out.push('\n');
43 }
44 }
45 }
46 }
47
48 out
49}