Coverage Report

Created: 2026-01-16 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/walkdir-2.5.0/src/error.rs
Line
Count
Source
1
use std::error;
2
use std::fmt;
3
use std::io;
4
use std::path::{Path, PathBuf};
5
6
use crate::DirEntry;
7
8
/// An error produced by recursively walking a directory.
9
///
10
/// This error type is a light wrapper around [`std::io::Error`]. In
11
/// particular, it adds the following information:
12
///
13
/// * The depth at which the error occurred in the file tree, relative to the
14
/// root.
15
/// * The path, if any, associated with the IO error.
16
/// * An indication that a loop occurred when following symbolic links. In this
17
/// case, there is no underlying IO error.
18
///
19
/// To maintain good ergonomics, this type has a
20
/// [`impl From<Error> for std::io::Error`][impl] defined which preserves the original context.
21
/// This allows you to use an [`io::Result`] with methods in this crate if you don't care about
22
/// accessing the underlying error data in a structured form.
23
///
24
/// [`std::io::Error`]: https://doc.rust-lang.org/stable/std/io/struct.Error.html
25
/// [`io::Result`]: https://doc.rust-lang.org/stable/std/io/type.Result.html
26
/// [impl]: struct.Error.html#impl-From%3CError%3E
27
#[derive(Debug)]
28
pub struct Error {
29
    depth: usize,
30
    inner: ErrorInner,
31
}
32
33
#[derive(Debug)]
34
enum ErrorInner {
35
    Io { path: Option<PathBuf>, err: io::Error },
36
    Loop { ancestor: PathBuf, child: PathBuf },
37
}
38
39
impl Error {
40
    /// Returns the path associated with this error if one exists.
41
    ///
42
    /// For example, if an error occurred while opening a directory handle,
43
    /// the error will include the path passed to [`std::fs::read_dir`].
44
    ///
45
    /// [`std::fs::read_dir`]: https://doc.rust-lang.org/stable/std/fs/fn.read_dir.html
46
0
    pub fn path(&self) -> Option<&Path> {
47
0
        match self.inner {
48
0
            ErrorInner::Io { path: None, .. } => None,
49
0
            ErrorInner::Io { path: Some(ref path), .. } => Some(path),
50
0
            ErrorInner::Loop { ref child, .. } => Some(child),
51
        }
52
0
    }
53
54
    /// Returns the path at which a cycle was detected.
55
    ///
56
    /// If no cycle was detected, [`None`] is returned.
57
    ///
58
    /// A cycle is detected when a directory entry is equivalent to one of
59
    /// its ancestors.
60
    ///
61
    /// To get the path to the child directory entry in the cycle, use the
62
    /// [`path`] method.
63
    ///
64
    /// [`None`]: https://doc.rust-lang.org/stable/std/option/enum.Option.html#variant.None
65
    /// [`path`]: struct.Error.html#path
66
0
    pub fn loop_ancestor(&self) -> Option<&Path> {
67
0
        match self.inner {
68
0
            ErrorInner::Loop { ref ancestor, .. } => Some(ancestor),
69
0
            _ => None,
70
        }
71
0
    }
72
73
    /// Returns the depth at which this error occurred relative to the root.
74
    ///
75
    /// The smallest depth is `0` and always corresponds to the path given to
76
    /// the [`new`] function on [`WalkDir`]. Its direct descendents have depth
77
    /// `1`, and their descendents have depth `2`, and so on.
78
    ///
79
    /// [`new`]: struct.WalkDir.html#method.new
80
    /// [`WalkDir`]: struct.WalkDir.html
81
0
    pub fn depth(&self) -> usize {
82
0
        self.depth
83
0
    }
84
85
    /// Inspect the original [`io::Error`] if there is one.
86
    ///
87
    /// [`None`] is returned if the [`Error`] doesn't correspond to an
88
    /// [`io::Error`]. This might happen, for example, when the error was
89
    /// produced because a cycle was found in the directory tree while
90
    /// following symbolic links.
91
    ///
92
    /// This method returns a borrowed value that is bound to the lifetime of the [`Error`]. To
93
    /// obtain an owned value, the [`into_io_error`] can be used instead.
94
    ///
95
    /// > This is the original [`io::Error`] and is _not_ the same as
96
    /// > [`impl From<Error> for std::io::Error`][impl] which contains additional context about the
97
    /// error.
98
    ///
99
    /// # Example
100
    ///
101
    /// ```rust,no_run
102
    /// use std::io;
103
    /// use std::path::Path;
104
    ///
105
    /// use walkdir::WalkDir;
106
    ///
107
    /// for entry in WalkDir::new("foo") {
108
    ///     match entry {
109
    ///         Ok(entry) => println!("{}", entry.path().display()),
110
    ///         Err(err) => {
111
    ///             let path = err.path().unwrap_or(Path::new("")).display();
112
    ///             println!("failed to access entry {}", path);
113
    ///             if let Some(inner) = err.io_error() {
114
    ///                 match inner.kind() {
115
    ///                     io::ErrorKind::InvalidData => {
116
    ///                         println!(
117
    ///                             "entry contains invalid data: {}",
118
    ///                             inner)
119
    ///                     }
120
    ///                     io::ErrorKind::PermissionDenied => {
121
    ///                         println!(
122
    ///                             "Missing permission to read entry: {}",
123
    ///                             inner)
124
    ///                     }
125
    ///                     _ => {
126
    ///                         println!(
127
    ///                             "Unexpected error occurred: {}",
128
    ///                             inner)
129
    ///                     }
130
    ///                 }
131
    ///             }
132
    ///         }
133
    ///     }
134
    /// }
135
    /// ```
136
    ///
137
    /// [`None`]: https://doc.rust-lang.org/stable/std/option/enum.Option.html#variant.None
138
    /// [`io::Error`]: https://doc.rust-lang.org/stable/std/io/struct.Error.html
139
    /// [`From`]: https://doc.rust-lang.org/stable/std/convert/trait.From.html
140
    /// [`Error`]: struct.Error.html
141
    /// [`into_io_error`]: struct.Error.html#method.into_io_error
142
    /// [impl]: struct.Error.html#impl-From%3CError%3E
143
0
    pub fn io_error(&self) -> Option<&io::Error> {
144
0
        match self.inner {
145
0
            ErrorInner::Io { ref err, .. } => Some(err),
146
0
            ErrorInner::Loop { .. } => None,
147
        }
148
0
    }
149
150
    /// Similar to [`io_error`] except consumes self to convert to the original
151
    /// [`io::Error`] if one exists.
152
    ///
153
    /// [`io_error`]: struct.Error.html#method.io_error
154
    /// [`io::Error`]: https://doc.rust-lang.org/stable/std/io/struct.Error.html
155
0
    pub fn into_io_error(self) -> Option<io::Error> {
156
0
        match self.inner {
157
0
            ErrorInner::Io { err, .. } => Some(err),
158
0
            ErrorInner::Loop { .. } => None,
159
        }
160
0
    }
161
162
0
    pub(crate) fn from_path(
163
0
        depth: usize,
164
0
        pb: PathBuf,
165
0
        err: io::Error,
166
0
    ) -> Self {
167
0
        Error { depth, inner: ErrorInner::Io { path: Some(pb), err } }
168
0
    }
169
170
0
    pub(crate) fn from_entry(dent: &DirEntry, err: io::Error) -> Self {
171
0
        Error {
172
0
            depth: dent.depth(),
173
0
            inner: ErrorInner::Io {
174
0
                path: Some(dent.path().to_path_buf()),
175
0
                err,
176
0
            },
177
0
        }
178
0
    }
179
180
0
    pub(crate) fn from_io(depth: usize, err: io::Error) -> Self {
181
0
        Error { depth, inner: ErrorInner::Io { path: None, err } }
182
0
    }
183
184
0
    pub(crate) fn from_loop(
185
0
        depth: usize,
186
0
        ancestor: &Path,
187
0
        child: &Path,
188
0
    ) -> Self {
189
0
        Error {
190
0
            depth,
191
0
            inner: ErrorInner::Loop {
192
0
                ancestor: ancestor.to_path_buf(),
193
0
                child: child.to_path_buf(),
194
0
            },
195
0
        }
196
0
    }
197
}
198
199
impl error::Error for Error {
200
    #[allow(deprecated)]
201
0
    fn description(&self) -> &str {
202
0
        match self.inner {
203
0
            ErrorInner::Io { ref err, .. } => err.description(),
204
0
            ErrorInner::Loop { .. } => "file system loop found",
205
        }
206
0
    }
207
208
0
    fn cause(&self) -> Option<&dyn error::Error> {
209
0
        self.source()
210
0
    }
211
212
0
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
213
0
        match self.inner {
214
0
            ErrorInner::Io { ref err, .. } => Some(err),
215
0
            ErrorInner::Loop { .. } => None,
216
        }
217
0
    }
218
}
219
220
impl fmt::Display for Error {
221
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
222
0
        match self.inner {
223
0
            ErrorInner::Io { path: None, ref err } => err.fmt(f),
224
0
            ErrorInner::Io { path: Some(ref path), ref err } => write!(
225
0
                f,
226
                "IO error for operation on {}: {}",
227
0
                path.display(),
228
                err
229
            ),
230
0
            ErrorInner::Loop { ref ancestor, ref child } => write!(
231
0
                f,
232
                "File system loop found: \
233
                 {} points to an ancestor {}",
234
0
                child.display(),
235
0
                ancestor.display()
236
            ),
237
        }
238
0
    }
239
}
240
241
impl From<Error> for io::Error {
242
    /// Convert the [`Error`] to an [`io::Error`], preserving the original
243
    /// [`Error`] as the ["inner error"]. Note that this also makes the display
244
    /// of the error include the context.
245
    ///
246
    /// This is different from [`into_io_error`] which returns the original
247
    /// [`io::Error`].
248
    ///
249
    /// [`Error`]: struct.Error.html
250
    /// [`io::Error`]: https://doc.rust-lang.org/stable/std/io/struct.Error.html
251
    /// ["inner error"]: https://doc.rust-lang.org/std/io/struct.Error.html#method.into_inner
252
    /// [`into_io_error`]: struct.WalkDir.html#method.into_io_error
253
0
    fn from(walk_err: Error) -> io::Error {
254
0
        let kind = match walk_err {
255
0
            Error { inner: ErrorInner::Io { ref err, .. }, .. } => err.kind(),
256
            Error { inner: ErrorInner::Loop { .. }, .. } => {
257
0
                io::ErrorKind::Other
258
            }
259
        };
260
0
        io::Error::new(kind, walk_err)
261
0
    }
262
}