Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/windirstat/walkdir.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/error.rs')
-rw-r--r--src/error.rs263
1 files changed, 263 insertions, 0 deletions
diff --git a/src/error.rs b/src/error.rs
new file mode 100644
index 0000000..ab9b17a
--- /dev/null
+++ b/src/error.rs
@@ -0,0 +1,263 @@
+use std::error;
+use std::fmt;
+use std::io;
+use std::path::{Path, PathBuf};
+
+use DirEntry;
+
+/// An error produced by recursively walking a directory.
+///
+/// This error type is a light wrapper around [`std::io::Error`]. In
+/// particular, it adds the following information:
+///
+/// * The depth at which the error occurred in the file tree, relative to the
+/// root.
+/// * The path, if any, associated with the IO error.
+/// * An indication that a loop occurred when following symbolic links. In this
+/// case, there is no underlying IO error.
+///
+/// To maintain good ergonomics, this type has a
+/// [`impl From<Error> for std::io::Error`][impl] defined which preserves the original context.
+/// This allows you to use an [`io::Result`] with methods in this crate if you don't care about
+/// accessing the underlying error data in a structured form.
+///
+/// [`std::io::Error`]: https://doc.rust-lang.org/stable/std/io/struct.Error.html
+/// [`io::Result`]: https://doc.rust-lang.org/stable/std/io/type.Result.html
+/// [impl]: struct.Error.html#impl-From%3CError%3E
+#[derive(Debug)]
+pub struct Error {
+ depth: usize,
+ inner: ErrorInner,
+}
+
+#[derive(Debug)]
+enum ErrorInner {
+ Io { path: Option<PathBuf>, err: io::Error },
+ Loop { ancestor: PathBuf, child: PathBuf },
+}
+
+impl Error {
+ /// Returns the path associated with this error if one exists.
+ ///
+ /// For example, if an error occurred while opening a directory handle,
+ /// the error will include the path passed to [`std::fs::read_dir`].
+ ///
+ /// [`std::fs::read_dir`]: https://doc.rust-lang.org/stable/std/fs/fn.read_dir.html
+ pub fn path(&self) -> Option<&Path> {
+ match self.inner {
+ ErrorInner::Io { path: None, .. } => None,
+ ErrorInner::Io { path: Some(ref path), .. } => Some(path),
+ ErrorInner::Loop { ref child, .. } => Some(child),
+ }
+ }
+
+ /// Returns the path at which a cycle was detected.
+ ///
+ /// If no cycle was detected, [`None`] is returned.
+ ///
+ /// A cycle is detected when a directory entry is equivalent to one of
+ /// its ancestors.
+ ///
+ /// To get the path to the child directory entry in the cycle, use the
+ /// [`path`] method.
+ ///
+ /// [`None`]: https://doc.rust-lang.org/stable/std/option/enum.Option.html#variant.None
+ /// [`path`]: struct.Error.html#path
+ pub fn loop_ancestor(&self) -> Option<&Path> {
+ match self.inner {
+ ErrorInner::Loop { ref ancestor, .. } => Some(ancestor),
+ _ => None,
+ }
+ }
+
+ /// Returns the depth at which this error occurred relative to the root.
+ ///
+ /// The smallest depth is `0` and always corresponds to the path given to
+ /// the [`new`] function on [`WalkDir`]. Its direct descendents have depth
+ /// `1`, and their descendents have depth `2`, and so on.
+ ///
+ /// [`new`]: struct.WalkDir.html#method.new
+ /// [`WalkDir`]: struct.WalkDir.html
+ pub fn depth(&self) -> usize {
+ self.depth
+ }
+
+ /// Inspect the original [`io::Error`] if there is one.
+ ///
+ /// [`None`] is returned if the [`Error`] doesn't correspond to an
+ /// [`io::Error`]. This might happen, for example, when the error was
+ /// produced because a cycle was found in the directory tree while
+ /// following symbolic links.
+ ///
+ /// This method returns a borrowed value that is bound to the lifetime of the [`Error`]. To
+ /// obtain an owned value, the [`into_io_error`] can be used instead.
+ ///
+ /// > This is the original [`io::Error`] and is _not_ the same as
+ /// > [`impl From<Error> for std::io::Error`][impl] which contains additional context about the
+ /// error.
+ ///
+ /// # Example
+ ///
+ /// ```rust,no-run
+ /// use std::io;
+ /// use std::path::Path;
+ ///
+ /// use walkdir::WalkDir;
+ ///
+ /// for entry in WalkDir::new("foo") {
+ /// match entry {
+ /// Ok(entry) => println!("{}", entry.path().display()),
+ /// Err(err) => {
+ /// let path = err.path().unwrap_or(Path::new("")).display();
+ /// println!("failed to access entry {}", path);
+ /// if let Some(inner) = err.io_error() {
+ /// match inner.kind() {
+ /// io::ErrorKind::InvalidData => {
+ /// println!(
+ /// "entry contains invalid data: {}",
+ /// inner)
+ /// }
+ /// io::ErrorKind::PermissionDenied => {
+ /// println!(
+ /// "Missing permission to read entry: {}",
+ /// inner)
+ /// }
+ /// _ => {
+ /// println!(
+ /// "Unexpected error occurred: {}",
+ /// inner)
+ /// }
+ /// }
+ /// }
+ /// }
+ /// }
+ /// }
+ /// ```
+ ///
+ /// [`None`]: https://doc.rust-lang.org/stable/std/option/enum.Option.html#variant.None
+ /// [`io::Error`]: https://doc.rust-lang.org/stable/std/io/struct.Error.html
+ /// [`From`]: https://doc.rust-lang.org/stable/std/convert/trait.From.html
+ /// [`Error`]: struct.Error.html
+ /// [`into_io_error`]: struct.Error.html#method.into_io_error
+ /// [impl]: struct.Error.html#impl-From%3CError%3E
+ pub fn io_error(&self) -> Option<&io::Error> {
+ match self.inner {
+ ErrorInner::Io { ref err, .. } => Some(err),
+ ErrorInner::Loop { .. } => None,
+ }
+ }
+
+ /// Similar to [`io_error`] except consumes self to convert to the original
+ /// [`io::Error`] if one exists.
+ ///
+ /// [`io_error`]: struct.Error.html#method.io_error
+ /// [`io::Error`]: https://doc.rust-lang.org/stable/std/io/struct.Error.html
+ pub fn into_io_error(self) -> Option<io::Error> {
+ match self.inner {
+ ErrorInner::Io { err, .. } => Some(err),
+ ErrorInner::Loop { .. } => None,
+ }
+ }
+
+ pub(crate) fn from_path(
+ depth: usize,
+ pb: PathBuf,
+ err: io::Error,
+ ) -> Self {
+ Error {
+ depth: depth,
+ inner: ErrorInner::Io { path: Some(pb), err: err },
+ }
+ }
+
+ pub(crate) fn from_entry(dent: &DirEntry, err: io::Error) -> Self {
+ Error {
+ depth: dent.depth(),
+ inner: ErrorInner::Io {
+ path: Some(dent.path().to_path_buf()),
+ err: err,
+ },
+ }
+ }
+
+ pub(crate) fn from_io(depth: usize, err: io::Error) -> Self {
+ Error {
+ depth: depth,
+ inner: ErrorInner::Io { path: None, err: err },
+ }
+ }
+
+ pub(crate) fn from_loop(
+ depth: usize,
+ ancestor: &Path,
+ child: &Path,
+ ) -> Self {
+ Error {
+ depth: depth,
+ inner: ErrorInner::Loop {
+ ancestor: ancestor.to_path_buf(),
+ child: child.to_path_buf(),
+ }
+ }
+ }
+}
+
+impl error::Error for Error {
+ fn description(&self) -> &str {
+ match self.inner {
+ ErrorInner::Io { ref err, .. } => err.description(),
+ ErrorInner::Loop { .. } => "file system loop found",
+ }
+ }
+
+ fn cause(&self) -> Option<&error::Error> {
+ match self.inner {
+ ErrorInner::Io { ref err, .. } => Some(err),
+ ErrorInner::Loop { .. } => None,
+ }
+ }
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self.inner {
+ ErrorInner::Io { path: None, ref err } => {
+ err.fmt(f)
+ }
+ ErrorInner::Io { path: Some(ref path), ref err } => {
+ write!(f, "IO error for operation on {}: {}",
+ path.display(), err)
+ }
+ ErrorInner::Loop { ref ancestor, ref child } => {
+ write!(f, "File system loop found: \
+ {} points to an ancestor {}",
+ child.display(), ancestor.display())
+ }
+ }
+ }
+}
+
+impl From<Error> for io::Error {
+ /// Convert the [`Error`] to an [`io::Error`], preserving the original
+ /// [`Error`] as the ["inner error"]. Note that this also makes the display
+ /// of the error include the context.
+ ///
+ /// This is different from [`into_io_error`] which returns the original
+ /// [`io::Error`].
+ ///
+ /// [`Error`]: struct.Error.html
+ /// [`io::Error`]: https://doc.rust-lang.org/stable/std/io/struct.Error.html
+ /// ["inner error"]: https://doc.rust-lang.org/std/io/struct.Error.html#method.into_inner
+ /// [`into_io_error`]: struct.WalkDir.html#method.into_io_error
+ fn from(walk_err: Error) -> io::Error {
+ let kind = match walk_err {
+ Error { inner: ErrorInner::Io { ref err, .. }, .. } => {
+ err.kind()
+ }
+ Error { inner: ErrorInner::Loop { .. }, .. } => {
+ io::ErrorKind::Other
+ }
+ };
+ io::Error::new(kind, walk_err)
+ }
+}