1use crate::display::*;
4
5pub fn print_diff(raw: &str, filtered: &str, tool: &str, preset: Option<&str>) {
10 let raw_pretty = prettify(raw);
11 let filtered_pretty = prettify(filtered);
12
13 let raw_lines: Vec<&str> = raw_pretty.lines().collect();
14 let filtered_lines: Vec<&str> = filtered_pretty.lines().collect();
15
16 eprintln!();
18 eprintln!(" {BOLD}{GREEN}mcp-rtk{RESET}{DIM} — diff{RESET}");
19 eprintln!(" {DIM}{}{RESET}", "─".repeat(56));
20 eprintln!();
21 eprintln!(" {DIM}Tool:{RESET} {BOLD}{tool}{RESET}");
22 if let Some(p) = preset {
23 eprintln!(" {DIM}Preset:{RESET} {BOLD}{p}{RESET}");
24 }
25
26 let input_bytes = raw.len();
27 let output_bytes = filtered.len();
28 let saved = input_bytes.saturating_sub(output_bytes);
29 let pct = if input_bytes > 0 {
30 (saved as f64 / input_bytes as f64) * 100.0
31 } else {
32 0.0
33 };
34 let pct_color = pct_to_color(pct);
35
36 eprintln!(
37 " {DIM}Input:{RESET} {} bytes (~{} tokens)",
38 input_bytes,
39 input_bytes / 4
40 );
41 eprintln!(
42 " {DIM}Output:{RESET} {} bytes (~{} tokens)",
43 output_bytes,
44 output_bytes / 4
45 );
46 eprintln!(" {DIM}Saved:{RESET} {pct_color}{BOLD}{saved} bytes ({pct:.1}%){RESET}");
47 eprintln!();
48 eprintln!(" {RED}--- raw{RESET} {GREEN}+++ filtered{RESET}");
49 eprintln!(" {DIM}{}{RESET}", "─".repeat(56));
50
51 let ops = compute_diff(&raw_lines, &filtered_lines);
53 for op in &ops {
54 match op {
55 DiffOp::Equal(line) => {
56 println!(" {DIM} {line}{RESET}");
57 }
58 DiffOp::Remove(line) => {
59 println!(" {RED}-{line}{RESET}");
60 }
61 DiffOp::Add(line) => {
62 println!(" {GREEN}+{line}{RESET}");
63 }
64 }
65 }
66 eprintln!();
67}
68
69fn prettify(s: &str) -> String {
70 serde_json::from_str::<serde_json::Value>(s)
71 .ok()
72 .and_then(|v| serde_json::to_string_pretty(&v).ok())
73 .unwrap_or_else(|| s.to_string())
74}
75
76enum DiffOp<'a> {
77 Equal(&'a str),
78 Remove(&'a str),
79 Add(&'a str),
80}
81
82const MAX_DIFF_LINES: usize = 10_000;
83
84fn compute_diff<'a>(old: &[&'a str], new: &[&'a str]) -> Vec<DiffOp<'a>> {
85 let m = old.len().min(MAX_DIFF_LINES);
86 let n = new.len().min(MAX_DIFF_LINES);
87 let old = &old[..m];
88 let new = &new[..n];
89
90 let mut table = vec![vec![0u32; n + 1]; m + 1];
92 for i in 1..=m {
93 for j in 1..=n {
94 if old[i - 1] == new[j - 1] {
95 table[i][j] = table[i - 1][j - 1] + 1;
96 } else {
97 table[i][j] = table[i - 1][j].max(table[i][j - 1]);
98 }
99 }
100 }
101
102 let mut ops = Vec::new();
104 let mut i = m;
105 let mut j = n;
106 while i > 0 || j > 0 {
107 if i > 0 && j > 0 && old[i - 1] == new[j - 1] {
108 ops.push(DiffOp::Equal(old[i - 1]));
109 i -= 1;
110 j -= 1;
111 } else if j > 0 && (i == 0 || table[i][j - 1] >= table[i - 1][j]) {
112 ops.push(DiffOp::Add(new[j - 1]));
113 j -= 1;
114 } else {
115 ops.push(DiffOp::Remove(old[i - 1]));
116 i -= 1;
117 }
118 }
119
120 ops.reverse();
121 ops
122}