pub mod constances;
pub mod ignoreset;
use crate::{constances::DOT_IGNORE_FILE_NAME, ignoreset::IgnoreSet};
use std::{
fs::{self, DirEntry},
path::PathBuf,
};
pub fn walk_dir(dir_path: &str, callback: impl Fn(DirEntry) -> ()) {
let dir_path = PathBuf::from(dir_path);
let mut ignore_set: Vec<IgnoreSet> = Vec::new();
dot_ignore_recursive_walk_dir(&dir_path, &dir_path, &mut ignore_set, &callback);
}
fn dot_ignore_recursive_walk_dir(
base_dir: &PathBuf,
current_dir: &PathBuf,
ignore_set: &mut Vec<IgnoreSet>,
callback: &impl Fn(DirEntry) -> (),
) {
let mut new_ignore_set = IgnoreSet::new(current_dir);
read_dot_ignore(¤t_dir, &mut new_ignore_set);
new_ignore_set.compile();
ignore_set.push(new_ignore_set);
for entry in std::fs::read_dir(¤t_dir)
.expect("Failed to read_dir")
.flatten()
{
let entry_path = entry.path();
if is_matched(ignore_set, current_dir, &entry_path) {
continue;
}
callback(entry);
if entry_path.is_dir() {
dot_ignore_recursive_walk_dir(base_dir, &entry_path, ignore_set, callback);
}
}
ignore_set.pop();
}
fn read_dot_ignore(dir_path: &PathBuf, ignore_set: &mut IgnoreSet) {
let contents = fs::read_to_string(dir_path.join(DOT_IGNORE_FILE_NAME)).unwrap_or_default();
for line in contents.lines() {
let line = line.trim();
if line.is_empty() || line.starts_with("#") {
continue;
}
let line = line
.trim_start_matches("/")
.trim_start_matches("./");
ignore_set.insert(line);
}
}
fn is_matched(ignore_set: &Vec<IgnoreSet>, _current_dir: &PathBuf, entry_path: &PathBuf) -> bool {
for ignore in ignore_set.iter() {
let relative_path = entry_path.strip_prefix(&ignore.dir).unwrap();
let relative_path = relative_path.to_str().unwrap();
if ignore.is_matched(relative_path) {
return true;
}
}
false
}