1pub mod constances;
2pub mod ignoreset;
3
4use crate::{constances::DOT_IGNORE_FILE_NAME, ignoreset::IgnoreSet};
5use std::{
6 fs::{self, DirEntry},
7 path::PathBuf,
8};
9
10pub fn walk_dir(dir_path: &str, callback: impl Fn(DirEntry) -> ()) {
30 let dir_path = PathBuf::from(dir_path);
31
32 let mut ignore_set: Vec<IgnoreSet> = Vec::new();
33
34 dot_ignore_recursive_walk_dir(&dir_path, &dir_path, &mut ignore_set, &callback);
35}
36
37fn dot_ignore_recursive_walk_dir(
41 base_dir: &PathBuf,
42 current_dir: &PathBuf,
43 ignore_set: &mut Vec<IgnoreSet>,
44 callback: &impl Fn(DirEntry) -> (),
45) {
46 let mut new_ignore_set = IgnoreSet::new(current_dir);
48
49 read_dot_ignore(¤t_dir, &mut new_ignore_set);
50
51 new_ignore_set.compile();
52
53 ignore_set.push(new_ignore_set);
54
55 for entry in std::fs::read_dir(¤t_dir)
57 .expect("Failed to read_dir")
58 .flatten()
59 {
60 let entry_path = entry.path();
61
62 if is_matched(ignore_set, current_dir, &entry_path) {
63 continue;
64 }
65
66 callback(entry);
67
68 if entry_path.is_dir() {
70 dot_ignore_recursive_walk_dir(base_dir, &entry_path, ignore_set, callback);
71 }
72 }
73
74 ignore_set.pop();
76}
77
78fn read_dot_ignore(dir_path: &PathBuf, ignore_set: &mut IgnoreSet) {
82 let contents = fs::read_to_string(dir_path.join(DOT_IGNORE_FILE_NAME)).unwrap_or_default();
83
84
85
86 for line in contents.lines() {
87 let line = line.trim();
88
89 if line.is_empty() || line.starts_with("#") {
90 continue;
91 }
92
93 let line = line
94 .trim_start_matches("/")
95 .trim_start_matches("./");
96
97 ignore_set.insert(line);
98 }
99}
100
101
102fn is_matched(ignore_set: &Vec<IgnoreSet>, _current_dir: &PathBuf, entry_path: &PathBuf) -> bool {
107 for ignore in ignore_set.iter() {
108 let relative_path = entry_path.strip_prefix(&ignore.dir).unwrap();
109 let relative_path = relative_path.to_str().unwrap();
110
111 if ignore.is_matched(relative_path) {
112 return true;
114 }
115 }
116 false
117}