diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c43d7a..a991f1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ All notable changes to similar are documented here. +## Unreleased + +* Change the `Change` type and associated methods to work on any `T: Clone` instead + of `&T`. This makes the `iter_changes` method also work on slices of integers + or other values. + ## 1.3.0 * Performance improvements for the LCS algorithm. diff --git a/examples/nonstring.rs b/examples/nonstring.rs new file mode 100644 index 0000000..ca102e2 --- /dev/null +++ b/examples/nonstring.rs @@ -0,0 +1,13 @@ +use similar::{capture_diff_slices, Algorithm}; + +fn main() { + let old = vec![1, 2, 3]; + let new = vec![1, 2, 4]; + let ops = capture_diff_slices(Algorithm::Myers, &old, &new); + + for op in ops { + for change in op.iter_changes(&old, &new) { + println!("{:?}", change); + } + } +} diff --git a/src/common.rs b/src/common.rs index 35e1a16..40d1ae7 100644 --- a/src/common.rs +++ b/src/common.rs @@ -159,3 +159,27 @@ pub fn group_diff_ops(mut ops: Vec, n: usize) -> Vec> { rv } + +#[test] +fn test_non_string_iter_change() { + use crate::ChangeTag; + + let old = vec![1, 2, 3]; + let new = vec![1, 2, 4]; + let ops = capture_diff_slices(Algorithm::Myers, &old, &new); + let changes: Vec<_> = ops + .iter() + .flat_map(|x| x.iter_changes(&old, &new)) + .map(|x| (x.tag(), x.value())) + .collect(); + + assert_eq!( + changes, + vec![ + (ChangeTag::Equal, 1), + (ChangeTag::Equal, 2), + (ChangeTag::Delete, 3), + (ChangeTag::Insert, 4), + ] + ); +} diff --git a/src/iter.rs b/src/iter.rs index 5ff460b..0a1ae53 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -11,7 +11,7 @@ use std::ops::{Index, Range}; use crate::{Change, ChangeTag, DiffOp, DiffTag}; /// Iterator for [`DiffOp::iter_changes`]. -pub struct ChangesIter<'lookup, 'data, Old: ?Sized, New: ?Sized, T: ?Sized> { +pub struct ChangesIter<'lookup, Old: ?Sized, New: ?Sized, T> { old: &'lookup Old, new: &'lookup New, old_range: Range, @@ -21,15 +21,13 @@ pub struct ChangesIter<'lookup, 'data, Old: ?Sized, New: ?Sized, T: ?Sized> { old_i: usize, new_i: usize, tag: DiffTag, - _marker: PhantomData<&'data T>, + _marker: PhantomData, } -impl<'lookup, 'data, Old, New, T> ChangesIter<'lookup, 'data, Old, New, T> +impl<'lookup, Old, New, T> ChangesIter<'lookup, Old, New, T> where - Old: Index + ?Sized, - New: Index + ?Sized, - T: 'data + ?Sized, - 'data: 'lookup, + Old: Index + ?Sized, + New: Index + ?Sized, { pub(crate) fn new(old: &'lookup Old, new: &'lookup New, op: DiffOp) -> Self { let (tag, old_range, new_range) = op.as_tag_tuple(); @@ -52,20 +50,19 @@ where } } -impl<'lookup, 'data, Old, New, T> Iterator for ChangesIter<'lookup, 'data, Old, New, T> +impl<'lookup, Old, New, T> Iterator for ChangesIter<'lookup, Old, New, T> where - Old: Index + ?Sized, - New: Index + ?Sized, - T: 'data + ?Sized, - 'data: 'lookup, + Old: Index + ?Sized, + New: Index + ?Sized, + T: Clone, { - type Item = Change<'data, T>; + type Item = Change; fn next(&mut self) -> Option { match self.tag { DiffTag::Equal => { if self.old_i < self.old_range.end { - let value = self.old[self.old_i]; + let value = self.old[self.old_i].clone(); self.old_i += 1; self.old_index += 1; self.new_index += 1; @@ -81,7 +78,7 @@ where } DiffTag::Delete => { if self.old_i < self.old_range.end { - let value = self.old[self.old_i]; + let value = self.old[self.old_i].clone(); self.old_i += 1; self.old_index += 1; Some(Change { @@ -96,7 +93,7 @@ where } DiffTag::Insert => { if self.new_i < self.new_range.end { - let value = self.new[self.new_i]; + let value = self.new[self.new_i].clone(); self.new_i += 1; self.new_index += 1; Some(Change { @@ -111,7 +108,7 @@ where } DiffTag::Replace => { if self.old_i < self.old_range.end { - let value = self.old[self.old_i]; + let value = self.old[self.old_i].clone(); self.old_i += 1; self.old_index += 1; Some(Change { @@ -121,7 +118,7 @@ where value, }) } else if self.new_i < self.new_range.end { - let value = self.new[self.new_i]; + let value = self.new[self.new_i].clone(); self.new_i += 1; self.new_index += 1; Some(Change { @@ -147,7 +144,7 @@ mod text { old: &'slf [&'data T], new: &'slf [&'data T], ops: &'slf [DiffOp], - current_iter: Option>, + current_iter: Option>, } impl<'slf, 'data, T> AllChangesIter<'slf, 'data, T> @@ -173,7 +170,7 @@ mod text { T: PartialEq + 'data + ?Sized, 'data: 'slf, { - type Item = Change<'data, T>; + type Item = Change<&'data T>; fn next(&mut self) -> Option { loop { diff --git a/src/text/inline.rs b/src/text/inline.rs index ecf09eb..5b221ba 100644 --- a/src/text/inline.rs +++ b/src/text/inline.rs @@ -159,8 +159,8 @@ impl<'s, T: DiffableStr + ?Sized> InlineChange<'s, T> { } } -impl<'s, T: DiffableStr + ?Sized> From> for InlineChange<'s, T> { - fn from(change: Change<'s, T>) -> InlineChange<'s, T> { +impl<'s, T: DiffableStr + ?Sized> From> for InlineChange<'s, T> { + fn from(change: Change<&'s T>) -> InlineChange<'s, T> { InlineChange { tag: change.tag(), old_index: change.old_index(), diff --git a/src/text/mod.rs b/src/text/mod.rs index 1d647fe..d9a11a2 100644 --- a/src/text/mod.rs +++ b/src/text/mod.rs @@ -495,7 +495,7 @@ impl<'old, 'new, 'bufs, T: DiffableStr + ?Sized + 'old + 'new> TextDiff<'old, 'n pub fn iter_changes<'x, 'slf>( &'slf self, op: &DiffOp, - ) -> ChangesIter<'slf, 'x, [&'x T], [&'x T], T> + ) -> ChangesIter<'slf, [&'x T], [&'x T], &'x T> where 'x: 'slf, 'old: 'x, @@ -728,7 +728,7 @@ fn test_get_close_matches() { fn test_lifetimes_on_iter() { use crate::Change; - fn diff_lines<'x, T>(old: &'x T, new: &'x T) -> Vec> + fn diff_lines<'x, T>(old: &'x T, new: &'x T) -> Vec> where T: DiffableStrRef + ?Sized, { diff --git a/src/types.rs b/src/types.rs index 4a1cead..c5ca854 100644 --- a/src/types.rs +++ b/src/types.rs @@ -59,15 +59,15 @@ impl fmt::Display for ChangeTag { /// This type has additional methods that are only available for types /// implementing [`DiffableStr`](crate::text::DiffableStr). #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Ord, PartialOrd)] -pub struct Change<'s, T: ?Sized> { +pub struct Change { pub(crate) tag: ChangeTag, pub(crate) old_index: Option, pub(crate) new_index: Option, - pub(crate) value: &'s T, + pub(crate) value: T, } /// These methods are available for all change types. -impl<'s, T: ?Sized> Change<'s, T> { +impl Change { /// Returns the change tag. pub fn tag(&self) -> ChangeTag { self.tag @@ -89,8 +89,8 @@ impl<'s, T: ?Sized> Change<'s, T> { /// this value is more or less useful. If you always want to have a utf-8 /// string it's best to use the [`Change::as_str`] and /// [`Change::to_string_lossy`] methods. - pub fn value(&self) -> &'s T { - self.value + pub fn value(&self) -> T { + self.value.clone() } } @@ -270,16 +270,14 @@ impl DiffOp { /// (ChangeTag::Insert, "blah"), /// ]); /// ``` - pub fn iter_changes<'x, 'lookup, Old, New, T>( + pub fn iter_changes<'lookup, Old, New, T>( &self, old: &'lookup Old, new: &'lookup New, - ) -> ChangesIter<'lookup, 'x, Old, New, T> + ) -> ChangesIter<'lookup, Old, New, T> where - Old: Index + ?Sized, - New: Index + ?Sized, - T: 'x + ?Sized, - 'x: 'lookup, + Old: Index + ?Sized, + New: Index + ?Sized, { ChangesIter::new(old, new, *self) } @@ -436,7 +434,7 @@ mod text_additions { /// The text interface can produce changes over [`DiffableStr`] implementing /// values. As those are generic interfaces for different types of strings /// utility methods to make working with standard rust strings more enjoyable. - impl<'s, T: DiffableStr + ?Sized> Change<'s, T> { + impl<'s, T: DiffableStr + ?Sized> Change<&'s T> { /// Returns the value as string if it is utf-8. pub fn as_str(&self) -> Option<&'s str> { T::as_str(self.value) @@ -457,7 +455,7 @@ mod text_additions { } } - impl<'s, T: DiffableStr + ?Sized> fmt::Display for Change<'s, T> { + impl<'s, T: DiffableStr + ?Sized> fmt::Display for Change<&'s T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f,