/rust/registry/src/index.crates.io-1949cf8c6b5b557f/walkdir-2.5.0/src/dent.rs
Line | Count | Source |
1 | | use std::ffi::OsStr; |
2 | | use std::fmt; |
3 | | use std::fs::{self, FileType}; |
4 | | use std::path::{Path, PathBuf}; |
5 | | |
6 | | use crate::error::Error; |
7 | | use crate::Result; |
8 | | |
9 | | /// A directory entry. |
10 | | /// |
11 | | /// This is the type of value that is yielded from the iterators defined in |
12 | | /// this crate. |
13 | | /// |
14 | | /// On Unix systems, this type implements the [`DirEntryExt`] trait, which |
15 | | /// provides efficient access to the inode number of the directory entry. |
16 | | /// |
17 | | /// # Differences with `std::fs::DirEntry` |
18 | | /// |
19 | | /// This type mostly mirrors the type by the same name in [`std::fs`]. There |
20 | | /// are some differences however: |
21 | | /// |
22 | | /// * All recursive directory iterators must inspect the entry's type. |
23 | | /// Therefore, the value is stored and its access is guaranteed to be cheap and |
24 | | /// successful. |
25 | | /// * [`path`] and [`file_name`] return borrowed variants. |
26 | | /// * If [`follow_links`] was enabled on the originating iterator, then all |
27 | | /// operations except for [`path`] operate on the link target. Otherwise, all |
28 | | /// operations operate on the symbolic link. |
29 | | /// |
30 | | /// [`std::fs`]: https://doc.rust-lang.org/stable/std/fs/index.html |
31 | | /// [`path`]: #method.path |
32 | | /// [`file_name`]: #method.file_name |
33 | | /// [`follow_links`]: struct.WalkDir.html#method.follow_links |
34 | | /// [`DirEntryExt`]: trait.DirEntryExt.html |
35 | | pub struct DirEntry { |
36 | | /// The path as reported by the [`fs::ReadDir`] iterator (even if it's a |
37 | | /// symbolic link). |
38 | | /// |
39 | | /// [`fs::ReadDir`]: https://doc.rust-lang.org/stable/std/fs/struct.ReadDir.html |
40 | | path: PathBuf, |
41 | | /// The file type. Necessary for recursive iteration, so store it. |
42 | | ty: FileType, |
43 | | /// Is set when this entry was created from a symbolic link and the user |
44 | | /// expects the iterator to follow symbolic links. |
45 | | follow_link: bool, |
46 | | /// The depth at which this entry was generated relative to the root. |
47 | | depth: usize, |
48 | | /// The underlying inode number (Unix only). |
49 | | #[cfg(unix)] |
50 | | ino: u64, |
51 | | /// The underlying metadata (Windows only). We store this on Windows |
52 | | /// because this comes for free while reading a directory. |
53 | | /// |
54 | | /// We use this to determine whether an entry is a directory or not, which |
55 | | /// works around a bug in Rust's standard library: |
56 | | /// https://github.com/rust-lang/rust/issues/46484 |
57 | | #[cfg(windows)] |
58 | | metadata: fs::Metadata, |
59 | | } |
60 | | |
61 | | impl DirEntry { |
62 | | /// The full path that this entry represents. |
63 | | /// |
64 | | /// The full path is created by joining the parents of this entry up to the |
65 | | /// root initially given to [`WalkDir::new`] with the file name of this |
66 | | /// entry. |
67 | | /// |
68 | | /// Note that this *always* returns the path reported by the underlying |
69 | | /// directory entry, even when symbolic links are followed. To get the |
70 | | /// target path, use [`path_is_symlink`] to (cheaply) check if this entry |
71 | | /// corresponds to a symbolic link, and [`std::fs::read_link`] to resolve |
72 | | /// the target. |
73 | | /// |
74 | | /// [`WalkDir::new`]: struct.WalkDir.html#method.new |
75 | | /// [`path_is_symlink`]: struct.DirEntry.html#method.path_is_symlink |
76 | | /// [`std::fs::read_link`]: https://doc.rust-lang.org/stable/std/fs/fn.read_link.html |
77 | 0 | pub fn path(&self) -> &Path { |
78 | 0 | &self.path |
79 | 0 | } |
80 | | |
81 | | /// The full path that this entry represents. |
82 | | /// |
83 | | /// Analogous to [`path`], but moves ownership of the path. |
84 | | /// |
85 | | /// [`path`]: struct.DirEntry.html#method.path |
86 | 0 | pub fn into_path(self) -> PathBuf { |
87 | 0 | self.path |
88 | 0 | } |
89 | | |
90 | | /// Returns `true` if and only if this entry was created from a symbolic |
91 | | /// link. This is unaffected by the [`follow_links`] setting. |
92 | | /// |
93 | | /// When `true`, the value returned by the [`path`] method is a |
94 | | /// symbolic link name. To get the full target path, you must call |
95 | | /// [`std::fs::read_link(entry.path())`]. |
96 | | /// |
97 | | /// [`path`]: struct.DirEntry.html#method.path |
98 | | /// [`follow_links`]: struct.WalkDir.html#method.follow_links |
99 | | /// [`std::fs::read_link(entry.path())`]: https://doc.rust-lang.org/stable/std/fs/fn.read_link.html |
100 | 0 | pub fn path_is_symlink(&self) -> bool { |
101 | 0 | self.ty.is_symlink() || self.follow_link |
102 | 0 | } |
103 | | |
104 | | /// Return the metadata for the file that this entry points to. |
105 | | /// |
106 | | /// This will follow symbolic links if and only if the [`WalkDir`] value |
107 | | /// has [`follow_links`] enabled. |
108 | | /// |
109 | | /// # Platform behavior |
110 | | /// |
111 | | /// This always calls [`std::fs::symlink_metadata`]. |
112 | | /// |
113 | | /// If this entry is a symbolic link and [`follow_links`] is enabled, then |
114 | | /// [`std::fs::metadata`] is called instead. |
115 | | /// |
116 | | /// # Errors |
117 | | /// |
118 | | /// Similar to [`std::fs::metadata`], returns errors for path values that |
119 | | /// the program does not have permissions to access or if the path does not |
120 | | /// exist. |
121 | | /// |
122 | | /// [`WalkDir`]: struct.WalkDir.html |
123 | | /// [`follow_links`]: struct.WalkDir.html#method.follow_links |
124 | | /// [`std::fs::metadata`]: https://doc.rust-lang.org/std/fs/fn.metadata.html |
125 | | /// [`std::fs::symlink_metadata`]: https://doc.rust-lang.org/stable/std/fs/fn.symlink_metadata.html |
126 | 0 | pub fn metadata(&self) -> Result<fs::Metadata> { |
127 | 0 | self.metadata_internal() |
128 | 0 | } |
129 | | |
130 | | #[cfg(windows)] |
131 | | fn metadata_internal(&self) -> Result<fs::Metadata> { |
132 | | if self.follow_link { |
133 | | fs::metadata(&self.path) |
134 | | } else { |
135 | | Ok(self.metadata.clone()) |
136 | | } |
137 | | .map_err(|err| Error::from_entry(self, err)) |
138 | | } |
139 | | |
140 | | #[cfg(not(windows))] |
141 | 0 | fn metadata_internal(&self) -> Result<fs::Metadata> { |
142 | 0 | if self.follow_link { |
143 | 0 | fs::metadata(&self.path) |
144 | | } else { |
145 | 0 | fs::symlink_metadata(&self.path) |
146 | | } |
147 | 0 | .map_err(|err| Error::from_entry(self, err)) |
148 | 0 | } |
149 | | |
150 | | /// Return the file type for the file that this entry points to. |
151 | | /// |
152 | | /// If this is a symbolic link and [`follow_links`] is `true`, then this |
153 | | /// returns the type of the target. |
154 | | /// |
155 | | /// This never makes any system calls. |
156 | | /// |
157 | | /// [`follow_links`]: struct.WalkDir.html#method.follow_links |
158 | 0 | pub fn file_type(&self) -> fs::FileType { |
159 | 0 | self.ty |
160 | 0 | } |
161 | | |
162 | | /// Return the file name of this entry. |
163 | | /// |
164 | | /// If this entry has no file name (e.g., `/`), then the full path is |
165 | | /// returned. |
166 | 0 | pub fn file_name(&self) -> &OsStr { |
167 | 0 | self.path.file_name().unwrap_or_else(|| self.path.as_os_str()) |
168 | 0 | } |
169 | | |
170 | | /// Returns the depth at which this entry was created relative to the root. |
171 | | /// |
172 | | /// The smallest depth is `0` and always corresponds to the path given |
173 | | /// to the `new` function on `WalkDir`. Its direct descendents have depth |
174 | | /// `1`, and their descendents have depth `2`, and so on. |
175 | 0 | pub fn depth(&self) -> usize { |
176 | 0 | self.depth |
177 | 0 | } |
178 | | |
179 | | /// Returns true if and only if this entry points to a directory. |
180 | 0 | pub(crate) fn is_dir(&self) -> bool { |
181 | 0 | self.ty.is_dir() |
182 | 0 | } |
183 | | |
184 | | #[cfg(windows)] |
185 | | pub(crate) fn from_entry( |
186 | | depth: usize, |
187 | | ent: &fs::DirEntry, |
188 | | ) -> Result<DirEntry> { |
189 | | let path = ent.path(); |
190 | | let ty = ent |
191 | | .file_type() |
192 | | .map_err(|err| Error::from_path(depth, path.clone(), err))?; |
193 | | let md = ent |
194 | | .metadata() |
195 | | .map_err(|err| Error::from_path(depth, path.clone(), err))?; |
196 | | Ok(DirEntry { path, ty, follow_link: false, depth, metadata: md }) |
197 | | } |
198 | | |
199 | | #[cfg(unix)] |
200 | 0 | pub(crate) fn from_entry( |
201 | 0 | depth: usize, |
202 | 0 | ent: &fs::DirEntry, |
203 | 0 | ) -> Result<DirEntry> { |
204 | | use std::os::unix::fs::DirEntryExt; |
205 | | |
206 | 0 | let ty = ent |
207 | 0 | .file_type() |
208 | 0 | .map_err(|err| Error::from_path(depth, ent.path(), err))?; |
209 | 0 | Ok(DirEntry { |
210 | 0 | path: ent.path(), |
211 | 0 | ty, |
212 | 0 | follow_link: false, |
213 | 0 | depth, |
214 | 0 | ino: ent.ino(), |
215 | 0 | }) |
216 | 0 | } |
217 | | |
218 | | #[cfg(not(any(unix, windows)))] |
219 | | pub(crate) fn from_entry( |
220 | | depth: usize, |
221 | | ent: &fs::DirEntry, |
222 | | ) -> Result<DirEntry> { |
223 | | let ty = ent |
224 | | .file_type() |
225 | | .map_err(|err| Error::from_path(depth, ent.path(), err))?; |
226 | | Ok(DirEntry { path: ent.path(), ty, follow_link: false, depth }) |
227 | | } |
228 | | |
229 | | #[cfg(windows)] |
230 | | pub(crate) fn from_path( |
231 | | depth: usize, |
232 | | pb: PathBuf, |
233 | | follow: bool, |
234 | | ) -> Result<DirEntry> { |
235 | | let md = if follow { |
236 | | fs::metadata(&pb) |
237 | | .map_err(|err| Error::from_path(depth, pb.clone(), err))? |
238 | | } else { |
239 | | fs::symlink_metadata(&pb) |
240 | | .map_err(|err| Error::from_path(depth, pb.clone(), err))? |
241 | | }; |
242 | | Ok(DirEntry { |
243 | | path: pb, |
244 | | ty: md.file_type(), |
245 | | follow_link: follow, |
246 | | depth, |
247 | | metadata: md, |
248 | | }) |
249 | | } |
250 | | |
251 | | #[cfg(unix)] |
252 | 0 | pub(crate) fn from_path( |
253 | 0 | depth: usize, |
254 | 0 | pb: PathBuf, |
255 | 0 | follow: bool, |
256 | 0 | ) -> Result<DirEntry> { |
257 | | use std::os::unix::fs::MetadataExt; |
258 | | |
259 | 0 | let md = if follow { |
260 | 0 | fs::metadata(&pb) |
261 | 0 | .map_err(|err| Error::from_path(depth, pb.clone(), err))? |
262 | | } else { |
263 | 0 | fs::symlink_metadata(&pb) |
264 | 0 | .map_err(|err| Error::from_path(depth, pb.clone(), err))? |
265 | | }; |
266 | 0 | Ok(DirEntry { |
267 | 0 | path: pb, |
268 | 0 | ty: md.file_type(), |
269 | 0 | follow_link: follow, |
270 | 0 | depth, |
271 | 0 | ino: md.ino(), |
272 | 0 | }) |
273 | 0 | } |
274 | | |
275 | | #[cfg(not(any(unix, windows)))] |
276 | | pub(crate) fn from_path( |
277 | | depth: usize, |
278 | | pb: PathBuf, |
279 | | follow: bool, |
280 | | ) -> Result<DirEntry> { |
281 | | let md = if follow { |
282 | | fs::metadata(&pb) |
283 | | .map_err(|err| Error::from_path(depth, pb.clone(), err))? |
284 | | } else { |
285 | | fs::symlink_metadata(&pb) |
286 | | .map_err(|err| Error::from_path(depth, pb.clone(), err))? |
287 | | }; |
288 | | Ok(DirEntry { |
289 | | path: pb, |
290 | | ty: md.file_type(), |
291 | | follow_link: follow, |
292 | | depth, |
293 | | }) |
294 | | } |
295 | | } |
296 | | |
297 | | impl Clone for DirEntry { |
298 | | #[cfg(windows)] |
299 | | fn clone(&self) -> DirEntry { |
300 | | DirEntry { |
301 | | path: self.path.clone(), |
302 | | ty: self.ty, |
303 | | follow_link: self.follow_link, |
304 | | depth: self.depth, |
305 | | metadata: self.metadata.clone(), |
306 | | } |
307 | | } |
308 | | |
309 | | #[cfg(unix)] |
310 | 0 | fn clone(&self) -> DirEntry { |
311 | 0 | DirEntry { |
312 | 0 | path: self.path.clone(), |
313 | 0 | ty: self.ty, |
314 | 0 | follow_link: self.follow_link, |
315 | 0 | depth: self.depth, |
316 | 0 | ino: self.ino, |
317 | 0 | } |
318 | 0 | } |
319 | | |
320 | | #[cfg(not(any(unix, windows)))] |
321 | | fn clone(&self) -> DirEntry { |
322 | | DirEntry { |
323 | | path: self.path.clone(), |
324 | | ty: self.ty, |
325 | | follow_link: self.follow_link, |
326 | | depth: self.depth, |
327 | | } |
328 | | } |
329 | | } |
330 | | |
331 | | impl fmt::Debug for DirEntry { |
332 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
333 | 0 | write!(f, "DirEntry({:?})", self.path) |
334 | 0 | } |
335 | | } |
336 | | |
337 | | /// Unix-specific extension methods for `walkdir::DirEntry` |
338 | | #[cfg(unix)] |
339 | | pub trait DirEntryExt { |
340 | | /// Returns the underlying `d_ino` field in the contained `dirent` |
341 | | /// structure. |
342 | | fn ino(&self) -> u64; |
343 | | } |
344 | | |
345 | | #[cfg(unix)] |
346 | | impl DirEntryExt for DirEntry { |
347 | | /// Returns the underlying `d_ino` field in the contained `dirent` |
348 | | /// structure. |
349 | 0 | fn ino(&self) -> u64 { |
350 | 0 | self.ino |
351 | 0 | } |
352 | | } |