Documentation
//! Custom error types that mirror crossbeam channel errors.
//!
//! This module provides custom error enums that have the same structure and behavior
//! as crossbeam channel errors, but allow for better abstraction and potential
//! future customization.

use std::fmt;

/// Error returned by [`Sender::send`].
///
/// This error indicates that all receivers have been dropped and the channel is closed.
#[derive(Clone, PartialEq, Eq)]
pub struct SendError<T>(pub T);

impl<T> SendError<T> {
    /// Returns the message that couldn't be sent.
    pub fn into_inner(self) -> T {
        self.0
    }
}

impl<T> fmt::Debug for SendError<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("SendError").finish_non_exhaustive()
    }
}

impl<T> fmt::Display for SendError<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "sending on a closed channel")
    }
}

impl<T> std::error::Error for SendError<T> {}

/// Error returned by [`Receiver::recv`].
///
/// This error indicates that all senders have been dropped and the channel is empty.
#[derive(Clone, PartialEq, Eq)]
pub struct RecvError;

impl fmt::Debug for RecvError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("RecvError").finish()
    }
}

impl fmt::Display for RecvError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "receiving on a closed channel")
    }
}

impl std::error::Error for RecvError {}

/// Error returned by [`Sender::try_send`].
#[derive(Clone, PartialEq, Eq)]
pub enum TrySendError<T> {
    /// The channel is full but not closed.
    Full(T),
    /// The channel is closed.
    Disconnected(T),
}

impl<T> TrySendError<T> {
    /// Returns the message that couldn't be sent.
    pub fn into_inner(self) -> T {
        match self {
            TrySendError::Full(t) | TrySendError::Disconnected(t) => t,
        }
    }
}

impl<T> fmt::Debug for TrySendError<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            TrySendError::Full(_) => f.debug_tuple("Full").finish(),
            TrySendError::Disconnected(_) => f.debug_tuple("Disconnected").finish(),
        }
    }
}

impl<T> fmt::Display for TrySendError<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            TrySendError::Full(_) => write!(f, "sending on a full channel"),
            TrySendError::Disconnected(_) => write!(f, "sending on a closed channel"),
        }
    }
}

impl<T> std::error::Error for TrySendError<T> {}

/// Error returned by [`Receiver::try_recv`].
#[derive(Clone, PartialEq, Eq)]
pub enum TryRecvError {
    /// The channel is empty but not closed.
    Empty,
    /// The channel is closed.
    Disconnected,
}

impl fmt::Debug for TryRecvError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            TryRecvError::Empty => f.debug_tuple("Empty").finish(),
            TryRecvError::Disconnected => f.debug_tuple("Disconnected").finish(),
        }
    }
}

impl fmt::Display for TryRecvError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            TryRecvError::Empty => write!(f, "receiving on an empty channel"),
            TryRecvError::Disconnected => write!(f, "receiving on a closed channel"),
        }
    }
}

impl std::error::Error for TryRecvError {}

/// Error returned by [`Sender::send_timeout`].
#[derive(Clone, PartialEq, Eq)]
pub enum SendTimeoutError<T> {
    /// The timeout expired.
    Timeout(T),
    /// The channel is closed.
    Disconnected(T),
}

impl<T> SendTimeoutError<T> {
    /// Returns the message that couldn't be sent.
    pub fn into_inner(self) -> T {
        match self {
            SendTimeoutError::Timeout(t) | SendTimeoutError::Disconnected(t) => t,
        }
    }
}

impl<T> fmt::Debug for SendTimeoutError<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            SendTimeoutError::Timeout(_) => f.debug_tuple("Timeout").finish(),
            SendTimeoutError::Disconnected(_) => f.debug_tuple("Disconnected").finish(),
        }
    }
}

impl<T> fmt::Display for SendTimeoutError<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            SendTimeoutError::Timeout(_) => write!(f, "sending timed out"),
            SendTimeoutError::Disconnected(_) => write!(f, "sending on a closed channel"),
        }
    }
}

impl<T> std::error::Error for SendTimeoutError<T> {}

/// Error returned by [`Receiver::recv_timeout`].
#[derive(Clone, PartialEq, Eq)]
pub enum RecvTimeoutError {
    /// The timeout expired.
    Timeout,
    /// The channel is closed.
    Disconnected,
}

impl fmt::Debug for RecvTimeoutError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            RecvTimeoutError::Timeout => f.debug_tuple("Timeout").finish(),
            RecvTimeoutError::Disconnected => f.debug_tuple("Disconnected").finish(),
        }
    }
}

impl fmt::Display for RecvTimeoutError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            RecvTimeoutError::Timeout => write!(f, "receiving timed out"),
            RecvTimeoutError::Disconnected => write!(f, "receiving on a closed channel"),
        }
    }
}

impl std::error::Error for RecvTimeoutError {}

/// Error returned by [`Select::try_select`].
#[derive(Clone, PartialEq, Eq)]
pub struct TrySelectError;

impl fmt::Debug for TrySelectError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("TrySelectError").finish()
    }
}

impl fmt::Display for TrySelectError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "no operations were ready")
    }
}

impl std::error::Error for TrySelectError {}

/// Error returned by [`Select::select_timeout`].
#[derive(Clone, PartialEq, Eq)]
pub struct SelectTimeoutError;

impl fmt::Debug for SelectTimeoutError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("SelectTimeoutError").finish()
    }
}

impl fmt::Display for SelectTimeoutError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "select timed out")
    }
}

impl std::error::Error for SelectTimeoutError {}

/// Error returned by [`Select::ready_timeout`].
#[derive(Clone, PartialEq, Eq)]
pub struct ReadyTimeoutError;

impl fmt::Debug for ReadyTimeoutError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("ReadyTimeoutError").finish()
    }
}

impl fmt::Display for ReadyTimeoutError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "ready timed out")
    }
}

impl std::error::Error for ReadyTimeoutError {}

// Conversion implementations from crossbeam errors to custom errors

impl<T> From<crossbeam_channel::SendError<T>> for SendError<T> {
    fn from(err: crossbeam_channel::SendError<T>) -> Self {
        SendError(err.into_inner())
    }
}

impl<T> From<SendError<T>> for crossbeam_channel::SendError<T> {
    fn from(err: SendError<T>) -> Self {
        crossbeam_channel::SendError(err.into_inner())
    }
}

impl From<crossbeam_channel::RecvError> for RecvError {
    fn from(_err: crossbeam_channel::RecvError) -> Self {
        RecvError
    }
}

impl From<RecvError> for crossbeam_channel::RecvError {
    fn from(_err: RecvError) -> Self {
        crossbeam_channel::RecvError
    }
}

impl<T> From<crossbeam_channel::TrySendError<T>> for TrySendError<T> {
    fn from(err: crossbeam_channel::TrySendError<T>) -> Self {
        match err {
            crossbeam_channel::TrySendError::Full(t) => TrySendError::Full(t),
            crossbeam_channel::TrySendError::Disconnected(t) => TrySendError::Disconnected(t),
        }
    }
}

impl<T> From<TrySendError<T>> for crossbeam_channel::TrySendError<T> {
    fn from(err: TrySendError<T>) -> Self {
        match err {
            TrySendError::Full(t) => crossbeam_channel::TrySendError::Full(t),
            TrySendError::Disconnected(t) => crossbeam_channel::TrySendError::Disconnected(t),
        }
    }
}

impl From<crossbeam_channel::TryRecvError> for TryRecvError {
    fn from(err: crossbeam_channel::TryRecvError) -> Self {
        match err {
            crossbeam_channel::TryRecvError::Empty => TryRecvError::Empty,
            crossbeam_channel::TryRecvError::Disconnected => TryRecvError::Disconnected,
        }
    }
}

impl From<TryRecvError> for crossbeam_channel::TryRecvError {
    fn from(err: TryRecvError) -> Self {
        match err {
            TryRecvError::Empty => crossbeam_channel::TryRecvError::Empty,
            TryRecvError::Disconnected => crossbeam_channel::TryRecvError::Disconnected,
        }
    }
}

impl<T> From<crossbeam_channel::SendTimeoutError<T>> for SendTimeoutError<T> {
    fn from(err: crossbeam_channel::SendTimeoutError<T>) -> Self {
        match err {
            crossbeam_channel::SendTimeoutError::Timeout(t) => SendTimeoutError::Timeout(t),
            crossbeam_channel::SendTimeoutError::Disconnected(t) => {
                SendTimeoutError::Disconnected(t)
            }
        }
    }
}

impl<T> From<SendTimeoutError<T>> for crossbeam_channel::SendTimeoutError<T> {
    fn from(err: SendTimeoutError<T>) -> Self {
        match err {
            SendTimeoutError::Timeout(t) => crossbeam_channel::SendTimeoutError::Timeout(t),
            SendTimeoutError::Disconnected(t) => {
                crossbeam_channel::SendTimeoutError::Disconnected(t)
            }
        }
    }
}

impl From<crossbeam_channel::RecvTimeoutError> for RecvTimeoutError {
    fn from(err: crossbeam_channel::RecvTimeoutError) -> Self {
        match err {
            crossbeam_channel::RecvTimeoutError::Timeout => RecvTimeoutError::Timeout,
            crossbeam_channel::RecvTimeoutError::Disconnected => RecvTimeoutError::Disconnected,
        }
    }
}

impl From<RecvTimeoutError> for crossbeam_channel::RecvTimeoutError {
    fn from(err: RecvTimeoutError) -> Self {
        match err {
            RecvTimeoutError::Timeout => crossbeam_channel::RecvTimeoutError::Timeout,
            RecvTimeoutError::Disconnected => crossbeam_channel::RecvTimeoutError::Disconnected,
        }
    }
}

impl From<crossbeam_channel::TrySelectError> for TrySelectError {
    fn from(_err: crossbeam_channel::TrySelectError) -> Self {
        TrySelectError
    }
}

impl From<TrySelectError> for crossbeam_channel::TrySelectError {
    fn from(_err: TrySelectError) -> Self {
        crossbeam_channel::TrySelectError
    }
}

impl From<crossbeam_channel::SelectTimeoutError> for SelectTimeoutError {
    fn from(_err: crossbeam_channel::SelectTimeoutError) -> Self {
        SelectTimeoutError
    }
}

impl From<SelectTimeoutError> for crossbeam_channel::SelectTimeoutError {
    fn from(_err: SelectTimeoutError) -> Self {
        crossbeam_channel::SelectTimeoutError
    }
}

impl From<crossbeam_channel::ReadyTimeoutError> for ReadyTimeoutError {
    fn from(_err: crossbeam_channel::ReadyTimeoutError) -> Self {
        ReadyTimeoutError
    }
}

impl From<ReadyTimeoutError> for crossbeam_channel::ReadyTimeoutError {
    fn from(_err: ReadyTimeoutError) -> Self {
        crossbeam_channel::ReadyTimeoutError
    }
}