diff --git a/src/diff/tests.rs b/src/diff/tests.rs index ab94e51..9ac8fa7 100644 --- a/src/diff/tests.rs +++ b/src/diff/tests.rs @@ -664,3 +664,45 @@ Second: assert_eq!(result, expected); } + +#[test] +fn reverse_empty_file() { + let p = create_patch("", "make it so"); + let reverse = p.reverse(); + + let hunk_lines = p.hunks().iter().map(|h| h.lines()); + let reverse_hunk_lines = reverse.hunks().iter().map(|h| h.lines()); + + for (lines, reverse_lines) in hunk_lines.zip(reverse_hunk_lines) { + for (line, reverse) in lines.iter().zip(reverse_lines.iter()) { + match line { + l @ Line::Context(_) => assert_eq!(l, reverse), + Line::Delete(d) => assert!(matches!(reverse, Line::Insert(i) if d == i)), + Line::Insert(i) => assert!(matches!(reverse, Line::Delete(d) if d == i)), + } + } + } + + let re_reverse = apply(&apply("", &p).unwrap(), &reverse).unwrap(); + assert_eq!(re_reverse, ""); +} + +#[test] +fn reverse_multi_line_file() { + let original = r"Commander Worf +What do you want this time, Picard?! +Commander Worf how dare you speak to mean that way! +"; + let modified = r"Commander Worf +Yes, Captain Picard? +Commander Worf, you are a valued member of my crew +Why, thank you Captain. As are you. A true warrior. Kupluh! +Kupluh, Indeed +"; + + let p = create_patch(original, modified); + let reverse = p.reverse(); + + let re_reverse = apply(&apply(original, &p).unwrap(), &reverse).unwrap(); + assert_eq!(re_reverse, original); +} diff --git a/src/patch/mod.rs b/src/patch/mod.rs index 1efb2cf..2b98246 100644 --- a/src/patch/mod.rs +++ b/src/patch/mod.rs @@ -52,6 +52,15 @@ impl<'a, T: ToOwned + ?Sized> Patch<'a, T> { pub fn hunks(&self) -> &[Hunk<'_, T>] { &self.hunks } + + pub fn reverse(&self) -> Patch<'_, T> { + let hunks = self.hunks.iter().map(Hunk::reverse).collect(); + Patch { + original: self.modified.clone(), + modified: self.original.clone(), + hunks, + } + } } impl + ToOwned + ?Sized> Patch<'_, T> { @@ -278,6 +287,18 @@ impl<'a, T: ?Sized> Hunk<'a, T> { pub fn lines(&self) -> &[Line<'a, T>] { &self.lines } + + /// Creates a reverse patch for the hunk. This is equivalent to what + /// XDL_PATCH_REVERSE would apply in libxdiff. + pub fn reverse(&self) -> Self { + let lines = self.lines.iter().map(Line::reverse).collect(); + Self { + old_range: self.new_range, + new_range: self.old_range, + function_context: self.function_context, + lines, + } + } } impl Clone for Hunk<'_, T> { @@ -362,3 +383,13 @@ impl Clone for Line<'_, T> { *self } } + +impl Line<'_, T> { + pub fn reverse(&self) -> Self { + match self { + Line::Context(s) => Line::Context(s), + Line::Delete(s) => Line::Insert(s), + Line::Insert(s) => Line::Delete(s), + } + } +}