Skip to main content

litcheck_core/fs/
filesearch.rs

1use std::path::Path;
2
3/// Searches `dir` for files which match `predicate`.
4///
5/// If `recursive` is false, the search only looks in `dir` and no deeper.
6/// If `recursive` is true, the search will always recurse into child directories.
7///
8/// NOTE: This search does not follow symlinks.
9pub fn search_directory<P, F>(
10    dir: P,
11    recursive: bool,
12    mut predicate: F,
13) -> impl Iterator<Item = Result<walkdir::DirEntry, walkdir::Error>>
14where
15    P: AsRef<Path>,
16    F: Fn(&walkdir::DirEntry) -> bool,
17{
18    Searcher::new(dir.as_ref(), recursive, move |e| {
19        apply_search_filter(e, &mut predicate)
20    })
21}
22
23pub struct Searcher<P> {
24    walker: walkdir::FilterEntry<walkdir::IntoIter, P>,
25}
26impl<P> Searcher<P>
27where
28    P: FnMut(&walkdir::DirEntry) -> bool,
29{
30    pub fn new(path: &Path, recursive: bool, predicate: P) -> Self {
31        use walkdir::WalkDir;
32
33        let mut walker = WalkDir::new(path).follow_links(true);
34        if !recursive {
35            walker = walker.max_depth(1);
36        }
37        let walker = walker.into_iter().filter_entry(predicate);
38
39        Self { walker }
40    }
41
42    #[inline(always)]
43    pub fn into_walker(self) -> walkdir::FilterEntry<walkdir::IntoIter, P> {
44        self.walker
45    }
46}
47
48impl<P> Iterator for Searcher<P>
49where
50    P: FnMut(&walkdir::DirEntry) -> bool,
51{
52    type Item = Result<walkdir::DirEntry, walkdir::Error>;
53
54    #[inline]
55    fn next(&mut self) -> Option<Self::Item> {
56        #[allow(clippy::while_let_on_iterator)]
57        while let Some(entry) = self.walker.next() {
58            if let Ok(entry) = entry {
59                if entry.file_type().is_dir() {
60                    continue;
61                }
62                return Some(Ok(entry));
63            } else {
64                return Some(entry);
65            }
66        }
67
68        None
69    }
70}
71
72#[inline]
73fn apply_search_filter<F>(entry: &walkdir::DirEntry, predicate: &mut F) -> bool
74where
75    F: FnMut(&walkdir::DirEntry) -> bool,
76{
77    let path = entry.path();
78    if path.is_dir() {
79        return true;
80    }
81    predicate(entry)
82}