Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prepare a 0.4.0 release. #51

Merged
merged 4 commits into from
Aug 30, 2024
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
35 changes: 35 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,38 @@
# fm 0.4.0 (2024-08-30)

* Add the `..~` "group match" wildcard, which searches until it finds a match
for a group of lines. This means that fm now backtracks! The behaviour of the
`...` interline wildcard is unchanged, and is preferred, because it has more
predictable performance and leads to stronger tests.

* Report names matched when matching fails. When matching fails, it can be hard
to work out what went wrong, particularly when name matching is used. fm now
reports the set of names and their values at the point of failure, which
helps debug matching problems.

* Add support for "ignorable" name matchers. Sometimes one needs to match a
particular pattern, but the specific text matched is irrelevant. As a one-off
this was easily handled, but if you had multiple places where you wanted to
do this, you had to use a fresh name for each such instance, which is at best
obfuscatory and at worst error prone. Ignorable name matchers allow you to
use the same name (e.g. `_`) multiple times, each matching the same pattern,
but not comparing the text matched with other instances of the ignorable
name.

* Report the text line where pattern failed to match from. Previously when
`...` failed, it told you how-far-it-got before realising it had failed,
rather than the more useful where-it-started.

* Remove "distinct name matching" in favour of the more powerful "name
matching validator". Distinct name matching can now be expressed as:

```
.name_matching_validator(|names| {
names.values().collect::<HashSet<_>>().len() == names.len()
})
```


# fm 0.3.0 (2024-03-20)

* Add `OutputFormatter`s. The default output formatting has now changed from a
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "fm"
description = "Non-backtracking fuzzy text matcher"
repository = "https://github.com/softdevteam/fm/"
version = "0.3.0"
version = "0.4.0"
authors = ["Edd Barrett <vext01@gmail.com>", "Laurence Tratt <laurie@tratt.net>"]
readme = "README.md"
license = "Apache-2.0/MIT"
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ either:

The interline wildcards are:

* The *prefix match* wildcard `...` matches until it finds a match for the
* The *prefix match* wildcard `...` searches until it finds a match for the
line immediately after the interline operator ("the prefix"), at which
point the search is anchored. This wildcard does not backtrack.
* The *group match* wildcard `..~` matches until it finds a match for the
* The *group match* wildcard `..~` searches until it finds a match for the
next group, at which point the search is anchored. This wildcard
backtracks, though never further than one group.

Expand Down
50 changes: 3 additions & 47 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@
#![allow(clippy::type_complexity)]

use std::{
collections::{
hash_map::{Entry, HashMap},
HashSet,
},
collections::hash_map::{Entry, HashMap},
default::Default,
error::Error,
fmt,
Expand Down Expand Up @@ -180,26 +177,6 @@ impl<'a> FMBuilder<'a> {
self
}

/// If `yes`, then different names cannot match the same text value. For example if `$1` binds
/// to `a` then `$2` will refuse to match against `a` (though `$1` will continue to match
/// against only `a`). Note that ignorable name matches (see [Self::name_matcher_ignore]) are
/// never subject to distinct name matching. Defaults to `false`.
///
/// # Warning
///
/// If you call this function with `true` then later with `false`, the latter will not take
/// effect.
#[deprecated(since = "0.3.1", note = "Please use name_matching_validator instead")]
pub fn distinct_name_matching(self, yes: bool) -> Self {
if yes {
self.name_matching_validator(|names| {
names.values().collect::<HashSet<_>>().len() == names.len()
})
} else {
self
}
}

/// Add a name matching validator: this takes a [HashMap] of `(key, value)` pairs and must
/// return `true` if this is a valid set of pairs or false otherwise. Name matching validators
/// allow you to customise what names are valid matches. For example, if you want distinct
Expand Down Expand Up @@ -246,6 +223,7 @@ impl<'a> FMBuilder<'a> {
/// If `yes`, then:
/// 1. Blank lines at the start/end of the pattern and text are ignored.
/// 2. Leading/trailing whitespace is ignored on each line of the pattern and text.
///
/// Defaults to "yes".
pub fn trim_whitespace(mut self, yes: bool) -> Self {
self.options.trim_whitespace = yes;
Expand Down Expand Up @@ -693,7 +671,7 @@ impl Error for FMatchError {
/// If `trim` is set to true:
/// 1. Leading/trailing blank space within lines is trimmed.
/// 2. Leading/trailing blank lines (including blank space) are trimmed.
fn line_trimmer<'a>(trim: bool, s: &'a str) -> (Vec<&'a str>, usize) {
fn line_trimmer(trim: bool, s: &str) -> (Vec<&str>, usize) {
let mut lines = s.lines().collect::<Vec<_>>();
if !trim {
return (lines, 1);
Expand Down Expand Up @@ -1184,28 +1162,6 @@ mod tests {
assert!(!helper("$1 $2", "a a"));
}

/// This test can be removed when [FMBuilder::distinct_name_matching] is removed.
#[test]
fn distinct_names_deprecated() {
let nameptn_re = Regex::new(r"\$.+?\b").unwrap();
let name_re = Regex::new(r".+?\b").unwrap();
let helper = |ptn: &str, text: &str| -> bool {
#[allow(deprecated)]
FMBuilder::new(ptn)
.unwrap()
.name_matcher(nameptn_re.clone(), name_re.clone())
.distinct_name_matching(true)
.build()
.unwrap()
.matches(text)
.is_ok()
};

assert!(helper("$1 $1", "a a"));
assert!(!helper("$1 $1", "a b"));
assert!(!helper("$1 $2", "a a"));
}

#[test]
fn error_display() {
let ptn_re = Regex::new("\\$.+?\\b").unwrap();
Expand Down