Skip to main content

vyn_core/
diff.rs

1use similar::{ChangeTag, TextDiff};
2
3pub fn is_binary(data: &[u8]) -> bool {
4    let probe = &data[..data.len().min(1024)];
5    if probe.contains(&0) {
6        return true;
7    }
8
9    std::str::from_utf8(probe).is_err()
10}
11
12pub fn unified_diff(old: &str, new: &str, old_label: &str, new_label: &str) -> String {
13    let diff = TextDiff::from_lines(old, new);
14    let mut output = String::new();
15    output.push_str(&format!("--- {old_label}\n"));
16    output.push_str(&format!("+++ {new_label}\n"));
17
18    for op in diff.ops() {
19        for change in diff.iter_changes(op) {
20            match change.tag() {
21                ChangeTag::Delete => output.push('-'),
22                ChangeTag::Insert => output.push('+'),
23                ChangeTag::Equal => output.push(' '),
24            }
25            output.push_str(&change.to_string());
26        }
27    }
28
29    output
30}
31
32#[cfg(test)]
33mod tests {
34    use super::{is_binary, unified_diff};
35
36    #[test]
37    fn line_diff_logic() {
38        let old = "A=1\nB=2\n";
39        let new = "A=1\nB=3\nC=4\n";
40        let rendered = unified_diff(old, new, "old/.env", "new/.env");
41
42        assert!(rendered.contains("--- old/.env"));
43        assert!(rendered.contains("+++ new/.env"));
44        assert!(rendered.contains("-B=2"));
45        assert!(rendered.contains("+B=3"));
46        assert!(rendered.contains("+C=4"));
47    }
48
49    #[test]
50    fn binary_detection() {
51        assert!(is_binary(&[0x00, 0x01, 0x02]));
52        assert!(is_binary(&[0xff, 0xfe, 0xfd]));
53        assert!(!is_binary(b"DATABASE_URL=postgres://localhost\n"));
54    }
55}