/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 | | } |