#![allow(clippy::missing_inline_in_public_items)]
use crate::fs::DirEntry;
use core::fmt;
use libc::{
EACCES, EAGAIN, EBADF, EBUSY, EEXIST, EFAULT, EFBIG, EINTR, EINVAL, EIO, EISDIR, ELOOP, EMFILE,
ENAMETOOLONG, ENFILE, ENOENT, ENOMEM, ENOTDIR, EOVERFLOW, EPERM, ETXTBSY,
};
use std::io;
#[derive(Debug)]
pub struct TraversalError {
pub(crate) dir: DirEntry,
pub(crate) error: DirEntryError,
}
impl TraversalError {
#[must_use]
pub const fn path(&self) -> &DirEntry {
&self.dir
}
#[must_use]
pub const fn error(&self) -> &DirEntryError {
&self.error
}
}
impl fmt::Display for TraversalError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Error accessing {}: {}", self.dir, self.error)
}
}
#[derive(Debug)]
#[allow(clippy::exhaustive_enums)]
pub enum FilesystemIOError {
AccessDenied(io::Error),
TemporarilyUnavailable,
InvalidPath,
FilesystemIO(io::Error),
TooManySymbolicLinks,
NameTooLong,
FileNotFound,
OutOfMemory,
NotADirectory,
BrokenPipe(io::Error),
FileExists(io::Error),
IsDirectory(io::Error),
FileTooLarge(io::Error),
ResourceBusy(io::Error),
InvalidFileDescriptor(io::Error),
ProcessFileLimitReached(io::Error),
SystemFileLimitReached(io::Error),
UnsupportedOperation(io::Error),
Other(io::Error),
}
impl fmt::Display for FilesystemIOError {
#[allow(clippy::pattern_type_mismatch)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::AccessDenied(e) => write!(f, "Permission denied: {e}"),
Self::TemporarilyUnavailable => {
write!(f, "Operation temporarily unavailable, retry later")
}
Self::InvalidPath => write!(f, "Invalid path or null pointer"),
Self::FilesystemIO(e) => write!(f, "Filesystem I/O error: {e}"),
Self::TooManySymbolicLinks => {
write!(f, "Too many symbolic links (recursion limit exceeded)")
}
Self::NameTooLong => write!(f, "Pathname component exceeds system limits"),
Self::FileNotFound => write!(f, "File or directory not found"),
Self::OutOfMemory => write!(f, "Out of memory for filesystem operation"),
Self::NotADirectory => write!(f, "Path exists but is not a directory"),
Self::BrokenPipe(e) => write!(f, "Broken pipe: {e}"),
Self::FileExists(e) => write!(f, "File already exists: {e}"),
Self::IsDirectory(e) => write!(f, "Path refers to a directory: {e}"),
Self::FileTooLarge(e) => write!(f, "File too large: {e}"),
Self::ResourceBusy(e) => write!(f, "Resource busy or locked: {e}"),
Self::InvalidFileDescriptor(e) => write!(f, "Invalid file descriptor: {e}"),
Self::ProcessFileLimitReached(e) => {
write!(f, "Process file descriptor limit reached: {e}")
}
Self::SystemFileLimitReached(e) => {
write!(f, "System-wide file descriptor limit reached: {e}")
}
Self::UnsupportedOperation(e) => write!(f, "Unsupported operation: {e}"),
Self::Other(e) => write!(f, "OS error: {e}"),
}
}
}
impl<E> From<E> for FilesystemIOError
where
E: Into<std::io::Error>,
{
fn from(error: E) -> Self {
Self::from_io_error(error.into())
}
}
impl FilesystemIOError {
#[must_use]
pub fn from_io_error(error: io::Error) -> Self {
if let Some(code) = error.raw_os_error() {
match code {
EACCES | EPERM => Self::AccessDenied(error),
EAGAIN | EINTR => Self::TemporarilyUnavailable,
EINVAL | EFAULT => Self::InvalidPath,
ENOENT => Self::FileNotFound,
EEXIST => Self::FileExists(error),
EISDIR => Self::IsDirectory(error),
ENOTDIR => Self::NotADirectory,
ELOOP => Self::TooManySymbolicLinks,
ENAMETOOLONG => Self::NameTooLong,
EIO => Self::FilesystemIO(error),
EBADF => Self::InvalidFileDescriptor(error),
ENOMEM => Self::OutOfMemory,
EFBIG | EOVERFLOW => Self::FileTooLarge(error),
EBUSY | ETXTBSY => Self::ResourceBusy(error),
EMFILE => Self::ProcessFileLimitReached(error),
ENFILE => Self::SystemFileLimitReached(error),
_ => Self::Other(error),
}
} else {
match error.kind() {
io::ErrorKind::BrokenPipe => Self::BrokenPipe(error),
io::ErrorKind::NotFound => Self::FileNotFound,
io::ErrorKind::PermissionDenied => Self::AccessDenied(error),
io::ErrorKind::NotADirectory => Self::NotADirectory,
io::ErrorKind::AlreadyExists => Self::FileExists(error),
io::ErrorKind::IsADirectory => Self::IsDirectory(error),
io::ErrorKind::NotSeekable => Self::InvalidFileDescriptor(error),
io::ErrorKind::ResourceBusy => Self::ResourceBusy(error),
io::ErrorKind::Unsupported => Self::UnsupportedOperation(error),
io::ErrorKind::UnexpectedEof => Self::FilesystemIO(error),
io::ErrorKind::OutOfMemory => Self::OutOfMemory,
_ => Self::Other(error),
}
}
}
}
#[derive(Debug)]
#[allow(clippy::exhaustive_enums)]
pub enum DirEntryError {
TimeError,
Utf8Error(core::str::Utf8Error),
NulError(std::ffi::NulError),
IOError(FilesystemIOError),
}
impl From<io::Error> for DirEntryError {
fn from(error: io::Error) -> Self {
Self::IOError(FilesystemIOError::from_io_error(error))
}
}
impl From<FilesystemIOError> for DirEntryError {
fn from(error: FilesystemIOError) -> Self {
Self::IOError(error)
}
}
impl From<core::str::Utf8Error> for DirEntryError {
fn from(e: core::str::Utf8Error) -> Self {
Self::Utf8Error(e)
}
}
impl fmt::Display for DirEntryError {
#[allow(clippy::pattern_type_mismatch)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::TimeError => write!(f, "Invalid time conversion"),
Self::Utf8Error(e) => write!(f, "UTF-8 conversion error: {e}"),
Self::NulError(e) => write!(f, "Invalid nulls detected in name {e}"),
Self::IOError(e) => write!(f, "I/O error: {e}"),
}
}
}
#[derive(Debug)]
#[allow(clippy::exhaustive_enums)]
pub enum SearchConfigError {
GlobToRegexError(crate::util::Error),
RegexError(regex::Error),
IOError(io::Error),
TraversalError(DirEntryError),
NotADirectory,
}
impl From<io::Error> for SearchConfigError {
fn from(error: io::Error) -> Self {
Self::IOError(error)
}
}
#[allow(clippy::pattern_type_mismatch)]
impl fmt::Display for SearchConfigError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::GlobToRegexError(e) => write!(f, "Glob to regex conversion error: {e}"),
Self::RegexError(e) => write!(f, "Regex error: {e}"),
Self::IOError(e) => write!(f, "IO error: {e}"),
Self::NotADirectory => write!(f, "Path is not a directory"),
Self::TraversalError(e) => write!(f, "Traversal error: {e}"),
}
}
}
impl core::error::Error for SearchConfigError {}