walkthrough/sync/
state.rs1use std::{cmp::Ordering, fmt, fs, path::PathBuf, vec};
2
3use crate::{Ancestor, DirEntry, Error, Result, WalkDir, iter::WalkDirOptions};
4
5#[derive(Debug)]
7pub struct Sync;
8
9struct LiveDirIter {
10 rd: fs::ReadDir,
11 path: PathBuf,
12 depth: usize,
13 follow_links: bool,
14}
15
16impl Iterator for LiveDirIter {
17 type Item = Result<DirEntry<Sync>>;
18
19 fn next(&mut self) -> Option<Self::Item> {
20 match self.rd.next()? {
21 Ok(raw) => Some(DirEntry::<Sync>::from_std(
22 &raw,
23 self.depth,
24 self.follow_links,
25 )),
26 Err(err) => Some(Err(Error::new_io_error(self.path.clone(), self.depth, err))),
27 }
28 }
29}
30
31enum DirStream {
32 Live(Box<LiveDirIter>),
33 Sorted(vec::IntoIter<Result<DirEntry<Sync>>>),
34}
35
36impl Iterator for DirStream {
37 type Item = Result<DirEntry<Sync>>;
38
39 fn next(&mut self) -> Option<Self::Item> {
40 match self {
41 DirStream::Live(iter) => iter.next(),
42 DirStream::Sorted(iter) => iter.next(),
43 }
44 }
45}
46
47impl fmt::Debug for DirStream {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 match self {
50 DirStream::Live(_) => f.write_str("DirStream::Live"),
51 DirStream::Sorted(_) => f.write_str("DirStream::Sorted"),
52 }
53 }
54}
55
56#[derive(Debug)]
58pub struct Walker {
59 opts: WalkDirOptions<Sync>,
60 ancestors: Vec<Ancestor>,
61 stack: Vec<DirStream>,
62 start: Option<Result<DirEntry<Sync>>>,
63}
64
65impl IntoIterator for WalkDir {
66 type IntoIter = Walker;
67 type Item = Result<DirEntry<Sync>>;
68
69 fn into_iter(self) -> Self::IntoIter {
70 let start = DirEntry::<Sync>::from_path(self.root, 0, self.opts.follow_links);
71 Walker {
72 start: Some(start),
73 stack: vec![],
74 ancestors: vec![],
75 opts: self.opts,
76 }
77 }
78}
79
80impl Walker {
81 fn push_dir(&mut self, entry: &DirEntry<Sync>) -> Result<()> {
82 let depth = entry.depth();
83 self.ancestors.truncate(depth);
86
87 if let Some(ancestor) = entry.ancestor() {
88 if self.ancestors.iter().any(|a| a == &ancestor) {
89 return Err(Error::loop_detected(entry.path().to_path_buf(), depth));
90 }
91 self.ancestors.push(ancestor);
92 }
93
94 let path = entry.path().to_path_buf();
95 let child_depth = depth + 1;
96 let follow_links = self.opts.follow_links;
97
98 if self.opts.sort_by.is_some() || self.opts.group_dir {
99 let entries = self.collect_sorted(&path, child_depth)?;
100 self.stack.push(DirStream::Sorted(entries.into_iter()));
101 } else {
102 let rd = fs::read_dir(&path)
103 .map_err(|err| Error::new_io_error(path.clone(), child_depth, err))?;
104 self.stack.push(DirStream::Live(Box::new(LiveDirIter {
105 rd,
106 path,
107 depth: child_depth,
108 follow_links,
109 })));
110 }
111 Ok(())
112 }
113
114 fn collect_sorted(
115 &mut self,
116 path: &std::path::Path,
117 depth: usize,
118 ) -> Result<Vec<Result<DirEntry<Sync>>>> {
119 let follow_links = self.opts.follow_links;
120
121 let rd = fs::read_dir(path)
122 .map_err(|err| Error::new_io_error(path.to_path_buf(), depth, err))?;
123
124 let mut entries: Vec<Result<DirEntry<Sync>>> = rd
125 .map(|res| {
126 res.map_err(|err| Error::new_io_error(path.to_path_buf(), depth, err))
127 .and_then(|raw| DirEntry::<Sync>::from_std(&raw, depth, follow_links))
128 })
129 .collect();
130
131 if let Some(ref mut sorter) = self.opts.sort_by {
132 entries.sort_by(|a, b| match (a, b) {
133 (Ok(a), Ok(b)) => sorter.cmp(a, b),
134 (Err(_), Ok(_)) => Ordering::Less,
135 (Ok(_), Err(_)) => Ordering::Greater,
136 (Err(_), Err(_)) => Ordering::Equal,
137 });
138 }
139
140 if self.opts.group_dir {
141 entries.sort_by(|a, b| {
142 let a_dir = a.as_ref().is_ok_and(DirEntry::is_dir);
143 let b_dir = b.as_ref().is_ok_and(DirEntry::is_dir);
144 b_dir.cmp(&a_dir)
145 });
146 }
147
148 Ok(entries)
149 }
150}
151
152impl Iterator for Walker {
153 type Item = Result<DirEntry<Sync>>;
154
155 fn next(&mut self) -> Option<Self::Item> {
156 if let Some(res) = self.start.take() {
159 let entry = match res {
160 Err(err) => return Some(Err(err)),
161 Ok(e) => e,
162 };
163 if entry.is_dir()
164 && entry.depth() < self.opts.max_depth
165 && let Err(err) = self.push_dir(&entry)
166 {
167 return Some(Err(err));
168 }
169 if entry.depth() >= self.opts.min_depth {
170 return Some(Ok(entry));
171 }
172 }
173
174 loop {
175 let res = {
176 let stream = self.stack.last_mut()?;
177 match stream.next() {
178 Some(res) => res,
179 None => {
180 self.stack.pop();
181 continue;
182 }
183 }
184 };
185
186 let entry = match res {
187 Err(err) => return Some(Err(err)),
188 Ok(e) => e,
189 };
190
191 if self.opts.skip_hidden && entry.is_hidden() {
192 continue;
193 }
194
195 if entry.is_dir()
196 && entry.depth() < self.opts.max_depth
197 && let Err(err) = self.push_dir(&entry)
198 {
199 return Some(Err(err));
200 }
201
202 if entry.depth() >= self.opts.min_depth {
203 return Some(Ok(entry));
204 }
205 }
206 }
207}