crux_cli/
diff.rs

1use std::{fmt, path::Path};
2
3use console::{style, Style};
4use similar::{ChangeTag, TextDiff};
5
6pub(crate) fn show(file_name: &Path, desired: &str, actual: &str) {
7    let diff = TextDiff::from_lines(actual, desired);
8    for (idx, group) in diff.grouped_ops(3).iter().enumerate() {
9        if idx == 0 {
10            println!("{:-<80}", file_name.to_string_lossy());
11        }
12        for op in group {
13            for change in diff.iter_inline_changes(op) {
14                let (sign, s) = match change.tag() {
15                    ChangeTag::Delete => ("-", Style::new().red()),
16                    ChangeTag::Insert => ("+", Style::new().green()),
17                    ChangeTag::Equal => (" ", Style::new().dim()),
18                };
19                print!(
20                    "{}{} |{}",
21                    style(Line(change.old_index())).dim(),
22                    style(Line(change.new_index())).dim(),
23                    s.apply_to(sign).bold(),
24                );
25                for (emphasized, value) in change.iter_strings_lossy() {
26                    if emphasized {
27                        print!("{}", s.apply_to(value).underlined());
28                    } else {
29                        print!("{}", s.apply_to(value));
30                    }
31                }
32                if change.missing_newline() {
33                    println!();
34                }
35            }
36        }
37        println!(); // empty line between diffs
38    }
39}
40struct Line(Option<usize>);
41
42impl fmt::Display for Line {
43    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
44        match self.0 {
45            None => write!(f, "    "),
46            Some(idx) => write!(f, "{:>4}", idx + 1),
47        }
48    }
49}