use ignore::{
WalkBuilder, WalkState,
overrides::{Override, OverrideBuilder},
};
use std::{
fs::File,
path::{Path, PathBuf},
sync::{
Arc,
atomic::{AtomicUsize, Ordering},
},
};
pub fn walk<T: 'static + Clone + Send + FnMut(&Path, File)>(
paths: &[PathBuf],
current_dir: &Path,
ignore_rules: &[String],
callback: T,
) -> Result<usize, String> {
let files_scanned = Arc::new(AtomicUsize::new(0));
let overrides = build_overrides(current_dir, ignore_rules)?;
for path in paths {
WalkBuilder::new(path)
.current_dir(current_dir)
.hidden(false)
.require_git(false)
.parents(false)
.overrides(overrides.clone())
.build_parallel()
.run(|| {
let mut callback = callback.clone();
let files_scanned = files_scanned.clone();
Box::new(move |result| {
if let Ok(dir_entry) = result {
if dir_entry.file_type().unwrap().is_file() {
let possible_file = File::open(dir_entry.path());
if let Ok(file) = possible_file {
callback(dir_entry.path(), file);
files_scanned.fetch_add(1, Ordering::SeqCst);
}
}
}
WalkState::Continue
})
});
}
Ok(files_scanned.load(Ordering::SeqCst))
}
fn build_overrides(current_dir: &Path, ignore_rules: &[String]) -> Result<Override, String> {
let mut override_builder = OverrideBuilder::new(current_dir);
override_builder
.add("!.git/")
.unwrap() .add("!.hg/")
.unwrap(); for ignore_rule in ignore_rules {
override_builder
.add(&format!("!{ignore_rule}"))
.map_err(|error| format!("Error parsing ignore rule `{ignore_rule}`: {error}"))?;
}
override_builder
.build()
.map_err(|error| format!("Error building ignore rules: {error}"))
}