use crate::Diff;
use proptest::prelude::*;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
enum Action {
Equal {
old: usize,
new: usize,
len: usize,
},
Delete {
old: usize,
len: usize,
new: usize,
},
Insert {
old: usize,
new: usize,
new_len: usize,
},
Replace {
old: usize,
old_len: usize,
new: usize,
new_len: usize,
},
Finish,
}
#[derive(Debug)]
struct AccumDiff(Vec<Action>);
impl Diff for AccumDiff {
type Error = ();
fn equal(&mut self, old: usize, new: usize, len: usize) -> Result<(), ()> {
Ok(self.0.push(Action::Equal { old, new, len }))
}
fn delete(&mut self, old: usize, len: usize, new: usize) -> Result<(), ()> {
Ok(self.0.push(Action::Delete { old, len, new }))
}
fn insert(&mut self, old: usize, new: usize, new_len: usize) -> Result<(), ()> {
Ok(self.0.push(Action::Insert { old, new, new_len }))
}
fn replace(
&mut self,
old: usize,
old_len: usize,
new: usize,
new_len: usize,
) -> Result<(), ()> {
Ok(self.0.push(Action::Replace {
old,
old_len,
new,
new_len,
}))
}
fn finish(&mut self) -> Result<(), ()> {
Ok(self.0.push(Action::Finish))
}
}
impl AccumDiff {
pub fn replay_rev<D: Diff>(&self, d: &mut D) -> Result<(), D::Error> {
for action in self.0.iter().rev() {
match *action {
Action::Equal { old, new, len } => d.equal(old, new, len)?,
Action::Delete { old, len, new } => d.delete(old, len, new)?,
Action::Insert { old, new, new_len } => d.insert(old, new, new_len)?,
Action::Replace {
old,
old_len,
new,
new_len,
} => d.replace(old, old_len, new, new_len)?,
Action::Finish => d.finish()?,
};
}
Ok(())
}
}
#[derive(Debug)]
struct ApplyDiff(Vec<u8>, Vec<u8>);
impl Diff for ApplyDiff {
type Error = ();
fn equal(&mut self, _old: usize, _new: usize, _len: usize) -> Result<(), ()> {
Ok(())
}
fn delete(&mut self, old: usize, len: usize, new: usize) -> Result<(), ()> {
self.replace(old, len, new, 0)
}
fn insert(&mut self, old: usize, new: usize, new_len: usize) -> Result<(), ()> {
self.replace(old, 0, new, new_len)
}
fn replace(
&mut self,
old: usize,
old_len: usize,
new: usize,
new_len: usize,
) -> Result<(), ()> {
dbg!(&self, old, old_len, new, new_len);
let mut v = self.0[..old].to_vec();
v.extend_from_slice(&self.1[new..new + new_len]);
v.extend_from_slice(&self.0[old + old_len..]);
self.0 = v;
Ok(())
}
fn finish(&mut self) -> Result<(), ()> {
Ok(())
}
}
prop_compose! {
fn vec_and_range()(vec in prop::collection::vec(1u8..10, 2..100))
(ix1 in 0..vec.len()-1, ix2 in 0..vec.len(), vec in Just(vec))
-> (Vec<u8>, usize, usize) {
(vec, ix1.min(ix2), ix1.max(ix2)+1)
}
}
proptest! {
#[test]
fn myers_proptest((v, i1, i2) in vec_and_range(), (w, j1, j2) in vec_and_range()) {
let mut accum = AccumDiff(Vec::new());
let mut apply = ApplyDiff(v.to_vec(), w.to_vec());
crate::myers::diff(&mut accum, &v, i1, i2, &w, j1, j2).unwrap();
accum.replay_rev(&mut apply).unwrap();
assert_eq!(apply.0[i1..i1+j2-j1], apply.1[j1..j2]);
}
#[test]
fn optimized_proptest((v, i1, i2) in vec_and_range(), (w, j1, j2) in vec_and_range()) {
let mut accum = AccumDiff(Vec::new());
let mut apply = ApplyDiff(v.to_vec(), w.to_vec());
crate::optimized::diff(&mut accum, &v, i1, i2, &w, j1, j2).unwrap();
accum.replay_rev(&mut apply).unwrap();
assert_eq!(apply.0[i1..i1+j2-j1], apply.1[j1..j2]);
}
#[test]
fn patience_proptest((v, i1, i2) in vec_and_range(), (w, j1, j2) in vec_and_range()) {
let mut accum = AccumDiff(Vec::new());
let mut apply = ApplyDiff(v.to_vec(), w.to_vec());
crate::patience::diff(&mut accum, &v, i1, i2, &w, j1, j2).unwrap();
accum.replay_rev(&mut apply).unwrap();
assert_eq!(apply.0[i1..i1+j2-j1], apply.1[j1..j2]);
}
#[test]
fn replace_proptest((v, i1, i2) in vec_and_range(), (w, j1, j2) in vec_and_range()) {
let accum = AccumDiff(Vec::new());
let mut apply = ApplyDiff(v.to_vec(), w.to_vec());
let mut replace = crate::replace::Replace::new(accum);
crate::myers::diff(&mut replace, &v, i1, i2, &w, j1, j2).unwrap();
replace.as_ref().replay_rev(&mut apply).unwrap();
assert_eq!(apply.0[i1..i1+j2-j1], apply.1[j1..j2]);
}
}