Skip to content

Commit

Permalink
add caret negation ([^...]) in addition to exclamation mark
Browse files Browse the repository at this point in the history
  • Loading branch information
tertsdiepraam committed Mar 11, 2024
1 parent 49ee1e9 commit 12c5081
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 6 deletions.
31 changes: 25 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,8 @@ impl fmt::Display for PatternError {
/// - `[!...]` is the negation of `[...]`, i.e. it matches any characters
/// **not** in the brackets.
///
/// - `[^...]` is identical to `[!...]`.
///
/// - The metacharacters `?`, `*`, `[`, `]` can be matched by using brackets
/// (e.g. `[?]`). When a `]` occurs immediately following `[` or `[!` then it
/// is interpreted as being part of, rather then ending, the character set, so
Expand Down Expand Up @@ -671,7 +673,7 @@ impl Pattern {
}
}
'[' => {
if i + 4 <= chars.len() && chars[i + 1] == '!' {
if i + 4 <= chars.len() && (chars[i + 1] == '!' || chars[i + 1] == '^') {
match chars[i + 3..].iter().position(|x| *x == ']') {
None => (),
Some(j) => {
Expand All @@ -682,7 +684,8 @@ impl Pattern {
continue;
}
}
} else if i + 3 <= chars.len() && chars[i + 1] != '!' {
} else if i + 3 <= chars.len() && !(chars[i + 1] == '!' || chars[i + 1] == '^')
{
match chars[i + 2..].iter().position(|x| *x == ']') {
None => (),
Some(j) => {
Expand Down Expand Up @@ -1112,12 +1115,16 @@ mod test {
fn test_unclosed_bracket_errors() {
assert!(Pattern::new("abc[def").unwrap_err().pos == 3);
assert!(Pattern::new("abc[!def").unwrap_err().pos == 3);
assert!(Pattern::new("abc[^def").unwrap_err().pos == 3);
assert!(Pattern::new("abc[").unwrap_err().pos == 3);
assert!(Pattern::new("abc[!").unwrap_err().pos == 3);
assert!(Pattern::new("abc[^").unwrap_err().pos == 3);
assert!(Pattern::new("abc[d").unwrap_err().pos == 3);
assert!(Pattern::new("abc[!d").unwrap_err().pos == 3);
assert!(Pattern::new("abc[^d").unwrap_err().pos == 3);
assert!(Pattern::new("abc[]").unwrap_err().pos == 3);
assert!(Pattern::new("abc[!]").unwrap_err().pos == 3);
assert!(Pattern::new("abc[^]").unwrap_err().pos == 3);
}

#[test]
Expand Down Expand Up @@ -1271,6 +1278,12 @@ mod test {
}
assert!(pat.matches("a_b"));

let pat = Pattern::new("a[^0-9]b").unwrap();
for i in 0..10 {
assert!(!pat.matches(&format!("a{}b", i)));
}
assert!(pat.matches("a_b"));

let pats = ["[a-z123]", "[1a-z23]", "[123a-z]"];
for &p in pats.iter() {
let pat = Pattern::new(p).unwrap();
Expand Down Expand Up @@ -1305,6 +1318,7 @@ mod test {

assert!(Pattern::new("[-]").unwrap().matches("-"));
assert!(!Pattern::new("[!-]").unwrap().matches("-"));
assert!(!Pattern::new("[^-]").unwrap().matches("-"));
}

#[test]
Expand Down Expand Up @@ -1350,7 +1364,8 @@ mod test {
#[test]
fn test_pattern_matches_case_insensitive_range() {
let pat_within = Pattern::new("[a]").unwrap();
let pat_except = Pattern::new("[!a]").unwrap();
let pat_except1 = Pattern::new("[!a]").unwrap();
let pat_except2 = Pattern::new("[^a]").unwrap();

let options_case_insensitive = MatchOptions {
case_sensitive: false,
Expand All @@ -1367,9 +1382,13 @@ mod test {
assert!(pat_within.matches_with("A", options_case_insensitive));
assert!(!pat_within.matches_with("A", options_case_sensitive));

assert!(!pat_except.matches_with("a", options_case_insensitive));
assert!(!pat_except.matches_with("A", options_case_insensitive));
assert!(pat_except.matches_with("A", options_case_sensitive));
assert!(!pat_except1.matches_with("a", options_case_insensitive));
assert!(!pat_except1.matches_with("A", options_case_insensitive));
assert!(pat_except1.matches_with("A", options_case_sensitive));

assert!(!pat_except2.matches_with("a", options_case_insensitive));
assert!(!pat_except2.matches_with("A", options_case_insensitive));
assert!(pat_except2.matches_with("A", options_case_sensitive));
}

#[test]
Expand Down
6 changes: 6 additions & 0 deletions tests/glob-std.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,12 @@ fn main() {
assert_eq!(glob_vec("aa[!a]"), Vec::<PathBuf>::new());
assert_eq!(glob_vec("aa[!abc]"), Vec::<PathBuf>::new());

assert_eq!(glob_vec("aa[^b]"), vec!(PathBuf::from("aaa")));
assert_eq!(glob_vec("aa[^bcd]"), vec!(PathBuf::from("aaa")));
assert_eq!(glob_vec("a[^bcd]a"), vec!(PathBuf::from("aaa")));
assert_eq!(glob_vec("aa[^a]"), Vec::<PathBuf>::new());
assert_eq!(glob_vec("aa[^abc]"), Vec::<PathBuf>::new());

assert_eq!(
glob_vec("bbb/specials/[[]"),
vec!(PathBuf::from("bbb/specials/["))
Expand Down

0 comments on commit 12c5081

Please sign in to comment.