11use std:: io;
22use std:: fs:: { self , canonicalize} ;
33use std:: path:: { Path , PathBuf } ;
4- use glob:: glob ;
4+ use glob:: { Pattern } ;
55
66const PRIORITY_CHARS : [ char ; 10 ] = [ '0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' ] ;
77
88use crate :: entries:: { Entry , EntryData , Location } ;
99
10+ #[ derive( Debug , PartialEq , Eq ) ]
11+ pub enum Exclude {
12+ Path ( PathBuf ) ,
13+ Glob ( Pattern ) ,
14+ }
15+
16+ impl Exclude {
17+ pub fn matches ( & self , path : & Path ) -> bool {
18+ match self {
19+ Exclude :: Path ( p) => p == path,
20+ Exclude :: Glob ( g) => g. matches_path ( path) ,
21+ }
22+ }
23+ }
24+
1025pub struct Stats {
1126 visited_folder_count : usize ,
1227 visited_file_count : usize ,
@@ -100,7 +115,7 @@ fn clean_line<'a>(line: &'a str, delimiter_word: &str) -> &'a str {
100115 . trim ( )
101116}
102117
103- pub fn add_excludes_from_gitignore ( base_dir : & PathBuf , excludes : & mut Vec < PathBuf > ) {
118+ pub fn add_excludes_from_gitignore ( base_dir : & PathBuf , excludes : & mut Vec < Exclude > ) {
104119 let mut gitignore = base_dir. clone ( ) ;
105120 gitignore. push ( ".gitignore" ) ;
106121
@@ -115,7 +130,7 @@ pub fn add_excludes_from_gitignore(base_dir: &PathBuf, excludes: &mut Vec<PathBu
115130
116131 if line. trim ( ) == "*" {
117132 if let Ok ( realpath) = canonicalize ( base_dir) {
118- excludes. push ( realpath) ;
133+ excludes. push ( Exclude :: Path ( realpath) ) ;
119134 }
120135
121136 break ;
@@ -132,12 +147,14 @@ pub fn add_excludes_from_gitignore(base_dir: &PathBuf, excludes: &mut Vec<PathBu
132147 let mut pattern = base_dir. clone ( ) ;
133148 pattern. push ( line. trim_end_matches ( "*/" ) . trim_matches ( '/' ) ) ;
134149
135- if let Some ( pattern_str ) = pattern. to_str ( ) {
136- for path in glob ( pattern_str ) . unwrap ( ) {
137- if let Ok ( exclude ) = canonicalize ( path . unwrap ( ) ) {
138- excludes. push ( exclude ) ;
150+ if pattern. to_str ( ) . unwrap ( ) . contains ( '*' ) {
151+ if let Some ( str ) = pattern . to_str ( ) {
152+ if let Ok ( p ) = Pattern :: new ( str ) {
153+ excludes. push ( Exclude :: Glob ( p ) ) ;
139154 }
140155 }
156+ } else {
157+ excludes. push ( Exclude :: Path ( pattern) ) ;
141158 }
142159 }
143160}
@@ -226,19 +243,19 @@ pub fn scan_file(path: &Path, entries: &mut Vec<Entry>) -> io::Result<()> {
226243 Ok ( ( ) )
227244}
228245
229- pub fn scan_dir ( dir : & Path , entries : & mut Vec < Entry > , excludes : & mut Vec < PathBuf > , stats : & mut Stats ) -> io:: Result < ( ) > {
246+ pub fn scan_dir ( dir : & Path , entries : & mut Vec < Entry > , excludes : & mut Vec < Exclude > , stats : & mut Stats ) -> io:: Result < ( ) > {
230247 let mut gitignore = dir. to_path_buf ( ) . clone ( ) ;
231248 gitignore. push ( ".gitignore" ) ;
232249
233250 if gitignore. exists ( ) {
234251 add_excludes_from_gitignore ( & dir. to_path_buf ( ) , excludes) ;
235252
236253 // `add_excludes_from_gitignore` can add the *entire* directory being scanned here to excludes
237- // e.g. if it contains a `*` line. Tthe directory is visited first, and gitignore is read second,
254+ // e.g. if it contains a `*` line. The directory is visited first, and gitignore is read second,
238255 // so the exclude would not affect anything inside the for loop. For that reason, we re-check if
239256 // `dir` hasn't become excluded after running `add_excludes_from_gitignore`.
240257 for exclude in & * excludes {
241- if canonicalize ( dir) . unwrap ( ) == * exclude {
258+ if Exclude :: Path ( canonicalize ( dir) . unwrap ( ) ) == * exclude {
242259 return Ok ( ( ) ) ;
243260 }
244261 }
@@ -255,7 +272,7 @@ pub fn scan_dir(dir: &Path, entries: &mut Vec<Entry>, excludes: &mut Vec<PathBuf
255272 }
256273
257274 for exclude in & * excludes {
258- if canonicalize ( & path) . unwrap ( ) == * exclude {
275+ if exclude . matches ( & path) {
259276 continue ' entry;
260277 }
261278 }
0 commit comments