Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
<!-- next-header -->
## [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
Expand Down
6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []
4 changes: 1 addition & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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()
Expand Down
4 changes: 2 additions & 2 deletions src/prelude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
128 changes: 20 additions & 108 deletions src/str/difference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<S>(mut self, split: S) -> Self
where
S: Into<borrow::Cow<'static, str>>,
{
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<str> 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<reflection::Case<'a>> {
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
}
}
}
Expand All @@ -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")
}
}

Expand All @@ -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<S>(orig: S) -> DifferencePredicate
where
S: Into<borrow::Cow<'static, str>>,
{
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<S>(orig: S) -> DifferencePredicate
pub fn diff<S>(orig: S) -> DifferencePredicate
where
S: Into<borrow::Cow<'static, str>>,
{
DifferencePredicate {
orig: orig.into(),
split: "\n".into(),
distance: 0,
op: DistanceOp::Similar,
}
DifferencePredicate { orig: orig.into() }
}
6 changes: 3 additions & 3 deletions src/str/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down