1#[cfg(any(feature = "walkdir", feature = "fs-walkdir-parallel"))]
11mod shared {
12 pub enum Parallelism {
14 Serial,
16 ThreadPoolPerTraversal {
18 thread_name: &'static str,
20 },
21 }
22}
23
24#[cfg(any(feature = "walkdir", feature = "fs-walkdir-parallel", feature = "fs-read-dir"))]
25mod walkdir_precompose {
26 use std::borrow::Cow;
27 use std::ffi::OsStr;
28 use std::path::Path;
29
30 #[derive(Debug)]
31 pub struct DirEntry<T: std::fmt::Debug> {
32 inner: T,
33 precompose_unicode: bool,
34 }
35
36 impl<T: std::fmt::Debug> DirEntry<T> {
37 pub fn new(inner: T, precompose_unicode: bool) -> Self {
39 Self {
40 inner,
41 precompose_unicode,
42 }
43 }
44 }
45
46 pub trait DirEntryApi {
47 fn path(&self) -> Cow<'_, Path>;
48 fn file_name(&self) -> Cow<'_, OsStr>;
49 fn file_type(&self) -> std::io::Result<std::fs::FileType>;
50 }
51
52 impl<T: DirEntryApi + std::fmt::Debug> DirEntry<T> {
53 pub fn path(&self) -> Cow<'_, Path> {
58 let path = self.inner.path();
59 if self.precompose_unicode {
60 gix_utils::str::precompose_path(path)
61 } else {
62 path
63 }
64 }
65
66 pub fn file_name(&self) -> Cow<'_, OsStr> {
68 let name = self.inner.file_name();
69 if self.precompose_unicode {
70 gix_utils::str::precompose_os_string(name)
71 } else {
72 name
73 }
74 }
75
76 pub fn file_type(&self) -> std::io::Result<std::fs::FileType> {
80 self.inner.file_type()
81 }
82 }
83
84 #[cfg(any(feature = "walkdir", feature = "fs-walkdir-parallel"))]
87 pub struct WalkDir<T> {
88 pub(crate) inner: Option<T>,
89 pub(crate) precompose_unicode: bool,
90 }
91
92 #[cfg(any(feature = "walkdir", feature = "fs-walkdir-parallel"))]
93 pub struct WalkDirIter<T, I, E>
94 where
95 T: Iterator<Item = Result<I, E>>,
96 I: DirEntryApi,
97 {
98 pub(crate) inner: T,
99 pub(crate) precompose_unicode: bool,
100 }
101
102 #[cfg(any(feature = "walkdir", feature = "fs-walkdir-parallel"))]
103 impl<T, I, E> Iterator for WalkDirIter<T, I, E>
104 where
105 T: Iterator<Item = Result<I, E>>,
106 I: DirEntryApi + std::fmt::Debug,
107 {
108 type Item = Result<DirEntry<I>, E>;
109
110 fn next(&mut self) -> Option<Self::Item> {
111 self.inner
112 .next()
113 .map(|res| res.map(|entry| DirEntry::new(entry, self.precompose_unicode)))
114 }
115 }
116}
117
118#[cfg(feature = "fs-read-dir")]
120pub mod read_dir {
121 use std::borrow::Cow;
122 use std::ffi::OsStr;
123 use std::fs::FileType;
124 use std::path::Path;
125
126 pub type DirEntry = super::walkdir_precompose::DirEntry<std::fs::DirEntry>;
128
129 impl super::walkdir_precompose::DirEntryApi for std::fs::DirEntry {
130 fn path(&self) -> Cow<'_, Path> {
131 self.path().into()
132 }
133
134 fn file_name(&self) -> Cow<'_, OsStr> {
135 self.file_name().into()
136 }
137
138 fn file_type(&self) -> std::io::Result<FileType> {
139 self.file_type()
140 }
141 }
142}
143
144#[cfg(feature = "fs-walkdir-parallel")]
146pub mod walkdir {
147 use std::borrow::Cow;
148 use std::ffi::OsStr;
149 use std::fs::FileType;
150 use std::path::Path;
151
152 use jwalk::WalkDir as WalkDirImpl;
153 pub use jwalk::{DirEntry as DirEntryGeneric, DirEntryIter as DirEntryIterGeneric, Error};
154
155 pub use super::shared::Parallelism;
156
157 type DirEntryImpl = DirEntryGeneric<((), ())>;
158
159 pub type DirEntry = super::walkdir_precompose::DirEntry<DirEntryImpl>;
161 pub type WalkDir = super::walkdir_precompose::WalkDir<WalkDirImpl>;
163
164 impl super::walkdir_precompose::DirEntryApi for DirEntryImpl {
165 fn path(&self) -> Cow<'_, Path> {
166 self.path().into()
167 }
168
169 fn file_name(&self) -> Cow<'_, OsStr> {
170 self.file_name().into()
171 }
172
173 fn file_type(&self) -> std::io::Result<FileType> {
174 Ok(self.file_type())
175 }
176 }
177
178 impl IntoIterator for WalkDir {
179 type Item = Result<DirEntry, jwalk::Error>;
180 type IntoIter = DirEntryIter;
181
182 fn into_iter(self) -> Self::IntoIter {
183 DirEntryIter {
184 inner: self.inner.expect("always set (builder fix)").into_iter(),
185 precompose_unicode: self.precompose_unicode,
186 }
187 }
188 }
189
190 impl WalkDir {
191 pub fn min_depth(mut self, min: usize) -> Self {
193 self.inner = Some(self.inner.take().expect("always set").min_depth(min));
194 self
195 }
196 pub fn max_depth(mut self, max: usize) -> Self {
198 self.inner = Some(self.inner.take().expect("always set").max_depth(max));
199 self
200 }
201 pub fn follow_links(mut self, toggle: bool) -> Self {
203 self.inner = Some(self.inner.take().expect("always set").follow_links(toggle));
204 self
205 }
206 }
207
208 impl From<Parallelism> for jwalk::Parallelism {
209 fn from(v: Parallelism) -> Self {
210 match v {
211 Parallelism::Serial => jwalk::Parallelism::Serial,
212 Parallelism::ThreadPoolPerTraversal { thread_name } => std::thread::available_parallelism()
213 .map_or_else(
214 |_| Parallelism::Serial.into(),
215 |threads| {
216 let pool = jwalk::rayon::ThreadPoolBuilder::new()
217 .num_threads(threads.get().min(16))
218 .stack_size(128 * 1024)
219 .thread_name(move |idx| format!("{thread_name} {idx}"))
220 .build()
221 .expect("we only set options that can't cause a build failure");
222 jwalk::Parallelism::RayonExistingPool {
223 pool: pool.into(),
224 busy_timeout: None,
225 }
226 },
227 ),
228 }
229 }
230 }
231
232 pub fn walkdir_new(root: &Path, parallelism: Parallelism, precompose_unicode: bool) -> WalkDir {
236 WalkDir {
237 inner: WalkDirImpl::new(root)
238 .skip_hidden(false)
239 .parallelism(parallelism.into())
240 .into(),
241 precompose_unicode,
242 }
243 }
244
245 pub fn walkdir_sorted_new(root: &Path, parallelism: Parallelism, precompose_unicode: bool) -> WalkDir {
249 WalkDir {
250 inner: WalkDirImpl::new(root)
251 .skip_hidden(false)
252 .sort(true)
253 .parallelism(parallelism.into())
254 .into(),
255 precompose_unicode,
256 }
257 }
258
259 type DirEntryIterImpl = DirEntryIterGeneric<((), ())>;
260
261 pub type DirEntryIter = super::walkdir_precompose::WalkDirIter<DirEntryIterImpl, DirEntryImpl, jwalk::Error>;
263}
264
265#[cfg(all(feature = "walkdir", not(feature = "fs-walkdir-parallel")))]
267pub mod walkdir {
268 use std::borrow::Cow;
269 use std::ffi::OsStr;
270 use std::fs::FileType;
271 use std::path::Path;
272
273 pub use walkdir::Error;
274 use walkdir::{DirEntry as DirEntryImpl, WalkDir as WalkDirImpl};
275
276 pub type DirEntry = super::walkdir_precompose::DirEntry<DirEntryImpl>;
278 pub type WalkDir = super::walkdir_precompose::WalkDir<WalkDirImpl>;
280
281 pub use super::shared::Parallelism;
282
283 impl super::walkdir_precompose::DirEntryApi for DirEntryImpl {
284 fn path(&self) -> Cow<'_, Path> {
285 self.path().into()
286 }
287
288 fn file_name(&self) -> Cow<'_, OsStr> {
289 self.file_name().into()
290 }
291
292 fn file_type(&self) -> std::io::Result<FileType> {
293 Ok(self.file_type())
294 }
295 }
296
297 impl IntoIterator for WalkDir {
298 type Item = Result<DirEntry, walkdir::Error>;
299 type IntoIter = DirEntryIter;
300
301 fn into_iter(self) -> Self::IntoIter {
302 DirEntryIter {
303 inner: self.inner.expect("always set (builder fix)").into_iter(),
304 precompose_unicode: self.precompose_unicode,
305 }
306 }
307 }
308
309 impl WalkDir {
310 pub fn min_depth(mut self, min: usize) -> Self {
312 self.inner = Some(self.inner.take().expect("always set").min_depth(min));
313 self
314 }
315 pub fn max_depth(mut self, max: usize) -> Self {
317 self.inner = Some(self.inner.take().expect("always set").max_depth(max));
318 self
319 }
320 pub fn follow_links(mut self, toggle: bool) -> Self {
322 self.inner = Some(self.inner.take().expect("always set").follow_links(toggle));
323 self
324 }
325 }
326
327 pub fn walkdir_new(root: &Path, _: Parallelism, precompose_unicode: bool) -> WalkDir {
331 WalkDir {
332 inner: WalkDirImpl::new(root).into(),
333 precompose_unicode,
334 }
335 }
336
337 pub fn walkdir_sorted_new(root: &Path, _: Parallelism, precompose_unicode: bool) -> WalkDir {
341 WalkDir {
342 inner: WalkDirImpl::new(root).sort_by_file_name().into(),
343 precompose_unicode,
344 }
345 }
346
347 pub type DirEntryIter = super::walkdir_precompose::WalkDirIter<walkdir::IntoIter, DirEntryImpl, walkdir::Error>;
349}
350
351#[cfg(any(feature = "walkdir", feature = "fs-walkdir-parallel"))]
352pub use self::walkdir::{walkdir_new, walkdir_sorted_new, WalkDir};
353
354pub fn open_options_no_follow() -> std::fs::OpenOptions {
358 #[cfg_attr(not(unix), allow(unused_mut))]
359 let mut options = std::fs::OpenOptions::new();
360 #[cfg(unix)]
361 {
362 use std::os::unix::fs::OpenOptionsExt;
366 options.custom_flags(libc::O_NOFOLLOW);
367 }
368 options
369}