//! Error types used and generated by Calloop.
//!
//! This module contains error types for Calloop's operations. They are designed
//! to make it easy to deal with errors arising from Calloop's internal I/O and
//! other operations.
//!
//! There are two top-level error types:
//!
//! - [`Error`]: used by callback functions, internal operations, and some event
//!   loop API calls
//!
//! - [`InsertError`]: used primarily by the [`insert_source()`] method when an
//!   event source cannot be added to the loop and needs to be given back to the
//!   caller
//!
//! [`insert_source()`]: crate::LoopHandle::insert_source()

use std::fmt::{self, Debug, Formatter};

/// The primary error type used by Calloop covering internal errors and I/O
/// errors that arise during loop operations such as source registration or
/// event dispatching.
#[derive(thiserror::Error, Debug)]
pub enum Error {
    /// When an event source is registered (or re- or un-registered) with the
    /// event loop, this error variant will occur if the token Calloop uses to
    /// keep track of the event source is not valid.
    #[error("invalid token provided to internal function")]
    InvalidToken,

    /// This variant wraps a [`std::io::Error`], which might arise from
    /// Calloop's internal operations.
    #[error("underlying IO error")]
    IoError(#[from] std::io::Error),

    /// Any other unexpected error kind (most likely from a user implementation of
    /// [`EventSource::process_events()`]) will be wrapped in this.
    ///
    /// [`EventSource::process_events()`]: crate::EventSource::process_events()
    #[error("other error during loop operation")]
    OtherError(#[from] Box<dyn std::error::Error + Sync + Send>),
}

impl From<Error> for std::io::Error {
    /// Converts Calloop's error type into a [`std::io::Error`].
    fn from(err: Error) -> Self {
        match err {
            Error::IoError(source) => source,
            Error::InvalidToken => Self::new(std::io::ErrorKind::InvalidInput, err.to_string()),
            Error::OtherError(source) => Self::new(std::io::ErrorKind::Other, source),
        }
    }
}

/// [`Result`] alias using Calloop's error type.
pub type Result<T> = core::result::Result<T, Error>;

/// An error generated when trying to insert an event source
#[derive(thiserror::Error)]
#[error("error inserting event source")]
pub struct InsertError<T> {
    /// The source that could not be inserted
    pub inserted: T,
    /// The generated error
    #[source]
    pub error: Error,
}

impl<T> Debug for InsertError<T> {
    #[cfg_attr(feature = "nightly_coverage", coverage(off))]
    fn fmt(&self, formatter: &mut Formatter) -> core::result::Result<(), fmt::Error> {
        write!(formatter, "{:?}", self.error)
    }
}

impl<T> From<InsertError<T>> for crate::Error {
    /// Converts the [`InsertError`] into Calloop's error type, throwing away
    /// the contained source.
    #[cfg_attr(feature = "nightly_coverage", coverage(off))]
    fn from(e: InsertError<T>) -> crate::Error {
        e.error
    }
}
