diff --git a/CHANGELOG.md b/CHANGELOG.md index 7e83d0d..549e7b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] - ReleaseDate +#### Breaking Changes + +- `predicates::str::diff` was removed +- `predicates::str::similar` was renamed to `diff` +- The `difference` feature flag was renamed to `diff` +- `diff().split` and `diff().distance` were removed + +#### Fixes + +- Shrink the output of Diffs because its redundant +- Moved off of an unmaintained Diff library + ## [1.0.8] - 2021-04-28 ## [1.0.7] - 2021-01-29 diff --git a/Cargo.toml b/Cargo.toml index 2734b42..bf4b05e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,14 +22,16 @@ azure-devops = { project = "assert-rs", pipeline = "predicates-rs" } [dependencies] predicates-core = { version = "1.0", path = "crates/core" } -difference = { version = "2.0", optional = true } +difflib = { version = "0.4", optional = true } normalize-line-endings = { version = "0.3.0", optional = true } regex = { version="1.0", optional = true } float-cmp = { version="0.8", optional = true } +itertools = "0.10" [dev-dependencies] predicates-tree = { version = "1.0", path = "crates/tree" } [features] -default = ["difference", "regex", "float-cmp", "normalize-line-endings"] +default = ["diff", "regex", "float-cmp", "normalize-line-endings"] +diff = ["difflib"] unstable = [] diff --git a/src/lib.rs b/src/lib.rs index 935595c..c3bf9a4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -137,9 +137,8 @@ //! - [`predicate::str::is_empty`]: Specified string must be empty //! - [`str_pred = predicate::path::eq_file(...).utf8`]: Specified string must equal the contents //! of the given file. -//! - [`predicate::str::similar`]: Same as `eq` except report a diff. See [`DifferencePredicate`] +//! - [`predicate::str::diff`]: Same as `eq` except report a diff. See [`DifferencePredicate`] //! for more features. -//! - [`predicate::str::diff`]: Same as `ne`. See [`DifferencePredicate`] for more features. //! - [`predicate::str::starts_with`]: Specified string must start with the given needle. //! - [`predicate::str::ends_with`]: Specified string must end with the given needle. //! - [`predicate::str::contains`]: Specified string must contain the given needle. @@ -194,7 +193,6 @@ //! [`predicate::str::is_empty`]: prelude::predicate::str::is_empty() //! [`predicate::str::is_match(...).count`]: str::RegexPredicate::count() //! [`predicate::str::is_match`]: prelude::predicate::str::is_match() -//! [`predicate::str::similar`]: prelude::predicate::str::similar() //! [`predicate::str::starts_with`]: prelude::predicate::str::starts_with() //! [`str_pred = predicate::path::eq_file(...).utf8`]: path::BinaryFilePredicate::utf8() //! [`str_pred.normalize`]: prelude::PredicateStrExt::normalize() diff --git a/src/prelude.rs b/src/prelude.rs index 565f597..8cfd447 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -30,8 +30,8 @@ pub mod predicate { pub use crate::str::is_empty; pub use crate::str::{contains, ends_with, starts_with}; - #[cfg(feature = "difference")] - pub use crate::str::{diff, similar}; + #[cfg(feature = "diff")] + pub use crate::str::diff; #[cfg(feature = "regex")] pub use crate::str::is_match; diff --git a/src/str/difference.rs b/src/str/difference.rs index c125be6..0e87eaa 100644 --- a/src/str/difference.rs +++ b/src/str/difference.rs @@ -12,95 +12,36 @@ use std::fmt; use crate::reflection; use crate::Predicate; -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -enum DistanceOp { - Similar, - Different, -} - -impl DistanceOp { - fn eval(self, limit: i32, distance: i32) -> bool { - match self { - DistanceOp::Similar => distance <= limit, - DistanceOp::Different => limit < distance, - } - } -} - /// Predicate that diffs two strings. /// -/// This is created by the `predicate::str::similar`. +/// This is created by the `predicate::str::diff`. #[derive(Debug, Clone, PartialEq, Eq)] pub struct DifferencePredicate { orig: borrow::Cow<'static, str>, - split: borrow::Cow<'static, str>, - distance: i32, - op: DistanceOp, -} - -impl DifferencePredicate { - /// The split used when identifying changes. - /// - /// Common splits include: - /// - `""` for char-level. - /// - `" "` for word-level. - /// - `"\n"` for line-level. - /// - /// Default: `"\n"` - /// - /// # Examples - /// - /// ``` - /// use predicates::prelude::*; - /// - /// let predicate_fn = predicate::str::similar("Hello World").split(" "); - /// assert_eq!(true, predicate_fn.eval("Hello World")); - /// ``` - pub fn split(mut self, split: S) -> Self - where - S: Into>, - { - self.split = split.into(); - self - } - - /// The maximum allowed edit distance. - /// - /// Default: `0` - /// - /// # Examples - /// - /// ``` - /// use predicates::prelude::*; - /// - /// let predicate_fn = predicate::str::similar("Hello World!").split("").distance(1); - /// assert_eq!(true, predicate_fn.eval("Hello World!")); - /// assert_eq!(true, predicate_fn.eval("Hello World")); - /// assert_eq!(false, predicate_fn.eval("Hello World?")); - /// ``` - pub fn distance(mut self, distance: i32) -> Self { - self.distance = distance; - self - } } impl Predicate for DifferencePredicate { fn eval(&self, edit: &str) -> bool { - let change = difference::Changeset::new(&self.orig, edit, &self.split); - self.op.eval(self.distance, change.distance) + edit == self.orig } fn find_case<'a>(&'a self, expected: bool, variable: &str) -> Option> { - let change = difference::Changeset::new(&self.orig, variable, &self.split); - let result = self.op.eval(self.distance, change.distance); + let result = variable != self.orig; if result == expected { + None + } else { + let orig: Vec<_> = self.orig.lines().map(|l| format!("{}\n", l)).collect(); + let variable: Vec<_> = variable.lines().map(|l| format!("{}\n", l)).collect(); + let mut diff = + difflib::unified_diff(&orig, &variable, "value", "value", "expected", "actual", 0); + diff.insert(0, "\n".to_owned()); + Some( - reflection::Case::new(Some(self), result) - .add_product(reflection::Product::new("actual distance", change.distance)) - .add_product(reflection::Product::new("diff", change)), + reflection::Case::new(Some(self), result).add_product(reflection::Product::new( + "diff", + itertools::join(diff.iter(), ""), + )), ) - } else { - None } } } @@ -114,10 +55,7 @@ impl reflection::PredicateReflection for DifferencePredicate { impl fmt::Display for DifferencePredicate { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.op { - DistanceOp::Similar => write!(f, "var - original <= {}", self.distance), - DistanceOp::Different => write!(f, "{} < var - original", self.distance), - } + write!(f, "diff var original") } } @@ -129,40 +67,14 @@ impl fmt::Display for DifferencePredicate { /// use predicates::prelude::*; /// /// let predicate_fn = predicate::str::diff("Hello World"); -/// assert_eq!(false, predicate_fn.eval("Hello World")); -/// assert_eq!(true, predicate_fn.eval("Goodbye World")); -/// ``` -pub fn diff(orig: S) -> DifferencePredicate -where - S: Into>, -{ - DifferencePredicate { - orig: orig.into(), - split: "\n".into(), - distance: 0, - op: DistanceOp::Different, - } -} - -/// Creates a new `Predicate` that checks strings for how similar they are. -/// -/// # Examples -/// -/// ``` -/// use predicates::prelude::*; -/// -/// let predicate_fn = predicate::str::similar("Hello World"); /// assert_eq!(true, predicate_fn.eval("Hello World")); +/// assert!(predicate_fn.find_case(false, "Hello World").is_none()); /// assert_eq!(false, predicate_fn.eval("Goodbye World")); +/// assert!(predicate_fn.find_case(false, "Goodbye World").is_some()); /// ``` -pub fn similar(orig: S) -> DifferencePredicate +pub fn diff(orig: S) -> DifferencePredicate where S: Into>, { - DifferencePredicate { - orig: orig.into(), - split: "\n".into(), - distance: 0, - op: DistanceOp::Similar, - } + DifferencePredicate { orig: orig.into() } } diff --git a/src/str/mod.rs b/src/str/mod.rs index d1f864e..0d0dffd 100644 --- a/src/str/mod.rs +++ b/src/str/mod.rs @@ -15,10 +15,10 @@ pub use self::basics::*; mod adapters; pub use self::adapters::*; -#[cfg(feature = "difference")] +#[cfg(feature = "diff")] mod difference; -#[cfg(feature = "difference")] -pub use self::difference::{diff, similar, DifferencePredicate}; +#[cfg(feature = "diff")] +pub use self::difference::{diff, DifferencePredicate}; #[cfg(feature = "normalize-line-endings")] mod normalize; #[cfg(feature = "normalize-line-endings")]