diff --git a/.travis.yml b/.travis.yml index a039727..cebc669 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ language: rust rust: + - 1.23.0 - stable - beta - nightly diff --git a/Cargo.toml b/Cargo.toml index 9b77144..176e306 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "glob" -version = "0.2.11" +version = "0.3.0" authors = ["The Rust Project Developers"] license = "MIT/Apache-2.0" homepage = "https://github.com/rust-lang/glob" repository = "https://github.com/rust-lang/glob" -documentation = "https://doc.rust-lang.org/glob" +documentation = "https://docs.rs/glob/0.3.0" description = """ Support for matching file paths against Unix shell style patterns. """ @@ -14,3 +14,4 @@ categories = ["filesystem"] [dev-dependencies] tempdir = "0.3" +doc-comment = "0.3" diff --git a/README.md b/README.md index be88d43..e61a976 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ To use `glob`, add this to your `Cargo.toml`: ```toml [dependencies] -glob = "0.2" +glob = "0.3.0" ``` And add this to your crate root: diff --git a/src/lib.rs b/src/lib.rs index 9b795da..d9f2762 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,33 +48,40 @@ //! require_literal_separator: false, //! require_literal_leading_dot: false, //! }; -//! for entry in glob_with("local/*a*", &options).unwrap() { +//! for entry in glob_with("local/*a*", options).unwrap() { //! if let Ok(path) = entry { //! println!("{:?}", path.display()) //! } //! } //! ``` -#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", - html_favicon_url = "https://www.rust-lang.org/favicon.ico", - html_root_url = "https://doc.rust-lang.org/glob/")] +#![doc( + html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "https://www.rust-lang.org/favicon.ico", + html_root_url = "https://docs.rs/glob/0.3.0" +)] #![deny(missing_docs)] #![cfg_attr(all(test, windows), feature(std_misc))] -#[allow(unused_imports)] -use std::ascii::AsciiExt; +#[cfg(test)] +#[macro_use] +extern crate doc_comment; + +#[cfg(test)] +doctest!("../README.md"); + use std::cmp; +use std::error::Error; use std::fmt; use std::fs; use std::io; -use std::path::{self, Path, PathBuf, Component}; +use std::path::{self, Component, Path, PathBuf}; use std::str::FromStr; -use std::error::Error; -use PatternToken::{Char, AnyChar, AnySequence, AnyRecursiveSequence, AnyWithin}; +use CharSpecifier::{CharRange, SingleChar}; +use MatchResult::{EntirePatternDoesntMatch, Match, SubPatternDoesntMatch}; use PatternToken::AnyExcept; -use CharSpecifier::{SingleChar, CharRange}; -use MatchResult::{Match, SubPatternDoesntMatch, EntirePatternDoesntMatch}; +use PatternToken::{AnyChar, AnyRecursiveSequence, AnySequence, AnyWithin, Char}; /// An iterator that yields `Path`s from the filesystem that match a particular /// pattern. @@ -151,7 +158,7 @@ pub struct Paths { /// ``` /// Paths are yielded in alphabetical order. pub fn glob(pattern: &str) -> Result { - glob_with(pattern, &MatchOptions::new()) + glob_with(pattern, MatchOptions::new()) } /// Return an iterator that produces all the `Path`s that match the given @@ -167,11 +174,7 @@ pub fn glob(pattern: &str) -> Result { /// passed to this function. /// /// Paths are yielded in alphabetical order. -pub fn glob_with(pattern: &str, options: &MatchOptions) - -> Result { - // make sure that the pattern is valid first, else early return with error - let _compiled = try!(Pattern::new(pattern)); - +pub fn glob_with(pattern: &str, options: MatchOptions) -> Result { #[cfg(windows)] fn check_windows_verbatim(p: &Path) -> bool { use std::path::Prefix; @@ -195,11 +198,15 @@ pub fn glob_with(pattern: &str, options: &MatchOptions) p.to_path_buf() } + // make sure that the pattern is valid first, else early return with error + if let Err(err) = Pattern::new(pattern) { + return Err(err); + } + let mut components = Path::new(pattern).components().peekable(); loop { match components.peek() { - Some(&Component::Prefix(..)) | - Some(&Component::RootDir) => { + Some(&Component::Prefix(..)) | Some(&Component::RootDir) => { components.next(); } _ => break, @@ -221,21 +228,20 @@ pub fn glob_with(pattern: &str, options: &MatchOptions) return Ok(Paths { dir_patterns: Vec::new(), require_dir: false, - options: options.clone(), + options, todo: Vec::new(), scope: None, }); } - let scope = root.map(to_scope).unwrap_or_else(|| PathBuf::from(".")); + let scope = root.map_or_else(|| PathBuf::from("."), to_scope); let mut dir_patterns = Vec::new(); - let components = pattern[cmp::min(root_len, pattern.len())..] - .split_terminator(path::is_separator); + let components = + pattern[cmp::min(root_len, pattern.len())..].split_terminator(path::is_separator); for component in components { - let compiled = try!(Pattern::new(component)); - dir_patterns.push(compiled); + dir_patterns.push(Pattern::new(component)?); } if root_len == pattern.len() { @@ -251,10 +257,10 @@ pub fn glob_with(pattern: &str, options: &MatchOptions) let todo = Vec::new(); Ok(Paths { - dir_patterns: dir_patterns, - require_dir: require_dir, - options: options.clone(), - todo: todo, + dir_patterns, + require_dir, + options, + todo, scope: Some(scope), }) } @@ -299,10 +305,12 @@ impl Error for GlobError { impl fmt::Display for GlobError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, - "attempting to read `{}` resulted in an error: {}", - self.path.display(), - self.error) + write!( + f, + "attempting to read `{}` resulted in an error: {}", + self.path.display(), + self.error + ) } } @@ -325,15 +333,11 @@ impl Iterator for Paths { // failing to fill the buffer is an iteration error construction of the // iterator (i.e. glob()) only fails if it fails to compile the Pattern if let Some(scope) = self.scope.take() { - if self.dir_patterns.len() > 0 { + if !self.dir_patterns.is_empty() { // Shouldn't happen, but we're using -1 as a special index. assert!(self.dir_patterns.len() < !0 as usize); - fill_todo(&mut self.todo, - &self.dir_patterns, - 0, - &scope, - &self.options); + fill_todo(&mut self.todo, &self.dir_patterns, 0, &scope, self.options); } } @@ -360,8 +364,9 @@ impl Iterator for Paths { let mut next = idx; // collapse consecutive recursive patterns - while (next + 1) < self.dir_patterns.len() && - self.dir_patterns[next + 1].is_recursive { + while (next + 1) < self.dir_patterns.len() + && self.dir_patterns[next + 1].is_recursive + { next += 1; } @@ -369,11 +374,13 @@ impl Iterator for Paths { // the path is a directory, so it's a match // push this directory's contents - fill_todo(&mut self.todo, - &self.dir_patterns, - next, - &path, - &self.options); + fill_todo( + &mut self.todo, + &self.dir_patterns, + next, + &path, + self.options, + ); if next == self.dir_patterns.len() - 1 { // pattern ends in recursive pattern, so return this @@ -383,26 +390,29 @@ impl Iterator for Paths { // advanced to the next pattern for this path idx = next + 1; } - } else if next != self.dir_patterns.len() - 1 { - // advanced to the next pattern for this path - idx = next + 1; - } else { + } else if next == self.dir_patterns.len() - 1 { // not a directory and it's the last pattern, meaning no // match continue; + } else { + // advanced to the next pattern for this path + idx = next + 1; } } // not recursive, so match normally - if self.dir_patterns[idx].matches_with({ - match path.file_name().and_then(|s| s.to_str()) { - // FIXME (#9639): How do we handle non-utf8 filenames? - // Ignore them for now; ideally we'd still match them - // against a * - None => continue, - Some(x) => x - } - }, &self.options) { + if self.dir_patterns[idx].matches_with( + { + match path.file_name().and_then(|s| s.to_str()) { + // FIXME (#9639): How do we handle non-utf8 filenames? + // Ignore them for now; ideally we'd still match them + // against a * + None => continue, + Some(x) => x, + } + }, + self.options, + ) { if idx == self.dir_patterns.len() - 1 { // it is not possible for a pattern to match a directory // *AND* its children so we don't need to check the @@ -412,8 +422,13 @@ impl Iterator for Paths { return Some(Ok(path)); } } else { - fill_todo(&mut self.todo, &self.dir_patterns, - idx + 1, &path, &self.options); + fill_todo( + &mut self.todo, + &self.dir_patterns, + idx + 1, + &path, + self.options, + ); } } } @@ -439,10 +454,11 @@ impl Error for PatternError { impl fmt::Display for PatternError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, - "Pattern syntax error near position {}: {}", - self.pos, - self.msg) + write!( + f, + "Pattern syntax error near position {}: {}", + self.pos, self.msg + ) } } @@ -488,8 +504,8 @@ impl fmt::Display for Pattern { impl FromStr for Pattern { type Err = PatternError; - fn from_str(s: &str) -> Result { - Pattern::new(s) + fn from_str(s: &str) -> Result { + Self::new(s) } } @@ -516,17 +532,16 @@ enum MatchResult { EntirePatternDoesntMatch, } -const ERROR_WILDCARDS: &'static str = "wildcards are either regular `*` or recursive `**`"; -const ERROR_RECURSIVE_WILDCARDS: &'static str = "recursive wildcards must form a single path \ - component"; -const ERROR_INVALID_RANGE: &'static str = "invalid range pattern"; +const ERROR_WILDCARDS: &str = "wildcards are either regular `*` or recursive `**`"; +const ERROR_RECURSIVE_WILDCARDS: &str = "recursive wildcards must form a single path \ + component"; +const ERROR_INVALID_RANGE: &str = "invalid range pattern"; impl Pattern { /// This function compiles Unix shell style patterns. /// /// An invalid glob pattern will yield a `PatternError`. - pub fn new(pattern: &str) -> Result { - + pub fn new(pattern: &str) -> Result { let chars = pattern.chars().collect::>(); let mut tokens = Vec::new(); let mut is_recursive = false; @@ -561,18 +576,18 @@ impl Pattern { if i < chars.len() && path::is_separator(chars[i]) { i += 1; true - // or the pattern ends here - // this enables the existing globbing mechanism + // or the pattern ends here + // this enables the existing globbing mechanism } else if i == chars.len() { true - // `**` ends in non-separator + // `**` ends in non-separator } else { return Err(PatternError { pos: i, msg: ERROR_RECURSIVE_WILDCARDS, }); } - // `**` begins with non-separator + // `**` begins with non-separator } else { return Err(PatternError { pos: old - 1, @@ -595,7 +610,6 @@ impl Pattern { } } '[' => { - if i + 4 <= chars.len() && chars[i + 1] == '!' { match chars[i + 3..].iter().position(|x| *x == ']') { None => (), @@ -632,10 +646,10 @@ impl Pattern { } } - Ok(Pattern { - tokens: tokens, + Ok(Self { + tokens, original: pattern.to_string(), - is_recursive: is_recursive, + is_recursive, }) } @@ -674,7 +688,7 @@ impl Pattern { /// assert!(Pattern::new("d*g").unwrap().matches("doog")); /// ``` pub fn matches(&self, str: &str) -> bool { - self.matches_with(str, &MatchOptions::new()) + self.matches_with(str, MatchOptions::new()) } /// Return if the given `Path`, when converted to a `str`, matches this @@ -686,29 +700,30 @@ impl Pattern { /// Return if the given `str` matches this `Pattern` using the specified /// match options. - pub fn matches_with(&self, str: &str, options: &MatchOptions) -> bool { + pub fn matches_with(&self, str: &str, options: MatchOptions) -> bool { self.matches_from(true, str.chars(), 0, options) == Match } /// Return if the given `Path`, when converted to a `str`, matches this /// `Pattern` using the specified match options. - pub fn matches_path_with(&self, path: &Path, options: &MatchOptions) -> bool { + pub fn matches_path_with(&self, path: &Path, options: MatchOptions) -> bool { // FIXME (#9639): This needs to handle non-utf8 paths - path.to_str().map_or(false, |s| self.matches_with(s, options)) + path.to_str() + .map_or(false, |s| self.matches_with(s, options)) } /// Access the original glob pattern. - pub fn as_str<'a>(&'a self) -> &'a str { + pub fn as_str(&self) -> &str { &self.original } - fn matches_from(&self, - mut follows_separator: bool, - mut file: std::str::Chars, - i: usize, - options: &MatchOptions) - -> MatchResult { - + fn matches_from( + &self, + mut follows_separator: bool, + mut file: std::str::Chars, + i: usize, + options: MatchOptions, + ) -> MatchResult { for (ti, token) in self.tokens[i..].iter().enumerate() { match *token { AnySequence | AnyRecursiveSequence => { @@ -731,14 +746,19 @@ impl Pattern { follows_separator = path::is_separator(c); match *token { AnyRecursiveSequence if !follows_separator => continue, - AnySequence if options.require_literal_separator && - follows_separator => return SubPatternDoesntMatch, + AnySequence + if options.require_literal_separator && follows_separator => + { + return SubPatternDoesntMatch + } _ => (), } - match self.matches_from(follows_separator, - file.clone(), - i + ti + 1, - options) { + match self.matches_from( + follows_separator, + file.clone(), + i + ti + 1, + options, + ) { SubPatternDoesntMatch => (), // keep trying m => return m, } @@ -754,9 +774,13 @@ impl Pattern { if !match *token { AnyChar | AnyWithin(..) | AnyExcept(..) - if (options.require_literal_separator && is_sep) || - (follows_separator && options.require_literal_leading_dot && - c == '.') => false, + if (options.require_literal_separator && is_sep) + || (follows_separator + && options.require_literal_leading_dot + && c == '.') => + { + false + } AnyChar => true, AnyWithin(ref specifiers) => in_char_specifiers(&specifiers, c, options), AnyExcept(ref specifiers) => !in_char_specifiers(&specifiers, c, options), @@ -782,21 +806,24 @@ impl Pattern { // Fills `todo` with paths under `path` to be matched by `patterns[idx]`, // special-casing patterns to match `.` and `..`, and avoiding `readdir()` // calls when there are no metacharacters in the pattern. -fn fill_todo(todo: &mut Vec>, - patterns: &[Pattern], - idx: usize, - path: &Path, - options: &MatchOptions) { +fn fill_todo( + todo: &mut Vec>, + patterns: &[Pattern], + idx: usize, + path: &Path, + options: MatchOptions, +) { // convert a pattern that's just many Char(_) to a string fn pattern_as_str(pattern: &Pattern) -> Option { let mut s = String::new(); - for token in pattern.tokens.iter() { + for token in &pattern.tokens { match *token { Char(c) => s.push(c), _ => return None, } } - return Some(s); + + Some(s) } let add = |todo: &mut Vec<_>, next_path: PathBuf| { @@ -833,15 +860,15 @@ fn fill_todo(todo: &mut Vec>, None if is_dir => { let dirs = fs::read_dir(path).and_then(|d| { d.map(|e| { - e.map(|e| { - if curdir { - PathBuf::from(e.path().file_name().unwrap()) - } else { - e.path() - } - }) - }) - .collect::, _>>() + e.map(|e| { + if curdir { + PathBuf::from(e.path().file_name().unwrap()) + } else { + e.path() + } + }) + }) + .collect::, _>>() }); match dirs { Ok(mut children) => { @@ -853,8 +880,8 @@ fn fill_todo(todo: &mut Vec>, // requires that the pattern has a leading dot, even if the // `MatchOptions` field `require_literal_leading_dot` is not // set. - if pattern.tokens.len() > 0 && pattern.tokens[0] == Char('.') { - for &special in [".", ".."].iter() { + if !pattern.tokens.is_empty() && pattern.tokens[0] == Char('.') { + for &special in &[".", ".."] { if pattern.matches_with(special, options) { add(todo, path.join(special)); } @@ -890,8 +917,7 @@ fn parse_char_specifiers(s: &[char]) -> Vec { cs } -fn in_char_specifiers(specifiers: &[CharSpecifier], c: char, options: &MatchOptions) -> bool { - +fn in_char_specifiers(specifiers: &[CharSpecifier], c: char, options: MatchOptions) -> bool { for &specifier in specifiers.iter() { match specifier { SingleChar(sc) => { @@ -900,10 +926,8 @@ fn in_char_specifiers(specifiers: &[CharSpecifier], c: char, options: &MatchOpti } } CharRange(start, end) => { - // FIXME: work with non-ascii chars properly (issue #1347) if !options.case_sensitive && c.is_ascii() && start.is_ascii() && end.is_ascii() { - let start = start.to_ascii_lowercase(); let end = end.to_ascii_lowercase(); @@ -942,10 +966,9 @@ fn chars_eq(a: char, b: char, case_sensitive: bool) -> bool { } } - /// Configuration options to modify the behaviour of `Pattern::matches_with(..)`. #[allow(missing_copy_implementations)] -#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct MatchOptions { /// Whether or not patterns should be matched in a case-sensitive manner. /// This currently only considers upper/lower case relationships between @@ -980,8 +1003,8 @@ impl MatchOptions { /// require_literal_leading_dot: false /// } /// ``` - pub fn new() -> MatchOptions { - MatchOptions { + pub fn new() -> Self { + Self { case_sensitive: true, require_literal_separator: false, require_literal_leading_dot: false, @@ -991,8 +1014,8 @@ impl MatchOptions { #[cfg(test)] mod test { + use super::{glob, MatchOptions, Pattern}; use std::path::Path; - use super::{glob, Pattern, MatchOptions}; #[test] fn test_pattern_from_str() { @@ -1030,7 +1053,7 @@ mod test { // this test assumes that there is a /root directory and that // the user running this test is not root or otherwise doesn't // have permission to read its contents - #[cfg(unix)] + #[cfg(all(unix, not(target_os = "macos")))] #[test] fn test_iteration_errors() { use std::io; @@ -1066,11 +1089,14 @@ mod test { // check windows absolute paths with host/device components let root_with_device = current_dir() - .ok() - .and_then(|p| p.prefix().map(|p| p.join("*"))) - .unwrap(); + .ok() + .and_then(|p| p.prefix().map(|p| p.join("*"))) + .unwrap(); // FIXME (#9639): This needs to handle non-utf8 paths - assert!(glob(root_with_device.as_os_str().to_str().unwrap()).unwrap().next().is_some()); + assert!(glob(root_with_device.as_os_str().to_str().unwrap()) + .unwrap() + .next() + .is_some()); } win() } @@ -1082,11 +1108,15 @@ mod test { assert!(!Pattern::new("a*b*c").unwrap().matches("abcd")); assert!(Pattern::new("a*b*c").unwrap().matches("a_b_c")); assert!(Pattern::new("a*b*c").unwrap().matches("a___b___c")); - assert!(Pattern::new("abc*abc*abc").unwrap().matches("abcabcabcabcabcabcabc")); - assert!(!Pattern::new("abc*abc*abc").unwrap().matches("abcabcabcabcabcabcabca")); + assert!(Pattern::new("abc*abc*abc") + .unwrap() + .matches("abcabcabcabcabcabcabc")); + assert!(!Pattern::new("abc*abc*abc") + .unwrap() + .matches("abcabcabcabcabcabcabca")); assert!(Pattern::new("a*a*a*a*a*a*a*a*a") - .unwrap() - .matches("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); + .unwrap() + .matches("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); assert!(Pattern::new("a*b[xyz]c*d").unwrap().matches("abxcdbxcddd")); } @@ -1108,7 +1138,6 @@ mod test { assert!(pat.matches(".asdf")); assert!(pat.matches("/x/.asdf")); - // collapse consecutive wildcards let pat = Pattern::new("some/**/**/needle.txt").unwrap(); assert!(pat.matches("some/needle.txt")); @@ -1147,7 +1176,6 @@ mod test { #[test] fn test_range_pattern() { - let pat = Pattern::new("a[0-9]b").unwrap(); for i in 0..10 { assert!(pat.matches(&format!("a{}b", i))); @@ -1167,8 +1195,11 @@ mod test { assert!(pat.matches(&c.to_string())); } for c in "ABCDEFGHIJKLMNOPQRSTUVWXYZ".chars() { - let options = MatchOptions { case_sensitive: false, ..MatchOptions::new() }; - assert!(pat.matches_with(&c.to_string(), &options)); + let options = MatchOptions { + case_sensitive: false, + ..MatchOptions::new() + }; + assert!(pat.matches_with(&c.to_string(), options)); } assert!(pat.matches("1")); assert!(pat.matches("2")); @@ -1220,7 +1251,6 @@ mod test { #[test] fn test_pattern_matches_case_insensitive() { - let pat = Pattern::new("aBcDeFg").unwrap(); let options = MatchOptions { case_sensitive: false, @@ -1228,15 +1258,14 @@ mod test { require_literal_leading_dot: false, }; - assert!(pat.matches_with("aBcDeFg", &options)); - assert!(pat.matches_with("abcdefg", &options)); - assert!(pat.matches_with("ABCDEFG", &options)); - assert!(pat.matches_with("AbCdEfG", &options)); + assert!(pat.matches_with("aBcDeFg", options)); + assert!(pat.matches_with("abcdefg", options)); + assert!(pat.matches_with("ABCDEFG", options)); + assert!(pat.matches_with("AbCdEfG", options)); } #[test] fn test_pattern_matches_case_insensitive_range() { - let pat_within = Pattern::new("[a]").unwrap(); let pat_except = Pattern::new("[!a]").unwrap(); @@ -1251,18 +1280,17 @@ mod test { require_literal_leading_dot: false, }; - assert!(pat_within.matches_with("a", &options_case_insensitive)); - assert!(pat_within.matches_with("A", &options_case_insensitive)); - assert!(!pat_within.matches_with("A", &options_case_sensitive)); + assert!(pat_within.matches_with("a", options_case_insensitive)); + 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_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)); } #[test] fn test_pattern_matches_require_literal_separator() { - let options_require_literal = MatchOptions { case_sensitive: true, require_literal_separator: true, @@ -1274,34 +1302,35 @@ mod test { require_literal_leading_dot: false, }; - assert!(Pattern::new("abc/def").unwrap().matches_with("abc/def", &options_require_literal)); + assert!(Pattern::new("abc/def") + .unwrap() + .matches_with("abc/def", options_require_literal)); assert!(!Pattern::new("abc?def") - .unwrap() - .matches_with("abc/def", &options_require_literal)); + .unwrap() + .matches_with("abc/def", options_require_literal)); assert!(!Pattern::new("abc*def") - .unwrap() - .matches_with("abc/def", &options_require_literal)); + .unwrap() + .matches_with("abc/def", options_require_literal)); assert!(!Pattern::new("abc[/]def") - .unwrap() - .matches_with("abc/def", &options_require_literal)); + .unwrap() + .matches_with("abc/def", options_require_literal)); assert!(Pattern::new("abc/def") - .unwrap() - .matches_with("abc/def", &options_not_require_literal)); + .unwrap() + .matches_with("abc/def", options_not_require_literal)); assert!(Pattern::new("abc?def") - .unwrap() - .matches_with("abc/def", &options_not_require_literal)); + .unwrap() + .matches_with("abc/def", options_not_require_literal)); assert!(Pattern::new("abc*def") - .unwrap() - .matches_with("abc/def", &options_not_require_literal)); + .unwrap() + .matches_with("abc/def", options_not_require_literal)); assert!(Pattern::new("abc[/]def") - .unwrap() - .matches_with("abc/def", &options_not_require_literal)); + .unwrap() + .matches_with("abc/def", options_not_require_literal)); } #[test] fn test_pattern_matches_require_literal_leading_dot() { - let options_require_literal_leading_dot = MatchOptions { case_sensitive: true, require_literal_separator: false, @@ -1313,39 +1342,65 @@ mod test { require_literal_leading_dot: false, }; - let f = |options| Pattern::new("*.txt").unwrap().matches_with(".hello.txt", options); - assert!(f(&options_not_require_literal_leading_dot)); - assert!(!f(&options_require_literal_leading_dot)); + let f = |options| { + Pattern::new("*.txt") + .unwrap() + .matches_with(".hello.txt", options) + }; + assert!(f(options_not_require_literal_leading_dot)); + assert!(!f(options_require_literal_leading_dot)); - let f = |options| Pattern::new(".*.*").unwrap().matches_with(".hello.txt", options); - assert!(f(&options_not_require_literal_leading_dot)); - assert!(f(&options_require_literal_leading_dot)); + let f = |options| { + Pattern::new(".*.*") + .unwrap() + .matches_with(".hello.txt", options) + }; + assert!(f(options_not_require_literal_leading_dot)); + assert!(f(options_require_literal_leading_dot)); - let f = |options| Pattern::new("aaa/bbb/*").unwrap().matches_with("aaa/bbb/.ccc", options); - assert!(f(&options_not_require_literal_leading_dot)); - assert!(!f(&options_require_literal_leading_dot)); + let f = |options| { + Pattern::new("aaa/bbb/*") + .unwrap() + .matches_with("aaa/bbb/.ccc", options) + }; + assert!(f(options_not_require_literal_leading_dot)); + assert!(!f(options_require_literal_leading_dot)); let f = |options| { - Pattern::new("aaa/bbb/*").unwrap().matches_with("aaa/bbb/c.c.c.", options) + Pattern::new("aaa/bbb/*") + .unwrap() + .matches_with("aaa/bbb/c.c.c.", options) }; - assert!(f(&options_not_require_literal_leading_dot)); - assert!(f(&options_require_literal_leading_dot)); + assert!(f(options_not_require_literal_leading_dot)); + assert!(f(options_require_literal_leading_dot)); - let f = |options| Pattern::new("aaa/bbb/.*").unwrap().matches_with("aaa/bbb/.ccc", options); - assert!(f(&options_not_require_literal_leading_dot)); - assert!(f(&options_require_literal_leading_dot)); + let f = |options| { + Pattern::new("aaa/bbb/.*") + .unwrap() + .matches_with("aaa/bbb/.ccc", options) + }; + assert!(f(options_not_require_literal_leading_dot)); + assert!(f(options_require_literal_leading_dot)); - let f = |options| Pattern::new("aaa/?bbb").unwrap().matches_with("aaa/.bbb", options); - assert!(f(&options_not_require_literal_leading_dot)); - assert!(!f(&options_require_literal_leading_dot)); + let f = |options| { + Pattern::new("aaa/?bbb") + .unwrap() + .matches_with("aaa/.bbb", options) + }; + assert!(f(options_not_require_literal_leading_dot)); + assert!(!f(options_require_literal_leading_dot)); - let f = |options| Pattern::new("aaa/[.]bbb").unwrap().matches_with("aaa/.bbb", options); - assert!(f(&options_not_require_literal_leading_dot)); - assert!(!f(&options_require_literal_leading_dot)); + let f = |options| { + Pattern::new("aaa/[.]bbb") + .unwrap() + .matches_with("aaa/.bbb", options) + }; + assert!(f(options_not_require_literal_leading_dot)); + assert!(!f(options_require_literal_leading_dot)); let f = |options| Pattern::new("**/*").unwrap().matches_with(".bbb", options); - assert!(f(&options_not_require_literal_leading_dot)); - assert!(!f(&options_require_literal_leading_dot)); + assert!(f(options_not_require_literal_leading_dot)); + assert!(!f(options_require_literal_leading_dot)); } #[test] diff --git a/tests/glob-std.rs b/tests/glob-std.rs index c426418..085eb6d 100644 --- a/tests/glob-std.rs +++ b/tests/glob-std.rs @@ -17,8 +17,8 @@ extern crate tempdir; use glob::glob; use std::env; -use std::path::PathBuf; use std::fs; +use std::path::PathBuf; use tempdir::TempDir; #[test] @@ -79,63 +79,91 @@ fn main() { mk_file("r/three/c.md", false); // all recursive entities - assert_eq!(glob_vec("r/**"), vec!( - PathBuf::from("r/another"), - PathBuf::from("r/one"), - PathBuf::from("r/one/another"), - PathBuf::from("r/one/another/deep"), - PathBuf::from("r/three"), - PathBuf::from("r/two"))); + assert_eq!( + glob_vec("r/**"), + vec!( + PathBuf::from("r/another"), + PathBuf::from("r/one"), + PathBuf::from("r/one/another"), + PathBuf::from("r/one/another/deep"), + PathBuf::from("r/three"), + PathBuf::from("r/two") + ) + ); // collapse consecutive recursive patterns - assert_eq!(glob_vec("r/**/**"), vec!( - PathBuf::from("r/another"), - PathBuf::from("r/one"), - PathBuf::from("r/one/another"), - PathBuf::from("r/one/another/deep"), - PathBuf::from("r/three"), - PathBuf::from("r/two"))); - - assert_eq!(glob_vec("r/**/*"), vec!( - PathBuf::from("r/another"), - PathBuf::from("r/another/a.md"), - PathBuf::from("r/current_dir.md"), - PathBuf::from("r/one"), - PathBuf::from("r/one/a.md"), - PathBuf::from("r/one/another"), - PathBuf::from("r/one/another/a.md"), - PathBuf::from("r/one/another/deep"), - PathBuf::from("r/one/another/deep/spelunking.md"), - PathBuf::from("r/three"), - PathBuf::from("r/three/c.md"), - PathBuf::from("r/two"), - PathBuf::from("r/two/b.md"))); + assert_eq!( + glob_vec("r/**/**"), + vec!( + PathBuf::from("r/another"), + PathBuf::from("r/one"), + PathBuf::from("r/one/another"), + PathBuf::from("r/one/another/deep"), + PathBuf::from("r/three"), + PathBuf::from("r/two") + ) + ); + + assert_eq!( + glob_vec("r/**/*"), + vec!( + PathBuf::from("r/another"), + PathBuf::from("r/another/a.md"), + PathBuf::from("r/current_dir.md"), + PathBuf::from("r/one"), + PathBuf::from("r/one/a.md"), + PathBuf::from("r/one/another"), + PathBuf::from("r/one/another/a.md"), + PathBuf::from("r/one/another/deep"), + PathBuf::from("r/one/another/deep/spelunking.md"), + PathBuf::from("r/three"), + PathBuf::from("r/three/c.md"), + PathBuf::from("r/two"), + PathBuf::from("r/two/b.md") + ) + ); // followed by a wildcard - assert_eq!(glob_vec("r/**/*.md"), vec!( - PathBuf::from("r/another/a.md"), - PathBuf::from("r/current_dir.md"), - PathBuf::from("r/one/a.md"), - PathBuf::from("r/one/another/a.md"), - PathBuf::from("r/one/another/deep/spelunking.md"), - PathBuf::from("r/three/c.md"), - PathBuf::from("r/two/b.md"))); + assert_eq!( + glob_vec("r/**/*.md"), + vec!( + PathBuf::from("r/another/a.md"), + PathBuf::from("r/current_dir.md"), + PathBuf::from("r/one/a.md"), + PathBuf::from("r/one/another/a.md"), + PathBuf::from("r/one/another/deep/spelunking.md"), + PathBuf::from("r/three/c.md"), + PathBuf::from("r/two/b.md") + ) + ); // followed by a precise pattern - assert_eq!(glob_vec("r/one/**/a.md"), vec!( - PathBuf::from("r/one/a.md"), - PathBuf::from("r/one/another/a.md"))); + assert_eq!( + glob_vec("r/one/**/a.md"), + vec!( + PathBuf::from("r/one/a.md"), + PathBuf::from("r/one/another/a.md") + ) + ); // followed by another recursive pattern // collapses consecutive recursives into one - assert_eq!(glob_vec("r/one/**/**/a.md"), vec!( - PathBuf::from("r/one/a.md"), - PathBuf::from("r/one/another/a.md"))); + assert_eq!( + glob_vec("r/one/**/**/a.md"), + vec!( + PathBuf::from("r/one/a.md"), + PathBuf::from("r/one/another/a.md") + ) + ); // followed by two precise patterns - assert_eq!(glob_vec("r/**/another/a.md"), vec!( - PathBuf::from("r/another/a.md"), - PathBuf::from("r/one/another/a.md"))); + assert_eq!( + glob_vec("r/**/another/a.md"), + vec!( + PathBuf::from("r/another/a.md"), + PathBuf::from("r/one/another/a.md") + ) + ); assert_eq!(glob_vec(""), Vec::::new()); assert_eq!(glob_vec("."), vec!(PathBuf::from("."))); @@ -155,20 +183,32 @@ fn main() { assert_eq!(glob_vec("aaa\\apple"), vec!(PathBuf::from("aaa/apple"))); } - assert_eq!(glob_vec("???/"), vec!( - PathBuf::from("aaa"), - PathBuf::from("bbb"), - PathBuf::from("ccc"), - PathBuf::from("xyz"))); - - assert_eq!(glob_vec("aaa/tomato/tom?to.txt"), vec!( - PathBuf::from("aaa/tomato/tomato.txt"), - PathBuf::from("aaa/tomato/tomoto.txt"))); - - assert_eq!(glob_vec("xyz/?"), vec!( - PathBuf::from("xyz/x"), - PathBuf::from("xyz/y"), - PathBuf::from("xyz/z"))); + assert_eq!( + glob_vec("???/"), + vec!( + PathBuf::from("aaa"), + PathBuf::from("bbb"), + PathBuf::from("ccc"), + PathBuf::from("xyz") + ) + ); + + assert_eq!( + glob_vec("aaa/tomato/tom?to.txt"), + vec!( + PathBuf::from("aaa/tomato/tomato.txt"), + PathBuf::from("aaa/tomato/tomoto.txt") + ) + ); + + assert_eq!( + glob_vec("xyz/?"), + vec!( + PathBuf::from("xyz/x"), + PathBuf::from("xyz/y"), + PathBuf::from("xyz/z") + ) + ); assert_eq!(glob_vec("a*"), vec!(PathBuf::from("aaa"))); assert_eq!(glob_vec("*a*"), vec!(PathBuf::from("aaa"))); @@ -179,23 +219,39 @@ fn main() { assert_eq!(glob_vec("*a*a*a*"), vec!(PathBuf::from("aaa"))); assert_eq!(glob_vec("aaa*/"), vec!(PathBuf::from("aaa"))); - assert_eq!(glob_vec("aaa/*"), vec!( - PathBuf::from("aaa/apple"), - PathBuf::from("aaa/orange"), - PathBuf::from("aaa/tomato"))); - - assert_eq!(glob_vec("aaa/*a*"), vec!( - PathBuf::from("aaa/apple"), - PathBuf::from("aaa/orange"), - PathBuf::from("aaa/tomato"))); - - assert_eq!(glob_vec("*/*/*.txt"), vec!( - PathBuf::from("aaa/tomato/tomato.txt"), - PathBuf::from("aaa/tomato/tomoto.txt"))); - - assert_eq!(glob_vec("*/*/t[aob]m?to[.]t[!y]t"), vec!( - PathBuf::from("aaa/tomato/tomato.txt"), - PathBuf::from("aaa/tomato/tomoto.txt"))); + assert_eq!( + glob_vec("aaa/*"), + vec!( + PathBuf::from("aaa/apple"), + PathBuf::from("aaa/orange"), + PathBuf::from("aaa/tomato") + ) + ); + + assert_eq!( + glob_vec("aaa/*a*"), + vec!( + PathBuf::from("aaa/apple"), + PathBuf::from("aaa/orange"), + PathBuf::from("aaa/tomato") + ) + ); + + assert_eq!( + glob_vec("*/*/*.txt"), + vec!( + PathBuf::from("aaa/tomato/tomato.txt"), + PathBuf::from("aaa/tomato/tomoto.txt") + ) + ); + + assert_eq!( + glob_vec("*/*/t[aob]m?to[.]t[!y]t"), + vec!( + PathBuf::from("aaa/tomato/tomato.txt"), + PathBuf::from("aaa/tomato/tomoto.txt") + ) + ); assert_eq!(glob_vec("./aaa"), vec!(PathBuf::from("aaa"))); assert_eq!(glob_vec("./*"), glob_vec("*")); @@ -219,60 +275,103 @@ fn main() { 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/["))); - assert_eq!(glob_vec("bbb/specials/!"), vec!(PathBuf::from("bbb/specials/!"))); - assert_eq!(glob_vec("bbb/specials/[]]"), vec!(PathBuf::from("bbb/specials/]"))); + assert_eq!( + glob_vec("bbb/specials/[[]"), + vec!(PathBuf::from("bbb/specials/[")) + ); + assert_eq!( + glob_vec("bbb/specials/!"), + vec!(PathBuf::from("bbb/specials/!")) + ); + assert_eq!( + glob_vec("bbb/specials/[]]"), + vec!(PathBuf::from("bbb/specials/]")) + ); if env::consts::FAMILY != "windows" { - assert_eq!(glob_vec("bbb/specials/[*]"), vec!(PathBuf::from("bbb/specials/*"))); - assert_eq!(glob_vec("bbb/specials/[?]"), vec!(PathBuf::from("bbb/specials/?"))); + assert_eq!( + glob_vec("bbb/specials/[*]"), + vec!(PathBuf::from("bbb/specials/*")) + ); + assert_eq!( + glob_vec("bbb/specials/[?]"), + vec!(PathBuf::from("bbb/specials/?")) + ); } if env::consts::FAMILY == "windows" { - - assert_eq!(glob_vec("bbb/specials/[![]"), vec!( - PathBuf::from("bbb/specials/!"), - PathBuf::from("bbb/specials/]"))); - - assert_eq!(glob_vec("bbb/specials/[!]]"), vec!( - PathBuf::from("bbb/specials/!"), - PathBuf::from("bbb/specials/["))); - - assert_eq!(glob_vec("bbb/specials/[!!]"), vec!( - PathBuf::from("bbb/specials/["), - PathBuf::from("bbb/specials/]"))); - + assert_eq!( + glob_vec("bbb/specials/[![]"), + vec!( + PathBuf::from("bbb/specials/!"), + PathBuf::from("bbb/specials/]") + ) + ); + + assert_eq!( + glob_vec("bbb/specials/[!]]"), + vec!( + PathBuf::from("bbb/specials/!"), + PathBuf::from("bbb/specials/[") + ) + ); + + assert_eq!( + glob_vec("bbb/specials/[!!]"), + vec!( + PathBuf::from("bbb/specials/["), + PathBuf::from("bbb/specials/]") + ) + ); } else { - - assert_eq!(glob_vec("bbb/specials/[![]"), vec!( - PathBuf::from("bbb/specials/!"), - PathBuf::from("bbb/specials/*"), - PathBuf::from("bbb/specials/?"), - PathBuf::from("bbb/specials/]"))); - - assert_eq!(glob_vec("bbb/specials/[!]]"), vec!( - PathBuf::from("bbb/specials/!"), - PathBuf::from("bbb/specials/*"), - PathBuf::from("bbb/specials/?"), - PathBuf::from("bbb/specials/["))); - - assert_eq!(glob_vec("bbb/specials/[!!]"), vec!( - PathBuf::from("bbb/specials/*"), - PathBuf::from("bbb/specials/?"), - PathBuf::from("bbb/specials/["), - PathBuf::from("bbb/specials/]"))); - - assert_eq!(glob_vec("bbb/specials/[!*]"), vec!( - PathBuf::from("bbb/specials/!"), - PathBuf::from("bbb/specials/?"), - PathBuf::from("bbb/specials/["), - PathBuf::from("bbb/specials/]"))); - - assert_eq!(glob_vec("bbb/specials/[!?]"), vec!( - PathBuf::from("bbb/specials/!"), - PathBuf::from("bbb/specials/*"), - PathBuf::from("bbb/specials/["), - PathBuf::from("bbb/specials/]"))); - + assert_eq!( + glob_vec("bbb/specials/[![]"), + vec!( + PathBuf::from("bbb/specials/!"), + PathBuf::from("bbb/specials/*"), + PathBuf::from("bbb/specials/?"), + PathBuf::from("bbb/specials/]") + ) + ); + + assert_eq!( + glob_vec("bbb/specials/[!]]"), + vec!( + PathBuf::from("bbb/specials/!"), + PathBuf::from("bbb/specials/*"), + PathBuf::from("bbb/specials/?"), + PathBuf::from("bbb/specials/[") + ) + ); + + assert_eq!( + glob_vec("bbb/specials/[!!]"), + vec!( + PathBuf::from("bbb/specials/*"), + PathBuf::from("bbb/specials/?"), + PathBuf::from("bbb/specials/["), + PathBuf::from("bbb/specials/]") + ) + ); + + assert_eq!( + glob_vec("bbb/specials/[!*]"), + vec!( + PathBuf::from("bbb/specials/!"), + PathBuf::from("bbb/specials/?"), + PathBuf::from("bbb/specials/["), + PathBuf::from("bbb/specials/]") + ) + ); + + assert_eq!( + glob_vec("bbb/specials/[!?]"), + vec!( + PathBuf::from("bbb/specials/!"), + PathBuf::from("bbb/specials/*"), + PathBuf::from("bbb/specials/["), + PathBuf::from("bbb/specials/]") + ) + ); } }