use similar::algorithms::{myers, DiffHook, Replace};
use std::hash::Hash;
use std::iter::FromIterator;
use std::ops::Range;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SeqDelta<T> {
pub splices: Vec<Splice<T>>,
}
impl<T> SeqDelta<T> {
pub fn is_empty(&self) -> bool {
self.splices.is_empty()
}
}
impl<T> Default for SeqDelta<T> {
fn default() -> Self {
SeqDelta {
splices: Vec::new(),
}
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Splice<T> {
pub at: usize,
pub remove: usize,
pub insert: Vec<T>,
}
#[derive(Default)]
struct RangeHook {
ops: Vec<(usize, usize, Range<usize>)>,
}
impl DiffHook for RangeHook {
type Error = std::convert::Infallible;
fn equal(
&mut self,
_old_index: usize,
_new_index: usize,
_len: usize,
) -> Result<(), Self::Error> {
Ok(())
}
fn delete(
&mut self,
old_index: usize,
old_len: usize,
new_index: usize,
) -> Result<(), Self::Error> {
self.ops.push((old_index, old_len, new_index..new_index));
Ok(())
}
fn insert(
&mut self,
old_index: usize,
new_index: usize,
new_len: usize,
) -> Result<(), Self::Error> {
self.ops
.push((old_index, 0, new_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.ops
.push((old_index, old_len, new_index..new_index + new_len));
Ok(())
}
}
pub fn diff<C, I>(old: C, new: C) -> SeqDelta<I>
where
C: IntoIterator<Item = I>,
I: Hash + Eq,
{
let old: Vec<I> = old.into_iter().collect();
let new: Vec<I> = new.into_iter().collect();
let mut hook = Replace::new(RangeHook::default());
match myers::diff(&mut hook, &old[..], 0..old.len(), &new[..], 0..new.len()) {
Ok(()) => {}
Err(never) => match never {},
}
let ops = hook.into_inner().ops;
let mut new = new.into_iter();
let mut cursor = 0;
let splices = ops
.into_iter()
.map(|(at, remove, range)| {
new.by_ref().take(range.start - cursor).for_each(drop);
let insert = new.by_ref().take(range.end - range.start).collect();
cursor = range.end;
Splice { at, remove, insert }
})
.collect();
SeqDelta { splices }
}
pub fn apply<C, I>(target: &mut C, delta: SeqDelta<I>)
where
C: IntoIterator<Item = I> + FromIterator<I>,
{
let old = std::mem::replace(target, std::iter::empty().collect());
let mut old = old.into_iter();
let mut out: Vec<I> = Vec::new();
let mut cursor = 0;
for Splice { at, remove, insert } in delta.splices {
out.extend(old.by_ref().take(at.saturating_sub(cursor)));
old.by_ref().take(remove).for_each(drop);
out.extend(insert);
cursor = at + remove;
}
out.extend(old);
*target = out.into_iter().collect();
}