my fork of dmp
A compatibility mode and efficiency mode
Anubhab Bandyopadhyay 2024-08-26
parent e46de78 · commit 6267eb5
-rw-r--r--benches/diff.rs4
-rw-r--r--src/dmp.rs180
-rw-r--r--src/lib.rs3
-rw-r--r--src/traits.rs49
-rw-r--r--tests/test.rs403
5 files changed, 438 insertions, 201 deletions
diff --git a/benches/diff.rs b/benches/diff.rs
index 4c58d5a..f426c3c 100644
--- a/benches/diff.rs
+++ b/benches/diff.rs
@@ -1,7 +1,7 @@
use std::path::Path;
use criterion::{criterion_group, criterion_main, Criterion};
-use diff_match_patch_rs::dmp::DiffMatchPatch;
+use diff_match_patch_rs::{dmp::DiffMatchPatch, Efficient};
fn diff_main(c: &mut Criterion) {
let basedir = Path::new("testdata");
@@ -11,7 +11,7 @@ fn diff_main(c: &mut Criterion) {
let dmp = DiffMatchPatch::default();
c.bench_function("diff-match-patch", |bencher| {
- bencher.iter(|| dmp.diff_main_compat(&old, &new).unwrap());
+ bencher.iter(|| dmp.diff_main::<Efficient>(&old, &new).unwrap());
});
}
diff --git a/src/dmp.rs b/src/dmp.rs
index 256c19f..959f1e6 100644
--- a/src/dmp.rs
+++ b/src/dmp.rs
@@ -2,7 +2,6 @@ use core::str;
use std::{char, collections::HashMap, fmt::Display};
use chrono::{NaiveTime, TimeDelta, Utc};
-use percent_encoding::{percent_decode, percent_encode};
use crate::{errors::Error, DType};
@@ -85,11 +84,6 @@ pub struct DiffMatchPatch {
checklines: bool,
/// A default timeout in num milliseconda, defaults to 1000 (1 second)
timeout: Option<u32>,
- /// enable/ disable `compatibility mode`
- /// If you are preparing `patches` that need to be compatible across other `diff-match-patch` libraries enable `compatibility` mode
- /// Compatibility mode adds some extra overhead of preparing diffs cohereant with `char` representation instead of bytes
- /// defaults to `false`
- compat_mode: bool,
/// At what point is no match declared (0.0 = perfection, 1.0 = very loose).
match_threshold: f32,
/// How far to search for a match (0 = exact location, 1000+ = broad match).
@@ -105,7 +99,7 @@ pub struct DiffMatchPatch {
/// end points of a delete need to match.
delete_threshold: f32,
/// Chunk size for context length.
- patch_margin: u16,
+ patch_margin: u8,
}
impl Default for DiffMatchPatch {
@@ -113,7 +107,6 @@ impl Default for DiffMatchPatch {
Self {
checklines: true,
timeout: Some(1000),
- compat_mode: false,
match_threshold: 0.5,
match_distance: 1000,
match_max_bits: 32,
@@ -182,7 +175,7 @@ impl DiffMatchPatch {
}
// returns the current patch margin
- fn patch_margin(&self) -> u16 {
+ fn patch_margin(&self) -> u8 {
self.patch_margin
}
@@ -357,11 +350,7 @@ impl DiffMatchPatch {
}
}
- fn half_match<'a, T: DType>(
- &self,
- old: &'a [T],
- new: &'a [T],
- ) -> Option<HalfMatch<'a, T>> {
+ fn half_match<'a, T: DType>(&self, old: &'a [T], new: &'a [T]) -> Option<HalfMatch<'a, T>> {
// Don't risk returning a suboptimal diff when we have unlimited time
self.timeout()?;
@@ -1185,7 +1174,9 @@ impl DiffMatchPatch {
// Scores range from 6 (best) to 0 (worst)
#[inline]
fn cleanup_semantic_score<T: DType>(one: &[T], two: &[T]) -> u8 {
- let (char1, char2) = if let (Some(&char1), Some(&char2)) = (one.last(), two.first()) && let (Some(c1), Some(c2)) = (char1.as_char(), char2.as_char()) {
+ let (char1, char2) = if let (Some(&char1), Some(&char2)) = (one.last(), two.first())
+ && let (Some(c1), Some(c2)) = (char1.as_char(), char2.as_char())
+ {
(c1, c2)
} else {
return 6;
@@ -1415,12 +1406,18 @@ impl DiffMatchPatch {
// format!("+{encoded}")
[&[T::from_char('+')], &encoded[..], &[T::from_char('\t')]].concat()
}
- Ops::Delete => {
- [&[T::from_char('-')], &T::from_str(diff.size().to_string().as_str())[..], &[T::from_char('\t')]].concat()
- }
- Ops::Equal => {
- [&[T::from_char('=')], &T::from_str(diff.size().to_string().as_str())[..], &[T::from_char('\t')]].concat()
- }
+ Ops::Delete => [
+ &[T::from_char('-')],
+ &T::from_str(diff.size().to_string().as_str())[..],
+ &[T::from_char('\t')],
+ ]
+ .concat(),
+ Ops::Equal => [
+ &[T::from_char('=')],
+ &T::from_str(diff.size().to_string().as_str())[..],
+ &[T::from_char('\t')],
+ ]
+ .concat(),
}
})
.collect::<Vec<_>>()
@@ -1431,11 +1428,14 @@ impl DiffMatchPatch {
data
}
- pub fn from_delta(old: &[u8], delta: &[u8]) -> Result<Vec<Diff<u8>>, crate::errors::Error> {
+ pub fn from_delta<T: DType>(
+ old: &[T],
+ delta: &[T],
+ ) -> Result<Vec<Diff<T>>, crate::errors::Error> {
let mut pointer = 0; // cursor to text
let mut diffs = vec![];
- for token in delta.split(|&k| k == b'\t') {
+ for token in delta.split(|&k| k == T::from_char('\t')) {
if token.is_empty() {
continue;
}
@@ -1445,22 +1445,13 @@ impl DiffMatchPatch {
let opcode = token.first();
let param = &token[1..];
- if opcode == Some(&b'+') {
- let param = percent_decode(param).collect::<Vec<_>>();
+ if opcode == Some(&T::from_char('+')) {
+ let param = T::percent_decode(param);
diffs.push(Diff::insert(&param));
- } else if opcode == Some(&b'-') || opcode == Some(&b'=') {
- let n = match std::str::from_utf8(param)
- .map_err(|_| crate::errors::Error::Utf8Error)
- .and_then(|t| {
- t.parse::<isize>()
- .map_err(|_| crate::errors::Error::InvalidInput)
- }) {
- Ok(n) => n,
- Err(_) => {
- return Err(crate::errors::Error::InvalidInput);
- }
- };
-
+ } else if opcode == Some(&T::from_char('-')) || opcode == Some(&T::from_char('=')) {
+ let n = T::to_string(param)?
+ .parse::<isize>()
+ .map_err(|_| Error::Utf8Error)?;
if n < 0 {
return Err(crate::errors::Error::InvalidInput);
}
@@ -1474,7 +1465,7 @@ impl DiffMatchPatch {
let txt = &old[pointer..new_pointer];
pointer = new_pointer;
- if opcode == Some(&b'=') {
+ if opcode == Some(&T::from_char('=')) {
diffs.push(Diff::equal(txt))
} else {
diffs.push(Diff::delete(txt))
@@ -1674,7 +1665,6 @@ impl DiffMatchPatch {
.concat()
}
-
pub fn diff_text_new<T: DType>(diffs: &[Diff<T>]) -> Vec<T> {
diffs
.iter()
@@ -2118,13 +2108,19 @@ pub struct Patch<T: DType> {
length2: usize,
}
-impl <T: DType>Default for Patch<T> {
+impl<T: DType> Default for Patch<T> {
fn default() -> Self {
- Self { diffs: Vec::new(), start1: 0, start2: 0, length1: 0, length2: 0 }
+ Self {
+ diffs: Vec::new(),
+ start1: 0,
+ start2: 0,
+ length1: 0,
+ length2: 0,
+ }
}
}
-impl <T: DType>Display for Patch<T> {
+impl<T: DType> Display for Patch<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let coord1 = if self.length1 == 0 {
format!("{},0", self.start1)
@@ -2169,16 +2165,19 @@ impl <T: DType>Display for Patch<T> {
};
let enc = T::percent_encode(diff.data());
- let segment = format!("{sign}{}\n", T::to_string(&enc).map_err(|_| std::fmt::Error)?);
+ let segment = format!(
+ "{sign}{}\n",
+ T::to_string(&enc).map_err(|_| std::fmt::Error)?
+ );
segments.push(segment)
- };
+ }
write!(f, "{}", segments.join(""))
}
}
-impl <T: DType>Patch<T> {
+impl<T: DType> Patch<T> {
pub fn diffs(&self) -> &[Diff<T>] {
&self.diffs[..]
}
@@ -2192,7 +2191,9 @@ pub enum PatchInput<'a, T: DType> {
pub type Patches<T> = Vec<Patch<T>>;
impl DiffMatchPatch {
- fn parse_patch_header<T: DType>(s: &[T]) -> Option<(usize, Option<usize>, usize, Option<usize>)> {
+ fn parse_patch_header<T: DType>(
+ s: &[T],
+ ) -> Option<(usize, Option<usize>, usize, Option<usize>)> {
let mut section = Vec::with_capacity(64);
let mut current_sect = 0;
@@ -2214,7 +2215,9 @@ impl DiffMatchPatch {
return None;
}
- let splits = section[1..].split(|&p| p == T::from_char(',')).collect::<Vec<_>>();
+ let splits = section[1..]
+ .split(|&p| p == T::from_char(','))
+ .collect::<Vec<_>>();
let ol = splits.first()?;
old_line = T::to_string(ol).ok()?.parse::<usize>().ok()?;
@@ -2223,7 +2226,11 @@ impl DiffMatchPatch {
}
}
2 => {
- let splits = section[if *section.first()? == T::from_char('+') { 1 } else { 0 }..]
+ let splits = section[if *section.first()? == T::from_char('+') {
+ 1
+ } else {
+ 0
+ }..]
.split(|&p| p == T::from_char(','))
.collect::<Vec<_>>();
@@ -2535,12 +2542,12 @@ impl DiffMatchPatch {
}
fn patch_add_padding<T: DType>(&self, patches: &mut Patches<T>) -> Vec<T> {
- let pad_len = self.patch_margin() as usize;
-
- let null_pad = (1..pad_len + 1)
+ let null_pad = (1..self.patch_margin() + 1)
.filter_map(|c| c.as_char().map(|c| T::from_char(c)))
.collect::<Vec<_>>();
+ let pad_len = self.patch_margin() as usize;
+
// Bump all the patches forward.
patches.iter_mut().for_each(|p| {
p.start1 += pad_len;
@@ -2608,14 +2615,14 @@ impl DiffMatchPatch {
/// Create a new instance of the struct with default settings
/// # Example
/// ```
- /// use diff_match_patch_rs::{DiffMatchPatch, Error};
+ /// use diff_match_patch_rs::{DiffMatchPatch, Error, Efficient};
///
/// # fn main() -> Result<(), Error> {
/// let mut dmp = DiffMatchPatch::new();
/// // change some settings, e.g. set `line mode` optimization to `false` because you know you have a small text and not many lines
/// dmp.set_checklines(false);
/// // do the diffing
- /// let diffs = dmp.diff_main("Fast enough", "Blazing fast")?;
+ /// let diffs = dmp.diff_main::<Efficient>("Fast enough", "Blazing fast")?;
/// # Ok(())
/// # }
/// ```
@@ -2628,14 +2635,14 @@ impl DiffMatchPatch {
/// Vec of changes (Diff).
/// # Example
/// ```
- /// use diff_match_patch_rs::{DiffMatchPatch, Error};
+ /// use diff_match_patch_rs::{DiffMatchPatch, Error, Efficient};
///
/// # fn main() -> Result<(), Error> {
/// let mut dmp = DiffMatchPatch::new();
/// // change some settings, e.g. set `line mode` optimization to `false` because you know you have a small text and not many lines
/// dmp.set_checklines(false);
/// // do the diffing
- /// let diffs = dmp.diff_main("Fast enough", "Blazing fast")?;
+ /// let diffs = dmp.diff_main::<Efficient>("Fast enough", "Blazing fast")?;
/// println!("{}", diffs.iter().map(|d| format!("d")).collect::<Vec<_>>().join("\n"));
/// // You should see the following output
/// // (Delete, F)
@@ -2645,31 +2652,17 @@ impl DiffMatchPatch {
/// # Ok(())
/// # }
/// ```
- pub fn diff_main(&self, old: &str, new: &str) -> Result<Vec<Diff<u8>>, crate::errors::Error> {
- self.diff_internal(
- old.as_bytes(),
- new.as_bytes(),
- self.checklines(),
- self.deadline(),
- )
- }
-
- pub fn diff_main_compat(&self, old: &str, new: &str) -> Result<Vec<Diff<char>>, crate::errors::Error> {
- let (old, new) = (
- char::from_str(old),
- char::from_str(new)
- );
+ pub fn diff_main<T: DType>(
+ &self,
+ old: &str,
+ new: &str,
+ ) -> Result<Vec<Diff<T>>, crate::errors::Error> {
+ let old = T::from_str(old);
+ let new = T::from_str(new);
- self.diff_internal(
- &old[..],
- &new[..],
- self.checklines(),
- self.deadline(),
- )
+ self.diff_internal(&old, &new, self.checklines(), self.deadline())
}
-
-
/// A diff of two unrelated texts can be filled with coincidental matches.
/// For example, the diff of "mouse" and "sofas" is [(-1, "m"), (1, "s"), (0, "o"), (-1, "u"), (1, "fa"), (0, "s"), (-1, "e")].
/// While this is the optimum diff, it is difficult for humans to understand. Semantic cleanup rewrites the diff, expanding it into a more intelligible format.
@@ -2870,13 +2863,16 @@ impl DiffMatchPatch {
/// Given two texts, or an already computed list of differences, return an array of patch objects.
/// The third form PatchInput::TextDiffs(...) is preferred, use it if you happen to have that data available, otherwise this function will compute the missing pieces.
/// TODO: add example
- pub fn patch_make<T: DType>(&self, input: PatchInput<T>) -> Result<Patches<T>, crate::errors::Error> {
+ pub fn patch_make<T: DType>(
+ &self,
+ input: PatchInput<T>,
+ ) -> Result<Patches<T>, crate::errors::Error> {
let mut diff_input;
let txt_old;
let (txt, diffs) = match input {
// No diffs provided, lets make our own
PatchInput::Texts(txt1, txt2) => {
- diff_input = T::differ(self, txt1, txt2)?;
+ diff_input = self.diff_main(txt1, txt2)?;
if diff_input.len() > 2 {
Self::cleanup_semantic(&mut diff_input);
}
@@ -2891,7 +2887,7 @@ impl DiffMatchPatch {
PatchInput::TextDiffs(txt, diffs) => {
txt_old = T::from_str(txt);
(txt_old, diffs)
- },
+ }
};
self.patch_make_internal(&txt, diffs)
@@ -2911,7 +2907,9 @@ impl DiffMatchPatch {
}
let txt_t = T::from_str(text);
- let mut text = txt_t.split(|&p| p == T::from_char('\n')).collect::<Vec<_>>();
+ let mut text = txt_t
+ .split(|&p| p == T::from_char('\n'))
+ .collect::<Vec<_>>();
let mut patches = vec![];
@@ -3022,7 +3020,7 @@ mod tests {
use crate::{
dmp::{Diff, HalfMatch, LineToChars},
- DiffMatchPatch, Error, Patch, PatchInput,
+ DiffMatchPatch, Efficient, Error, Patch, PatchInput,
};
#[test]
@@ -3770,8 +3768,9 @@ mod tests {
fn test_patch_add_padding() -> Result<(), Error> {
let dmp = DiffMatchPatch::default();
// Both edges full.
- let mut patches = dmp.patch_make(PatchInput::Texts("", "test"))?;
+ let mut patches = dmp.patch_make(PatchInput::Texts::<Efficient>("", "test"))?;
assert_eq!("@@ -0,0 +1,4 @@\n+test\n", dmp.patch_to_text(&patches));
+
dmp.patch_add_padding(&mut patches);
assert_eq!(
"@@ -1,8 +1,12 @@\n %01%02%03%04\n+test\n %01%02%03%04\n",
@@ -3779,7 +3778,7 @@ mod tests {
);
// Both edges partial.
- let mut patches = dmp.patch_make(PatchInput::Texts("XY", "XtestY"))?;
+ let mut patches = dmp.patch_make(PatchInput::Texts::<Efficient>("XY", "XtestY"))?;
assert_eq!(
"@@ -1,2 +1,6 @@\n X\n+test\n Y\n",
dmp.patch_to_text(&patches)
@@ -3791,7 +3790,8 @@ mod tests {
);
// Both edges none.
- let mut patches = dmp.patch_make(PatchInput::Texts("XXXXYYYY", "XXXXtestYYYY"))?;
+ let mut patches =
+ dmp.patch_make(PatchInput::Texts::<Efficient>("XXXXYYYY", "XXXXtestYYYY"))?;
assert_eq!(
"@@ -1,8 +1,12 @@\n XXXX\n+test\n YYYY\n",
dmp.patch_to_text(&patches)
@@ -3812,7 +3812,7 @@ mod tests {
let dmp = DiffMatchPatch::default();
// Assumes that dmp.Match_MaxBits is 32.
- let mut patches = dmp.patch_make(PatchInput::Texts(
+ let mut patches = dmp.patch_make(PatchInput::Texts::<char>(
"abcdefghijklmnopqrstuvwxyz01234567890",
"XabXcdXefXghXijXklXmnXopXqrXstXuvXwxXyzX01X23X45X67X89X0",
))?;
@@ -3822,7 +3822,7 @@ mod tests {
dmp.patch_to_text(&patches)
);
- let mut patches = dmp.patch_make(PatchInput::Texts(
+ let mut patches = dmp.patch_make(PatchInput::Texts::<char>(
"abcdef1234567890123456789012345678901234567890123456789012345678901234567890uvwxyz",
"abcdefuvwxyz",
))?;
@@ -3830,7 +3830,7 @@ mod tests {
dmp.split_max(&mut patches);
assert_eq!(p2t, dmp.patch_to_text(&patches));
- let mut patches = dmp.patch_make(PatchInput::Texts(
+ let mut patches = dmp.patch_make(PatchInput::Texts::<u8>(
"1234567890123456789012345678901234567890123456789012345678901234567890",
"abc",
))?;
@@ -3840,7 +3840,7 @@ mod tests {
dmp.patch_to_text(&patches)
);
- let mut patches = dmp.patch_make(PatchInput::Texts(
+ let mut patches = dmp.patch_make(PatchInput::Texts::<char>(
"abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1 abcdefghij , h : 0 , t : 1",
"abcdefghij , h : 1 , t : 1 abcdefghij , h : 1 , t : 1 abcdefghij , h : 0 , t : 1",
))?;
diff --git a/src/lib.rs b/src/lib.rs
index 1b0decc..b8394c9 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -4,6 +4,7 @@ pub mod dmp;
pub mod errors;
pub mod traits;
-pub(crate) use traits::DType;
pub use dmp::{DiffMatchPatch, Ops, Patch, PatchInput, Patches};
pub use errors::Error;
+pub(crate) use traits::DType;
+pub use traits::{Compat, Efficient};
diff --git a/src/traits.rs b/src/traits.rs
index 73635f8..3818837 100644
--- a/src/traits.rs
+++ b/src/traits.rs
@@ -1,10 +1,13 @@
use std::hash::Hash;
use chrono::NaiveTime;
-use percent_encoding::{AsciiSet, CONTROLS};
+use percent_encoding::{percent_decode, AsciiSet, CONTROLS};
use crate::dmp::{Diff, DiffMatchPatch};
+pub type Efficient = u8;
+pub type Compat = char;
+
// Appending controls to ensure exact same encoding as cpp variant
const ENCODE_SET: &AsciiSet = &CONTROLS
.add(b'"')
@@ -20,9 +23,8 @@ const ENCODE_SET: &AsciiSet = &CONTROLS
.add(b'^')
.add(b'|');
-
pub trait DType: Copy + Ord + Eq + Hash {
- fn differ(dmp: &DiffMatchPatch, txt_old: &str, txt_new: &str) -> Result<Vec<Diff<Self>>, crate::errors::Error>;
+ // fn differ(dmp: &DiffMatchPatch, txt_old: &str, txt_new: &str) -> Result<Vec<Diff<Self>>, crate::errors::Error>;
fn bisect_split(
dmp: &DiffMatchPatch,
old: &[Self],
@@ -45,10 +47,6 @@ pub trait DType: Copy + Ord + Eq + Hash {
}
impl DType for u8 {
- fn differ(dmp: &DiffMatchPatch, txt_old: &str, txt_new: &str) -> Result<Vec<Diff<Self>>, crate::errors::Error> {
- dmp.diff_main(txt_old, txt_new)
- }
-
fn bisect_split(
dmp: &DiffMatchPatch,
old: &[u8],
@@ -84,7 +82,9 @@ impl DType for u8 {
#[inline]
fn to_string(data: &[Self]) -> Result<String, crate::Error> {
- std::str::from_utf8(data).map_err(|_| crate::Error::Utf8Error).map(|s| s.to_string())
+ std::str::from_utf8(data)
+ .map_err(|_| crate::Error::Utf8Error)
+ .map(|s| s.to_string())
}
#[inline]
@@ -102,21 +102,19 @@ impl DType for u8 {
#[inline]
fn percent_encode(input: &[Self]) -> Vec<Self> {
- percent_encoding::percent_encode(input, ENCODE_SET).collect::<String>().as_bytes().to_vec()
+ percent_encoding::percent_encode(input, ENCODE_SET)
+ .collect::<String>()
+ .as_bytes()
+ .to_vec()
}
#[inline]
fn percent_decode(input: &[Self]) -> Vec<Self> {
- // percent_encoding::percent_encode(input, ENCODE_SET).collect::<String>().as_bytes().to_vec()
- todo!()
+ percent_decode(input).collect()
}
}
impl DType for char {
- fn differ(dmp: &DiffMatchPatch, txt_old: &str, txt_new: &str) -> Result<Vec<Diff<Self>>, crate::errors::Error> {
- dmp.diff_main_compat(txt_old, txt_new)
- }
-
fn bisect_split(
dmp: &DiffMatchPatch,
old: &[char],
@@ -146,7 +144,7 @@ impl DType for char {
Some(*self)
}
- fn from_str(str: &str) -> Vec<Self> {
+ fn from_str(str: &str) -> Vec<Self> {
str.chars().collect::<Vec<_>>()
}
@@ -175,12 +173,12 @@ impl DType for char {
.map(|c| {
let mut b = vec![0; c.len_utf8()];
c.encode_utf8(&mut b);
-
+
b
- }).collect::<Vec<_>>()
+ })
+ .collect::<Vec<_>>()
.concat();
-
let encoded = percent_encoding::percent_encode(&d[..], ENCODE_SET).collect::<String>();
Self::from_str(&encoded)
@@ -188,15 +186,16 @@ impl DType for char {
#[inline]
fn percent_decode(input: &[Self]) -> Vec<Self> {
- todo!()
+ let ip = input.iter().collect::<String>();
+ percent_decode(ip.as_bytes())
+ .decode_utf8()
+ .unwrap()
+ .chars()
+ .collect()
}
}
impl DType for usize {
- fn differ(_: &DiffMatchPatch, _: &str, _: &str) -> Result<Vec<Diff<Self>>, crate::errors::Error> {
- unimplemented!()
- }
-
fn bisect_split(
dmp: &DiffMatchPatch,
old: &[usize],
@@ -252,4 +251,4 @@ impl DType for usize {
fn percent_decode(_: &[Self]) -> Vec<Self> {
unimplemented!()
}
-} \ No newline at end of file
+}
diff --git a/tests/test.rs b/tests/test.rs
index f32ff24..bc8262b 100644
--- a/tests/test.rs
+++ b/tests/test.rs
@@ -4,7 +4,7 @@ use chrono::Utc;
use diff_match_patch_rs::dmp::Diff;
-use diff_match_patch_rs::{DiffMatchPatch, Error, Ops, PatchInput};
+use diff_match_patch_rs::{Compat, DiffMatchPatch, Efficient, Error, Ops, PatchInput};
// const tests = [
// 'testDiffIsDestructurable',
@@ -85,7 +85,7 @@ fn test_diff_pretty_html() -> Result<(), Error> {
// Which means the the diff should an equality block of 3 bytes folloed by insert and delete
let old = "πŸ€ͺ"; // [240, 159, 164, 170]
let new = "πŸ€”"; // [240, 159, 164, 148]
- let diffs = dmp.diff_main(old, new)?;
+ let diffs = dmp.diff_main::<Efficient>(old, new)?;
assert_eq!(
"<span></span><del style=\"background:#ffe6e6;\">πŸ€ͺ</del><ins style=\"background:#e6ffe6;\">πŸ€”</ins>",
dmp.diff_pretty_html(&diffs)?
@@ -94,7 +94,7 @@ fn test_diff_pretty_html() -> Result<(), Error> {
// Now Case 1. but with some text before and after
let old = "I'm puzzledπŸ€ͺ or am I?";
let new = "I'm puzzledπŸ€” or thinking I guess!";
- let diffs = dmp.diff_main(old, new)?;
+ let diffs = dmp.diff_main::<Efficient>(old, new)?;
assert_eq!(
"<span>I'm puzzled</span><del style=\"background:#ffe6e6;\">πŸ€ͺ</del><ins style=\"background:#e6ffe6;\">πŸ€”</ins><span> or </span><del style=\"background:#ffe6e6;\">am I?</del><ins style=\"background:#e6ffe6;\">thinking I guess!</ins>",
dmp.diff_pretty_html(&diffs)?
@@ -103,7 +103,7 @@ fn test_diff_pretty_html() -> Result<(), Error> {
// Case 2. Emoticons with the third position different
let old = "🍊"; // [240, 159, 141, 138]
let new = "🌊"; // [240, 159, 140, 138]
- let diffs = dmp.diff_main(old, new)?;
+ let diffs = dmp.diff_main::<Efficient>(old, new)?;
assert_eq!(
"<span></span><del style=\"background:#ffe6e6;\">🍊</del><ins style=\"background:#e6ffe6;\">🌊</ins>",
dmp.diff_pretty_html(&diffs)?
@@ -112,7 +112,7 @@ fn test_diff_pretty_html() -> Result<(), Error> {
// Now Case 2. but with some text, lets complicate this
let old = "🍊, aah orange is the new black!"; // [240, 159, 141, 138]
let new = "Aah orange!🌊is the new 🌊"; // [240, 159, 140, 138]
- let diffs = dmp.diff_main(old, new)?;
+ let diffs = dmp.diff_main::<Efficient>(old, new)?;
assert_eq!(
"<del style=\"background:#ffe6e6;\">🍊, a</del><ins style=\"background:#e6ffe6;\">A</ins><span>ah orange</span><del style=\"background:#ffe6e6;\"> </del><ins style=\"background:#e6ffe6;\">!🌊</ins><span>is the new </span><del style=\"background:#ffe6e6;\">black!</del><ins style=\"background:#e6ffe6;\">🌊</ins>",
dmp.diff_pretty_html(&diffs)?
@@ -121,7 +121,7 @@ fn test_diff_pretty_html() -> Result<(), Error> {
// Case 3. with second and third different, but lets complicate this with an equality
let old = "𠌊"; // [240, 160, 140, 138]
let new = "π– Š"; // [240, 150, 160, 138]
- let diffs = dmp.diff_main(old, new)?;
+ let diffs = dmp.diff_main::<Efficient>(old, new)?;
assert_eq!(
"<span></span><ins style=\"background:#e6ffe6;\">π– Š</ins><del style=\"background:#ffe6e6;\">𠌊</del>",
dmp.diff_pretty_html(&diffs)?
@@ -130,7 +130,7 @@ fn test_diff_pretty_html() -> Result<(), Error> {
// Case 3. but let there be a swap
let old = "πž „"; // [240, 158, 160, 132]
let new = std::str::from_utf8(&[240, 160, 158, 132]).unwrap(); // basically an undefined element `π ž„`. Should still work
- let diffs = dmp.diff_main(old, new)?;
+ let diffs = dmp.diff_main::<Efficient>(old, new)?;
assert_eq!(
"<span></span><del style=\"background:#ffe6e6;\">πž „</del><ins style=\"background:#e6ffe6;\">π ž„</ins>",
dmp.diff_pretty_html(&diffs)?
@@ -139,7 +139,7 @@ fn test_diff_pretty_html() -> Result<(), Error> {
// Case 4. swap at the last 2 positions
let old = "🍌"; // [240, 159, 141, 140] -- FINALLY A BANANA
let new = "🌍"; // [240, 159, 140, 141] -- interesting revelation - last 2 bytes swapped and 🍌 becomes 🌍. Guess the world is going `Bananas!!`
- let diffs = dmp.diff_main(old, new)?;
+ let diffs = dmp.diff_main::<Efficient>(old, new)?;
assert_eq!(
"<span></span><del style=\"background:#ffe6e6;\">🍌</del><ins style=\"background:#e6ffe6;\">🌍</ins>",
dmp.diff_pretty_html(&diffs)?
@@ -148,7 +148,7 @@ fn test_diff_pretty_html() -> Result<(), Error> {
// Let's do this with a slightly longish string
let old = "Now, let's explore some emotional extremes 🌊.\nWe've got your ecstatic face 🀩, your devastated face 😭, and your utterly confused face 🀯. But that's not all! πŸ€” We've also got some subtle emotions like 😐, πŸ™ƒ, and πŸ‘€.";
let new = "Let's start with some basics 😊.\nWe've got your standard smiley face πŸ™‚, your sad face ☹️, and your angry face 😠. But wait, there's more! 🀩 We've also got some more complex emotions like 😍, 🀀, and πŸš€. And let's not forget about the classics: πŸ˜‰, πŸ‘, and πŸ‘.";
- let diffs = dmp.diff_main(old, new)?;
+ let diffs = dmp.diff_main::<Efficient>(old, new)?;
assert_eq!(
"<del style=\"background:#ffe6e6;\">Now, let's explore some emotional extreme</del><ins style=\"background:#e6ffe6;\">Let's start with some basic</ins><span>s </span><del style=\"background:#ffe6e6;\">🌊</del><ins style=\"background:#e6ffe6;\">😊</ins><span>.&para;<br>We've got your </span><del style=\"background:#ffe6e6;\">ec</del><span>sta</span><del style=\"background:#ffe6e6;\">tic</del><ins style=\"background:#e6ffe6;\">ndard smiley</ins><span> face </span><del style=\"background:#ffe6e6;\">🀩</del><ins style=\"background:#e6ffe6;\">πŸ™‚</ins><span>, your </span><del style=\"background:#ffe6e6;\">devastate</del><ins style=\"background:#e6ffe6;\">sa</ins><span>d face </span><del style=\"background:#ffe6e6;\">😭</del><ins style=\"background:#e6ffe6;\">☹️</ins><span>, and your </span><del style=\"background:#ffe6e6;\">utterly confused</del><ins style=\"background:#e6ffe6;\">angry</ins><span> face </span><del style=\"background:#ffe6e6;\">🀯</del><ins style=\"background:#e6ffe6;\">😠</ins><span>. But </span><del style=\"background:#ffe6e6;\">that's not all</del><ins style=\"background:#e6ffe6;\">wait, there's more</ins><span>! </span><del style=\"background:#ffe6e6;\">πŸ€”</del><ins style=\"background:#e6ffe6;\">🀩</ins><span> We've also got some </span><del style=\"background:#ffe6e6;\">subt</del><ins style=\"background:#e6ffe6;\">more comp</ins><span>le</span><ins style=\"background:#e6ffe6;\">x</ins><span> emotions like </span><del style=\"background:#ffe6e6;\">😐</del><ins style=\"background:#e6ffe6;\">😍, 🀀, and πŸš€. And let's not forget about the classics: πŸ˜‰</ins><span>, </span><del style=\"background:#ffe6e6;\">πŸ™ƒ</del><ins style=\"background:#e6ffe6;\">πŸ‘</ins><span>, and </span><del style=\"background:#ffe6e6;\">πŸ‘€</del><ins style=\"background:#e6ffe6;\">πŸ‘</ins><span>.</span>",
@@ -164,21 +164,24 @@ fn test_diff_main() -> Result<(), Error> {
// Perform a trivial diff.
// Null case.
- assert!(dmp.diff_main("", "")?.is_empty());
+ assert!(dmp.diff_main::<Efficient>("", "")?.is_empty());
// Equality
- assert_eq!(vec![Diff::equal(b"abc")], dmp.diff_main("abc", "abc")?);
+ assert_eq!(
+ vec![Diff::equal(b"abc")],
+ dmp.diff_main::<Efficient>("abc", "abc")?
+ );
// Simple insert
assert_eq!(
vec![Diff::equal(b"ab"), Diff::insert(b"123"), Diff::equal(b"c")],
- dmp.diff_main("abc", "ab123c")?
+ dmp.diff_main::<Efficient>("abc", "ab123c")?
);
// Simple delete
assert_eq!(
vec![Diff::equal(b"a"), Diff::delete(b"123"), Diff::equal(b"bc")],
- dmp.diff_main("a123bc", "abc")?
+ dmp.diff_main::<Efficient>("a123bc", "abc")?
);
// Two insertions
@@ -190,7 +193,7 @@ fn test_diff_main() -> Result<(), Error> {
Diff::insert(b"456"),
Diff::equal(b"c"),
],
- dmp.diff_main("abc", "a123b456c")?
+ dmp.diff_main::<Efficient>("abc", "a123b456c")?
);
// Two deletions.
@@ -202,7 +205,7 @@ fn test_diff_main() -> Result<(), Error> {
Diff::delete(b"456"),
Diff::equal(b"c"),
],
- dmp.diff_main("a123b456c", "abc")?
+ dmp.diff_main::<Efficient>("a123b456c", "abc")?
);
// Perform a real diff.
@@ -211,7 +214,7 @@ fn test_diff_main() -> Result<(), Error> {
// Simple cases.
assert_eq!(
vec![Diff::delete(b"a"), Diff::insert(b"b"),],
- dmp.diff_main("a", "b")?
+ dmp.diff_main::<Efficient>("a", "b")?
);
assert_eq!(
@@ -222,7 +225,7 @@ fn test_diff_main() -> Result<(), Error> {
Diff::insert(b"lso"),
Diff::equal(b" fruit.")
],
- dmp.diff_main("Apples are a fruit.", "Bananas are also fruit.")?
+ dmp.diff_main::<Efficient>("Apples are a fruit.", "Bananas are also fruit.")?
);
assert_eq!(
@@ -233,7 +236,7 @@ fn test_diff_main() -> Result<(), Error> {
Diff::delete(b"\t"),
Diff::insert(b"\0")
],
- dmp.diff_main("ax\t", "\u{0680}x\0")?
+ dmp.diff_main::<Efficient>("ax\t", "\u{0680}x\0")?
);
// Overlaps.
@@ -246,7 +249,7 @@ fn test_diff_main() -> Result<(), Error> {
Diff::delete(b"2"),
Diff::insert(b"xab"),
],
- dmp.diff_main("1ayb2", "abxab")?
+ dmp.diff_main::<Efficient>("1ayb2", "abxab")?
);
assert_eq!(
@@ -255,7 +258,7 @@ fn test_diff_main() -> Result<(), Error> {
Diff::equal(b"abc"),
Diff::delete(b"y"),
],
- dmp.diff_main("abcy", "xaxcxabc")?
+ dmp.diff_main::<Efficient>("abcy", "xaxcxabc")?
);
assert_eq!(
@@ -270,7 +273,7 @@ fn test_diff_main() -> Result<(), Error> {
Diff::equal(b"efghijklmnopqrs"),
Diff::delete(b"EFGHIJKLMNOefg"),
],
- dmp.diff_main(
+ dmp.diff_main::<Efficient>(
"ABCDa=bcd=efghijklmnopqrsEFGHIJKLMNOefg",
"a-bcd-efghijklmnopqrs"
)?
@@ -285,7 +288,7 @@ fn test_diff_main() -> Result<(), Error> {
Diff::equal(b" [[Hepatopancreatic]]"),
Diff::delete(b" and [[New"),
],
- dmp.diff_main(
+ dmp.diff_main::<Efficient>(
"a [[Hepatopancreatic]] and [[New",
" and [[Hepatopancreatic]]"
)?
@@ -298,7 +301,7 @@ fn test_diff_main() -> Result<(), Error> {
let b = vec!["I am the very model of a modern major general,\nI\'ve information vegetable, animal, and mineral,\nI know the kings of England, and I quote the fights historical,\nFrom Marathon to Waterloo, in order categorical.\n"; 2048].join("");
let start = Utc::now().time();
- dmp.diff_main(&a, &b)?;
+ dmp.diff_main::<Efficient>(&a, &b)?;
let end = Utc::now().time();
// Test that we took at least the timeout period (+ 5ms being generous).
assert!((end - start).num_milliseconds() <= LOW_TIMEOUT as i64 + 5);
@@ -310,9 +313,9 @@ fn test_diff_main() -> Result<(), Error> {
let a = "12345678901234567890123456789 0123456 78901234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n";
let b = "abcdefghij abcdefghij abcdefghij abcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\n";
dmp.set_checklines(false);
- let res_no_lm = dmp.diff_main(a, b)?;
+ let res_no_lm = dmp.diff_main::<Efficient>(a, b)?;
dmp.set_checklines(true);
- let res_yes_lm = dmp.diff_main(a, b)?;
+ let res_yes_lm = dmp.diff_main::<Efficient>(a, b)?;
// Now, we'll run 2 checks - one for result equality
assert_eq!(res_no_lm, res_yes_lm);
@@ -321,18 +324,18 @@ fn test_diff_main() -> Result<(), Error> {
let a = "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";
let b = "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij";
dmp.set_checklines(false);
- let yes_lm = dmp.diff_main(a, b)?;
+ let yes_lm = dmp.diff_main::<Efficient>(a, b)?;
dmp.set_checklines(true);
- let no_lm = dmp.diff_main(a, b)?;
+ let no_lm = dmp.diff_main::<Efficient>(a, b)?;
assert_eq!(no_lm, yes_lm);
// Overlap line-mode.
let a = "1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n";
let b = "abcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n";
dmp.set_checklines(false);
- let no_lm = dmp.diff_main(a, b)?;
+ let no_lm = dmp.diff_main::<Efficient>(a, b)?;
dmp.set_checklines(true);
- let yes_lm = dmp.diff_main(a, b)?;
+ let yes_lm = dmp.diff_main::<Efficient>(a, b)?;
assert_eq!(rebuild_text(&yes_lm[..])?, rebuild_text(&no_lm[..])?);
// Benefits of checklines can only be realized in text with many lines
@@ -341,13 +344,13 @@ fn test_diff_main() -> Result<(), Error> {
let new = std::fs::read_to_string("testdata/txt_new.txt").unwrap();
let start = Instant::now();
- let diff_yes_lm = dmp.diff_main(&old, &new);
+ let diff_yes_lm = dmp.diff_main::<Efficient>(&old, &new);
let yes_lm_dur = Instant::now() - start;
assert!(diff_yes_lm.is_ok());
dmp.set_checklines(false);
let start = Instant::now();
- let diff_no_lm = dmp.diff_main(&old, &new);
+ let diff_no_lm = dmp.diff_main::<Efficient>(&old, &new);
let no_lm_dur = Instant::now() - start;
assert!(diff_no_lm.is_ok());
@@ -362,21 +365,32 @@ fn test_diff_main_compat() -> Result<(), Error> {
// Perform a trivial diff.
// Null case.
- assert!(dmp.diff_main_compat("", "")?.is_empty());
+ assert!(dmp.diff_main::<Compat>("", "")?.is_empty());
// Equality
- assert_eq!(vec![Diff::equal(&"abc".chars().collect::<Vec<_>>()[..])], dmp.diff_main_compat("abc", "abc")?);
+ assert_eq!(
+ vec![Diff::equal(&"abc".chars().collect::<Vec<_>>()[..])],
+ dmp.diff_main::<Compat>("abc", "abc")?
+ );
// Simple insert
assert_eq!(
- vec![Diff::equal(&"ab".chars().collect::<Vec<_>>()[..]), Diff::insert(&"123".chars().collect::<Vec<_>>()[..]), Diff::equal(&['c'])],
- dmp.diff_main_compat("abc", "ab123c")?
+ vec![
+ Diff::equal(&"ab".chars().collect::<Vec<_>>()[..]),
+ Diff::insert(&"123".chars().collect::<Vec<_>>()[..]),
+ Diff::equal(&['c'])
+ ],
+ dmp.diff_main::<Compat>("abc", "ab123c")?
);
// Simple delete
assert_eq!(
- vec![Diff::equal(&['a']), Diff::delete(&"123".chars().collect::<Vec<_>>()[..]), Diff::equal(&['b','c'])],
- dmp.diff_main_compat("a123bc", "abc")?
+ vec![
+ Diff::equal(&['a']),
+ Diff::delete(&"123".chars().collect::<Vec<_>>()[..]),
+ Diff::equal(&['b', 'c'])
+ ],
+ dmp.diff_main::<Compat>("a123bc", "abc")?
);
// Two insertions
@@ -385,10 +399,10 @@ fn test_diff_main_compat() -> Result<(), Error> {
Diff::equal(&['a']),
Diff::insert(&"123".chars().collect::<Vec<_>>()[..]),
Diff::equal(&['b']),
- Diff::insert(&['4','5','6']),
+ Diff::insert(&['4', '5', '6']),
Diff::equal(&['c']),
],
- dmp.diff_main_compat("abc", "a123b456c")?
+ dmp.diff_main::<Compat>("abc", "a123b456c")?
);
// Two deletions.
@@ -400,7 +414,7 @@ fn test_diff_main_compat() -> Result<(), Error> {
Diff::delete(&"456".chars().collect::<Vec<_>>()[..]),
Diff::equal(&['c']),
],
- dmp.diff_main_compat("a123b456c", "abc")?
+ dmp.diff_main::<Compat>("a123b456c", "abc")?
);
// Perform a real diff.
@@ -409,7 +423,7 @@ fn test_diff_main_compat() -> Result<(), Error> {
// Simple cases.
assert_eq!(
vec![Diff::delete(&['a']), Diff::insert(&['b']),],
- dmp.diff_main_compat("a", "b")?
+ dmp.diff_main::<Compat>("a", "b")?
);
assert_eq!(
@@ -420,7 +434,7 @@ fn test_diff_main_compat() -> Result<(), Error> {
Diff::insert(&"lso".chars().collect::<Vec<_>>()[..]),
Diff::equal(&" fruit.".chars().collect::<Vec<_>>()[..])
],
- dmp.diff_main_compat("Apples are a fruit.", "Bananas are also fruit.")?
+ dmp.diff_main::<Compat>("Apples are a fruit.", "Bananas are also fruit.")?
);
assert_eq!(
@@ -431,7 +445,7 @@ fn test_diff_main_compat() -> Result<(), Error> {
Diff::delete(&['\t']),
Diff::insert(&['\0'])
],
- dmp.diff_main_compat("ax\t", "\u{0680}x\0")?
+ dmp.diff_main::<Compat>("ax\t", "\u{0680}x\0")?
);
// Overlaps.
@@ -444,7 +458,7 @@ fn test_diff_main_compat() -> Result<(), Error> {
Diff::delete(&['2']),
Diff::insert(&"xab".chars().collect::<Vec<_>>()[..]),
],
- dmp.diff_main_compat("1ayb2", "abxab")?
+ dmp.diff_main::<Compat>("1ayb2", "abxab")?
);
assert_eq!(
@@ -453,7 +467,7 @@ fn test_diff_main_compat() -> Result<(), Error> {
Diff::equal(&"abc".chars().collect::<Vec<_>>()[..]),
Diff::delete(&['y']),
],
- dmp.diff_main_compat("abcy", "xaxcxabc")?
+ dmp.diff_main::<Compat>("abcy", "xaxcxabc")?
);
assert_eq!(
@@ -468,7 +482,7 @@ fn test_diff_main_compat() -> Result<(), Error> {
Diff::equal(&"efghijklmnopqrs".chars().collect::<Vec<_>>()[..]),
Diff::delete(&"EFGHIJKLMNOefg".chars().collect::<Vec<_>>()[..]),
],
- dmp.diff_main_compat(
+ dmp.diff_main::<Compat>(
"ABCDa=bcd=efghijklmnopqrsEFGHIJKLMNOefg",
"a-bcd-efghijklmnopqrs"
)?
@@ -479,11 +493,14 @@ fn test_diff_main_compat() -> Result<(), Error> {
vec![
Diff::insert(&[' ']),
Diff::equal(&['a']),
- Diff::insert(&['n','d']),
- Diff::equal(&[' ','[','[','H','e','p','a','t','o','p','a','n','c','r','e','a','t','i','c',']',']']),
+ Diff::insert(&['n', 'd']),
+ Diff::equal(&[
+ ' ', '[', '[', 'H', 'e', 'p', 'a', 't', 'o', 'p', 'a', 'n', 'c', 'r', 'e', 'a',
+ 't', 'i', 'c', ']', ']'
+ ]),
Diff::delete(&" and [[New".chars().collect::<Vec<_>>()[..]),
],
- dmp.diff_main_compat(
+ dmp.diff_main::<Compat>(
"a [[Hepatopancreatic]] and [[New",
" and [[Hepatopancreatic]]"
)?
@@ -496,7 +513,7 @@ fn test_diff_main_compat() -> Result<(), Error> {
let b = vec!["I am the very model of a modern major general,\nI\'ve information vegetable, animal, and mineral,\nI know the kings of England, and I quote the fights historical,\nFrom Marathon to Waterloo, in order categorical.\n"; 2048].join("");
let start = Utc::now().time();
- dmp.diff_main(&a, &b)?;
+ dmp.diff_main::<Efficient>(&a, &b)?;
let end = Utc::now().time();
// Test that we took at least the timeout period (+ 5ms being generous).
assert!((end - start).num_milliseconds() <= LOW_TIMEOUT as i64 + 5);
@@ -508,9 +525,9 @@ fn test_diff_main_compat() -> Result<(), Error> {
let a = "12345678901234567890123456789 0123456 78901234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n";
let b = "abcdefghij abcdefghij abcdefghij abcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\nabcdefghij\n";
dmp.set_checklines(false);
- let res_no_lm = dmp.diff_main(a, b)?;
+ let res_no_lm = dmp.diff_main::<Efficient>(a, b)?;
dmp.set_checklines(true);
- let res_yes_lm = dmp.diff_main(a, b)?;
+ let res_yes_lm = dmp.diff_main::<Efficient>(a, b)?;
// Now, we'll run 2 checks - one for result equality
assert_eq!(res_no_lm, res_yes_lm);
@@ -519,18 +536,18 @@ fn test_diff_main_compat() -> Result<(), Error> {
let a = "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";
let b = "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghij";
dmp.set_checklines(false);
- let yes_lm = dmp.diff_main(a, b)?;
+ let yes_lm = dmp.diff_main::<Efficient>(a, b)?;
dmp.set_checklines(true);
- let no_lm = dmp.diff_main(a, b)?;
+ let no_lm = dmp.diff_main::<Efficient>(a, b)?;
assert_eq!(no_lm, yes_lm);
// Overlap line-mode.
let a = "1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n1234567890\n";
let b = "abcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n1234567890\n1234567890\n1234567890\nabcdefghij\n";
dmp.set_checklines(false);
- let no_lm = dmp.diff_main(a, b)?;
+ let no_lm = dmp.diff_main::<Efficient>(a, b)?;
dmp.set_checklines(true);
- let yes_lm = dmp.diff_main(a, b)?;
+ let yes_lm = dmp.diff_main::<Efficient>(a, b)?;
assert_eq!(rebuild_text(&yes_lm[..])?, rebuild_text(&no_lm[..])?);
// Benefits of checklines can only be realized in text with many lines
@@ -539,13 +556,13 @@ fn test_diff_main_compat() -> Result<(), Error> {
let new = std::fs::read_to_string("testdata/txt_new.txt").unwrap();
let start = Instant::now();
- let diff_yes_lm = dmp.diff_main(&old, &new);
+ let diff_yes_lm = dmp.diff_main::<Efficient>(&old, &new);
let yes_lm_dur = Instant::now() - start;
assert!(diff_yes_lm.is_ok());
dmp.set_checklines(false);
let start = Instant::now();
- let diff_no_lm = dmp.diff_main(&old, &new);
+ let diff_no_lm = dmp.diff_main::<Efficient>(&old, &new);
let no_lm_dur = Instant::now() - start;
assert!(diff_no_lm.is_ok());
@@ -640,28 +657,51 @@ fn rebuild_text(diffs: &[Diff<u8>]) -> Result<(String, String), Error> {
fn test_patch_from_text() -> Result<(), Error> {
let dmp = DiffMatchPatch::new();
- assert!(dmp.patch_from_text("")?.is_empty());
+ assert!(dmp.patch_from_text::<Efficient>("")?.is_empty());
+
+ let strp = "@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n %0Alaz\n";
+ assert_eq!(strp, dmp.patch_from_text::<Efficient>(strp)?[0].to_string());
+
+ assert_eq!(
+ "@@ -1 +1 @@\n-a\n+b\n",
+ dmp.patch_from_text::<Efficient>("@@ -1 +1 @@\n-a\n+b\n")?[0].to_string()
+ );
+
+ assert_eq!(
+ "@@ -1,3 +0,0 @@\n-abc\n",
+ dmp.patch_from_text::<Efficient>("@@ -1,3 +0,0 @@\n-abc\n")?[0].to_string()
+ );
+
+ assert_eq!(
+ "@@ -0,0 +1,3 @@\n+abc\n",
+ dmp.patch_from_text::<Efficient>("@@ -0,0 +1,3 @@\n+abc\n")?[0].to_string()
+ );
+
+ // Generates error.
+ assert!(dmp.patch_from_text::<Efficient>("Bad\nPatch\n").is_err());
+
+ assert!(dmp.patch_from_text::<Compat>("")?.is_empty());
let strp = "@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n %0Alaz\n";
- assert_eq!(strp, dmp.patch_from_text(strp)?[0].to_string());
+ assert_eq!(strp, dmp.patch_from_text::<Compat>(strp)?[0].to_string());
assert_eq!(
"@@ -1 +1 @@\n-a\n+b\n",
- dmp.patch_from_text("@@ -1 +1 @@\n-a\n+b\n")?[0].to_string()
+ dmp.patch_from_text::<Compat>("@@ -1 +1 @@\n-a\n+b\n")?[0].to_string()
);
assert_eq!(
"@@ -1,3 +0,0 @@\n-abc\n",
- dmp.patch_from_text("@@ -1,3 +0,0 @@\n-abc\n")?[0].to_string()
+ dmp.patch_from_text::<Compat>("@@ -1,3 +0,0 @@\n-abc\n")?[0].to_string()
);
assert_eq!(
"@@ -0,0 +1,3 @@\n+abc\n",
- dmp.patch_from_text("@@ -0,0 +1,3 @@\n+abc\n")?[0].to_string()
+ dmp.patch_from_text::<Compat>("@@ -0,0 +1,3 @@\n+abc\n")?[0].to_string()
);
// Generates error.
- assert!(dmp.patch_from_text("Bad\nPatch\n").is_err());
+ assert!(dmp.patch_from_text::<Compat>("Bad\nPatch\n").is_err());
Ok(())
}
@@ -671,11 +711,19 @@ fn test_patch_to_text() -> Result<(), Error> {
let dmp = DiffMatchPatch::new();
let strp = "@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n";
- let patches = dmp.patch_from_text(strp)?;
+ let patches = dmp.patch_from_text::<Efficient>(strp)?;
assert_eq!(strp, dmp.patch_to_text(&patches));
let strp = "@@ -1,9 +1,9 @@\n-f\n+F\n oo+fooba\n@@ -7,9 +7,9 @@\n obar\n-,\n+.\n tes\n";
- let patches = dmp.patch_from_text(strp)?;
+ let patches = dmp.patch_from_text::<Efficient>(strp)?;
+ assert_eq!(strp, dmp.patch_to_text(&patches));
+
+ let strp = "@@ -21,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n";
+ let patches = dmp.patch_from_text::<Compat>(strp)?;
+ assert_eq!(strp, dmp.patch_to_text(&patches));
+
+ let strp = "@@ -1,9 +1,9 @@\n-f\n+F\n oo+fooba\n@@ -7,9 +7,9 @@\n obar\n-,\n+.\n tes\n";
+ let patches = dmp.patch_from_text::<Compat>(strp)?;
assert_eq!(strp, dmp.patch_to_text(&patches));
Ok(())
@@ -684,22 +732,22 @@ fn test_patch_to_text() -> Result<(), Error> {
#[test]
fn test_patch_make() -> Result<(), Error> {
let dmp = DiffMatchPatch::default();
- let patches = dmp.patch_make(PatchInput::Texts("", ""))?;
+ let patches = dmp.patch_make(PatchInput::Texts::<Efficient>("", ""))?;
assert!(patches.is_empty());
let txt1 = "The quick brown fox jumps over the lazy dog.";
let txt2 = "That quick brown fox jumped over a lazy dog.";
// The second patch must be "-21,17 +21,18", not "-22,17 +21,18" due to rolling context.
- let patches = dmp.patch_make(PatchInput::Texts(txt2, txt1))?;
+ let patches = dmp.patch_make(PatchInput::Texts::<Efficient>(txt2, txt1))?;
assert_eq!("@@ -1,8 +1,7 @@\n Th\n-at\n+e\n qui\n@@ -21,17 +21,18 @@\n jump\n-ed\n+s\n over \n-a\n+the\n laz\n", dmp.patch_to_text(&patches));
// Text1+Text2 inputs.
- let patches = dmp.patch_make(PatchInput::Texts(txt1, txt2))?;
+ let patches = dmp.patch_make(PatchInput::Texts::<Efficient>(txt1, txt2))?;
assert_eq!("@@ -1,11 +1,12 @@\n Th\n-e\n+at\n quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n", dmp.patch_to_text(&patches));
// Diff input.
- let diffs = dmp.diff_main(txt1, txt2)?;
+ let diffs = dmp.diff_main::<Efficient>(txt1, txt2)?;
let patches = dmp.patch_make(PatchInput::Diffs(&diffs[..]))?;
assert_eq!("@@ -1,11 +1,12 @@\n Th\n-e\n+at\n quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n", dmp.patch_to_text(&patches));
@@ -708,7 +756,7 @@ fn test_patch_make() -> Result<(), Error> {
assert_eq!("@@ -1,11 +1,12 @@\n Th\n-e\n+at\n quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n", dmp.patch_to_text(&patches));
// Character encoding.
- let patches = dmp.patch_make(PatchInput::Texts(
+ let patches = dmp.patch_make(PatchInput::Texts::<Efficient>(
"`1234567890-=[]\\;',./",
"~!@#$%^&*()_+{}|:\"<>?",
))?;
@@ -731,7 +779,60 @@ fn test_patch_make() -> Result<(), Error> {
// Long string with repeats.
let txt1 = vec!["abcdef"; 100].join("");
let txt2 = [&txt1, "123"].join("");
- let patches = dmp.patch_make(PatchInput::Texts(&txt1, &txt2))?;
+ let patches = dmp.patch_make(PatchInput::Texts::<Efficient>(&txt1, &txt2))?;
+ assert_eq!(
+ "@@ -573,28 +573,31 @@\n cdefabcdefabcdefabcdefabcdef\n+123\n",
+ dmp.patch_to_text(&patches)
+ );
+
+ let patches = dmp.patch_make(PatchInput::Texts::<Compat>("", ""))?;
+ assert!(patches.is_empty());
+
+ let txt1 = "The quick brown fox jumps over the lazy dog.";
+ let txt2 = "That quick brown fox jumped over a lazy dog.";
+
+ // The second patch must be "-21,17 +21,18", not "-22,17 +21,18" due to rolling context.
+ let patches = dmp.patch_make(PatchInput::Texts::<Compat>(txt2, txt1))?;
+ assert_eq!("@@ -1,8 +1,7 @@\n Th\n-at\n+e\n qui\n@@ -21,17 +21,18 @@\n jump\n-ed\n+s\n over \n-a\n+the\n laz\n", dmp.patch_to_text(&patches));
+
+ // Text1+Text2 inputs.
+ let patches = dmp.patch_make(PatchInput::Texts::<Compat>(txt1, txt2))?;
+ assert_eq!("@@ -1,11 +1,12 @@\n Th\n-e\n+at\n quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n", dmp.patch_to_text(&patches));
+
+ // Diff input.
+ let diffs = dmp.diff_main::<Efficient>(txt1, txt2)?;
+ let patches = dmp.patch_make(PatchInput::Diffs(&diffs[..]))?;
+ assert_eq!("@@ -1,11 +1,12 @@\n Th\n-e\n+at\n quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n", dmp.patch_to_text(&patches));
+
+ // Text1+Diff inputs.
+ let patches = dmp.patch_make(PatchInput::TextDiffs(txt1, &diffs[..]))?;
+ assert_eq!("@@ -1,11 +1,12 @@\n Th\n-e\n+at\n quick b\n@@ -22,18 +22,17 @@\n jump\n-s\n+ed\n over \n-the\n+a\n laz\n", dmp.patch_to_text(&patches));
+
+ // Character encoding.
+ let patches = dmp.patch_make(PatchInput::Texts::<Compat>(
+ "`1234567890-=[]\\;',./",
+ "~!@#$%^&*()_+{}|:\"<>?",
+ ))?;
+
+ assert_eq!(
+ "@@ -1,21 +1,21 @@\n-%601234567890-=%5B%5D%5C;',./\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\n",
+ dmp.patch_to_text(&patches)
+ );
+
+ // Character decoding.
+ let diffs = vec![
+ Diff::delete(b"`1234567890-=[]\\;',./"),
+ Diff::insert(b"~!@#$%^&*()_+{}|:\"<>?"),
+ ];
+ assert_eq!(
+ diffs,
+ dmp.patch_from_text("@@ -1,21 +1,21 @@\n-%601234567890-=%5B%5D%5C;',./\n+~!@#$%25%5E&*()_+%7B%7D%7C:%22%3C%3E?\n")?[0].diffs()
+ );
+
+ // Long string with repeats.
+ let txt1 = vec!["abcdef"; 100].join("");
+ let txt2 = [&txt1, "123"].join("");
+ let patches = dmp.patch_make(PatchInput::Texts::<Compat>(&txt1, &txt2))?;
assert_eq!(
"@@ -573,28 +573,31 @@\n cdefabcdefabcdefabcdefabcdef\n+123\n",
dmp.patch_to_text(&patches)
@@ -766,11 +867,147 @@ fn test_diff_text() {
fn test_patch_apply() -> Result<(), Error> {
let mut dmp = DiffMatchPatch::default();
- let patches = dmp.patch_make(PatchInput::Texts("", ""))?;
+ let patches = dmp.patch_make(PatchInput::Texts::<Efficient>("", ""))?;
+ let (txt, results) = dmp.patch_apply(&patches, "Hello world.")?;
+ assert_eq!(format!("{}\t{}", txt, results.len()), "Hello world.\t0");
+
+ let patches = dmp.patch_make(PatchInput::Texts::<Efficient>(
+ "The quick brown fox jumps over the lazy dog.",
+ "That quick brown fox jumped over a lazy dog.",
+ ))?;
+
+ // Exact match
+ assert_eq!(
+ (
+ "That quick brown fox jumped over a lazy dog.".to_string(),
+ vec![true, true]
+ ),
+ dmp.patch_apply(&patches, "The quick brown fox jumps over the lazy dog.")?
+ );
+
+ // Partial match
+ assert_eq!(
+ (
+ "That quick red rabbit jumped over a tired tiger.".to_string(),
+ vec![true, true]
+ ),
+ dmp.patch_apply(&patches, "The quick red rabbit jumps over the tired tiger.")?
+ );
+
+ // Failed match
+ assert_eq!(
+ (
+ "I am the very model of a modern major general.".to_string(),
+ vec![false, false]
+ ),
+ dmp.patch_apply(&patches, "I am the very model of a modern major general.")?
+ );
+
+ // Big delete, small change
+ let patches = dmp.patch_make(PatchInput::Texts::<Efficient>(
+ "x1234567890123456789012345678901234567890123456789012345678901234567890y",
+ "xabcy",
+ ))?;
+ assert_eq!(
+ ("xabcy".to_string(), vec![true, true]),
+ dmp.patch_apply(
+ &patches,
+ "x123456789012345678901234567890-----++++++++++-----123456789012345678901234567890y"
+ )?
+ );
+
+ // Big delete, large change
+ let patches = dmp.patch_make(PatchInput::Texts::<Efficient>(
+ "x1234567890123456789012345678901234567890123456789012345678901234567890y",
+ "xabcy",
+ ))?;
+ assert_eq!(
+ (
+ "xabc12345678901234567890---------------++++++++++---------------12345678901234567890y"
+ .to_string(),
+ vec![false, true]
+ ),
+ dmp.patch_apply(
+ &patches,
+ "x12345678901234567890---------------++++++++++---------------12345678901234567890y"
+ )?
+ );
+
+ dmp.set_delete_threshold(0.6);
+ let patches = dmp.patch_make(PatchInput::Texts::<Efficient>(
+ "x1234567890123456789012345678901234567890123456789012345678901234567890y",
+ "xabcy",
+ ))?;
+ assert_eq!(
+ ("xabcy".to_string(), vec![true, true]),
+ dmp.patch_apply(
+ &patches,
+ "x12345678901234567890---------------++++++++++---------------12345678901234567890y"
+ )?
+ );
+ dmp.set_delete_threshold(0.5);
+
+ // Compesate for failed patch
+ dmp.set_match_threshold(0.);
+ dmp.set_match_distance(0);
+ let patches = dmp.patch_make(PatchInput::Texts::<Efficient>(
+ "abcdefghijklmnopqrstuvwxyz--------------------1234567890",
+ "abcXXXXXXXXXXdefghijklmnopqrstuvwxyz--------------------1234567YYYYYYYYYY890",
+ ))?;
+ assert_eq!(
+ (
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567YYYYYYYYYY890".to_string(),
+ vec![false, true]
+ ),
+ dmp.patch_apply(
+ &patches,
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ--------------------1234567890"
+ )?
+ );
+
+ dmp.set_match_threshold(0.5);
+ dmp.set_match_distance(1000);
+
+ // No side-effects - kinds useless cos patches is not mutable in rust
+ let patches = dmp.patch_make(PatchInput::Texts::<Efficient>("", "test"))?;
+ let srcstr = dmp.patch_to_text(&patches);
+ dmp.patch_apply(&patches, "")?;
+ assert_eq!(srcstr, dmp.patch_to_text(&patches));
+
+ let patches = dmp.patch_make(PatchInput::Texts::<Efficient>(
+ "The quick brown fox jumps over the lazy dog.",
+ "Woof",
+ ))?;
+ let srcstr = dmp.patch_to_text(&patches);
+ dmp.patch_apply(&patches, "The quick brown fox jumps over the lazy dog.")?;
+ assert_eq!(srcstr, dmp.patch_to_text(&patches));
+
+ // Edge exact match
+ let patches = dmp.patch_make(PatchInput::Texts::<Efficient>("", "test"))?;
+ assert_eq!(
+ ("test".to_string(), vec![true]),
+ dmp.patch_apply(&patches, "")?
+ );
+
+ // Near edge exact match
+ let patches = dmp.patch_make(PatchInput::Texts::<Efficient>("XY", "XtestY"))?;
+ assert_eq!(
+ ("XtestY".to_string(), vec![true]),
+ dmp.patch_apply(&patches, "XY")?
+ );
+
+ // Edge partial match
+ let patches = dmp.patch_make(PatchInput::Texts::<Efficient>("y", "y123"))?;
+ assert_eq!(
+ ("x123".to_string(), vec![true]),
+ dmp.patch_apply(&patches, "x")?
+ );
+
+ let patches = dmp.patch_make(PatchInput::Texts::<Compat>("", ""))?;
let (txt, results) = dmp.patch_apply(&patches, "Hello world.")?;
assert_eq!(format!("{}\t{}", txt, results.len()), "Hello world.\t0");
- let patches = dmp.patch_make(PatchInput::Texts(
+ let patches = dmp.patch_make(PatchInput::Texts::<Compat>(
"The quick brown fox jumps over the lazy dog.",
"That quick brown fox jumped over a lazy dog.",
))?;
@@ -803,7 +1040,7 @@ fn test_patch_apply() -> Result<(), Error> {
);
// Big delete, small change
- let patches = dmp.patch_make(PatchInput::Texts(
+ let patches = dmp.patch_make(PatchInput::Texts::<Compat>(
"x1234567890123456789012345678901234567890123456789012345678901234567890y",
"xabcy",
))?;
@@ -816,7 +1053,7 @@ fn test_patch_apply() -> Result<(), Error> {
);
// Big delete, large change
- let patches = dmp.patch_make(PatchInput::Texts(
+ let patches = dmp.patch_make(PatchInput::Texts::<Compat>(
"x1234567890123456789012345678901234567890123456789012345678901234567890y",
"xabcy",
))?;
@@ -833,7 +1070,7 @@ fn test_patch_apply() -> Result<(), Error> {
);
dmp.set_delete_threshold(0.6);
- let patches = dmp.patch_make(PatchInput::Texts(
+ let patches = dmp.patch_make(PatchInput::Texts::<Compat>(
"x1234567890123456789012345678901234567890123456789012345678901234567890y",
"xabcy",
))?;
@@ -849,7 +1086,7 @@ fn test_patch_apply() -> Result<(), Error> {
// Compesate for failed patch
dmp.set_match_threshold(0.);
dmp.set_match_distance(0);
- let patches = dmp.patch_make(PatchInput::Texts(
+ let patches = dmp.patch_make(PatchInput::Texts::<Compat>(
"abcdefghijklmnopqrstuvwxyz--------------------1234567890",
"abcXXXXXXXXXXdefghijklmnopqrstuvwxyz--------------------1234567YYYYYYYYYY890",
))?;
@@ -868,12 +1105,12 @@ fn test_patch_apply() -> Result<(), Error> {
dmp.set_match_distance(1000);
// No side-effects - kinds useless cos patches is not mutable in rust
- let patches = dmp.patch_make(PatchInput::Texts("", "test"))?;
+ let patches = dmp.patch_make(PatchInput::Texts::<Compat>("", "test"))?;
let srcstr = dmp.patch_to_text(&patches);
dmp.patch_apply(&patches, "")?;
assert_eq!(srcstr, dmp.patch_to_text(&patches));
- let patches = dmp.patch_make(PatchInput::Texts(
+ let patches = dmp.patch_make(PatchInput::Texts::<Compat>(
"The quick brown fox jumps over the lazy dog.",
"Woof",
))?;
@@ -882,21 +1119,21 @@ fn test_patch_apply() -> Result<(), Error> {
assert_eq!(srcstr, dmp.patch_to_text(&patches));
// Edge exact match
- let patches = dmp.patch_make(PatchInput::Texts("", "test"))?;
+ let patches = dmp.patch_make(PatchInput::Texts::<Compat>("", "test"))?;
assert_eq!(
("test".to_string(), vec![true]),
dmp.patch_apply(&patches, "")?
);
// Near edge exact match
- let patches = dmp.patch_make(PatchInput::Texts("XY", "XtestY"))?;
+ let patches = dmp.patch_make(PatchInput::Texts::<Compat>("XY", "XtestY"))?;
assert_eq!(
("XtestY".to_string(), vec![true]),
dmp.patch_apply(&patches, "XY")?
);
// Edge partial match
- let patches = dmp.patch_make(PatchInput::Texts("y", "y123"))?;
+ let patches = dmp.patch_make(PatchInput::Texts::<Compat>("y", "y123"))?;
assert_eq!(
("x123".to_string(), vec![true]),
dmp.patch_apply(&patches, "x")?