Skip to content

Commit dd49238

Browse files
committed
Merge pull request #15 from blaenk/recursive
implement recursive wildstars
2 parents 2f29bba + 06f26c7 commit dd49238

File tree

2 files changed

+54
-10
lines changed

2 files changed

+54
-10
lines changed

src/lib.rs

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ use std::io::fs::{mod, PathExtensions};
3030
use std::path::is_sep;
3131
use std::string::String;
3232

33-
use PatternToken::{Char, AnyChar, AnySequence, AnyWithin, AnyExcept};
33+
use PatternToken::{Char, AnyChar, AnySequence, AnyRecursiveSequence, AnyWithin, AnyExcept};
3434
use CharSpecifier::{SingleChar, CharRange};
3535
use MatchResult::{Match, SubPatternDoesntMatch, EntirePatternDoesntMatch};
3636

@@ -193,6 +193,7 @@ enum PatternToken {
193193
Char(char),
194194
AnyChar,
195195
AnySequence,
196+
AnyRecursiveSequence,
196197
AnyWithin(Vec<CharSpecifier> ),
197198
AnyExcept(Vec<CharSpecifier> )
198199
}
@@ -220,6 +221,12 @@ impl Pattern {
220221
/// of characters, as ordered by Unicode, so e.g. `[0-9]` specifies any
221222
/// character between 0 and 9 inclusive.
222223
///
224+
/// A sequence of two `*` characters, `**`, acts like a single `*` except
225+
/// that it also matches path separators, making it useful for matching
226+
/// on arbitrary subdirectories.
227+
///
228+
/// A sequence of more than two consecutive `*` characters is treated literally.
229+
///
223230
/// The metacharacters `?`, `*`, `[`, `]` can be matched by using brackets
224231
/// (e.g. `[?]`). When a `]` occurs immediately following `[` or `[!` then
225232
/// it is interpreted as being part of, rather then ending, the character
@@ -242,11 +249,23 @@ impl Pattern {
242249
i += 1;
243250
}
244251
'*' => {
245-
// *, **, ***, ****, ... are all equivalent
252+
let old = i;
253+
246254
while i < chars.len() && chars[i] == '*' {
247255
i += 1;
248256
}
249-
tokens.push(AnySequence);
257+
258+
let count = i - old;
259+
260+
if count > 2 {
261+
for _ in range(0u, count) {
262+
tokens.push(Char('*'));
263+
}
264+
} else if count == 2 {
265+
tokens.push(AnyRecursiveSequence);
266+
} else {
267+
tokens.push(AnySequence);
268+
}
250269
}
251270
'[' => {
252271

@@ -364,7 +383,7 @@ impl Pattern {
364383

365384
for (ti, token) in self.tokens.slice_from(i).iter().enumerate() {
366385
match *token {
367-
AnySequence => {
386+
AnySequence | AnyRecursiveSequence => {
368387
loop {
369388
match self.matches_from(prev_char.get(), file, i + ti + 1, options) {
370389
SubPatternDoesntMatch => (), // keep trying
@@ -376,9 +395,12 @@ impl Pattern {
376395
Some(pair) => pair
377396
};
378397

379-
if require_literal(c) {
380-
return SubPatternDoesntMatch;
398+
if let AnySequence = *token {
399+
if require_literal(c) {
400+
return SubPatternDoesntMatch;
401+
}
381402
}
403+
382404
prev_char.set(Some(c));
383405
file = next;
384406
}
@@ -408,7 +430,7 @@ impl Pattern {
408430
Char(c2) => {
409431
chars_eq(c, c2, options.case_sensitive)
410432
}
411-
AnySequence => {
433+
AnySequence | AnyRecursiveSequence => {
412434
unreachable!()
413435
}
414436
};
@@ -628,10 +650,9 @@ mod test {
628650
}
629651

630652
#[test]
631-
fn test_wildcard_optimizations() {
632-
assert!(Pattern::new("a*b").matches("a___b"));
653+
fn test_wildcards() {
654+
assert!(Pattern::new("a*b").matches("a_b"));
633655
assert!(Pattern::new("a**b").matches("a___b"));
634-
assert!(Pattern::new("a***b").matches("a___b"));
635656
assert!(Pattern::new("a*b*c").matches("abc"));
636657
assert!(!Pattern::new("a*b*c").matches("abcd"));
637658
assert!(Pattern::new("a*b*c").matches("a_b_c"));
@@ -642,6 +663,16 @@ mod test {
642663
assert!(Pattern::new("a*b[xyz]c*d").matches("abxcdbxcddd"));
643664
}
644665

666+
#[test]
667+
fn test_recursive_wildstars() {
668+
let pat = Pattern::new("some/**/needle.txt");
669+
assert!(pat.matches("some/one/needle.txt"));
670+
assert!(pat.matches("some/one/two/needle.txt"));
671+
assert!(pat.matches("some/other/needle.txt"));
672+
// more than 2 consecutive wildcards and they're all treated literally
673+
assert!(Pattern::new("a***b").matches("a***b"));
674+
}
675+
645676
#[test]
646677
fn test_lots_of_files() {
647678
// this is a good test because it touches lots of differently named files

tests/glob-std.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,19 @@ fn main() {
7171
mk_file("xyz/y", false);
7272
mk_file("xyz/z", false);
7373

74+
mk_file("r", true);
75+
mk_file("r/one", true);
76+
mk_file("r/one/a.md", false);
77+
mk_file("r/two", true);
78+
mk_file("r/two/b.md", false);
79+
mk_file("r/three", true);
80+
mk_file("r/three/c.md", false);
81+
82+
assert_eq!(glob_vec("r/**/*.md"), vec!(
83+
abs_path("r/one/a.md"),
84+
abs_path("r/three/c.md"),
85+
abs_path("r/two/b.md")));
86+
7487
assert_eq!(glob_vec(""), Vec::new());
7588
assert_eq!(glob_vec("."), vec!(os::getcwd().unwrap()));
7689
assert_eq!(glob_vec(".."), vec!(os::getcwd().unwrap().join("..")));

0 commit comments

Comments
 (0)