Make the change type be generic over any T rather &T (#31)
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.
This commit is contained in:
parent
0b8e237280
commit
a3e10af892
7 changed files with 75 additions and 37 deletions
|
|
@ -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.
|
||||
|
|
|
|||
13
examples/nonstring.rs
Normal file
13
examples/nonstring.rs
Normal file
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
src/iter.rs
37
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<usize>,
|
||||
|
|
@ -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();
|
||||
|
|
@ -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;
|
||||
|
|
@ -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<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>
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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<Change<'x, T::Output>>
|
||||
fn diff_lines<'x, T>(old: &'x T, new: &'x T) -> Vec<Change<&'x T::Output>>
|
||||
where
|
||||
T: DiffableStrRef + ?Sized,
|
||||
{
|
||||
|
|
|
|||
24
src/types.rs
24
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<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
|
||||
|
|
@ -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<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)
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue