use std::fmt;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum StorageErrorKind {
NotFound,
DirectoryNotFound,
PermissionDenied,
AlreadyExists,
IsDirectory,
NotDirectory,
DiskFull,
IoError,
FileNameTooLong,
PathTooLong,
TooManyOpenFiles,
ReadOnly,
StorageFull,
NetworkError,
NetworkTimeout,
InvalidFilename,
InvalidPath,
SymlinkLoop,
TooManySymlinks,
}
impl StorageErrorKind {
#[inline]
pub fn is_retryable(&self) -> bool {
match self {
StorageErrorKind::NotFound => false,
StorageErrorKind::DirectoryNotFound => false,
StorageErrorKind::PermissionDenied => false,
StorageErrorKind::AlreadyExists => false,
StorageErrorKind::IsDirectory => false,
StorageErrorKind::NotDirectory => false,
StorageErrorKind::DiskFull => false,
StorageErrorKind::IoError => true,
StorageErrorKind::FileNameTooLong => false,
StorageErrorKind::PathTooLong => false,
StorageErrorKind::TooManyOpenFiles => true,
StorageErrorKind::ReadOnly => false,
StorageErrorKind::StorageFull => false,
StorageErrorKind::NetworkError => true,
StorageErrorKind::NetworkTimeout => true,
StorageErrorKind::InvalidFilename => false,
StorageErrorKind::InvalidPath => false,
StorageErrorKind::SymlinkLoop => false,
StorageErrorKind::TooManySymlinks => false,
}
}
#[inline]
pub fn is_path_error(&self) -> bool {
matches!(
self,
StorageErrorKind::NotFound
| StorageErrorKind::DirectoryNotFound
| StorageErrorKind::InvalidPath
| StorageErrorKind::InvalidFilename
| StorageErrorKind::IsDirectory
| StorageErrorKind::NotDirectory
| StorageErrorKind::SymlinkLoop
| StorageErrorKind::TooManySymlinks
)
}
#[inline]
pub fn is_permission_error(&self) -> bool {
matches!(
self,
StorageErrorKind::PermissionDenied | StorageErrorKind::ReadOnly
)
}
#[inline]
pub fn is_capacity_error(&self) -> bool {
matches!(
self,
StorageErrorKind::DiskFull
| StorageErrorKind::StorageFull
| StorageErrorKind::TooManyOpenFiles
)
}
#[inline]
pub fn is_network_error(&self) -> bool {
matches!(
self,
StorageErrorKind::NetworkError | StorageErrorKind::NetworkTimeout
)
}
#[inline]
pub fn is_io_error(&self) -> bool {
matches!(self, StorageErrorKind::IoError)
}
#[inline]
pub fn category(&self) -> &str {
if self.is_path_error() {
"Path"
} else if self.is_permission_error() {
"Permission"
} else if self.is_capacity_error() {
"Capacity"
} else if self.is_network_error() {
"Network"
} else if self.is_io_error() {
"I/O"
} else {
"Other"
}
}
#[inline]
pub fn is_existence_error(&self) -> bool {
matches!(
self,
StorageErrorKind::AlreadyExists
| StorageErrorKind::NotFound
| StorageErrorKind::DirectoryNotFound
)
}
#[inline]
pub fn to_machine_string(&self) -> &'static str {
match self {
StorageErrorKind::NotFound => "not_found",
StorageErrorKind::DirectoryNotFound => "directory_not_found",
StorageErrorKind::PermissionDenied => "permission_denied",
StorageErrorKind::AlreadyExists => "already_exists",
StorageErrorKind::IsDirectory => "is_directory",
StorageErrorKind::NotDirectory => "not_directory",
StorageErrorKind::DiskFull => "disk_full",
StorageErrorKind::IoError => "io_error",
StorageErrorKind::FileNameTooLong => "file_name_too_long",
StorageErrorKind::PathTooLong => "path_too_long",
StorageErrorKind::TooManyOpenFiles => "too_many_open_files",
StorageErrorKind::ReadOnly => "read_only",
StorageErrorKind::StorageFull => "storage_full",
StorageErrorKind::NetworkError => "network_error",
StorageErrorKind::NetworkTimeout => "network_timeout",
StorageErrorKind::InvalidFilename => "invalid_filename",
StorageErrorKind::InvalidPath => "invalid_path",
StorageErrorKind::SymlinkLoop => "symlink_loop",
StorageErrorKind::TooManySymlinks => "too_many_symlinks",
}
}
}
impl fmt::Display for StorageErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
StorageErrorKind::NotFound => write!(f, "not found"),
StorageErrorKind::DirectoryNotFound => write!(f, "directory not found"),
StorageErrorKind::PermissionDenied => write!(f, "permission denied"),
StorageErrorKind::AlreadyExists => write!(f, "already exists"),
StorageErrorKind::IsDirectory => write!(f, "is a directory"),
StorageErrorKind::NotDirectory => write!(f, "is not a directory"),
StorageErrorKind::DiskFull => write!(f, "disk full"),
StorageErrorKind::IoError => write!(f, "I/O error"),
StorageErrorKind::FileNameTooLong => write!(f, "file name too long"),
StorageErrorKind::PathTooLong => write!(f, "path too long"),
StorageErrorKind::TooManyOpenFiles => write!(f, "too many open files"),
StorageErrorKind::ReadOnly => write!(f, "read-only"),
StorageErrorKind::StorageFull => write!(f, "storage full"),
StorageErrorKind::NetworkError => write!(f, "network error"),
StorageErrorKind::NetworkTimeout => write!(f, "network timeout"),
StorageErrorKind::InvalidFilename => write!(f, "invalid filename"),
StorageErrorKind::InvalidPath => write!(f, "invalid path"),
StorageErrorKind::SymlinkLoop => write!(f, "symlink loop"),
StorageErrorKind::TooManySymlinks => write!(f, "too many symbolic links"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_not_found_not_retryable() {
assert!(!StorageErrorKind::NotFound.is_retryable());
}
#[test]
fn test_io_error_retryable() {
assert!(StorageErrorKind::IoError.is_retryable());
}
#[test]
fn test_network_error_retryable() {
assert!(StorageErrorKind::NetworkError.is_retryable());
}
#[test]
fn test_permission_denied_not_retryable() {
assert!(!StorageErrorKind::PermissionDenied.is_retryable());
}
#[test]
fn test_display() {
assert_eq!(StorageErrorKind::NotFound.to_string(), "not found");
assert_eq!(StorageErrorKind::IoError.to_string(), "I/O error");
assert_eq!(StorageErrorKind::DiskFull.to_string(), "disk full");
}
}