//! Custom error types and result aliases for the obliteration process.
//!
//! This module defines the [`Error`] enum, which categorizes various failures 
//! that can occur during unmounting or recursive file removal.

use core::fmt;
use std::path::PathBuf;

/// A specialized [`Result`] type for obliteration operations.
pub type Result<T> = std::result::Result<T, Error>;

/// Represents the possible errors encountered during resource deconstruction.
#[derive(Debug)]
pub enum Error {
    /// The specified path does not exist on the filesystem.
    NotFound,
    /// The target path is invalid (e.g., attempt to remove `..` or empty paths).
    InvalidTarget(String),
    /// An underlying standard I/O error occurred.
    IoError(std::io::Error),
    /// A nested mount-point could not be detached before obliteration.
    UnmountFailed {
        /// The absolute path of the failed unmount.
        path: PathBuf,
        /// The filesystem type (e.g., ext4, fuse).
        fstype: String,
        /// The specific reason or error message from the unmounted attempt.
        reason: String,
    },
}

impl fmt::Display for Error {
    /// Formats the error for user-facing output.
    ///
    /// Provides clear messaging for path missing, invalid targets, I/O issues,
    /// and detailed reports when a mount-point fails to unmount.
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Error::NotFound => write!(f, "path not found"),
            Error::InvalidTarget(reason) => write!(f, "{}", reason),
            Error::IoError(e) => e.fmt(f),
            Error::UnmountFailed {
                path,
                fstype,
                reason,
            } => write!(
                f,
                "cannot unmount '{}' ({}): {}",
                path.display(),
                fstype,
                reason,
            ),
        }
    }
}

impl std::error::Error for Error {
    /// Returns the underlying source of the error if it is an I/O failure.
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            Error::IoError(e) => Some(e),
            _ => None,
        }
    }
}

impl From<std::io::Error> for Error {
    /// Automatically converts a [`std::io::Error`] into an [`Error::IoError`].
    fn from(err: std::io::Error) -> Error {
        Error::IoError(err)
    }
}
