Skip to main content

rs_dot_ignore/
lib.rs

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
10/// Walk Dir
11///
12///
13///
14/// ```
15/// let base_dir = std::env::current_dir().unwrap();
16///
17/// rs_dot_ignore::walk_dir(base_dir.to_str().unwrap(), |entry| {
18///     let entry_path = entry.path();
19///     let path = entry_path.to_str().unwrap();
20///
21///     if entry_path.is_dir() {
22///         println!("Dir : {path}");
23///     } else {
24///         println!("File: {path}");
25///     }
26/// });
27/// ```
28///
29pub 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
37/// Dot Ignore Recursive Walk Dir
38///
39///
40fn 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    // setup scope ignore set
47    let mut new_ignore_set = IgnoreSet::new(current_dir);
48
49    read_dot_ignore(&current_dir, &mut new_ignore_set);
50
51    new_ignore_set.compile();
52
53    ignore_set.push(new_ignore_set);
54
55    // read dir
56    for entry in std::fs::read_dir(&current_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        // recursive call
69        if entry_path.is_dir() {
70            dot_ignore_recursive_walk_dir(base_dir, &entry_path, ignore_set, callback);
71        }
72    }
73
74    // cleaning scope ignore set
75    ignore_set.pop();
76}
77
78/// Read Dot Ignore
79///
80///
81fn 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
102/// Is Matched
103/// 
104/// 
105/// 
106fn 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            // println!("          > {relative_path}"); // Debug
113            return true;
114        }
115    }
116    false
117}