jwalk/core/
dir_entry_iter.rs

1use std::iter::Peekable;
2
3use super::*;
4use crate::Result;
5
6/// DirEntry iterator from `WalkDir.into_iter()`.
7///
8/// Yields entries from recursive traversal of filesystem.
9pub struct DirEntryIter<C: ClientState> {
10    min_depth: usize,
11    // iterator yielding next ReadDir results when needed
12    pub(crate) read_dir_iter: Option<Peekable<ReadDirIter<C>>>,
13    // stack of ReadDir results, track location in filesystem traversal
14    read_dir_results_stack: Vec<vec::IntoIter<Result<DirEntry<C>>>>,
15}
16
17impl<C: ClientState> DirEntryIter<C> {
18    pub(crate) fn new(
19        root_entry_results: Vec<Result<DirEntry<C>>>,
20        parallelism: Parallelism,
21        min_depth: usize,
22        root_read_dir_state: C::ReadDirState,
23        core_read_dir_callback: Arc<ReadDirCallback<C>>,
24    ) -> DirEntryIter<C> {
25        // 1. Gather read_dir_specs from root level
26        let read_dir_specs: Vec<_> = root_entry_results
27            .iter()
28            .flat_map(|dir_entry_result| {
29                dir_entry_result
30                    .as_ref()
31                    .ok()?
32                    .read_children_spec(root_read_dir_state.clone())
33            })
34            .collect();
35
36        // 2. Init new read_dir_iter from those specs
37        let read_dir_iter =
38            ReadDirIter::try_new(read_dir_specs, parallelism, core_read_dir_callback)
39                .map(|iter| iter.peekable());
40
41        // 3. Return DirEntryIter that will return initial root entries and then
42        //    fill and process read_dir_iter until complete
43        DirEntryIter {
44            min_depth,
45            read_dir_iter,
46            read_dir_results_stack: vec![root_entry_results.into_iter()],
47        }
48    }
49
50    fn push_next_read_dir_results(
51        iter: &mut Peekable<ReadDirIter<C>>,
52        results: &mut Vec<vec::IntoIter<Result<DirEntry<C>>>>,
53    ) -> Result<()> {
54        // Push next read dir results or return error if read failed
55        let read_dir_result = iter.next().unwrap();
56        let read_dir = match read_dir_result {
57            Ok(read_dir) => read_dir,
58            Err(err) => return Err(err),
59        };
60
61        let ReadDir { results_list, .. } = read_dir;
62        results.push(results_list.into_iter());
63
64        Ok(())
65    }
66}
67
68impl<C: ClientState> Iterator for DirEntryIter<C> {
69    type Item = Result<DirEntry<C>>;
70    fn next(&mut self) -> Option<Self::Item> {
71        loop {
72            // 1. Get current read dir results iter from top of stack
73            let top_read_dir_results = self.read_dir_results_stack.last_mut()?;
74
75            // 2. If more results in current read dir then process
76            if let Some(dir_entry_result) = top_read_dir_results.next() {
77                // 2.1 Handle error case
78                let mut dir_entry = match dir_entry_result {
79                    Ok(dir_entry) => dir_entry,
80                    Err(err) => return Some(Err(err)),
81                };
82                // 2.2 If dir_entry has a read_children_path means we need to read a new
83                // directory and push those results onto read_dir_results_stack
84                if dir_entry.read_children_path.is_some() {
85                    let iter = match self.read_dir_iter.as_mut().ok_or_else(Error::busy) {
86                        Ok(iter) => iter,
87                        Err(err) => return Some(Err(err)),
88                    };
89                    if let Err(err) =
90                        Self::push_next_read_dir_results(iter, &mut self.read_dir_results_stack)
91                    {
92                        dir_entry.read_children_error = Some(err);
93                    }
94                }
95
96                if dir_entry.depth >= self.min_depth {
97                    // 2.3 Finished, return dir_entry
98                    return Some(Ok(dir_entry));
99                }
100            } else {
101                // If no more results in current then pop stack
102                self.read_dir_results_stack.pop();
103            }
104        }
105    }
106}