Skip to main content

weavatrix_scan/
walk_iter.rs

1use crate::walk_types::{WalkEntry, WalkError, WalkOperation};
2use crate::walker::Walker;
3
4impl Iterator for Walker {
5    type Item = Result<WalkEntry, WalkError>;
6
7    #[inline]
8    fn next(&mut self) -> Option<Self::Item> {
9        'walk: loop {
10            if self.finished {
11                return None;
12            }
13            if let Some(entry) = self.deferred_entry.take() {
14                return Some(Ok(entry));
15            }
16            if self.yield_root {
17                if let Some(entry) = self.take_root_entry() {
18                    return Some(entry);
19                }
20                continue;
21            }
22            if let Some(error) = self.schedule_pending_directory() {
23                return Some(self.yield_error(error));
24            }
25            loop {
26                let frame = self.frames.last_mut()?;
27                let depth = frame.depth + 1;
28                let entry = match frame.entries.next() {
29                    Some(Ok(entry)) => entry,
30                    Some(Err(source)) => {
31                        let error =
32                            WalkError::new(&frame.path, depth, WalkOperation::ReadEntry, source);
33                        return Some(self.yield_error(error));
34                    }
35                    None => {
36                        let was_open = frame.entries.is_open();
37                        let identity = frame.identity;
38                        let post_entry = self.frames.pop().expect("last frame exists").post_entry;
39                        if was_open {
40                            self.open_handles -= 1;
41                        }
42                        if let Some(identity) = identity {
43                            self.active_directories.remove(&identity);
44                        }
45                        if let Some(entry) = post_entry {
46                            return Some(Ok(entry));
47                        }
48                        continue;
49                    }
50                };
51                let path = entry.path();
52                let file_type = match entry.file_type() {
53                    Ok(file_type) => file_type,
54                    Err(source) => {
55                        let error =
56                            WalkError::new(path, depth, WalkOperation::ReadMetadata, source);
57                        return Some(self.yield_error(error));
58                    }
59                };
60                if self.plain_entries {
61                    return Some(Ok(self.visit_plain(path, depth, file_type)));
62                }
63                let (bytes, version) = if self.options.collect_metadata && file_type.is_file() {
64                    match entry.metadata() {
65                        Ok(metadata) => (
66                            Some(metadata.len()),
67                            Some(crate::file_version::from_metadata(&metadata)),
68                        ),
69                        Err(source) => {
70                            let error =
71                                WalkError::new(path, depth, WalkOperation::ReadMetadata, source);
72                            return Some(self.yield_error(error));
73                        }
74                    }
75                } else {
76                    (None, None)
77                };
78                match self.visit(path, depth, Some(file_type), bytes, version) {
79                    Ok(entry) => {
80                        if let Some(entry) = self.prepare_entry(entry) {
81                            return Some(Ok(entry));
82                        }
83                        continue 'walk;
84                    }
85                    Err(error) => return Some(self.yield_error(error)),
86                }
87            }
88        }
89    }
90}
91
92impl Walker {
93    fn take_root_entry(&mut self) -> Option<Result<WalkEntry, WalkError>> {
94        self.yield_root = false;
95        let root = self.root.as_ref().clone();
96        if self.plain_entries {
97            return Some(Ok(self.visit_plain(
98                root,
99                0,
100                self.root_file_type.expect("root metadata is present"),
101            )));
102        }
103        match self.visit(
104            root,
105            0,
106            Some(self.root_file_type.expect("root metadata is present")),
107            self.root_bytes,
108            self.root_version,
109        ) {
110            Ok(entry) => self.prepare_entry(entry).map(Ok),
111            Err(error) => Some(self.yield_error(error)),
112        }
113    }
114
115    fn prepare_entry(&mut self, entry: WalkEntry) -> Option<WalkEntry> {
116        if entry.is_file()
117            && self.skip_stdout.is_some_and(|identity| {
118                crate::stdout::path_matches(entry.path(), identity).unwrap_or(false)
119            })
120        {
121            return None;
122        }
123        let accepted = self.filter.as_ref().is_none_or(|filter| filter(&entry));
124        if !accepted {
125            if entry.is_dir() {
126                self.skip_current_dir();
127                if entry.depth() == 0 {
128                    self.finished = true;
129                }
130            }
131            return None;
132        }
133        if self.contents_first && entry.is_dir() && entry.skip_reason().is_none() {
134            if entry.depth() >= self.options.min_depth
135                && let Some(pending) = self.pending_directory.as_mut()
136            {
137                pending.post_entry = Some(entry);
138            }
139            return None;
140        }
141        if entry.depth() >= self.options.min_depth {
142            Some(entry)
143        } else {
144            None
145        }
146    }
147}