1#[doc = include_str!("../README.md")]
2#[cfg(test)]
3mod tests {
4 use super::*;
5
6 fn display<T: PartialEq + Clone + Debug>(a: &[T], b: &[T], d: &[Change<T>]) {
7 println!("a = {:?}", a);
8 println!("b = {:?}", b);
9 for i in d {
10 println!("i = {:?}", i);
11 }
12 }
13
14 fn test_states<T: PartialEq + Clone + Debug>(
15 states: &[&[T]],
16 diff: &dyn Fn(&[T], &[T]) -> Vec<Change<T>>,
17 ) {
18 for i in 0..states.len() - 1 {
19 let a = &states[i];
20 let b = &states[i + 1];
21 let d = diff(&a, &b);
22 display(&a, &b, &d);
23 let c = patch(&a, &d);
24 assert_eq!(&c, b);
25 }
26 }
27
28 #[test]
29 fn diff_int() {
30 test_states(
31 &[
32 &[],
33 &[2],
34 &[2, 6],
35 &[2, 4, 6],
36 &[2, 4, 6, 8],
37 &[1, 2, 4, 6, 8],
38 &[1, 2, 3, 5, 8],
39 &[1, 2, 3, 5, 8],
40 &[2, 3, 5, 8],
41 &[2, 5, 8],
42 &[2, 5],
43 &[],
44 ],
45 &diff_diff,
46 );
47 }
48
49 #[test]
50 fn diff_str() {
51 test_states(
52 &[
53 &[],
54 &["alpha"],
55 &["alpha", "delta"],
56 &["alpha", "bravo", "delta"],
57 &["alpha", "bravo", "charlie", "delta"],
58 &["pre-alpha", "alpha", "pre-bravo", "pre-charlie", "delta"],
59 &["pre-alpha", "alpha", "pre-bravo", "pre-charlie"],
60 &["pre-alpha", "pre-bravo", "pre-charlie"],
61 &["pre-bravo", "pre-charlie"],
62 &["pre-bravo"],
63 &[],
64 ],
65 &diff_diff,
66 );
67 }
68
69 #[test]
70 fn lcs_int() {
71 test_states(
72 &[
73 &[],
74 &[2],
75 &[2, 6],
76 &[2, 4, 6],
77 &[2, 4, 6, 8],
78 &[1, 2, 4, 6, 8],
79 &[1, 2, 3, 5, 8],
80 &[1, 2, 3, 5, 8],
81 &[2, 3, 5, 8],
82 &[2, 5, 8],
83 &[2, 5],
84 &[],
85 ],
86 &lcs_diff,
87 );
88 }
89
90 #[test]
91 fn lcs_str() {
92 test_states(
93 &[
94 &[],
95 &["alpha"],
96 &["alpha", "delta"],
97 &["alpha", "bravo", "delta"],
98 &["alpha", "bravo", "charlie", "delta"],
99 &["pre-alpha", "alpha", "pre-bravo", "pre-charlie", "delta"],
100 &["pre-alpha", "alpha", "pre-bravo", "pre-charlie"],
101 &["pre-alpha", "pre-bravo", "pre-charlie"],
102 &["pre-bravo", "pre-charlie"],
103 &["pre-bravo"],
104 &[],
105 ],
106 &lcs_diff,
107 );
108 }
109
110 #[test]
111 fn wu_int() {
112 test_states(
113 &[
114 &[],
115 &[2],
116 &[2, 6],
117 &[2, 4, 6],
118 &[2, 4, 6, 8],
119 &[1, 2, 4, 6, 8],
120 &[1, 2, 3, 5, 8],
121 &[1, 2, 3, 5, 8],
122 &[2, 3, 5, 8],
123 &[2, 5, 8],
124 &[2, 5],
125 &[],
126 ],
127 &wu_diff,
128 );
129 }
130
131 #[test]
132 fn wu_str() {
133 test_states(
134 &[
135 &[],
136 &["alpha"],
137 &["alpha", "delta"],
138 &["alpha", "bravo", "delta"],
139 &["alpha", "bravo", "charlie", "delta"],
140 &["pre-alpha", "alpha", "pre-bravo", "pre-charlie", "delta"],
141 &["pre-alpha", "alpha", "pre-bravo", "pre-charlie"],
142 &["pre-alpha", "pre-bravo", "pre-charlie"],
143 &["pre-bravo", "pre-charlie"],
144 &["pre-bravo"],
145 &[],
146 ],
147 &wu_diff,
148 );
149 }
150
151 fn update<T: PartialEq + Clone + Debug>(
152 a: &[T],
153 b: &[T],
154 changes: Vec<Change<T>>,
155 diff: &dyn Fn(&[T], &[T]) -> Vec<Change<T>>,
156 ) {
157 assert_eq!(diff(&a, &b), changes);
158 }
159
160 #[test]
161 fn diff_update() {
162 update(&[1], &[2], vec![Change::Update((0, 2))], &diff_diff);
163 update(&[1, 2], &[1, 3], vec![Change::Update((1, 3))], &diff_diff);
164 update(
165 &[1, 2, 3],
166 &[1, 2, 4],
167 vec![Change::Update((2, 4))],
168 &diff_diff,
169 );
170 update(
171 &["alpha"],
172 &["bravo"],
173 vec![Change::Update((0, "bravo"))],
174 &diff_diff,
175 );
176 update(
177 &["alpha", "bravo"],
178 &["alpha", "charlie"],
179 vec![Change::Update((1, "charlie"))],
180 &diff_diff,
181 );
182 update(
183 &["alpha", "bravo", "charlie"],
184 &["alpha", "bravo", "delta"],
185 vec![Change::Update((2, "delta"))],
186 &diff_diff,
187 );
188 }
189
190 #[test]
191 fn lcs_update() {
192 update(&[1], &[2], vec![Change::Update((0, 2))], &lcs_diff);
193 update(&[1, 2], &[1, 3], vec![Change::Update((1, 3))], &lcs_diff);
194 update(
195 &[1, 2, 3],
196 &[1, 2, 4],
197 vec![Change::Update((2, 4))],
198 &lcs_diff,
199 );
200 update(
201 &["alpha"],
202 &["bravo"],
203 vec![Change::Update((0, "bravo"))],
204 &lcs_diff,
205 );
206 update(
207 &["alpha", "bravo"],
208 &["alpha", "charlie"],
209 vec![Change::Update((1, "charlie"))],
210 &lcs_diff,
211 );
212 update(
213 &["alpha", "bravo", "charlie"],
214 &["alpha", "bravo", "delta"],
215 vec![Change::Update((2, "delta"))],
216 &lcs_diff,
217 );
218 }
219
220 #[test]
221 fn wu_update() {
222 update(&[1], &[2], vec![Change::Update((0, 2))], &wu_diff);
223 update(&[1, 2], &[1, 3], vec![Change::Update((1, 3))], &wu_diff);
224 update(
225 &[1, 2, 3],
226 &[1, 2, 4],
227 vec![Change::Update((2, 4))],
228 &wu_diff,
229 );
230 update(
231 &["alpha"],
232 &["bravo"],
233 vec![Change::Update((0, "bravo"))],
234 &wu_diff,
235 );
236 update(
237 &["alpha", "bravo"],
238 &["alpha", "charlie"],
239 vec![Change::Update((1, "charlie"))],
240 &wu_diff,
241 );
242 update(
243 &["alpha", "bravo", "charlie"],
244 &["alpha", "bravo", "delta"],
245 vec![Change::Update((2, "delta"))],
246 &wu_diff,
247 );
248 }
249}
250
251use std::fmt::Debug;
252
253pub fn insert<T: PartialEq + Clone + Debug>(n: usize, item: &T, changes: &mut Vec<Change<T>>) {
257 if let Some(prev_change) = changes.pop() {
258 if let Change::Remove(prev_n) = prev_change {
259 if n == prev_n {
260 changes.push(Change::Update((n, (*item).clone())));
261 return;
262 }
263 }
264 changes.push(prev_change);
265 }
266 changes.push(Change::Insert((n, (*item).clone())));
267}
268
269pub fn remove<T: PartialEq + Clone + Debug>(n: usize, changes: &mut Vec<Change<T>>) {
273 if let Some(prev_change) = changes.pop() {
274 if let Change::Insert((prev_n, ref item)) = prev_change {
275 if n == prev_n + 1 {
276 changes.push(Change::Update((prev_n, item.clone())));
277 return;
278 }
279 }
280 changes.push(prev_change);
281 }
282 changes.push(Change::Remove(n));
283}
284
285#[derive(Debug, Clone, PartialEq)]
293pub enum Change<T: PartialEq + Clone + Debug> {
294 Remove(usize),
295 Insert((usize, T)),
296 Update((usize, T)),
297}
298
299pub fn diff_changes<T: PartialEq + Clone + Debug>(d: &[diff::Result<&T>]) -> Vec<Change<T>> {
306 let mut changes = vec![];
307 let mut removed = 0;
308 for (i, j) in d.iter().enumerate() {
309 let n = i - removed;
310 match j {
311 diff::Result::Left(_) => {
312 remove(n, &mut changes);
313 removed += 1;
314 }
315 diff::Result::Right(r) => {
316 insert(n, *r, &mut changes);
317 }
318 _ => {}
319 }
320 }
321 changes
322}
323
324pub fn diff_diff<T: PartialEq + Clone + Debug>(a: &[T], b: &[T]) -> Vec<Change<T>> {
328 diff_changes(&diff::slice(a, b))
329}
330
331pub fn lcs_changes<T: PartialEq + Clone + Debug>(d: &[lcs_diff::DiffResult<T>]) -> Vec<Change<T>> {
338 let mut changes = vec![];
339 let mut removed = 0;
340 let mut added = 0;
341 for i in d {
342 match i {
343 lcs_diff::DiffResult::Removed(r) => {
344 let n = r.old_index.unwrap() + added - removed;
345 remove(n, &mut changes);
346 removed += 1;
347 }
348 lcs_diff::DiffResult::Added(r) => {
349 let n = r.new_index.unwrap();
350 insert(n, &r.data, &mut changes);
351 added += 1;
352 }
353 _ => {}
354 }
355 }
356 changes
357}
358
359pub fn lcs_diff<T: PartialEq + Clone + Debug>(a: &[T], b: &[T]) -> Vec<Change<T>> {
363 lcs_changes(lcs_diff::diff(a, b).as_slice())
364}
365
366pub fn wu_changes<T: PartialEq + Clone + Debug>(
373 d: &[wu_diff::DiffResult],
374 b: &[T],
375) -> Vec<Change<T>> {
376 let mut changes = vec![];
377 let mut removed = 0;
378 let mut added = 0;
379 for i in d {
380 match i {
381 wu_diff::DiffResult::Removed(r) => {
382 let n = r.old_index.unwrap() + added - removed;
383 remove(n, &mut changes);
384 removed += 1;
385 }
386 wu_diff::DiffResult::Added(r) => {
387 let n = r.new_index.unwrap();
388 insert(n, &b[n], &mut changes);
389 added += 1;
390 }
391 _ => {}
392 }
393 }
394 changes
395}
396
397pub fn wu_diff<T: PartialEq + Clone + Debug>(a: &[T], b: &[T]) -> Vec<Change<T>> {
401 wu_changes(&wu_diff::diff(a, b), b)
402}
403
404pub fn patch<T: PartialEq + Clone + Debug>(a: &[T], changes: &[Change<T>]) -> Vec<T> {
406 let mut a = a.to_vec();
407 for i in changes {
408 match i {
409 Change::Remove(n) => {
410 a.remove(*n);
411 }
412 Change::Insert((n, item)) => {
413 a.insert(*n, item.clone());
414 }
415 Change::Update((n, item)) => {
416 a.remove(*n);
417 a.insert(*n, item.clone());
418 }
419 }
420 }
421 a
422}