]> git.proxmox.com Git - rustc.git/blob - vendor/walkdir/src/error.rs
New upstream version 1.49.0+dfsg1
[rustc.git] / vendor / walkdir / src / error.rs
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 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
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
81 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
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
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
162 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
173 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
183 pub(crate) fn from_io(depth: usize, err: io::Error) -> Self {
184 Error { depth: depth, inner: ErrorInner::Io { path: None, err: err } }
185 }
186
187 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)]
204 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
211 fn cause(&self) -> Option<&dyn error::Error> {
212 self.source()
213 }
214
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 {
224 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
256 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 }