diff --git a/src/algorithms/hook.rs b/src/algorithms/hook.rs index 251ad19..fbbbab9 100644 --- a/src/algorithms/hook.rs +++ b/src/algorithms/hook.rs @@ -1,3 +1,6 @@ +use std::convert::Infallible; +use std::mem; + /// A trait for reacting to an edit script from the "old" version to /// the "new" version. pub trait DiffHook: Sized { @@ -103,3 +106,115 @@ impl<'a, D: DiffHook + 'a> DiffHook for &'a mut D { (*self).finish() } } + +/// Utility enum to capture a diff operation. +#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] +pub enum DiffOp { + Equal { + old_index: usize, + new_index: usize, + len: usize, + }, + Delete { + old_index: usize, + old_len: usize, + new_index: usize, + }, + Insert { + old_index: usize, + new_index: usize, + new_len: usize, + }, + Replace { + old_index: usize, + old_len: usize, + new_index: usize, + new_len: usize, + }, +} + +/// Captures diff operations. +#[derive(Default, Clone)] +pub struct CaptureHook(Vec); + +impl CaptureHook { + /// Creates a new capture hook. + pub fn new() -> CaptureHook { + CaptureHook::default() + } + + /// Removes all replace operations. + pub fn resolve_replace(&mut self) { + self.0 = mem::replace(&mut self.0, Vec::new()) + .into_iter() + .filter_map(|op| Some(op)) + .collect(); + } + + /// Converts the capture hook into a vector. + pub fn into_vec(self) -> Vec { + self.0 + } + + /// Accesses the captured operations. + pub fn ops(&self) -> &[DiffOp] { + &self.0 + } +} + +impl DiffHook for CaptureHook { + type Error = Infallible; + + fn equal(&mut self, old_index: usize, new_index: usize, len: usize) -> Result<(), Self::Error> { + self.0.push(DiffOp::Equal { + old_index, + new_index, + len, + }); + Ok(()) + } + + fn delete( + &mut self, + old_index: usize, + old_len: usize, + new_index: usize, + ) -> Result<(), Self::Error> { + self.0.push(DiffOp::Delete { + old_index, + old_len, + new_index, + }); + Ok(()) + } + + fn insert( + &mut self, + old_index: usize, + new_index: usize, + new_len: usize, + ) -> Result<(), Self::Error> { + self.0.push(DiffOp::Insert { + old_index, + new_index, + new_len, + }); + Ok(()) + } + + fn replace( + &mut self, + old_index: usize, + old_len: usize, + new_index: usize, + new_len: usize, + ) -> Result<(), Self::Error> { + self.0.push(DiffOp::Replace { + old_index, + old_len, + new_index, + new_len, + }); + Ok(()) + } +} diff --git a/src/algorithms/myers.rs b/src/algorithms/myers.rs index 7834f85..6c0257e 100644 --- a/src/algorithms/myers.rs +++ b/src/algorithms/myers.rs @@ -237,21 +237,23 @@ fn test_replace() { let a: &[usize] = &[0, 1, 2, 3, 4]; let b: &[usize] = &[0, 1, 2, 7, 8, 9]; - struct D; - impl DiffHook for D { - type Error = (); - fn delete(&mut self, _o: usize, _len: usize, _new: usize) -> Result<(), ()> { - panic!("should not delete") - } - fn insert(&mut self, _o: usize, _n: usize, _len: usize) -> Result<(), ()> { - panic!("should not insert") - } - fn replace(&mut self, _o: usize, _l: usize, _n: usize, _nl: usize) -> Result<(), ()> { - Ok(()) - } - } - let mut d = crate::algorithms::Replace::new(D); - diff(&mut d, a, 0..a.len(), b, 0..b.len()).unwrap(); + let mut d = crate::algorithms::Replace::new(crate::algorithms::CaptureHook::new()); + diff_slices(&mut d, a, b).unwrap(); + insta::assert_debug_snapshot!(d.into_inner().ops(), @r###" + [ + Equal { + old_index: 0, + new_index: 0, + len: 3, + }, + Replace { + old_index: 3, + old_len: 2, + new_index: 3, + new_len: 3, + }, + ] + "###); } #[test] @@ -259,23 +261,35 @@ fn test_pat() { let a: &[usize] = &[0, 1, 3, 4, 5]; let b: &[usize] = &[0, 1, 4, 5, 8, 9]; - struct D; - impl DiffHook for D { - type Error = (); - fn delete(&mut self, _o: usize, _len: usize, _new: usize) -> Result<(), ()> { - println!("delete {:?} {:?} {:?}", _o, _len, _new); - Ok(()) - } - fn insert(&mut self, _o: usize, _n: usize, _len: usize) -> Result<(), ()> { - println!("insert {:?} {:?} {:?}", _o, _n, _len); - Ok(()) - } - fn replace(&mut self, _o: usize, _l: usize, _n: usize, _nl: usize) -> Result<(), ()> { - println!("replace {:?} {:?} {:?} {:?}", _o, _l, _n, _nl); - Ok(()) - } - } - - let mut d = crate::algorithms::Replace::new(D); - diff(&mut d, a, 0..a.len(), b, 0..b.len()).unwrap(); + let mut d = crate::algorithms::CaptureHook::new(); + diff_slices(&mut d, a, b).unwrap(); + insta::assert_debug_snapshot!(d.ops(), @r###" + [ + Equal { + old_index: 0, + new_index: 0, + len: 2, + }, + Delete { + old_index: 2, + old_len: 1, + new_index: 2, + }, + Equal { + old_index: 3, + new_index: 2, + len: 2, + }, + Insert { + old_index: 5, + new_index: 4, + new_len: 1, + }, + Insert { + old_index: 5, + new_index: 5, + new_len: 1, + }, + ] + "###); } diff --git a/src/algorithms/replace.rs b/src/algorithms/replace.rs index 06ca560..8bca395 100644 --- a/src/algorithms/replace.rs +++ b/src/algorithms/replace.rs @@ -128,7 +128,7 @@ impl DiffHook for Replace { } #[test] -fn myers() { +fn test_mayers_replace() { use crate::algorithms::myers; let a: &[&str] = &[ ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n", @@ -153,36 +153,62 @@ fn myers() { "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n", ]; - struct D(Vec); - impl DiffHook for D { - type Error = (); - fn equal(&mut self, o: usize, n: usize, len: usize) -> Result<(), ()> { - self.0.push(format!("equal {:?} {:?} {:?}", o, n, len)); - Ok(()) - } - fn delete(&mut self, o: usize, len: usize, new: usize) -> Result<(), ()> { - self.0.push(format!("delete {:?} {:?} {:?}", o, len, new)); - Ok(()) - } - fn insert(&mut self, o: usize, n: usize, len: usize) -> Result<(), ()> { - self.0.push(format!("insert {:?} {:?} {:?}", o, n, len)); - Ok(()) - } - fn replace(&mut self, o: usize, l: usize, n: usize, nl: usize) -> Result<(), ()> { - self.0 - .push(format!("replace {:?} {:?} {:?} {:?}", o, l, n, nl)); - Ok(()) - } - } - let mut d = Replace::new(D(Vec::new())); - myers::diff(&mut d, a, 0..a.len(), b, 0..b.len()).unwrap(); + let mut d = Replace::new(crate::algorithms::CaptureHook::new()); + myers::diff_slices(&mut d, a, b).unwrap(); - insta::assert_yaml_snapshot!(&d.into_inner().0, @r###" - --- - - equal 0 0 1 - - replace 1 1 1 1 - - equal 2 2 3 - - replace 5 1 5 1 - - equal 6 6 3 + insta::assert_debug_snapshot!(&d.into_inner().ops(), @r###" + [ + Equal { + old_index: 0, + new_index: 0, + len: 1, + }, + Replace { + old_index: 1, + old_len: 1, + new_index: 1, + new_len: 1, + }, + Equal { + old_index: 2, + new_index: 2, + len: 3, + }, + Replace { + old_index: 5, + old_len: 1, + new_index: 5, + new_len: 1, + }, + Equal { + old_index: 6, + new_index: 6, + len: 3, + }, + ] + "###); +} + +#[test] +fn test_replace() { + let a: &[usize] = &[0, 1, 2, 3, 4]; + let b: &[usize] = &[0, 1, 2, 7, 8, 9]; + + let mut d = Replace::new(crate::algorithms::CaptureHook::new()); + crate::algorithms::myers::diff_slices(&mut d, a, b).unwrap(); + insta::assert_debug_snapshot!(d.into_inner().ops(), @r###" + [ + Equal { + old_index: 0, + new_index: 0, + len: 3, + }, + Replace { + old_index: 3, + old_len: 2, + new_index: 3, + new_len: 3, + }, + ] "###); }