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, hidden) = if self.options.collect_metadata
64                    && file_type.is_file()
65                {
66                    match entry.metadata() {
67                        Ok(metadata) => (
68                            Some(metadata.len()),
69                            Some(crate::file_version::from_metadata(&metadata)),
70                            Some(crate::hidden::is_hidden_metadata(&path, &metadata)),
71                        ),
72                        Err(source) => {
73                            let error =
74                                WalkError::new(path, depth, WalkOperation::ReadMetadata, source);
75                            return Some(self.yield_error(error));
76                        }
77                    }
78                } else {
79                    (None, None, None)
80                };
81                match self.visit(path, depth, Some(file_type), bytes, version, hidden) {
82                    Ok(entry) => {
83                        if let Some(entry) = self.prepare_entry(entry) {
84                            return Some(Ok(entry));
85                        }
86                        continue 'walk;
87                    }
88                    Err(error) => return Some(self.yield_error(error)),
89                }
90            }
91        }
92    }
93}
94
95impl Walker {
96    fn take_root_entry(&mut self) -> Option<Result<WalkEntry, WalkError>> {
97        self.yield_root = false;
98        let root = self.root.as_ref().clone();
99        if self.plain_entries {
100            return Some(Ok(self.visit_plain(
101                root,
102                0,
103                self.root_file_type.expect("root metadata is present"),
104            )));
105        }
106        match self.visit(
107            root,
108            0,
109            Some(self.root_file_type.expect("root metadata is present")),
110            self.root_bytes,
111            self.root_version,
112            None,
113        ) {
114            Ok(entry) => self.prepare_entry(entry).map(Ok),
115            Err(error) => Some(self.yield_error(error)),
116        }
117    }
118
119    fn prepare_entry(&mut self, entry: WalkEntry) -> Option<WalkEntry> {
120        if entry.is_file()
121            && self.skip_stdout.is_some_and(|identity| {
122                crate::stdout::path_matches(entry.path(), identity).unwrap_or(false)
123            })
124        {
125            return None;
126        }
127        let accepted = self.filter.as_ref().is_none_or(|filter| filter(&entry));
128        if !accepted {
129            if entry.is_dir() {
130                self.skip_current_dir();
131                if entry.depth() == 0 {
132                    self.finished = true;
133                }
134            }
135            return None;
136        }
137        if self.contents_first && entry.is_dir() && entry.skip_reason().is_none() {
138            if entry.depth() >= self.options.min_depth
139                && let Some(pending) = self.pending_directory.as_mut()
140            {
141                pending.post_entry = Some(entry);
142            }
143            return None;
144        }
145        if entry.depth() >= self.options.min_depth {
146            Some(entry)
147        } else {
148            None
149        }
150    }
151}