autocorrect/
diff.rs

1// autocorrect: false
2use crate::result::LineResult;
3use owo_colors::AnsiColors::{Black, Green, Red, Yellow};
4use owo_colors::OwoColorize;
5use std::fmt::Write;
6
7/// Diff a LineResult with colorize output.
8pub(crate) fn diff_line_result(line: &LineResult, no_diff_bg_color: bool) -> String {
9    let mut err_color = Red;
10    if line.severity.is_warning() {
11        err_color = Yellow;
12    }
13
14    let on_color = match no_diff_bg_color {
15        true => on_color_transparent,
16        false => on_color,
17    };
18
19    diff_lines_with_err_color(line.old.trim(), line.new.trim(), err_color, on_color)
20}
21
22#[allow(dead_code)]
23pub(crate) fn diff_lines(old_str: &str, new_str: &str) -> String {
24    diff_lines_with_err_color(old_str, new_str, Red, on_color)
25}
26
27type OnColorFn = fn(s: char, color: owo_colors::AnsiColors) -> String;
28
29#[inline]
30fn on_color(s: char, color: owo_colors::AnsiColors) -> String {
31    let t = s.on_color(color).color(Black);
32    format!("{t}")
33}
34
35#[inline]
36fn on_color_transparent(s: char, color: owo_colors::AnsiColors) -> String {
37    let t = s.color(color);
38    format!("{t}")
39}
40
41/// Diff two strings and colorize the output.
42///
43/// Screenshot:
44/// https://raw.githubusercontent.com/johannhof/difference.rs/master/assets/github-style.png
45///
46/// Forked from:
47/// https://github.com/johannhof/difference.rs/blob/master/examples/github-style.rs
48#[allow(unused_must_use)]
49pub(crate) fn diff_lines_with_err_color(
50    old_str: &str,
51    new_str: &str,
52    err_color: owo_colors::AnsiColors,
53    on_color: OnColorFn,
54) -> String {
55    let diffs = diff::lines(old_str, new_str);
56
57    let mut out = String::new();
58
59    for i in 0..diffs.len() {
60        match diffs[i] {
61            diff::Result::Both(x, _) => {
62                out.push(' ');
63                out.push_str(x);
64                out.push('\n');
65            }
66            // -
67            diff::Result::Left(x) => {
68                match diffs.get(i + 1) {
69                    Some(diff::Result::Right(y)) => {
70                        write!(out, "{}", "-".color(err_color));
71                        let sub_diffs = diff::chars(y, x);
72                        for c in sub_diffs {
73                            match c {
74                                diff::Result::Both(z, _) => {
75                                    write!(out, "{}", z.color(err_color));
76                                }
77                                diff::Result::Right(z) => {
78                                    write!(out, "{}", on_color(z, err_color));
79                                }
80                                _ => (),
81                            }
82                        }
83                        out.push('\n');
84                    }
85                    _ => {
86                        writeln!(out, "{}", format!("-{x}").color(err_color));
87                    }
88                };
89            }
90            // +
91            diff::Result::Right(x) => {
92                match diffs.get(i - 1) {
93                    Some(diff::Result::Left(y)) => {
94                        write!(out, "{}", "+".color(Green));
95                        let sub_diffs = diff::chars(y, x);
96
97                        for c in sub_diffs {
98                            match c {
99                                diff::Result::Both(z, _) => {
100                                    write!(out, "{}", z.color(Green));
101                                }
102                                diff::Result::Right(z) => {
103                                    write!(out, "{}", on_color(z, Green));
104                                }
105                                _ => (),
106                            }
107                        }
108                        out.push('\n');
109                    }
110                    _ => {
111                        writeln!(out, "{}", format!("+{x}").color(Green));
112                    }
113                };
114            }
115        }
116    }
117
118    // leave a blank line between each diff
119    out.push('\n');
120
121    out
122}
123
124#[cfg(test)]
125mod tests {
126    use super::*;
127    use owo_colors::AnsiColors::{Red, Yellow};
128
129    #[test]
130    fn test_color_output() {
131        let old_str = " Hello你好 ";
132        let new_str = "Hallo 你好";
133
134        let diff = diff_lines_with_err_color(old_str, new_str, Red, on_color);
135        assert_eq!(
136            "\u{1b}[31m-\u{1b}[39m\u{1b}[30;41m \u{1b}[0m\u{1b}[31mH\u{1b}[39m\u{1b}[30;41me\u{1b}[0m\u{1b}[31ml\u{1b}[39m\u{1b}[31ml\u{1b}[39m\u{1b}[31mo\u{1b}[39m\u{1b}[31m你\u{1b}[39m\u{1b}[31m好\u{1b}[39m\u{1b}[30;41m \u{1b}[0m\n\u{1b}[32m+\u{1b}[39m\u{1b}[32mH\u{1b}[39m\u{1b}[30;42ma\u{1b}[0m\u{1b}[32ml\u{1b}[39m\u{1b}[32ml\u{1b}[39m\u{1b}[32mo\u{1b}[39m\u{1b}[30;42m \u{1b}[0m\u{1b}[32m你\u{1b}[39m\u{1b}[32m好\u{1b}[39m\n\n",
137            diff
138        );
139
140        let diff = diff_lines_with_err_color(old_str, new_str, Yellow, on_color);
141        assert_eq!(
142            "\u{1b}[33m-\u{1b}[39m\u{1b}[30;43m \u{1b}[0m\u{1b}[33mH\u{1b}[39m\u{1b}[30;43me\u{1b}[0m\u{1b}[33ml\u{1b}[39m\u{1b}[33ml\u{1b}[39m\u{1b}[33mo\u{1b}[39m\u{1b}[33m你\u{1b}[39m\u{1b}[33m好\u{1b}[39m\u{1b}[30;43m \u{1b}[0m\n\u{1b}[32m+\u{1b}[39m\u{1b}[32mH\u{1b}[39m\u{1b}[30;42ma\u{1b}[0m\u{1b}[32ml\u{1b}[39m\u{1b}[32ml\u{1b}[39m\u{1b}[32mo\u{1b}[39m\u{1b}[30;42m \u{1b}[0m\u{1b}[32m你\u{1b}[39m\u{1b}[32m好\u{1b}[39m\n\n",
143            diff
144        );
145
146        let diff = diff_lines_with_err_color(old_str, new_str, Red, on_color_transparent);
147        assert_eq!("\u{1b}[31m-\u{1b}[39m\u{1b}[31m \u{1b}[39m\u{1b}[31mH\u{1b}[39m\u{1b}[31me\u{1b}[39m\u{1b}[31ml\u{1b}[39m\u{1b}[31ml\u{1b}[39m\u{1b}[31mo\u{1b}[39m\u{1b}[31m你\u{1b}[39m\u{1b}[31m好\u{1b}[39m\u{1b}[31m \u{1b}[39m\n\u{1b}[32m+\u{1b}[39m\u{1b}[32mH\u{1b}[39m\u{1b}[32ma\u{1b}[39m\u{1b}[32ml\u{1b}[39m\u{1b}[32ml\u{1b}[39m\u{1b}[32mo\u{1b}[39m\u{1b}[32m \u{1b}[39m\u{1b}[32m你\u{1b}[39m\u{1b}[32m好\u{1b}[39m\n\n", diff);
148
149        let diff = diff_lines_with_err_color(old_str, new_str, Yellow, on_color_transparent);
150        assert_eq!("\u{1b}[33m-\u{1b}[39m\u{1b}[33m \u{1b}[39m\u{1b}[33mH\u{1b}[39m\u{1b}[33me\u{1b}[39m\u{1b}[33ml\u{1b}[39m\u{1b}[33ml\u{1b}[39m\u{1b}[33mo\u{1b}[39m\u{1b}[33m你\u{1b}[39m\u{1b}[33m好\u{1b}[39m\u{1b}[33m \u{1b}[39m\n\u{1b}[32m+\u{1b}[39m\u{1b}[32mH\u{1b}[39m\u{1b}[32ma\u{1b}[39m\u{1b}[32ml\u{1b}[39m\u{1b}[32ml\u{1b}[39m\u{1b}[32mo\u{1b}[39m\u{1b}[32m \u{1b}[39m\u{1b}[32m你\u{1b}[39m\u{1b}[32m好\u{1b}[39m\n\n", diff);
151    }
152}