aico/diffing/
diff_utils.rs

1use similar::TextDiff;
2use std::borrow::Cow;
3
4pub fn generate_diff(
5    filename: &str,
6    old_content: Option<&str>,
7    new_content: Option<&str>,
8) -> String {
9    let from_header = if old_content.is_none() {
10        "/dev/null".to_string()
11    } else {
12        format!("a/{}", filename)
13    };
14
15    let to_header = if new_content.is_none()
16        || (new_content == Some("") && !old_content.unwrap_or("").is_empty())
17    {
18        "/dev/null".to_string()
19    } else {
20        format!("b/{}", filename)
21    };
22
23    let old_text = old_content.unwrap_or("");
24    let new_text = new_content.unwrap_or("");
25
26    let diff = TextDiff::from_lines(old_text, new_text)
27        .unified_diff()
28        .header(&quote_filename(&from_header), &quote_filename(&to_header))
29        .missing_newline_hint(true)
30        .to_string();
31
32    // The 'similar' crate omits headers if there are no hunks.
33    // We force headers for creations/deletions of empty files.
34    if diff.is_empty() && old_content.is_none() != new_content.is_none() {
35        return format!(
36            "--- {}\n+++ {}\n",
37            quote_filename(&from_header),
38            quote_filename(&to_header)
39        );
40    }
41
42    diff
43}
44
45fn quote_filename(filename: &str) -> Cow<'_, str> {
46    if filename.contains(' ') {
47        format!("\"{}\"", filename).into()
48    } else {
49        filename.into()
50    }
51}