Skip to main content

iced_swdir_tree/directory_tree/
error.rs

1//! Crate-level error type.
2//!
3//! We wrap [`std::io::Error`] (via [`swdir::ScanError`]) in our own enum
4//! rather than re-exporting swdir's error so the public API stays
5//! self-contained and future-flexible. In particular, cloning is required
6//! because iced messages are `Clone` — and `std::io::Error` is not
7//! `Clone`, so we carry the [`io::ErrorKind`] and a formatted message
8//! instead of the live error object.
9
10use std::io;
11use std::path::{Path, PathBuf};
12
13/// Everything the widget can fail at.
14///
15/// `Clone` is implemented because iced messages flow across channels
16/// and must be cloneable. The originating [`io::Error`] cannot be
17/// cloned, so we preserve its [`io::ErrorKind`] and its formatted
18/// message instead.
19#[derive(Debug, Clone)]
20pub enum Error {
21    /// An I/O error surfaced while scanning a directory.
22    ///
23    /// `path` is always the path the error refers to (the directory
24    /// being scanned, or a specific child entry).
25    Io {
26        /// Path the error occurred on.
27        path: PathBuf,
28        /// Kind of the underlying [`io::Error`] (e.g. `NotFound`,
29        /// `PermissionDenied`).
30        kind: io::ErrorKind,
31        /// Human-readable rendering of the original I/O error.
32        message: String,
33    },
34}
35
36impl Error {
37    /// Construct an [`Error::Io`] from the pieces of a failed scan.
38    pub(crate) fn io(path: impl Into<PathBuf>, kind: io::ErrorKind, message: String) -> Self {
39        Self::Io {
40            path: path.into(),
41            kind,
42            message,
43        }
44    }
45
46    /// Path associated with this error, if any.
47    pub fn path(&self) -> &Path {
48        match self {
49            Self::Io { path, .. } => path,
50        }
51    }
52
53    /// [`io::ErrorKind`] of the underlying I/O failure.
54    pub fn io_kind(&self) -> io::ErrorKind {
55        match self {
56            Self::Io { kind, .. } => *kind,
57        }
58    }
59
60    /// `true` if the error is a permission-denied failure — the widget
61    /// uses this to gray out the offending directory in the view.
62    pub fn is_permission_denied(&self) -> bool {
63        self.io_kind() == io::ErrorKind::PermissionDenied
64    }
65}
66
67impl std::fmt::Display for Error {
68    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69        match self {
70            Self::Io { path, message, .. } => {
71                write!(f, "I/O error at {}: {}", path.display(), message)
72            }
73        }
74    }
75}
76
77impl std::error::Error for Error {}
78
79/// Convert a [`swdir::ScanError`] into our crate error.
80///
81/// Kept private to keep swdir's types out of our public API.
82impl From<&swdir::ScanError> for Error {
83    fn from(e: &swdir::ScanError) -> Self {
84        Self::io(e.path(), e.io_kind(), e.to_string())
85    }
86}