Coverage Report

Created: 2026-11-03 08:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}