@@ -5,8 +5,12 @@ use crate::sys::SysReadDirEntry;
55use crate :: { error:: * , NonFatalErrorHandler } ;
66#[ cfg( feature = "regex" ) ]
77use regex:: Regex ;
8+ #[ cfg( feature = "regex" ) ]
9+ use std:: borrow:: Borrow ;
810use std:: borrow:: Cow ;
911use std:: ffi:: OsStr ;
12+ #[ cfg( feature = "regex" ) ]
13+ use std:: io;
1014use std:: path:: { Component , Path , PathBuf } ;
1115use std:: vec;
1216
@@ -77,7 +81,7 @@ impl<TSys: Sys> Finder<TSys> {
7781 WhichFindIterator :: new_cwd ( path, cwd. as_ref ( ) , self . sys , nonfatal_error_handler)
7882 }
7983 _ => {
80- #[ cfg( feature = "tracing" ) ]
84+ #[ cfg( feature = "tracing" ) ]
8185 tracing:: trace!( "{} has no path seperators, so only paths in PATH environment variable will be searched." , path. display( ) ) ;
8286 // Search binary in PATHs(defined in environment variable).
8387 let paths = paths. ok_or ( Error :: CannotGetCurrentDirAndPathListEmpty ) ?;
@@ -100,30 +104,12 @@ impl<TSys: Sys> Finder<TSys> {
100104 self ,
101105 binary_regex : impl std:: borrow:: Borrow < Regex > ,
102106 paths : Option < T > ,
103- mut nonfatal_error_handler : F ,
107+ nonfatal_error_handler : F ,
104108 ) -> Result < impl Iterator < Item = PathBuf > >
105109 where
106110 T : AsRef < OsStr > ,
107111 {
108- let p = paths. ok_or ( Error :: CannotGetCurrentDirAndPathListEmpty ) ?;
109- let paths = self . sys . env_split_paths ( p. as_ref ( ) ) ;
110-
111- let matching_re = paths
112- . into_iter ( )
113- . flat_map ( move |p| self . sys . read_dir ( & p) )
114- . flatten ( )
115- . flatten ( )
116- . map ( |e| e. path ( ) )
117- . filter ( move |p| {
118- if let Some ( unicode_file_name) = p. file_name ( ) . unwrap ( ) . to_str ( ) {
119- binary_regex. borrow ( ) . is_match ( unicode_file_name)
120- } else {
121- false
122- }
123- } )
124- . filter ( move |p| is_valid ( self . sys , p, & mut nonfatal_error_handler) ) ;
125-
126- Ok ( matching_re)
112+ WhichFindRegexIter :: new ( self . sys , paths, binary_regex, nonfatal_error_handler)
127113 }
128114}
129115
@@ -142,21 +128,37 @@ impl<TSys: Sys, F: NonFatalErrorHandler> WhichFindIterator<TSys, F> {
142128 } ;
143129 Self {
144130 sys,
145- paths : PathsIter { paths : vec ! [ binary_name. to_absolute( cwd) ] . into_iter ( ) , current_path_with_index : None , path_extensions } ,
131+ paths : PathsIter {
132+ paths : vec ! [ binary_name. to_absolute( cwd) ] . into_iter ( ) ,
133+ current_path_with_index : None ,
134+ path_extensions,
135+ } ,
146136 nonfatal_error_handler,
147137 }
148138 }
149139
150- pub fn new_paths ( binary_name : PathBuf , paths : Vec < PathBuf > , sys : TSys , nonfatal_error_handler : F ) -> Self {
140+ pub fn new_paths (
141+ binary_name : PathBuf ,
142+ paths : Vec < PathBuf > ,
143+ sys : TSys ,
144+ nonfatal_error_handler : F ,
145+ ) -> Self {
151146 let path_extensions = if sys. is_windows ( ) {
152147 sys. env_windows_path_ext ( )
153148 } else {
154149 Cow :: Borrowed ( Default :: default ( ) )
155150 } ;
156- let paths = paths. iter ( ) . map ( |p| tilde_expansion ( & sys, p) . join ( & binary_name) ) . collect :: < Vec < _ > > ( ) ;
151+ let paths = paths
152+ . iter ( )
153+ . map ( |p| tilde_expansion ( & sys, p) . join ( & binary_name) )
154+ . collect :: < Vec < _ > > ( ) ;
157155 Self {
158156 sys,
159- paths : PathsIter { paths : paths. into_iter ( ) , current_path_with_index : None , path_extensions } ,
157+ paths : PathsIter {
158+ paths : paths. into_iter ( ) ,
159+ current_path_with_index : None ,
160+ path_extensions,
161+ } ,
160162 nonfatal_error_handler,
161163 }
162164 }
@@ -168,70 +170,74 @@ impl<TSys: Sys, F: NonFatalErrorHandler> Iterator for WhichFindIterator<TSys, F>
168170 fn next ( & mut self ) -> Option < Self :: Item > {
169171 for path in & mut self . paths {
170172 if is_valid ( & self . sys , & path, & mut self . nonfatal_error_handler ) {
171- return Some ( correct_casing ( & self . sys , path, & mut self . nonfatal_error_handler ) )
173+ return Some ( correct_casing (
174+ & self . sys ,
175+ path,
176+ & mut self . nonfatal_error_handler ,
177+ ) ) ;
172178 }
173179 }
174180 None
175181 }
176182}
177183
178184struct PathsIter < P >
179- where
180- P : Iterator < Item = PathBuf > ,
181- {
182- paths : P ,
183- current_path_with_index : Option < ( PathBuf , usize ) > ,
184- path_extensions : Cow < ' static , [ String ] > ,
185- }
185+ where
186+ P : Iterator < Item = PathBuf > ,
187+ {
188+ paths : P ,
189+ current_path_with_index : Option < ( PathBuf , usize ) > ,
190+ path_extensions : Cow < ' static , [ String ] > ,
191+ }
186192
187- impl < P > Iterator for PathsIter < P >
188- where
189- P : Iterator < Item = PathBuf > ,
190- {
191- type Item = PathBuf ;
193+ impl < P > Iterator for PathsIter < P >
194+ where
195+ P : Iterator < Item = PathBuf > ,
196+ {
197+ type Item = PathBuf ;
192198
193- fn next ( & mut self ) -> Option < Self :: Item > {
194- if self . path_extensions . is_empty ( ) {
195- self . paths . next ( )
196- } else if let Some ( ( p, index) ) = self . current_path_with_index . take ( ) {
197- let next_index = index + 1 ;
198- if next_index < self . path_extensions . len ( ) {
199- self . current_path_with_index = Some ( ( p. clone ( ) , next_index) ) ;
200- }
201- // Append the extension.
202- let mut p = p. into_os_string ( ) ;
203- p. push ( & self . path_extensions [ index] ) ;
204- let ret = PathBuf :: from ( p) ;
199+ fn next ( & mut self ) -> Option < Self :: Item > {
200+ if self . path_extensions . is_empty ( ) {
201+ self . paths . next ( )
202+ } else if let Some ( ( p, index) ) = self . current_path_with_index . take ( ) {
203+ let next_index = index + 1 ;
204+ if next_index < self . path_extensions . len ( ) {
205+ self . current_path_with_index = Some ( ( p. clone ( ) , next_index) ) ;
206+ }
207+ // Append the extension.
208+ let mut p = p. into_os_string ( ) ;
209+ p. push ( & self . path_extensions [ index] ) ;
210+ let ret = PathBuf :: from ( p) ;
211+ #[ cfg( feature = "tracing" ) ]
212+ tracing:: trace!( "possible extension: {}" , ret. display( ) ) ;
213+ Some ( ret)
214+ } else {
215+ let p = self . paths . next ( ) ?;
216+ if has_executable_extension ( & p, & self . path_extensions ) {
205217 #[ cfg( feature = "tracing" ) ]
206- tracing:: trace!( "possible extension: {}" , ret. display( ) ) ;
207- Some ( ret)
218+ tracing:: trace!(
219+ "{} already has an executable extension, not modifying it further" ,
220+ p. display( )
221+ ) ;
208222 } else {
209- let p = self . paths . next ( ) ?;
210- if has_executable_extension ( & p, & self . path_extensions ) {
211- #[ cfg( feature = "tracing" ) ]
212- tracing:: trace!(
213- "{} already has an executable extension, not modifying it further" ,
214- p. display( )
215- ) ;
216- } else {
217- #[ cfg( feature = "tracing" ) ]
218- tracing:: trace!(
219- "{} has no extension, using PATHEXT environment variable to infer one" ,
220- p. display( )
221- ) ;
222- // Appended paths with windows executable extensions.
223- // e.g. path `c:/windows/bin[.ext]` will expand to:
224- // [c:/windows/bin.ext]
225- // c:/windows/bin[.ext].COM
226- // c:/windows/bin[.ext].EXE
227- // c:/windows/bin[.ext].CMD
228- // ...
229- self . current_path_with_index = Some ( ( p. clone ( ) , 0 ) ) ;
230- }
231- Some ( p)
223+ #[ cfg( feature = "tracing" ) ]
224+ tracing:: trace!(
225+ "{} has no extension, using PATHEXT environment variable to infer one" ,
226+ p. display( )
227+ ) ;
228+ // Appended paths with windows executable extensions.
229+ // e.g. path `c:/windows/bin[.ext]` will expand to:
230+ // [c:/windows/bin.ext]
231+ // c:/windows/bin[.ext].COM
232+ // c:/windows/bin[.ext].EXE
233+ // c:/windows/bin[.ext].CMD
234+ // ...
235+ self . current_path_with_index = Some ( ( p. clone ( ) , 0 ) ) ;
232236 }
237+ Some ( p)
233238 }
234239 }
240+ }
235241
236242fn tilde_expansion < TSys : Sys > ( sys : TSys , p : & Path ) -> Cow < ' _ , Path > {
237243 let mut component_iter = p. components ( ) ;
@@ -282,3 +288,81 @@ fn correct_casing<TSys: Sys, F: NonFatalErrorHandler>(
282288 }
283289 p
284290}
291+
292+ #[ cfg( feature = "regex" ) ]
293+ struct WhichFindRegexIter < TSys : Sys , B : Borrow < Regex > , F : NonFatalErrorHandler > {
294+ sys : TSys ,
295+ re : B ,
296+ paths : vec:: IntoIter < PathBuf > ,
297+ nonfatal_error_handler : F ,
298+ current_read_dir_iter : Option < Box < dyn Iterator < Item = io:: Result < TSys :: ReadDirEntry > > > > ,
299+ }
300+
301+ #[ cfg( feature = "regex" ) ]
302+ impl < TSys : Sys , B : Borrow < Regex > , F : NonFatalErrorHandler > WhichFindRegexIter < TSys , B , F > {
303+ pub fn new < T : AsRef < OsStr > > (
304+ sys : TSys ,
305+ paths : Option < T > ,
306+ re : B ,
307+ nonfatal_error_handler : F ,
308+ ) -> Result < Self > {
309+ let p = paths. ok_or ( Error :: CannotGetCurrentDirAndPathListEmpty ) ?;
310+ let paths = sys. env_split_paths ( p. as_ref ( ) ) ;
311+ Ok ( WhichFindRegexIter {
312+ sys,
313+ re,
314+ paths : paths. into_iter ( ) ,
315+ nonfatal_error_handler,
316+ current_read_dir_iter : None ,
317+ } )
318+ }
319+ }
320+
321+ #[ cfg( feature = "regex" ) ]
322+ impl < TSys : Sys , B : Borrow < Regex > , F : NonFatalErrorHandler > Iterator
323+ for WhichFindRegexIter < TSys , B , F >
324+ {
325+ type Item = PathBuf ;
326+
327+ fn next ( & mut self ) -> Option < Self :: Item > {
328+ loop {
329+ if let Some ( iter) = & mut self . current_read_dir_iter {
330+ match iter. next ( ) {
331+ Some ( Ok ( path) ) => {
332+ if let Some ( unicode_file_name) = path. file_name ( ) . to_str ( ) {
333+ if self . re . borrow ( ) . is_match ( unicode_file_name) {
334+ return Some ( path. path ( ) ) ;
335+ } else {
336+ #[ cfg( feature = "tracing" ) ]
337+ tracing:: debug!( "regex filtered out {}" , unicode_file_name) ;
338+ }
339+ } else {
340+ #[ cfg( feature = "tracing" ) ]
341+ tracing:: debug!( "regex unable to evaluate filename as it's not valid unicode. Lossy filename conversion: {}" , path. file_name( ) . to_string_lossy( ) ) ;
342+ }
343+ }
344+ Some ( Err ( e) ) => {
345+ self . nonfatal_error_handler . handle ( NonFatalError :: Io ( e) ) ;
346+ }
347+ None => {
348+ self . current_read_dir_iter = None ;
349+ }
350+ }
351+ } else {
352+ let path = self . paths . next ( ) ;
353+ if let Some ( path) = path {
354+ match self . sys . read_dir ( & path) {
355+ Ok ( new_read_dir_iter) => {
356+ self . current_read_dir_iter = Some ( new_read_dir_iter) ;
357+ }
358+ Err ( e) => {
359+ self . nonfatal_error_handler . handle ( NonFatalError :: Io ( e) ) ;
360+ }
361+ }
362+ } else {
363+ return None ;
364+ }
365+ }
366+ }
367+ }
368+ }
0 commit comments