//! Contains [`Error`]

use thiserror::Error;

/// List of features whose non-activation may cause a runtime error.
/// Used to indicate which lack of feature caused [`Error::FeatureNotActive`].
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Feature {
    /// Snappy compression and decompression
    Snappy,
    /// Brotli compression and decompression
    Brotli,
    /// Gzip compression and decompression
    Gzip,
    /// Lz4 raw compression and decompression
    Lz4,
    /// Zstd compression and decompression
    Zstd,
}

/// Errors generated by this crate
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum Error {
    /// When the parquet file is known to be out of spec.
    #[error("File out of specification: {0}")]
    OutOfSpec(String),

    /// Error presented when trying to use a code branch that requires activating a feature.
    #[error("The feature \"{1:?}\" needs to be active to {1}")]
    FeatureNotActive(Feature, String),

    /// Error presented when trying to use a feature from parquet that is not yet supported
    #[error("Not yet supported: {0}")]
    FeatureNotSupported(String),

    /// When encoding, the user passed an invalid parameter
    #[error("Invalid parameter: {0}")]
    InvalidParameter(String),

    /// When decoding or decompressing, the page would allocate more memory than allowed
    #[error("Operation would exceed memory use threshold")]
    WouldOverAllocate,

    /// When a transport error occurs when reading data
    #[error("Transport error: {0}")]
    Transport(String),

    #[error("Can't deserialize to parquet native type: {0}")]
    TryFromSliceError(#[from] std::array::TryFromSliceError),

    #[error("underlying IO error: {0}")]
    IoError(#[from] std::io::Error),

    #[error("OOM: {0}")]
    TryReserveError(#[from] std::collections::TryReserveError),

    #[error("Number must be zero or positive: {0}")]
    TryFromIntError(#[from] std::num::TryFromIntError),

    #[cfg(feature = "snappy")]
    #[error("underlying snap error: {0}")]
    SnapError(#[from] snap::Error),

    #[cfg(feature = "lz4_flex")]
    #[error("underlying lz4_flex error: {0}")]
    DecompressError(#[from] lz4_flex::block::DecompressError),

    #[cfg(feature = "lz4_flex")]
    #[error("underlying lz4_flex error: {0}")]
    CompressError(#[from] lz4_flex::block::CompressError),
}

impl Error {
    pub(crate) fn oos<I: Into<String>>(message: I) -> Self {
        Self::OutOfSpec(message.into())
    }
}

impl From<parquet_format_safe::thrift::Error> for Error {
    fn from(e: parquet_format_safe::thrift::Error) -> Error {
        match e {
            parquet_format_safe::thrift::Error::Transport(msg) => {
                Error::Transport(format!("io error occurred when decoding thrift: {}", msg))
            }
            _ => Error::OutOfSpec(format!("Invalid thrift: {}", e)),
        }
    }
}

/// A specialized `Result` for Parquet errors.
pub type Result<T> = std::result::Result<T, Error>;
