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