• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
path(&self) -> Option<&Path>46     pub fn path(&self) -> Option<&Path> {
47         match self.inner {
48             ErrorInner::Io { path: None, .. } => None,
49             ErrorInner::Io { path: Some(ref path), .. } => Some(path),
50             ErrorInner::Loop { ref child, .. } => Some(child),
51         }
52     }
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
loop_ancestor(&self) -> Option<&Path>66     pub fn loop_ancestor(&self) -> Option<&Path> {
67         match self.inner {
68             ErrorInner::Loop { ref ancestor, .. } => Some(ancestor),
69             _ => None,
70         }
71     }
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
depth(&self) -> usize81     pub fn depth(&self) -> usize {
82         self.depth
83     }
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
io_error(&self) -> Option<&io::Error>143     pub fn io_error(&self) -> Option<&io::Error> {
144         match self.inner {
145             ErrorInner::Io { ref err, .. } => Some(err),
146             ErrorInner::Loop { .. } => None,
147         }
148     }
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
into_io_error(self) -> Option<io::Error>155     pub fn into_io_error(self) -> Option<io::Error> {
156         match self.inner {
157             ErrorInner::Io { err, .. } => Some(err),
158             ErrorInner::Loop { .. } => None,
159         }
160     }
161 
from_path( depth: usize, pb: PathBuf, err: io::Error, ) -> Self162     pub(crate) fn from_path(
163         depth: usize,
164         pb: PathBuf,
165         err: io::Error,
166     ) -> Self {
167         Error {
168             depth: depth,
169             inner: ErrorInner::Io { path: Some(pb), err: err },
170         }
171     }
172 
from_entry(dent: &DirEntry, err: io::Error) -> Self173     pub(crate) fn from_entry(dent: &DirEntry, err: io::Error) -> Self {
174         Error {
175             depth: dent.depth(),
176             inner: ErrorInner::Io {
177                 path: Some(dent.path().to_path_buf()),
178                 err: err,
179             },
180         }
181     }
182 
from_io(depth: usize, err: io::Error) -> Self183     pub(crate) fn from_io(depth: usize, err: io::Error) -> Self {
184         Error { depth: depth, inner: ErrorInner::Io { path: None, err: err } }
185     }
186 
from_loop( depth: usize, ancestor: &Path, child: &Path, ) -> Self187     pub(crate) fn from_loop(
188         depth: usize,
189         ancestor: &Path,
190         child: &Path,
191     ) -> Self {
192         Error {
193             depth: depth,
194             inner: ErrorInner::Loop {
195                 ancestor: ancestor.to_path_buf(),
196                 child: child.to_path_buf(),
197             },
198         }
199     }
200 }
201 
202 impl error::Error for Error {
203     #[allow(deprecated)]
description(&self) -> &str204     fn description(&self) -> &str {
205         match self.inner {
206             ErrorInner::Io { ref err, .. } => err.description(),
207             ErrorInner::Loop { .. } => "file system loop found",
208         }
209     }
210 
cause(&self) -> Option<&dyn error::Error>211     fn cause(&self) -> Option<&dyn error::Error> {
212         self.source()
213     }
214 
source(&self) -> Option<&(dyn error::Error + 'static)>215     fn source(&self) -> Option<&(dyn error::Error + 'static)> {
216         match self.inner {
217             ErrorInner::Io { ref err, .. } => Some(err),
218             ErrorInner::Loop { .. } => None,
219         }
220     }
221 }
222 
223 impl fmt::Display for Error {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result224     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225         match self.inner {
226             ErrorInner::Io { path: None, ref err } => err.fmt(f),
227             ErrorInner::Io { path: Some(ref path), ref err } => write!(
228                 f,
229                 "IO error for operation on {}: {}",
230                 path.display(),
231                 err
232             ),
233             ErrorInner::Loop { ref ancestor, ref child } => write!(
234                 f,
235                 "File system loop found: \
236                  {} points to an ancestor {}",
237                 child.display(),
238                 ancestor.display()
239             ),
240         }
241     }
242 }
243 
244 impl From<Error> for io::Error {
245     /// Convert the [`Error`] to an [`io::Error`], preserving the original
246     /// [`Error`] as the ["inner error"]. Note that this also makes the display
247     /// of the error include the context.
248     ///
249     /// This is different from [`into_io_error`] which returns the original
250     /// [`io::Error`].
251     ///
252     /// [`Error`]: struct.Error.html
253     /// [`io::Error`]: https://doc.rust-lang.org/stable/std/io/struct.Error.html
254     /// ["inner error"]: https://doc.rust-lang.org/std/io/struct.Error.html#method.into_inner
255     /// [`into_io_error`]: struct.WalkDir.html#method.into_io_error
from(walk_err: Error) -> io::Error256     fn from(walk_err: Error) -> io::Error {
257         let kind = match walk_err {
258             Error { inner: ErrorInner::Io { ref err, .. }, .. } => err.kind(),
259             Error { inner: ErrorInner::Loop { .. }, .. } => {
260                 io::ErrorKind::Other
261             }
262         };
263         io::Error::new(kind, walk_err)
264     }
265 }
266