1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
12 * Support for matching file paths against Unix shell style patterns.
14 * The `glob` and `glob_with` functions, in concert with the `Paths`
15 * type, allow querying the filesystem for all files that match a particular
16 * pattern - just like the libc `glob` function (for an example see the `glob`
17 * documentation). The methods on the `Pattern` type provide functionality
18 * for checking if individual paths match a particular pattern - in a similar
19 * manner to the libc `fnmatch` function
21 * For consistency across platforms, and for Windows support, this module
22 * is implemented entirely in Rust rather than deferring to the libc
23 * `glob`/`fnmatch` functions.
26 #![crate_name = "glob"]
27 #![deprecated = "This is now a cargo package located at: \
28 https://github.com/rust-lang/glob"]
29 #![crate_type = "rlib"]
30 #![crate_type = "dylib"]
31 #![license = "MIT/ASL2"]
32 #![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
33 html_favicon_url = "http://www.rust-lang.org/favicon.ico",
34 html_root_url = "http://doc.rust-lang.org/master/",
35 html_playground_url = "http://play.rust-lang.org/")]
39 use std::{cmp, os, path};
40 use std::io::fs::PathExtensions;
42 use std::path::is_sep;
43 use std::string::String;
46 * An iterator that yields Paths from the filesystem that match a particular
47 * pattern - see the `glob` function for more details.
50 dir_patterns: Vec<Pattern>,
52 options: MatchOptions,
53 todo: Vec<(Path,uint)>,
57 /// Return an iterator that produces all the Paths that match the given pattern,
58 /// which may be absolute or relative to the current working directory.
60 /// This method uses the default match options and is equivalent to calling
61 /// `glob_with(pattern, MatchOptions::new())`. Use `glob_with` directly if you
62 /// want to use non-default match options.
66 /// Consider a directory `/media/pictures` containing only the files `kittens.jpg`,
67 /// `puppies.jpg` and `hamsters.gif`:
70 /// # #![allow(deprecated)]
73 /// for path in glob("/media/pictures/*.jpg") {
74 /// println!("{}", path.display());
78 /// The above code will print:
81 /// /media/pictures/kittens.jpg
82 /// /media/pictures/puppies.jpg
85 pub fn glob(pattern: &str) -> Paths {
86 glob_with(pattern, MatchOptions::new())
90 * Return an iterator that produces all the Paths that match the given pattern,
91 * which may be absolute or relative to the current working directory.
93 * This function accepts Unix shell style patterns as described by `Pattern::new(..)`.
94 * The options given are passed through unchanged to `Pattern::matches_with(..)` with
95 * the exception that `require_literal_separator` is always set to `true` regardless of the
96 * value passed to this function.
98 * Paths are yielded in alphabetical order, as absolute paths.
100 pub fn glob_with(pattern: &str, options: MatchOptions) -> Paths {
102 fn check_windows_verbatim(p: &Path) -> bool { path::windows::is_verbatim(p) }
104 fn check_windows_verbatim(_: &Path) -> bool { false }
106 // calculate root this way to handle volume-relative Windows paths correctly
107 let mut root = os::getcwd();
108 let pat_root = Path::new(pattern).root_path();
109 if pat_root.is_some() {
110 if check_windows_verbatim(pat_root.as_ref().unwrap()) {
111 // FIXME: How do we want to handle verbatim paths? I'm inclined to return nothing,
112 // since we can't very well find all UNC shares with a 1-letter server name.
114 dir_patterns: Vec::new(),
120 root.push(pat_root.as_ref().unwrap());
123 let root_len = pat_root.map_or(0u, |p| p.as_vec().len());
124 let dir_patterns = pattern.slice_from(cmp::min(root_len, pattern.len()))
125 .split_terminator(is_sep)
126 .map(|s| Pattern::new(s))
127 .collect::<Vec<Pattern>>();
128 let require_dir = pattern.chars().next_back().map(is_sep) == Some(true);
130 let mut todo = Vec::new();
131 if dir_patterns.len() > 0 {
132 // Shouldn't happen, but we're using -1 as a special index.
133 assert!(dir_patterns.len() < -1 as uint);
135 fill_todo(&mut todo, dir_patterns.as_slice(), 0, &root, options);
139 dir_patterns: dir_patterns,
140 require_dir: require_dir,
146 impl Iterator<Path> for Paths {
148 fn next(&mut self) -> Option<Path> {
150 if self.dir_patterns.is_empty() || self.todo.is_empty() {
154 let (path,idx) = self.todo.pop().unwrap();
155 // idx -1: was already checked by fill_todo, maybe path was '.' or
156 // '..' that we can't match here because of normalization.
157 if idx == -1 as uint {
158 if self.require_dir && !path.is_dir() { continue; }
161 let ref pattern = self.dir_patterns[idx];
163 if pattern.matches_with(match path.filename_str() {
164 // this ugly match needs to go here to avoid a borrowck error
166 // FIXME (#9639): How do we handle non-utf8 filenames? Ignore them for now
167 // Ideally we'd still match them against a *
172 if idx == self.dir_patterns.len() - 1 {
173 // it is not possible for a pattern to match a directory *AND* its children
174 // so we don't need to check the children
176 if !self.require_dir || path.is_dir() {
180 fill_todo(&mut self.todo, self.dir_patterns.as_slice(),
181 idx + 1, &path, self.options);
189 fn list_dir_sorted(path: &Path) -> Option<Vec<Path>> {
190 match fs::readdir(path) {
191 Ok(mut children) => {
192 children.sort_by(|p1, p2| p2.filename().cmp(&p1.filename()));
193 Some(children.into_iter().collect())
200 * A compiled Unix shell style pattern.
202 #[deriving(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
204 tokens: Vec<PatternToken>,
207 #[deriving(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
212 AnyWithin(Vec<CharSpecifier> ),
213 AnyExcept(Vec<CharSpecifier> )
216 #[deriving(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
219 CharRange(char, char)
222 #[deriving(PartialEq)]
225 SubPatternDoesntMatch,
226 EntirePatternDoesntMatch
232 * This function compiles Unix shell style patterns: `?` matches any single
233 * character, `*` matches any (possibly empty) sequence of characters and
234 * `[...]` matches any character inside the brackets, unless the first
235 * character is `!` in which case it matches any character except those
236 * between the `!` and the `]`. Character sequences can also specify ranges
237 * of characters, as ordered by Unicode, so e.g. `[0-9]` specifies any
238 * character between 0 and 9 inclusive.
240 * The metacharacters `?`, `*`, `[`, `]` can be matched by using brackets
241 * (e.g. `[?]`). When a `]` occurs immediately following `[` or `[!` then
242 * it is interpreted as being part of, rather then ending, the character
243 * set, so `]` and NOT `]` can be matched by `[]]` and `[!]]` respectively.
244 * The `-` character can be specified inside a character sequence pattern by
245 * placing it at the start or the end, e.g. `[abc-]`.
247 * When a `[` does not have a closing `]` before the end of the string then
248 * the `[` will be treated literally.
250 pub fn new(pattern: &str) -> Pattern {
252 let chars = pattern.chars().collect::<Vec<_>>();
253 let mut tokens = Vec::new();
256 while i < chars.len() {
259 tokens.push(AnyChar);
263 // *, **, ***, ****, ... are all equivalent
264 while i < chars.len() && chars[i] == '*' {
267 tokens.push(AnySequence);
271 if i <= chars.len() - 4 && chars[i + 1] == '!' {
272 match chars.slice_from(i + 3).position_elem(&']') {
275 let chars = chars.slice(i + 2, i + 3 + j);
276 let cs = parse_char_specifiers(chars);
277 tokens.push(AnyExcept(cs));
283 else if i <= chars.len() - 3 && chars[i + 1] != '!' {
284 match chars.slice_from(i + 2).position_elem(&']') {
287 let cs = parse_char_specifiers(chars.slice(i + 1, i + 2 + j));
288 tokens.push(AnyWithin(cs));
295 // if we get here then this is not a valid range pattern
296 tokens.push(Char('['));
300 tokens.push(Char(c));
306 Pattern { tokens: tokens }
310 * Escape metacharacters within the given string by surrounding them in
311 * brackets. The resulting string will, when compiled into a `Pattern`,
312 * match the input string and nothing else.
314 pub fn escape(s: &str) -> String {
315 let mut escaped = String::new();
318 // note that ! does not need escaping because it is only special inside brackets
319 '?' | '*' | '[' | ']' => {
320 escaped.push_char('[');
321 escaped.push_char(c);
322 escaped.push_char(']');
325 escaped.push_char(c);
333 * Return if the given `str` matches this `Pattern` using the default
334 * match options (i.e. `MatchOptions::new()`).
341 * assert!(Pattern::new("c?t").matches("cat"));
342 * assert!(Pattern::new("k[!e]tteh").matches("kitteh"));
343 * assert!(Pattern::new("d*g").matches("doog"));
346 pub fn matches(&self, str: &str) -> bool {
347 self.matches_with(str, MatchOptions::new())
351 * Return if the given `Path`, when converted to a `str`, matches this `Pattern`
352 * using the default match options (i.e. `MatchOptions::new()`).
354 pub fn matches_path(&self, path: &Path) -> bool {
355 // FIXME (#9639): This needs to handle non-utf8 paths
356 path.as_str().map_or(false, |s| {
362 * Return if the given `str` matches this `Pattern` using the specified match options.
364 pub fn matches_with(&self, str: &str, options: MatchOptions) -> bool {
365 self.matches_from(None, str, 0, options) == Match
369 * Return if the given `Path`, when converted to a `str`, matches this `Pattern`
370 * using the specified match options.
372 pub fn matches_path_with(&self, path: &Path, options: MatchOptions) -> bool {
373 // FIXME (#9639): This needs to handle non-utf8 paths
374 path.as_str().map_or(false, |s| {
375 self.matches_with(s, options)
379 fn matches_from(&self,
380 prev_char: Option<char>,
383 options: MatchOptions) -> MatchResult {
385 let prev_char = Cell::new(prev_char);
387 let require_literal = |c| {
388 (options.require_literal_separator && is_sep(c)) ||
389 (options.require_literal_leading_dot && c == '.'
390 && is_sep(prev_char.get().unwrap_or('/')))
393 for (ti, token) in self.tokens.slice_from(i).iter().enumerate() {
397 match self.matches_from(prev_char.get(), file, i + ti + 1, options) {
398 SubPatternDoesntMatch => (), // keep trying
403 return EntirePatternDoesntMatch;
406 let (some_c, next) = file.slice_shift_char();
407 if require_literal(some_c.unwrap()) {
408 return SubPatternDoesntMatch;
410 prev_char.set(some_c);
416 return EntirePatternDoesntMatch;
419 let (some_c, next) = file.slice_shift_char();
420 let c = some_c.unwrap();
421 let matches = match *token {
425 AnyWithin(ref specifiers) => {
426 !require_literal(c) &&
427 in_char_specifiers(specifiers.as_slice(),
431 AnyExcept(ref specifiers) => {
432 !require_literal(c) &&
433 !in_char_specifiers(specifiers.as_slice(),
438 chars_eq(c, c2, options.case_sensitive)
445 return SubPatternDoesntMatch;
447 prev_char.set(some_c);
456 SubPatternDoesntMatch
462 // Fills `todo` with paths under `path` to be matched by `patterns[idx]`,
463 // special-casing patterns to match `.` and `..`, and avoiding `readdir()`
464 // calls when there are no metacharacters in the pattern.
465 fn fill_todo(todo: &mut Vec<(Path, uint)>, patterns: &[Pattern], idx: uint, path: &Path,
466 options: MatchOptions) {
467 // convert a pattern that's just many Char(_) to a string
468 fn pattern_as_str(pattern: &Pattern) -> Option<String> {
469 let mut s = String::new();
470 for token in pattern.tokens.iter() {
472 Char(c) => s.push_char(c),
479 let add = |todo: &mut Vec<_>, next_path: Path| {
480 if idx + 1 == patterns.len() {
481 // We know it's good, so don't make the iterator match this path
482 // against the pattern again. In particular, it can't match
483 // . or .. globs since these never show up as path components.
484 todo.push((next_path, -1 as uint));
486 fill_todo(todo, patterns, idx + 1, &next_path, options);
490 let pattern = &patterns[idx];
492 match pattern_as_str(pattern) {
494 // This pattern component doesn't have any metacharacters, so we
495 // don't need to read the current directory to know where to
496 // continue. So instead of passing control back to the iterator,
497 // we can just check for that one entry and potentially recurse
499 let special = "." == s.as_slice() || ".." == s.as_slice();
500 let next_path = path.join(s.as_slice());
501 if (special && path.is_dir()) || (!special && next_path.exists()) {
502 add(todo, next_path);
506 match list_dir_sorted(path) {
508 todo.extend(entries.into_iter().map(|x|(x, idx)));
510 // Matching the special directory entries . and .. that refer to
511 // the current and parent directory respectively requires that
512 // the pattern has a leading dot, even if the `MatchOptions` field
513 // `require_literal_leading_dot` is not set.
514 if pattern.tokens.len() > 0 && pattern.tokens[0] == Char('.') {
515 for &special in [".", ".."].iter() {
516 if pattern.matches_with(special, options) {
517 add(todo, path.join(special));
528 fn parse_char_specifiers(s: &[char]) -> Vec<CharSpecifier> {
529 let mut cs = Vec::new();
532 if i + 3 <= s.len() && s[i + 1] == '-' {
533 cs.push(CharRange(s[i], s[i + 2]));
536 cs.push(SingleChar(s[i]));
543 fn in_char_specifiers(specifiers: &[CharSpecifier], c: char, options: MatchOptions) -> bool {
545 for &specifier in specifiers.iter() {
548 if chars_eq(c, sc, options.case_sensitive) {
552 CharRange(start, end) => {
554 // FIXME: work with non-ascii chars properly (issue #1347)
555 if !options.case_sensitive && c.is_ascii() && start.is_ascii() && end.is_ascii() {
557 let start = start.to_ascii().to_lowercase();
558 let end = end.to_ascii().to_lowercase();
560 let start_up = start.to_uppercase();
561 let end_up = end.to_uppercase();
563 // only allow case insensitive matching when
564 // both start and end are within a-z or A-Z
565 if start != start_up && end != end_up {
566 let start = start.to_char();
567 let end = end.to_char();
568 let c = c.to_ascii().to_lowercase().to_char();
569 if c >= start && c <= end {
575 if c >= start && c <= end {
585 /// A helper function to determine if two chars are (possibly case-insensitively) equal.
586 fn chars_eq(a: char, b: char, case_sensitive: bool) -> bool {
587 if cfg!(windows) && path::windows::is_sep(a) && path::windows::is_sep(b) {
589 } else if !case_sensitive && a.is_ascii() && b.is_ascii() {
590 // FIXME: work with non-ascii chars properly (issue #1347)
591 a.to_ascii().eq_ignore_case(b.to_ascii())
598 * Configuration options to modify the behaviour of `Pattern::matches_with(..)`
600 #[deriving(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
601 pub struct MatchOptions {
604 * Whether or not patterns should be matched in a case-sensitive manner. This
605 * currently only considers upper/lower case relationships between ASCII characters,
606 * but in future this might be extended to work with Unicode.
608 pub case_sensitive: bool,
611 * If this is true then path-component separator characters (e.g. `/` on Posix)
612 * must be matched by a literal `/`, rather than by `*` or `?` or `[...]`
614 pub require_literal_separator: bool,
617 * If this is true then paths that contain components that start with a `.` will
618 * not match unless the `.` appears literally in the pattern: `*`, `?` or `[...]`
619 * will not match. This is useful because such files are conventionally considered
620 * hidden on Unix systems and it might be desirable to skip them when listing files.
622 pub require_literal_leading_dot: bool
628 * Constructs a new `MatchOptions` with default field values. This is used
629 * when calling functions that do not take an explicit `MatchOptions` parameter.
631 * This function always returns this value:
635 * case_sensitive: true,
636 * require_literal_separator: false.
637 * require_literal_leading_dot: false
641 pub fn new() -> MatchOptions {
643 case_sensitive: true,
644 require_literal_separator: false,
645 require_literal_leading_dot: false
654 use super::{glob, Pattern, MatchOptions};
657 fn test_absolute_pattern() {
658 // assume that the filesystem is not empty!
659 assert!(glob("/*").next().is_some());
660 assert!(glob("//").next().is_some());
662 // check windows absolute paths with host/device components
663 let root_with_device = os::getcwd().root_path().unwrap().join("*");
664 // FIXME (#9639): This needs to handle non-utf8 paths
665 assert!(glob(root_with_device.as_str().unwrap()).next().is_some());
669 fn test_wildcard_optimizations() {
670 assert!(Pattern::new("a*b").matches("a___b"));
671 assert!(Pattern::new("a**b").matches("a___b"));
672 assert!(Pattern::new("a***b").matches("a___b"));
673 assert!(Pattern::new("a*b*c").matches("abc"));
674 assert!(!Pattern::new("a*b*c").matches("abcd"));
675 assert!(Pattern::new("a*b*c").matches("a_b_c"));
676 assert!(Pattern::new("a*b*c").matches("a___b___c"));
677 assert!(Pattern::new("abc*abc*abc").matches("abcabcabcabcabcabcabc"));
678 assert!(!Pattern::new("abc*abc*abc").matches("abcabcabcabcabcabcabca"));
679 assert!(Pattern::new("a*a*a*a*a*a*a*a*a").matches("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
680 assert!(Pattern::new("a*b[xyz]c*d").matches("abxcdbxcddd"));
684 #[ignore(cfg(windows))] // FIXME (#9406)
685 fn test_lots_of_files() {
686 // this is a good test because it touches lots of differently named files
687 glob("/*/*/*/*").skip(10000).next();
691 fn test_range_pattern() {
693 let pat = Pattern::new("a[0-9]b");
694 for i in range(0u, 10) {
695 assert!(pat.matches(format!("a{}b", i).as_slice()));
697 assert!(!pat.matches("a_b"));
699 let pat = Pattern::new("a[!0-9]b");
700 for i in range(0u, 10) {
701 assert!(!pat.matches(format!("a{}b", i).as_slice()));
703 assert!(pat.matches("a_b"));
705 let pats = ["[a-z123]", "[1a-z23]", "[123a-z]"];
706 for &p in pats.iter() {
707 let pat = Pattern::new(p);
708 for c in "abcdefghijklmnopqrstuvwxyz".chars() {
709 assert!(pat.matches(c.to_string().as_slice()));
711 for c in "ABCDEFGHIJKLMNOPQRSTUVWXYZ".chars() {
712 let options = MatchOptions {case_sensitive: false, .. MatchOptions::new()};
713 assert!(pat.matches_with(c.to_string().as_slice(), options));
715 assert!(pat.matches("1"));
716 assert!(pat.matches("2"));
717 assert!(pat.matches("3"));
720 let pats = ["[abc-]", "[-abc]", "[a-c-]"];
721 for &p in pats.iter() {
722 let pat = Pattern::new(p);
723 assert!(pat.matches("a"));
724 assert!(pat.matches("b"));
725 assert!(pat.matches("c"));
726 assert!(pat.matches("-"));
727 assert!(!pat.matches("d"));
730 let pat = Pattern::new("[2-1]");
731 assert!(!pat.matches("1"));
732 assert!(!pat.matches("2"));
734 assert!(Pattern::new("[-]").matches("-"));
735 assert!(!Pattern::new("[!-]").matches("-"));
739 fn test_unclosed_bracket() {
740 // unclosed `[` should be treated literally
741 assert!(Pattern::new("abc[def").matches("abc[def"));
742 assert!(Pattern::new("abc[!def").matches("abc[!def"));
743 assert!(Pattern::new("abc[").matches("abc["));
744 assert!(Pattern::new("abc[!").matches("abc[!"));
745 assert!(Pattern::new("abc[d").matches("abc[d"));
746 assert!(Pattern::new("abc[!d").matches("abc[!d"));
747 assert!(Pattern::new("abc[]").matches("abc[]"));
748 assert!(Pattern::new("abc[!]").matches("abc[!]"));
752 fn test_pattern_matches() {
753 let txt_pat = Pattern::new("*hello.txt");
754 assert!(txt_pat.matches("hello.txt"));
755 assert!(txt_pat.matches("gareth_says_hello.txt"));
756 assert!(txt_pat.matches("some/path/to/hello.txt"));
757 assert!(txt_pat.matches("some\\path\\to\\hello.txt"));
758 assert!(txt_pat.matches("/an/absolute/path/to/hello.txt"));
759 assert!(!txt_pat.matches("hello.txt-and-then-some"));
760 assert!(!txt_pat.matches("goodbye.txt"));
762 let dir_pat = Pattern::new("*some/path/to/hello.txt");
763 assert!(dir_pat.matches("some/path/to/hello.txt"));
764 assert!(dir_pat.matches("a/bigger/some/path/to/hello.txt"));
765 assert!(!dir_pat.matches("some/path/to/hello.txt-and-then-some"));
766 assert!(!dir_pat.matches("some/other/path/to/hello.txt"));
770 fn test_pattern_escape() {
771 let s = "_[_]_?_*_!_";
772 assert_eq!(Pattern::escape(s), "_[[]_[]]_[?]_[*]_!_".to_string());
773 assert!(Pattern::new(Pattern::escape(s).as_slice()).matches(s));
777 fn test_pattern_matches_case_insensitive() {
779 let pat = Pattern::new("aBcDeFg");
780 let options = MatchOptions {
781 case_sensitive: false,
782 require_literal_separator: false,
783 require_literal_leading_dot: false
786 assert!(pat.matches_with("aBcDeFg", options));
787 assert!(pat.matches_with("abcdefg", options));
788 assert!(pat.matches_with("ABCDEFG", options));
789 assert!(pat.matches_with("AbCdEfG", options));
793 fn test_pattern_matches_case_insensitive_range() {
795 let pat_within = Pattern::new("[a]");
796 let pat_except = Pattern::new("[!a]");
798 let options_case_insensitive = MatchOptions {
799 case_sensitive: false,
800 require_literal_separator: false,
801 require_literal_leading_dot: false
803 let options_case_sensitive = MatchOptions {
804 case_sensitive: true,
805 require_literal_separator: false,
806 require_literal_leading_dot: false
809 assert!(pat_within.matches_with("a", options_case_insensitive));
810 assert!(pat_within.matches_with("A", options_case_insensitive));
811 assert!(!pat_within.matches_with("A", options_case_sensitive));
813 assert!(!pat_except.matches_with("a", options_case_insensitive));
814 assert!(!pat_except.matches_with("A", options_case_insensitive));
815 assert!(pat_except.matches_with("A", options_case_sensitive));
819 fn test_pattern_matches_require_literal_separator() {
821 let options_require_literal = MatchOptions {
822 case_sensitive: true,
823 require_literal_separator: true,
824 require_literal_leading_dot: false
826 let options_not_require_literal = MatchOptions {
827 case_sensitive: true,
828 require_literal_separator: false,
829 require_literal_leading_dot: false
832 assert!(Pattern::new("abc/def").matches_with("abc/def", options_require_literal));
833 assert!(!Pattern::new("abc?def").matches_with("abc/def", options_require_literal));
834 assert!(!Pattern::new("abc*def").matches_with("abc/def", options_require_literal));
835 assert!(!Pattern::new("abc[/]def").matches_with("abc/def", options_require_literal));
837 assert!(Pattern::new("abc/def").matches_with("abc/def", options_not_require_literal));
838 assert!(Pattern::new("abc?def").matches_with("abc/def", options_not_require_literal));
839 assert!(Pattern::new("abc*def").matches_with("abc/def", options_not_require_literal));
840 assert!(Pattern::new("abc[/]def").matches_with("abc/def", options_not_require_literal));
844 fn test_pattern_matches_require_literal_leading_dot() {
846 let options_require_literal_leading_dot = MatchOptions {
847 case_sensitive: true,
848 require_literal_separator: false,
849 require_literal_leading_dot: true
851 let options_not_require_literal_leading_dot = MatchOptions {
852 case_sensitive: true,
853 require_literal_separator: false,
854 require_literal_leading_dot: false
857 let f = |options| Pattern::new("*.txt").matches_with(".hello.txt", options);
858 assert!(f(options_not_require_literal_leading_dot));
859 assert!(!f(options_require_literal_leading_dot));
861 let f = |options| Pattern::new(".*.*").matches_with(".hello.txt", options);
862 assert!(f(options_not_require_literal_leading_dot));
863 assert!(f(options_require_literal_leading_dot));
865 let f = |options| Pattern::new("aaa/bbb/*").matches_with("aaa/bbb/.ccc", options);
866 assert!(f(options_not_require_literal_leading_dot));
867 assert!(!f(options_require_literal_leading_dot));
869 let f = |options| Pattern::new("aaa/bbb/*").matches_with("aaa/bbb/c.c.c.", options);
870 assert!(f(options_not_require_literal_leading_dot));
871 assert!(f(options_require_literal_leading_dot));
873 let f = |options| Pattern::new("aaa/bbb/.*").matches_with("aaa/bbb/.ccc", options);
874 assert!(f(options_not_require_literal_leading_dot));
875 assert!(f(options_require_literal_leading_dot));
877 let f = |options| Pattern::new("aaa/?bbb").matches_with("aaa/.bbb", options);
878 assert!(f(options_not_require_literal_leading_dot));
879 assert!(!f(options_require_literal_leading_dot));
881 let f = |options| Pattern::new("aaa/[.]bbb").matches_with("aaa/.bbb", options);
882 assert!(f(options_not_require_literal_leading_dot));
883 assert!(!f(options_require_literal_leading_dot));
887 fn test_matches_path() {
888 // on windows, (Path::new("a/b").as_str().unwrap() == "a\\b"), so this
889 // tests that / and \ are considered equivalent on windows
890 assert!(Pattern::new("a/b").matches_path(&Path::new("a/b")));