dirx 0.1.0

Creates an in-memory index of all the files in a directory tree, and allows efficient scanning of only those files that have been modified since the index got created
Documentation
use std::error;
use std::fmt;
use std::io;
use std::path::Path;
use std::path::PathBuf;

#[cfg(feature = "unstable")]
type RawOsError = io::RawOsError;

#[cfg(not(feature = "unstable"))]
type RawOsError = i32;

#[derive(Debug)]
pub struct Error {
    path: PathBuf,
    operation: ErrorOperation,
    source: io::Error,
}

#[derive(Copy, Clone, Debug)]
pub enum ErrorOperation {
    OpenFile,
    OpenDirectory,
    ReadDirectory,
    ReadMetadata,
    GenerateData,
}

impl Error {
    #[inline]
    pub fn from_io_error(path: PathBuf, operation: ErrorOperation, source: io::Error) -> Self {
        Self {
            path,
            operation,
            source,
        }
    }

    #[inline]
    pub fn from_raw_os_error(path: PathBuf, operation: ErrorOperation, code: RawOsError) -> Self {
        Self::from_io_error(path, operation, io::Error::from_raw_os_error(code))
    }

    #[inline]
    pub fn last_os_error(path: PathBuf, operation: ErrorOperation) -> Self {
        Self::from_io_error(path, operation, io::Error::last_os_error())
    }

    #[inline]
    pub fn path(&self) -> &Path {
        &self.path
    }

    #[inline]
    pub fn operation(&self) -> ErrorOperation {
        self.operation
    }

    #[inline]
    pub fn source(&self) -> &io::Error {
        &self.source
    }

    #[inline]
    pub fn into_source(self) -> io::Error {
        self.source
    }

    #[inline]
    pub fn into_path_source(self) -> (PathBuf, io::Error) {
        (self.path, self.source)
    }

    #[inline]
    pub fn raw_os_error(&self) -> Option<RawOsError> {
        self.source.raw_os_error()
    }

    #[inline]
    pub fn kind(&self) -> io::ErrorKind {
        self.source.kind()
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let op = match self.operation {
            ErrorOperation::OpenFile => "opening file",
            ErrorOperation::OpenDirectory => "opening directory",
            ErrorOperation::ReadDirectory => "reading directory",
            ErrorOperation::ReadMetadata => "reading metadata",
            ErrorOperation::GenerateData => "generating associated data",
        };
        write!(
            f,
            "{}: error while {}: {}",
            self.path.display(),
            op,
            self.source
        )
    }
}

impl error::Error for Error {
    #[inline]
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        Some(&self.source)
    }
}

impl From<Error> for io::Error {
    #[inline]
    fn from(err: Error) -> io::Error {
        err.source
    }
}

pub(super) trait IntoScanError {
    fn into_scan_error(self, path: PathBuf, operation: ErrorOperation) -> Error;
}

impl<T> IntoScanError for T
where
    T: Into<io::Error>,
{
    #[inline]
    fn into_scan_error(self, path: PathBuf, operation: ErrorOperation) -> Error {
        Error::from_io_error(path, operation, self.into())
    }
}

#[derive(Debug)]
pub struct FilesystemLoopError {
    start_path: PathBuf,
}

impl FilesystemLoopError {
    pub fn new(start_path: PathBuf) -> Self {
        Self { start_path }
    }

    pub fn start_path(&self) -> &Path {
        &self.start_path
    }

    pub fn into_start_path(self) -> PathBuf {
        self.start_path
    }
}

impl fmt::Display for FilesystemLoopError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "File system loop detected: this is the same file system as {}",
            self.start_path.display()
        )
    }
}

impl error::Error for FilesystemLoopError {}