keepr/
lib.rs

1use std::fs;
2use std::path::Path;
3use regex::Regex;
4
5
6use serde::{Serialize, Deserialize};
7
8#[derive(Serialize, Deserialize)]
9pub struct Finder {
10    pub ignore_dirs: Vec<String>,
11    pub ignore_files: Vec<String>,
12    pub match_files: Vec<String>,
13    pub max_depth: Option<usize>,
14
15    #[serde(default)]
16    pub filter_path: Vec<String>,
17}
18
19struct FinderReg {
20    ignore_dirs: Vec<Regex>,
21    ignore_files: Vec<Regex>,
22    match_files: Vec<Regex>,
23    max_depth: usize,
24    filter_path: Vec<Regex>,
25}
26
27fn check_dir(path:&Path, patterns: &Vec<Regex>, default:bool) -> bool {
28    if patterns.len() == 0 {
29	return default;
30    }
31    if let Some(path) = path.to_str() {
32	return patterns.iter().any(|reg| {reg.is_match(path)});
33    }
34
35    return false;
36}
37
38fn check_file(path:&Path, patterns: &Vec<Regex>, default:bool) -> bool { 
39    if patterns.len() == 0 {
40	return default;
41    }
42    if let Some(filename) = path.file_name().and_then(|n| n.to_str()) {
43	return patterns.iter().any(|reg| {reg.is_match(filename)});
44    }
45
46    return false;
47}
48
49
50impl Finder {
51    pub fn find <P: AsRef<Path>>(&self, path: P, cb: &mut impl FnMut(&Path)) {
52	let _conf =  FinderReg {
53	    ignore_files :
54	    self.ignore_files
55		.iter()
56		.filter_map(|p| Regex::new(&p).ok()).collect(),
57	    
58	    match_files :
59	    self.match_files
60		.iter()
61		.filter_map(|p| Regex::new(&p).ok()).collect(),
62	    ignore_dirs :
63	    self.ignore_dirs.iter()
64		.filter_map(|p| Regex::new(&p).ok()).collect(),
65	    max_depth:self.max_depth.unwrap_or(100),
66	    filter_path:
67	    self.filter_path
68		.iter()
69		.filter_map(|p| Regex::new(&p).ok()).collect()
70	};
71	
72	self._find(path, cb, &_conf, 0);
73	
74    }
75
76    fn _find<P: AsRef<Path>>(&self,
77			     path: P,
78			     cb: &mut impl FnMut(&Path),
79			     conf:&FinderReg,
80			     depth:usize) {
81	if depth > conf.max_depth {
82	    return
83	}
84	if let Ok(entries) = fs::read_dir(path) {
85            for entry in entries.flatten() {
86		let path = entry.path();
87
88		
89		if path.is_dir() {
90		    if !check_dir(&path, &conf.ignore_dirs, false) {
91			self._find(&path, cb, conf, depth + 1);
92		    }
93		} else if path.is_file() {
94		    if !check_dir(&path, &conf.filter_path, true) {
95			continue;
96		    }
97		    
98		    if !check_file(&path, &conf.ignore_files, false)
99			&& check_file(&path, &conf.match_files, true) {
100			    cb(&path);
101			}
102		}
103	    }
104	}
105    }
106}
107
108
109#[cfg(test)]
110mod tests {
111    use super::*;
112    use std::path::Path;
113
114    #[test]
115    fn test_check_file_match() {
116	
117	let regex:Vec<Regex> = vec![Regex::new("error").unwrap(),
118				    Regex::new("\\.rs$").unwrap()];
119	let empty: Vec<Regex>= vec![];
120	{
121	    let path0 = Path::new("foo/error.log");
122	    assert!(check_file(path0, &empty, true));
123
124	    let path1 = Path::new("coin");
125	    assert!(check_file(path1, &empty, true));
126	}{
127	    let path = Path::new("foo/error.log");
128	    assert!(check_file(path, &regex, true));
129	}{
130	    let path = Path::new("error.py");
131	    assert!(check_file(path, &regex, true));
132	}{
133	    let path = Path::new("coin.rs");
134	    assert!(check_file(path, &regex, true));
135	}{
136	    let path = Path::new("rscorsin.py");
137	    assert!(!check_file(path, &regex, true));
138	}
139    }
140
141    #[test]
142    fn test_check_dir_match() {
143	let regex = vec![Regex::new("error").unwrap(),
144			 Regex::new("\\.rs$").unwrap()];
145	let empty: Vec<Regex> = vec![];
146	{
147	    let path = Path::new("/home/user/node_modules/awesome");
148	    assert!(check_dir(path, &empty, true));
149	}{
150	    let path = Path::new("/home/user/node_modules/awesome");
151	    assert!(!check_dir(path, &regex, true));
152	}{
153	    let path = Path::new("/home/user/.git/awesome");
154	    assert!(!check_dir(path, &regex, true));
155	}{
156	    let path = Path::new("/home/user_modules/awesome");
157	    assert!(!check_dir(path, &regex, true));
158	}{
159	    let path = Path::new("/home/user/git/awesome");
160	    assert!(!check_dir(path, &regex, true));
161	}
162    }
163}