Skip to content

Commit

Permalink
Make the change type be generic over any T rather &T (#31)
Browse files Browse the repository at this point in the history
This makes the interface of this crate more flexible as the utility
methods such as `iter_changes` now also work if a container does not
contain references.
  • Loading branch information
mitsuhiko committed Sep 11, 2021
1 parent 0b8e237 commit a3e10af
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 37 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
13 changes: 13 additions & 0 deletions examples/nonstring.rs
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
24 changes: 24 additions & 0 deletions src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,27 @@ pub fn group_diff_ops(mut ops: Vec<DiffOp>, n: usize) -> Vec<Vec<DiffOp>> {

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),
]
);
}
37 changes: 17 additions & 20 deletions src/iter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<usize>,
Expand All @@ -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<T>,
}

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<usize, Output = &'data T> + ?Sized,
New: Index<usize, Output = &'data T> + ?Sized,
T: 'data + ?Sized,
'data: 'lookup,
Old: Index<usize, Output = T> + ?Sized,
New: Index<usize, Output = T> + ?Sized,
{
pub(crate) fn new(old: &'lookup Old, new: &'lookup New, op: DiffOp) -> Self {
let (tag, old_range, new_range) = op.as_tag_tuple();
Expand All @@ -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<usize, Output = &'data T> + ?Sized,
New: Index<usize, Output = &'data T> + ?Sized,
T: 'data + ?Sized,
'data: 'lookup,
Old: Index<usize, Output = T> + ?Sized,
New: Index<usize, Output = T> + ?Sized,
T: Clone,
{
type Item = Change<'data, T>;
type Item = Change<T>;

fn next(&mut self) -> Option<Self::Item> {
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;
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -147,7 +144,7 @@ mod text {
old: &'slf [&'data T],
new: &'slf [&'data T],
ops: &'slf [DiffOp],
current_iter: Option<ChangesIter<'slf, 'data, [&'data T], [&'data T], T>>,
current_iter: Option<ChangesIter<'slf, [&'data T], [&'data T], &'data T>>,
}

impl<'slf, 'data, T> AllChangesIter<'slf, 'data, T>
Expand All @@ -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<Self::Item> {
loop {
Expand Down
4 changes: 2 additions & 2 deletions src/text/inline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,8 @@ impl<'s, T: DiffableStr + ?Sized> InlineChange<'s, T> {
}
}

impl<'s, T: DiffableStr + ?Sized> From<Change<'s, T>> for InlineChange<'s, T> {
fn from(change: Change<'s, T>) -> InlineChange<'s, T> {
impl<'s, T: DiffableStr + ?Sized> From<Change<&'s T>> for InlineChange<'s, T> {
fn from(change: Change<&'s T>) -> InlineChange<'s, T> {
InlineChange {
tag: change.tag(),
old_index: change.old_index(),
Expand Down
4 changes: 2 additions & 2 deletions src/text/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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<Change<'x, T::Output>>
fn diff_lines<'x, T>(old: &'x T, new: &'x T) -> Vec<Change<&'x T::Output>>
where
T: DiffableStrRef + ?Sized,
{
Expand Down
24 changes: 11 additions & 13 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> {
pub(crate) tag: ChangeTag,
pub(crate) old_index: Option<usize>,
pub(crate) new_index: Option<usize>,
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<T: Clone> Change<T> {
/// Returns the change tag.
pub fn tag(&self) -> ChangeTag {
self.tag
Expand All @@ -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()
}
}

Expand Down Expand Up @@ -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<usize, Output = &'x T> + ?Sized,
New: Index<usize, Output = &'x T> + ?Sized,
T: 'x + ?Sized,
'x: 'lookup,
Old: Index<usize, Output = T> + ?Sized,
New: Index<usize, Output = T> + ?Sized,
{
ChangesIter::new(old, new, *self)
}
Expand Down Expand Up @@ -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)
Expand All @@ -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,
Expand Down

0 comments on commit a3e10af

Please sign in to comment.