//! Error types for all fstab parsing and validation operations.
//!
//! This module defines the error hierarchy used throughout the library.
//! Each error type implements `std::error::Error` via `thiserror`.

/// Error parsing a spec (fstab field 1).
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum SpecError {
    /// Spec field was empty.
    #[error("empty spec field")]
    Empty,
}

/// Error parsing a filesystem type (fstab field 3).
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum FsTypeError {
    /// Filesystem type field was empty.
    #[error("empty filesystem type (field 3)")]
    Empty,
}

/// Error parsing mount options (fstab field 4).
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum OptionsError {
    /// An option had an empty name (e.g., leading comma or `=value` without a name).
    #[error("empty option name in mount options")]
    EmptyOptionName,
}

/// Error constructing an [`OptItem`](crate::options::OptItem).
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum OptItemError {
    /// The option name was empty.
    #[error("option name must not be empty")]
    EmptyName,
}

/// Error constructing a [`MountPoint`](crate::types::MountPoint).
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum MountPointError {
    /// Mount point is not an absolute path and not `none` or `swap`.
    #[error("mount point must be an absolute path (start with '/') or 'none'")]
    NotAbsolute,
    /// Mount point was empty.
    #[error("mount point must not be empty")]
    Empty,
}

/// Error building an [`Entry`](crate::types::Entry) via the builder pattern.
///
/// Returned by [`EntryBuilder::build`](crate::types::EntryBuilder::build)
/// when a required field is missing.
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[non_exhaustive]
pub enum EntryBuilderError {
    /// The `spec` field was not set before calling `build()`.
    #[error("missing required field 'spec' in Entry builder")]
    MissingSpec,
    /// The `file` (mount point) field was not set before calling `build()`.
    #[error("missing required field 'file' (mount point) in Entry builder")]
    MissingFile,
    /// The `vfstype` field was not set before calling `build()`.
    #[error("missing required field 'vfstype' in Entry builder")]
    MissingFsType,
}

/// Fstab parsing and editing error.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum FstabError {
    /// A parse error on a specific line.
    #[error("line {line}: {kind}")]
    Parse {
        /// The 1-indexed line number where the error occurred.
        line: usize,
        /// The specific kind of parse error.
        #[source]
        kind: ParseErrorKind,
    },
    /// An I/O error (file not found, permission denied, etc.).
    #[error("I/O error: {0}")]
    Io(#[from] std::io::Error),
    /// An index was out of bounds when accessing an entry.
    #[error("index {0} out of bounds (len={1})")]
    IndexOutOfBounds(usize, usize),
}

/// Specific kind of parse error on a single fstab line.
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
pub enum ParseErrorKind {
    /// A required field was missing.
    #[error("missing field: {0}")]
    MissingField(&'static str),
    /// The spec field (field 1) was invalid.
    #[error("invalid spec: {0}")]
    InvalidSpec(#[from] SpecError),
    /// The mount point (field 2) was invalid.
    #[error("invalid mount point: {0}")]
    InvalidMountPoint(#[from] MountPointError),
    /// The filesystem type (field 3) was invalid.
    #[error("invalid filesystem type: {0}")]
    InvalidFsType(#[from] FsTypeError),
    /// The mount options (field 4) were invalid.
    #[error("invalid mount options: {0}")]
    InvalidOptions(#[from] OptionsError),
    /// The freq value (field 5) was not a valid integer.
    #[error("invalid freq value: {0}")]
    InvalidFreq(String),
    /// The passno value (field 6) was not a valid integer.
    #[error("invalid passno value: {0}")]
    InvalidPassNo(String),
}
