From 12c508143e80cb7fd63b1e721b5649621b16a6e2 Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Mon, 11 Mar 2024 18:21:15 +0100 Subject: [PATCH] add caret negation (`[^...]`) in addition to exclamation mark --- src/lib.rs | 31 +++++++++++++++++++++++++------ tests/glob-std.rs | 6 ++++++ 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index fef8090..4fd3002 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 @@ -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) => { @@ -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) => { @@ -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] @@ -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(); @@ -1305,6 +1318,7 @@ mod test { assert!(Pattern::new("[-]").unwrap().matches("-")); assert!(!Pattern::new("[!-]").unwrap().matches("-")); + assert!(!Pattern::new("[^-]").unwrap().matches("-")); } #[test] @@ -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, @@ -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] diff --git a/tests/glob-std.rs b/tests/glob-std.rs index 4466413..8ee0139 100644 --- a/tests/glob-std.rs +++ b/tests/glob-std.rs @@ -351,6 +351,12 @@ fn main() { assert_eq!(glob_vec("aa[!a]"), Vec::::new()); assert_eq!(glob_vec("aa[!abc]"), Vec::::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::::new()); + assert_eq!(glob_vec("aa[^abc]"), Vec::::new()); + assert_eq!( glob_vec("bbb/specials/[[]"), vec!(PathBuf::from("bbb/specials/["))