Skip to content

Commit 147c8e1

Browse files
committed
Implement --skip argument to prevent scanning dirs
- The new skip argument fully prevents further scanning of the specified directories - This is in contrast to the ignore argument that still scans the directories but marks them as ignored by default - Feature implementation for #10
1 parent d4eadf8 commit 147c8e1

File tree

1 file changed

+37
-24
lines changed

1 file changed

+37
-24
lines changed

src/main.rs

Lines changed: 37 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use is_executable::is_executable;
66
use 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

Comments
 (0)