diff --git a/crates/fnmatch/src/lib.rs b/crates/fnmatch/src/lib.rs index 8f7d66dc8..2bedc1073 100644 --- a/crates/fnmatch/src/lib.rs +++ b/crates/fnmatch/src/lib.rs @@ -27,6 +27,9 @@ enum Fragment { MatchOne, /// `*` + MatchAnyExceptForwardSlash, + + /// `**` MatchAny, /// `\` @@ -79,6 +82,10 @@ impl<'a> StringWalker<'a> { self.index += much; } + pub fn peek(&self) -> Option { + self.data.get(self.index..self.index + 1).and_then(|s| s.chars().nth(0)) + } + /// Find next occurrence of the character, and substring up to it pub fn substring_to(&self, c: char) -> Option<&'a str> { // Clone ourselves and search that iterator. @@ -161,7 +168,11 @@ fn fragments_from_string(s: &str) -> Result, Error> { while let Some(ch) = walker.next() { let next_token = match ch { '?' => Some(Fragment::MatchOne), - '*' => Some(Fragment::MatchAny), + '*' if walker.peek() == Some('*') => { + walker.eat(1); + Some(Fragment::MatchAny) + } + '*' => Some(Fragment::MatchAnyExceptForwardSlash), '\\' => Some(Fragment::BackSlash), '/' => Some(Fragment::ForwardSlash), '.' => Some(Fragment::Dot), @@ -211,7 +222,8 @@ fn fragment_to_regex_str(fragment: &Fragment) -> (String, Vec) { let mut groups = vec![]; let string = match fragment { Fragment::MatchOne => ".".into(), - Fragment::MatchAny => "[^\\/]*".into(), + Fragment::MatchAnyExceptForwardSlash => "[^\\/]*".into(), + Fragment::MatchAny => ".*".into(), Fragment::BackSlash => "\\".into(), Fragment::ForwardSlash => "\\/".into(), Fragment::Dot => "\\.".into(), @@ -311,4 +323,10 @@ pub mod path_tests { let wide = k.match_path("/usr/lib/modules/6.6.67-51.kvm/kernel/net/netfilter/nft_hash.ko.zst"); assert!(wide.is_none()); } + + #[test] + fn test_match_any_regex() { + let pattern = "/usr/share/fonts/**/*.ttf".parse::().unwrap(); + assert_eq!(pattern.regex.as_str(), r#"^\/usr\/share\/fonts\/.*\/[^\/]*\.ttf$"#); + } }