Skip to content

Commit

Permalink
Use associated type for event source errors.
Browse files Browse the repository at this point in the history
  • Loading branch information
detly committed Jan 18, 2022
1 parent 9c70505 commit 15d06c1
Show file tree
Hide file tree
Showing 10 changed files with 38 additions and 123 deletions.
126 changes: 14 additions & 112 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
//! Error types used and generated by Calloop.
//!
//! This module contains error types for working with Calloop. They are designed
//! to make it easy to deal with errors arising from Calloop's internal I/O
//! operations alongside errors generated by event sources and callbacks.
//! 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:
//!
Expand All @@ -14,23 +14,11 @@
//! caller
//!
//! [`insert_source()`]: crate::LoopHandle::insert_source()
//!
//! If you are writing an event source (and specifically the
//! [`process_events()`] function of an event source) you should be able to
//! simply use the `?` operator or return an error converted with
//! [`std::convert::Into::into()`]. Those should work seamlessly if your error
//! is a [`std::io::Error`] or if it is a boxed `std::error::Error
//! + Sync + Send`. In all other cases you can use the `map_callback_err()`
//! method on the [`CalloopResult`] trait. If your error type doesn't implement
//! `Sync + Send`, see [`callback_err_nonsync()`] and
//! `map_callback_err_nonsync()`.
//!
//! [`process_events()`]: crate::EventSource::process_events()
use std::fmt::{self, Debug, Formatter};

/// The primary error type used by Calloop covering internal errors, I/O errors
/// and errors arising from callbacks and composed event sources.
/// The primary error type used by Calloop covering internal errors and I/O
/// errors.
#[derive(thiserror::Error, Debug)]
pub enum Error {
/// When an event source is registered (or re- or un-registered) with the
Expand All @@ -40,21 +28,17 @@ pub enum Error {
InvalidToken,

/// This variant wraps a [`std::io::Error`], which might arise from
/// Calloop's internal operations or within a user-defined callback or event
/// source.
/// Calloop's internal operations.
#[error("underlying IO error")]
IoError(#[from] std::io::Error),

/// This variant wraps any kind of boxed error ie. `Box<dyn
/// std::error::Error + Sync + Send>` (which includes a plain old
/// [`String`]). You should aim to use this for all non-I/O-related errors
/// arising inside composed sources and callbacks. It should happen
/// automatically if you use the `?` operator or the standard
/// [`std::convert::Into::into()`] method, but there is also the
/// [`CalloopResult`] extension trait with its `map_callback_err()`
/// function.
#[error("error generated by event callback")]
CallbackError(#[from] Box<dyn std::error::Error + Sync + Send>),
/// This variant wraps an error returned from an
/// [`EventSource::process_events()`] call during event
/// dispatching.
///
/// [`EventSource::process_events()`]: crate::EventSource::process_events()
#[error("error dispatching event")]
DispatchError(#[from] Box<dyn std::error::Error + Sync + Send>),
}

impl From<nix::errno::Errno> for Error {
Expand All @@ -71,7 +55,7 @@ impl From<Error> for std::io::Error {
match err {
Error::IoError(source) => Self::new(source.kind(), source),
Error::InvalidToken => Self::new(std::io::ErrorKind::InvalidInput, err),
Error::CallbackError(src) => Self::new(std::io::ErrorKind::Other, src),
Error::DispatchError(source) => Self::new(std::io::ErrorKind::Other, source),
}
}
}
Expand All @@ -90,88 +74,6 @@ pub struct InsertError<T> {
pub error: Error,
}

/// Error mapping function for errors that require an extra conversion.
///
/// If you use an error type that (a) isn't already `Box<dyn std::error::Error +
/// Sync + Send>` and (b) doesn't implement `std::error::Error + Sync + Send`
/// (such as `anyhow`) you will need to perform an extra conversion to construct
/// [`Error::CallbackError`]. This function can be used in `map_err()` to do
/// those conversions for you. Usually you would just call
/// `map_err(callback_err_other)` on a `Result`.
pub fn callback_err_other(error: impl Into<Box<dyn std::error::Error + Sync + Send>>) -> Error {
Error::CallbackError(error.into())
}

/// Error mapping function for errors that are not thread-safe.
///
/// You might need to return a [`Error::CallbackError`] which implements
/// [`std::error::Error`] but *not* `Sync + Send`, which is often the case for
/// errors that "give back" a value eg. channel sending errors. In that case you
/// can sacrifice your traceback structure with [`callback_err_nonsync()`],
/// usually by calling `.map_err(callback_err_nonsync)` on a `Result`. This will
/// render the error as a string inside a [`Error::CallbackError`] variant.
pub fn callback_err_nonsync(error: impl std::error::Error + 'static) -> Error {
Error::CallbackError(error.to_string().into())
}

/// An extension crate for easily converting `Result` values _inside_ Calloop
/// callbacks/trait methods to match the type _returned_ by the callback or
/// method.
///
/// Example:
///
/// ```rust-but-not-for-testing-see-https://github.com/rust-lang/rust/issues/63193
/// use calloop::CalloopResult;
///
/// // Method in calloop::EventSource.
/// fn process_events<F>(
/// &mut self,
/// readiness: calloop::Readiness,
/// token: calloop::Token,
/// mut callback: F,
/// ) -> calloop::Result<calloop::PostAction>
/// where
/// F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
/// {
/// // Do an operation that might fail. Let's pretend that read() here returns a
/// // core::result::Result<T, E> where E is some other crate's error type. With the
/// // CalloopResult trait in scope, map_callback_err() will convert the E into a
/// // calloop::Error::CallbackError so that using the ? operator works.
/// let data = self.socket.read().map_callback_err()?;
///
/// callback(data, &mut ());
///
/// Ok(calloop::PostAction::Continue)
/// }
/// ```
pub trait CalloopResult<T, E> {
/// Extension trait wrapper for [`callback_err_other()`].
fn map_callback_err(self) -> Result<T>
where
E: Into<Box<dyn std::error::Error + Sync + Send>>;

/// Extension trait wrapper for [`callback_err_nonsync()`].
fn map_callback_err_nonsync(self) -> Result<T>
where
E: std::error::Error + 'static;
}

impl<T, E> CalloopResult<T, E> for core::result::Result<T, E> {
fn map_callback_err(self) -> Result<T>
where
E: Into<Box<dyn std::error::Error + Sync + Send>>,
{
self.map_err(callback_err_other)
}

fn map_callback_err_nonsync(self) -> Result<T>
where
E: std::error::Error + 'static,
{
self.map_err(callback_err_nonsync)
}
}

#[cfg(not(tarpaulin_include))]
impl<T> Debug for InsertError<T> {
fn fmt(&self, formatter: &mut Formatter) -> core::result::Result<(), fmt::Error> {
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ pub use self::loop_logic::{EventLoop, LoopHandle, LoopSignal, RegistrationToken}
pub use self::sources::*;

pub mod error;
pub use error::{CalloopResult, Error, InsertError, Result};
pub use error::{Error, InsertError, Result};

pub mod io;
mod loop_logic;
Expand Down
6 changes: 4 additions & 2 deletions src/loop_logic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -609,13 +609,14 @@ mod tests {
type Event = u32;
type Metadata = ();
type Ret = ();
type Error = crate::Error;

fn process_events<F>(
&mut self,
readiness: Readiness,
token: Token,
mut callback: F,
) -> crate::Result<PostAction>
) -> Result<PostAction, Self::Error>
where
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
Expand Down Expand Up @@ -913,13 +914,14 @@ mod tests {
type Event = ();
type Metadata = ();
type Ret = ();
type Error = crate::Error;

fn process_events<F>(
&mut self,
_: Readiness,
_: Token,
mut callback: F,
) -> crate::Result<PostAction>
) -> Result<PostAction, Self::Error>
where
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
Expand Down
3 changes: 2 additions & 1 deletion src/sources/channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,14 @@ impl<T> EventSource for Channel<T> {
type Event = Event<T>;
type Metadata = ();
type Ret = ();
type Error = crate::Error;

fn process_events<C>(
&mut self,
readiness: Readiness,
token: Token,
mut callback: C,
) -> crate::Result<PostAction>
) -> Result<PostAction, Self::Error>
where
C: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
Expand Down
3 changes: 2 additions & 1 deletion src/sources/futures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,14 @@ impl<T> EventSource for Executor<T> {
type Event = T;
type Metadata = ();
type Ret = ();
type Error = crate::Error;

fn process_events<F>(
&mut self,
readiness: Readiness,
token: Token,
mut callback: F,
) -> crate::Result<PostAction>
) -> Result<PostAction, Self::Error>
where
F: FnMut(T, &mut ()),
{
Expand Down
3 changes: 2 additions & 1 deletion src/sources/generic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,14 @@ impl<F: AsRawFd> EventSource for Generic<F> {
type Event = Readiness;
type Metadata = F;
type Ret = crate::Result<PostAction>;
type Error = crate::Error;

fn process_events<C>(
&mut self,
readiness: Readiness,
token: Token,
mut callback: C,
) -> crate::Result<PostAction>
) -> Result<PostAction, Self::Error>
where
C: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
Expand Down
9 changes: 7 additions & 2 deletions src/sources/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,9 @@ pub trait EventSource {
///
/// Set to `()` if not needed.
type Ret;
/// The error type returned from [`process_events()`] (not the user
/// callback!).
type Error: std::error::Error + Sync + Send + 'static;

/// Process any relevant events
///
Expand All @@ -87,7 +90,7 @@ pub trait EventSource {
readiness: Readiness,
token: Token,
callback: F,
) -> crate::Result<PostAction>
) -> Result<PostAction, Self::Error>
where
F: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret;

Expand Down Expand Up @@ -159,7 +162,9 @@ where
ref mut source,
ref mut callback,
} = *disp;
source.process_events(readiness, token, |event, meta| callback(event, meta, data))
source
.process_events(readiness, token, |event, meta| callback(event, meta, data))
.map_err(|e| crate::Error::DispatchError(e.into()))
}

fn register(&self, poll: &mut Poll, token_factory: &mut TokenFactory) -> crate::Result<()> {
Expand Down
3 changes: 2 additions & 1 deletion src/sources/ping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,14 @@ impl EventSource for PingSource {
type Event = ();
type Metadata = ();
type Ret = ();
type Error = crate::Error;

fn process_events<C>(
&mut self,
readiness: Readiness,
token: Token,
mut callback: C,
) -> crate::Result<PostAction>
) -> Result<PostAction, Self::Error>
where
C: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
Expand Down
3 changes: 2 additions & 1 deletion src/sources/signals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,13 +125,14 @@ impl EventSource for Signals {
type Event = Event;
type Metadata = ();
type Ret = ();
type Error = crate::Error;

fn process_events<C>(
&mut self,
readiness: Readiness,
token: Token,
mut callback: C,
) -> crate::Result<PostAction>
) -> Result<PostAction, Self::Error>
where
C: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
Expand Down
3 changes: 2 additions & 1 deletion src/sources/timer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,13 +107,14 @@ impl<T> EventSource for Timer<T> {
type Event = T;
type Metadata = TimerHandle<T>;
type Ret = ();
type Error = crate::Error;

fn process_events<C>(
&mut self,
readiness: Readiness,
token: Token,
mut callback: C,
) -> crate::Result<PostAction>
) -> Result<PostAction, Self::Error>
where
C: FnMut(Self::Event, &mut Self::Metadata) -> Self::Ret,
{
Expand Down

0 comments on commit 15d06c1

Please sign in to comment.