1#[cfg(feature = "walkdir")]
10mod shared {
11 pub enum Parallelism {
13 Serial,
15 ThreadPoolPerTraversal {
17 thread_name: &'static str,
19 },
20 }
21}
22
23#[cfg(any(feature = "walkdir", feature = "fs-read-dir"))]
24mod walkdir_precompose {
25 use std::{borrow::Cow, ffi::OsStr, path::Path};
26
27 #[derive(Debug)]
28 pub struct DirEntry<T: std::fmt::Debug> {
29 inner: T,
30 precompose_unicode: bool,
31 }
32
33 impl<T: std::fmt::Debug> DirEntry<T> {
34 pub fn new(inner: T, precompose_unicode: bool) -> Self {
36 Self {
37 inner,
38 precompose_unicode,
39 }
40 }
41 }
42
43 pub trait DirEntryApi {
44 fn path(&self) -> Cow<'_, Path>;
45 fn file_name(&self) -> Cow<'_, OsStr>;
46 fn file_type(&self) -> std::io::Result<std::fs::FileType>;
47 }
48
49 impl<T: DirEntryApi + std::fmt::Debug> DirEntry<T> {
50 pub fn path(&self) -> Cow<'_, Path> {
55 let path = self.inner.path();
56 if self.precompose_unicode {
57 gix_utils::str::precompose_path(path)
58 } else {
59 path
60 }
61 }
62
63 pub fn file_name(&self) -> Cow<'_, OsStr> {
65 let name = self.inner.file_name();
66 if self.precompose_unicode {
67 gix_utils::str::precompose_os_string(name)
68 } else {
69 name
70 }
71 }
72
73 pub fn file_type(&self) -> std::io::Result<std::fs::FileType> {
77 self.inner.file_type()
78 }
79 }
80
81 #[cfg(feature = "walkdir")]
84 pub struct WalkDir<T> {
85 pub(crate) inner: Option<T>,
86 pub(crate) precompose_unicode: bool,
87 }
88
89 #[cfg(feature = "walkdir")]
90 pub struct WalkDirIter<T, I, E>
91 where
92 T: Iterator<Item = Result<I, E>>,
93 I: DirEntryApi,
94 {
95 pub(crate) inner: T,
96 pub(crate) precompose_unicode: bool,
97 }
98
99 #[cfg(feature = "walkdir")]
100 impl<T, I, E> Iterator for WalkDirIter<T, I, E>
101 where
102 T: Iterator<Item = Result<I, E>>,
103 I: DirEntryApi + std::fmt::Debug,
104 {
105 type Item = Result<DirEntry<I>, E>;
106
107 fn next(&mut self) -> Option<Self::Item> {
108 self.inner
109 .next()
110 .map(|res| res.map(|entry| DirEntry::new(entry, self.precompose_unicode)))
111 }
112 }
113}
114
115#[cfg(feature = "fs-read-dir")]
117pub mod read_dir {
118 use std::{borrow::Cow, ffi::OsStr, fs::FileType, path::Path};
119
120 pub type DirEntry = super::walkdir_precompose::DirEntry<std::fs::DirEntry>;
122
123 impl super::walkdir_precompose::DirEntryApi for std::fs::DirEntry {
124 fn path(&self) -> Cow<'_, Path> {
125 self.path().into()
126 }
127
128 fn file_name(&self) -> Cow<'_, OsStr> {
129 self.file_name().into()
130 }
131
132 fn file_type(&self) -> std::io::Result<FileType> {
133 self.file_type()
134 }
135 }
136}
137
138#[cfg(feature = "walkdir")]
140pub mod walkdir {
141 use std::{borrow::Cow, ffi::OsStr, fs::FileType, path::Path};
142
143 pub use walkdir::Error;
144 use walkdir::{DirEntry as DirEntryImpl, WalkDir as WalkDirImpl};
145
146 pub type DirEntry = super::walkdir_precompose::DirEntry<DirEntryImpl>;
148 pub type WalkDir = super::walkdir_precompose::WalkDir<WalkDirImpl>;
150
151 pub use super::shared::Parallelism;
152
153 impl super::walkdir_precompose::DirEntryApi for DirEntryImpl {
154 fn path(&self) -> Cow<'_, Path> {
155 self.path().into()
156 }
157
158 fn file_name(&self) -> Cow<'_, OsStr> {
159 self.file_name().into()
160 }
161
162 fn file_type(&self) -> std::io::Result<FileType> {
163 Ok(self.file_type())
164 }
165 }
166
167 impl IntoIterator for WalkDir {
168 type Item = Result<DirEntry, walkdir::Error>;
169 type IntoIter = DirEntryIter;
170
171 fn into_iter(self) -> Self::IntoIter {
172 DirEntryIter {
173 inner: self.inner.expect("always set (builder fix)").into_iter(),
174 precompose_unicode: self.precompose_unicode,
175 }
176 }
177 }
178
179 impl WalkDir {
180 pub fn min_depth(mut self, min: usize) -> Self {
182 self.inner = Some(self.inner.take().expect("always set").min_depth(min));
183 self
184 }
185 pub fn max_depth(mut self, max: usize) -> Self {
187 self.inner = Some(self.inner.take().expect("always set").max_depth(max));
188 self
189 }
190 pub fn follow_links(mut self, toggle: bool) -> Self {
192 self.inner = Some(self.inner.take().expect("always set").follow_links(toggle));
193 self
194 }
195 }
196
197 pub fn walkdir_new(root: &Path, _: Parallelism, precompose_unicode: bool) -> WalkDir {
201 WalkDir {
202 inner: WalkDirImpl::new(root).into(),
203 precompose_unicode,
204 }
205 }
206
207 pub fn walkdir_sorted_new(root: &Path, _: Parallelism, max_depth: usize, precompose_unicode: bool) -> WalkDir {
218 WalkDir {
219 inner: WalkDirImpl::new(root)
220 .max_depth(max_depth)
221 .sort_by(|a, b| {
222 let storage_a;
223 let storage_b;
224 let a_name = match gix_path::os_str_into_bstr(a.file_name()) {
225 Ok(f) => f,
226 Err(_) => {
227 storage_a = a.file_name().to_string_lossy();
228 storage_a.as_ref().into()
229 }
230 };
231 let b_name = match gix_path::os_str_into_bstr(b.file_name()) {
232 Ok(f) => f,
233 Err(_) => {
234 storage_b = b.file_name().to_string_lossy();
235 storage_b.as_ref().into()
236 }
237 };
238 let common = a_name.len().min(b_name.len());
240 a_name[..common].cmp(&b_name[..common]).then_with(|| {
241 let a = a_name.get(common).or_else(|| a.file_type().is_dir().then_some(&b'/'));
242 let b = b_name.get(common).or_else(|| b.file_type().is_dir().then_some(&b'/'));
243 a.cmp(&b)
244 })
245 })
246 .into(),
247 precompose_unicode,
248 }
249 }
250
251 pub type DirEntryIter = super::walkdir_precompose::WalkDirIter<walkdir::IntoIter, DirEntryImpl, walkdir::Error>;
253}
254
255#[cfg(feature = "walkdir")]
256pub use self::walkdir::{walkdir_new, walkdir_sorted_new, WalkDir};
257
258pub fn open_options_no_follow() -> std::fs::OpenOptions {
262 #[cfg_attr(not(unix), allow(unused_mut))]
263 let mut options = std::fs::OpenOptions::new();
264 #[cfg(unix)]
265 {
266 use std::os::unix::fs::OpenOptionsExt;
270 options.custom_flags(libc::O_NOFOLLOW);
271 }
272 options
273}