1use crate::stream::{Extracted, Op};
4use similar::{capture_diff_slices, Algorithm, DiffOp};
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub enum Change {
8 Same,
9 Removed,
10 Added,
11 Modified,
13}
14
15#[derive(Debug)]
16pub struct DiffLine {
17 pub change: Change,
18 pub text: String,
20 pub new_params: Option<String>,
22}
23
24pub struct DiffResult {
25 pub lines: Vec<DiffLine>,
26 pub added: usize,
27 pub removed: usize,
28 pub modified: usize,
29}
30
31impl DiffResult {
32 pub fn has_changes(&self) -> bool {
33 self.added + self.removed + self.modified > 0
34 }
35}
36
37pub fn diff(a: &Extracted, b: &Extracted) -> DiffResult {
40 let keys_a: Vec<String> = a.ops.iter().map(Op::align_key).collect();
41 let keys_b: Vec<String> = b.ops.iter().map(Op::align_key).collect();
42 let ops = capture_diff_slices(Algorithm::Patience, &keys_a, &keys_b);
43
44 let mut lines = Vec::new();
45 let (mut added, mut removed, mut modified) = (0usize, 0usize, 0usize);
46
47 for op in ops {
48 match op {
49 DiffOp::Equal {
50 old_index,
51 new_index,
52 len,
53 } => {
54 for k in 0..len {
55 let oa = &a.ops[old_index + k];
56 let ob = &b.ops[new_index + k];
57 if oa.params == ob.params {
58 lines.push(DiffLine {
59 change: Change::Same,
60 text: oa.render(),
61 new_params: None,
62 });
63 } else {
64 modified += 1;
65 lines.push(DiffLine {
66 change: Change::Modified,
67 text: oa.render(),
68 new_params: Some(ob.params.clone()),
69 });
70 }
71 }
72 }
73 DiffOp::Delete {
74 old_index, old_len, ..
75 } => {
76 for k in 0..old_len {
77 removed += 1;
78 lines.push(DiffLine {
79 change: Change::Removed,
80 text: a.ops[old_index + k].render(),
81 new_params: None,
82 });
83 }
84 }
85 DiffOp::Insert {
86 new_index, new_len, ..
87 } => {
88 for k in 0..new_len {
89 added += 1;
90 lines.push(DiffLine {
91 change: Change::Added,
92 text: b.ops[new_index + k].render(),
93 new_params: None,
94 });
95 }
96 }
97 DiffOp::Replace {
98 old_index,
99 old_len,
100 new_index,
101 new_len,
102 } => {
103 for k in 0..old_len {
104 removed += 1;
105 lines.push(DiffLine {
106 change: Change::Removed,
107 text: a.ops[old_index + k].render(),
108 new_params: None,
109 });
110 }
111 for k in 0..new_len {
112 added += 1;
113 lines.push(DiffLine {
114 change: Change::Added,
115 text: b.ops[new_index + k].render(),
116 new_params: None,
117 });
118 }
119 }
120 }
121 }
122
123 DiffResult {
124 lines,
125 added,
126 removed,
127 modified,
128 }
129}