@@ -6,6 +6,7 @@ use is_executable::is_executable;
66use std:: {
77 fmt:: Display ,
88 path:: { Path , PathBuf } ,
9+ thread,
910 time:: { Duration , SystemTime } ,
1011} ;
1112
@@ -74,6 +75,11 @@ struct AppArgs {
7475 /// Moves the executable to a new folder outside of target.
7576 #[ arg( short = 'e' , long = "keep-executable" ) ]
7677 executable : bool ,
78+
79+ /// Directories that should be fully skipped during scanning, including subdirectories. This
80+ /// will speed up the scanning time by not doing any reads for the specified directories.
81+ #[ arg( long = "skip" ) ]
82+ skip : Vec < String > ,
7783}
7884
7985/// Wrap the bytefmt::parse function to return the error as an owned String
@@ -142,7 +148,7 @@ fn main() {
142148 scan_progress. enable_steady_tick ( Duration :: from_millis ( 100 ) ) ;
143149
144150 // Find project dirs and analyze them
145- let mut projects: Vec < _ > = find_cargo_projects ( scan_path, args. number_of_threads , args. verbose )
151+ let mut projects: Vec < _ > = find_cargo_projects ( scan_path, args. number_of_threads , & args)
146152 . into_iter ( )
147153 . filter_map ( |proj| proj. 1 . then ( || ProjectTargetAnalysis :: analyze ( & proj. 0 ) ) )
148154 . collect ( ) ;
@@ -339,49 +345,52 @@ struct ProjectDir(PathBuf, bool);
339345/// Recursively scan the given path for cargo projects using the specified number of threads.
340346///
341347/// When the number of threads is 0, use as many threads as virtual CPU cores.
342- fn find_cargo_projects ( path : & Path , mut num_threads : usize , verbose : bool ) -> Vec < ProjectDir > {
348+ fn find_cargo_projects ( path : & Path , mut num_threads : usize , args : & AppArgs ) -> Vec < ProjectDir > {
343349 if num_threads == 0 {
344350 num_threads = num_cpus:: get ( ) ;
345351 }
346352
347- {
348- let ( job_tx, job_rx) = crossbeam_channel:: unbounded :: < Job > ( ) ;
349- let ( result_tx, result_rx) = crossbeam_channel:: unbounded :: < ProjectDir > ( ) ;
350-
351- ( 0 ..num_threads)
352- . map ( |_| ( job_rx. clone ( ) , result_tx. clone ( ) ) )
353- . for_each ( |( job_rx, result_tx) | {
354- std:: thread:: spawn ( move || {
355- job_rx
356- . into_iter ( )
357- . for_each ( |job| find_cargo_projects_task ( job, result_tx. clone ( ) , verbose) )
353+ thread:: scope ( |scope| {
354+ {
355+ let ( job_tx, job_rx) = crossbeam_channel:: unbounded :: < Job > ( ) ;
356+ let ( result_tx, result_rx) = crossbeam_channel:: unbounded :: < ProjectDir > ( ) ;
357+
358+ ( 0 ..num_threads)
359+ . map ( |_| ( job_rx. clone ( ) , result_tx. clone ( ) ) )
360+ . for_each ( |( job_rx, result_tx) | {
361+ scope. spawn ( move || {
362+ job_rx
363+ . into_iter ( )
364+ . for_each ( |job| find_cargo_projects_task ( job, result_tx. clone ( ) , & args) )
365+ } ) ;
358366 } ) ;
359- } ) ;
360367
361- job_tx
362- . clone ( )
363- . send ( Job ( path. to_path_buf ( ) , job_tx) )
364- . unwrap ( ) ;
368+ job_tx
369+ . clone ( )
370+ . send ( Job ( path. to_path_buf ( ) , job_tx) )
371+ . unwrap ( ) ;
365372
366- result_rx
367- }
368- . into_iter ( )
369- . collect ( )
373+ result_rx
374+ }
375+ . into_iter ( )
376+ . collect ( )
377+ } )
370378}
371379
372380/// Scan the given directory and report to the results Sender if the directory contains a
373381/// Cargo.toml . Detected subdirectories should be queued as a new job in with the job_sender.
374382///
375383/// This function is supposed to be called by the threadpool in find_cargo_projects
376- fn find_cargo_projects_task ( job : Job , results : Sender < ProjectDir > , verbose : bool ) {
384+ fn find_cargo_projects_task ( job : Job , results : Sender < ProjectDir > , args : & AppArgs ) {
377385 let path = job. 0 ;
378386 let job_sender = job. 1 ;
379387 let mut has_target = false ;
380388
381389 let read_dir = match path. read_dir ( ) {
382390 Ok ( it) => it,
383391 Err ( e) => {
384- verbose. then ( || eprintln ! ( "Error reading directory: '{}' {}" , path. display( ) , e) ) ;
392+ args. verbose
393+ . then ( || eprintln ! ( "Error reading directory: '{}' {}" , path. display( ) , e) ) ;
385394 return ;
386395 }
387396 } ;
@@ -396,6 +405,10 @@ fn find_cargo_projects_task(job: Job, results: Sender<ProjectDir>, verbose: bool
396405
397406 // Iterate through the subdirectories of path, ignoring entries that caused errors
398407 for it in dirs {
408+ if args. skip . iter ( ) . any ( |p| starts_with_canonicalized ( & it, p) ) {
409+ continue ;
410+ }
411+
399412 let filename = it. file_name ( ) . unwrap_or_default ( ) . to_string_lossy ( ) ;
400413 match filename. as_ref ( ) {
401414 // No need to search .git directories for cargo projects. Also skip .cargo directories
0 commit comments