@@ -52,6 +52,27 @@ pub(crate) enum Edit<T> {
52
52
53
53
/// An element was added to each sequence.
54
54
Both ( T ) ,
55
+
56
+ /// Additional (unlisted) elements are present in the left sequence.
57
+ ///
58
+ /// This is only output in the mode [`Mode::Prefix`]. Its presence precludes
59
+ /// reconstructing the left sequence from the right sequence.
60
+ AdditionalLeft ,
61
+ }
62
+
63
+ /// Controls the termination condition of [`edit_list`].
64
+ pub ( crate ) enum Mode {
65
+ /// Indicates that the two arguments are intended to be equal.
66
+ ///
67
+ /// The entire edit list to transform between `left` and `right` is
68
+ /// returned.
69
+ Exact ,
70
+
71
+ /// Indicates that `right` is inteded to be a prefix of `left`.
72
+ ///
73
+ /// Any additional parts of `left` after the prefix `right` are omitted from
74
+ /// the output.
75
+ Prefix ,
55
76
}
56
77
57
78
/// Computes the edit list of `left` and `right`.
@@ -69,6 +90,7 @@ pub(crate) enum Edit<T> {
69
90
pub ( crate ) fn edit_list < T : PartialEq + Copy > (
70
91
left : impl IntoIterator < Item = T > ,
71
92
right : impl IntoIterator < Item = T > ,
93
+ mode : Mode ,
72
94
) -> Difference < T > {
73
95
let left: Vec < _ > = left. into_iter ( ) . collect ( ) ;
74
96
let right: Vec < _ > = right. into_iter ( ) . collect ( ) ;
@@ -151,6 +173,22 @@ pub(crate) fn edit_list<T: PartialEq + Copy>(
151
173
path. right_endpoint = right_endpoint;
152
174
paths_current. push ( path) ;
153
175
}
176
+
177
+ if matches ! ( mode, Mode :: Prefix ) {
178
+ if let Some ( path) = paths_current
179
+ . iter_mut ( )
180
+ . filter ( |p| p. right_endpoint == right. len ( ) )
181
+ . max_by ( |p1, p2| p1. edits . len ( ) . cmp ( & p2. edits . len ( ) ) )
182
+ {
183
+ path. edits . push ( Edit :: AdditionalLeft ) ;
184
+ return if path. edits . iter ( ) . any ( |v| !matches ! ( v, Edit :: Both ( _) ) ) {
185
+ Difference :: Editable ( std:: mem:: take ( & mut path. edits ) )
186
+ } else {
187
+ Difference :: Equal
188
+ } ;
189
+ }
190
+ }
191
+
154
192
paths_last = paths_current;
155
193
}
156
194
@@ -192,19 +230,23 @@ mod tests {
192
230
193
231
#[ test]
194
232
fn returns_equal_when_strings_are_equal ( ) -> Result < ( ) > {
195
- let result = edit_list ( [ "A string" ] , [ "A string" ] ) ;
233
+ let result = edit_list ( [ "A string" ] , [ "A string" ] , Mode :: Exact ) ;
196
234
verify_that ! ( result, matches_pattern!( Difference :: Equal ) )
197
235
}
198
236
199
237
#[ test]
200
238
fn returns_sequence_of_two_common_parts ( ) -> Result < ( ) > {
201
- let result = edit_list ( [ "A string (1)" , "A string (2)" ] , [ "A string (1)" , "A string (2)" ] ) ;
239
+ let result = edit_list (
240
+ [ "A string (1)" , "A string (2)" ] ,
241
+ [ "A string (1)" , "A string (2)" ] ,
242
+ Mode :: Exact ,
243
+ ) ;
202
244
verify_that ! ( result, matches_pattern!( Difference :: Equal ) )
203
245
}
204
246
205
247
#[ test]
206
248
fn returns_extra_left_when_only_left_has_content ( ) -> Result < ( ) > {
207
- let result = edit_list ( [ "A string" ] , [ ] ) ;
249
+ let result = edit_list ( [ "A string" ] , [ ] , Mode :: Exact ) ;
208
250
verify_that ! (
209
251
result,
210
252
matches_pattern!( Difference :: Editable ( elements_are![ matches_pattern!(
@@ -215,7 +257,7 @@ mod tests {
215
257
216
258
#[ test]
217
259
fn returns_extra_right_when_only_right_has_content ( ) -> Result < ( ) > {
218
- let result = edit_list ( [ ] , [ "A string" ] ) ;
260
+ let result = edit_list ( [ ] , [ "A string" ] , Mode :: Exact ) ;
219
261
verify_that ! (
220
262
result,
221
263
matches_pattern!( Difference :: Editable ( elements_are![ matches_pattern!(
@@ -226,7 +268,7 @@ mod tests {
226
268
227
269
#[ test]
228
270
fn returns_extra_left_followed_by_extra_right_with_two_unequal_strings ( ) -> Result < ( ) > {
229
- let result = edit_list ( [ "A string" ] , [ "Another string" ] ) ;
271
+ let result = edit_list ( [ "A string" ] , [ "Another string" ] , Mode :: Exact ) ;
230
272
verify_that ! (
231
273
result,
232
274
matches_pattern!( Difference :: Editable ( elements_are![
@@ -238,7 +280,8 @@ mod tests {
238
280
239
281
#[ test]
240
282
fn interleaves_extra_left_and_extra_right_when_multiple_lines_differ ( ) -> Result < ( ) > {
241
- let result = edit_list ( [ "A string" , "A string" ] , [ "Another string" , "Another string" ] ) ;
283
+ let result =
284
+ edit_list ( [ "A string" , "A string" ] , [ "Another string" , "Another string" ] , Mode :: Exact ) ;
242
285
verify_that ! (
243
286
result,
244
287
matches_pattern!( Difference :: Editable ( elements_are![
@@ -252,7 +295,8 @@ mod tests {
252
295
253
296
#[ test]
254
297
fn returns_common_part_plus_difference_when_there_is_common_prefix ( ) -> Result < ( ) > {
255
- let result = edit_list ( [ "Common part" , "Left only" ] , [ "Common part" , "Right only" ] ) ;
298
+ let result =
299
+ edit_list ( [ "Common part" , "Left only" ] , [ "Common part" , "Right only" ] , Mode :: Exact ) ;
256
300
verify_that ! (
257
301
result,
258
302
matches_pattern!( Difference :: Editable ( elements_are![
@@ -265,7 +309,7 @@ mod tests {
265
309
266
310
#[ test]
267
311
fn returns_common_part_plus_extra_left_when_left_has_extra_suffix ( ) -> Result < ( ) > {
268
- let result = edit_list ( [ "Common part" , "Left only" ] , [ "Common part" ] ) ;
312
+ let result = edit_list ( [ "Common part" , "Left only" ] , [ "Common part" ] , Mode :: Exact ) ;
269
313
verify_that ! (
270
314
result,
271
315
matches_pattern!( Difference :: Editable ( elements_are![
@@ -277,7 +321,7 @@ mod tests {
277
321
278
322
#[ test]
279
323
fn returns_common_part_plus_extra_right_when_right_has_extra_suffix ( ) -> Result < ( ) > {
280
- let result = edit_list ( [ "Common part" ] , [ "Common part" , "Right only" ] ) ;
324
+ let result = edit_list ( [ "Common part" ] , [ "Common part" , "Right only" ] , Mode :: Exact ) ;
281
325
verify_that ! (
282
326
result,
283
327
matches_pattern!( Difference :: Editable ( elements_are![
@@ -289,7 +333,8 @@ mod tests {
289
333
290
334
#[ test]
291
335
fn returns_difference_plus_common_part_when_there_is_common_suffix ( ) -> Result < ( ) > {
292
- let result = edit_list ( [ "Left only" , "Common part" ] , [ "Right only" , "Common part" ] ) ;
336
+ let result =
337
+ edit_list ( [ "Left only" , "Common part" ] , [ "Right only" , "Common part" ] , Mode :: Exact ) ;
293
338
verify_that ! (
294
339
result,
295
340
matches_pattern!( Difference :: Editable ( elements_are![
@@ -306,6 +351,7 @@ mod tests {
306
351
let result = edit_list (
307
352
[ "Left only (1)" , "Common part" , "Left only (2)" ] ,
308
353
[ "Right only (1)" , "Common part" , "Right only (2)" ] ,
354
+ Mode :: Exact ,
309
355
) ;
310
356
verify_that ! (
311
357
result,
@@ -325,6 +371,7 @@ mod tests {
325
371
let result = edit_list (
326
372
[ "Common part (1)" , "Left only" , "Common part (2)" ] ,
327
373
[ "Common part (1)" , "Right only" , "Common part (2)" ] ,
374
+ Mode :: Exact ,
328
375
) ;
329
376
verify_that ! (
330
377
result,
@@ -343,6 +390,7 @@ mod tests {
343
390
let result = edit_list (
344
391
[ "Common part (1)" , "Left only" , "Common part (2)" ] ,
345
392
[ "Common part (1)" , "Common part (2)" ] ,
393
+ Mode :: Exact ,
346
394
) ;
347
395
verify_that ! (
348
396
result,
@@ -360,6 +408,7 @@ mod tests {
360
408
let result = edit_list (
361
409
[ "Common part (1)" , "Common part (2)" ] ,
362
410
[ "Common part (1)" , "Right only" , "Common part (2)" ] ,
411
+ Mode :: Exact ,
363
412
) ;
364
413
verify_that ! (
365
414
result,
@@ -371,9 +420,36 @@ mod tests {
371
420
)
372
421
}
373
422
423
+ #[ test]
424
+ fn skips_extra_parts_on_left_at_end_in_prefix_mode ( ) -> Result < ( ) > {
425
+ let result =
426
+ edit_list ( [ "Common part" , "Left only" ] , [ "Right only" , "Common part" ] , Mode :: Prefix ) ;
427
+ verify_that ! (
428
+ result,
429
+ matches_pattern!( Difference :: Editable ( not( contains( matches_pattern!(
430
+ Edit :: ExtraLeft ( eq( "Left only" ) )
431
+ ) ) ) ) )
432
+ )
433
+ }
434
+
435
+ #[ test]
436
+ fn does_not_skip_extra_parts_on_left_in_prefix_mode_at_end_when_they_are_in_common ( )
437
+ -> Result < ( ) > {
438
+ let result =
439
+ edit_list ( [ "Left only" , "Common part" ] , [ "Right only" , "Common part" ] , Mode :: Prefix ) ;
440
+ verify_that ! (
441
+ result,
442
+ matches_pattern!( Difference :: Editable ( elements_are![
443
+ matches_pattern!( Edit :: ExtraLeft ( eq( "Left only" ) ) ) ,
444
+ matches_pattern!( Edit :: ExtraRight ( eq( "Right only" ) ) ) ,
445
+ matches_pattern!( Edit :: Both ( eq( "Common part" ) ) ) ,
446
+ ] ) )
447
+ )
448
+ }
449
+
374
450
#[ test]
375
451
fn returns_unrelated_when_maximum_distance_exceeded ( ) -> Result < ( ) > {
376
- let result = edit_list ( 0 ..=20 , 20 ..40 ) ;
452
+ let result = edit_list ( 0 ..=20 , 20 ..40 , Mode :: Exact ) ;
377
453
verify_that ! ( result, matches_pattern!( Difference :: Unrelated ) )
378
454
}
379
455
@@ -382,7 +458,7 @@ mod tests {
382
458
left: Vec <Alphabet >,
383
459
right: Vec <Alphabet >
384
460
) -> TestResult {
385
- match edit_list( left. clone( ) , right. clone( ) ) {
461
+ match edit_list( left. clone( ) , right. clone( ) , Mode :: Exact ) {
386
462
Difference :: Equal => TestResult :: from_bool( left == right) ,
387
463
Difference :: Editable ( edit_list) => {
388
464
TestResult :: from_bool( apply_edits_to_left( & edit_list, & left) == right)
@@ -403,7 +479,7 @@ mod tests {
403
479
left: Vec <Alphabet >,
404
480
right: Vec <Alphabet >
405
481
) -> TestResult {
406
- match edit_list( left. clone( ) , right. clone( ) ) {
482
+ match edit_list( left. clone( ) , right. clone( ) , Mode :: Exact ) {
407
483
Difference :: Equal => TestResult :: from_bool( left == right) ,
408
484
Difference :: Editable ( edit_list) => {
409
485
TestResult :: from_bool( apply_edits_to_right( & edit_list, & right) == left)
@@ -450,6 +526,9 @@ mod tests {
450
526
assert_that ! ( left_iter. next( ) , some( eq( value) ) ) ;
451
527
result. push ( * value) ;
452
528
}
529
+ Edit :: AdditionalLeft => {
530
+ fail ! ( "Unexpected Edit::AdditionalLeft" ) . unwrap ( ) ;
531
+ }
453
532
}
454
533
}
455
534
assert_that ! ( left_iter. next( ) , none( ) ) ;
@@ -474,6 +553,9 @@ mod tests {
474
553
assert_that ! ( right_iter. next( ) , some( eq( value) ) ) ;
475
554
result. push ( * value) ;
476
555
}
556
+ Edit :: AdditionalLeft => {
557
+ fail ! ( "Unexpected Edit::AdditionalLeft" ) . unwrap ( ) ;
558
+ }
477
559
}
478
560
}
479
561
assert_that ! ( right_iter. next( ) , none( ) ) ;
0 commit comments