//! Trait and implementations for hook handlers. //! //! You can implement the trait yourself, or use any of the provided implementations: //! - for closures, //! - for std and tokio channels, //! - for printing to writers, in `Debug` and `Display` (where supported) modes (generally used for //! debugging and testing, as they don't allow any other output customisation), //! - for `()`, as placeholder. //! //! The implementation for [`FnMut`] only supports fns that return a [`Future`]. Unfortunately //! it's not possible to provide an implementation for fns that don't return a `Future` as well, //! so to call sync code you must either provide an async handler, or use the [`SyncFnHandler`] //! wrapper. //! //! # Examples //! //! In each example `on_data` is the following function: //! //! ``` //! # use watchexec::handler::Handler; //! fn on_data>>(_: T) {} //! ``` //! //! Async closure: //! //! ``` //! use tokio::io::{AsyncWriteExt, stdout}; //! # use watchexec::handler::Handler; //! # fn on_data>>(_: T) {} //! on_data(|data: Vec| async move { //! stdout().write_all(&data).await //! }); //! ``` //! //! Sync code in async closure: //! //! ``` //! use std::io::{Write, stdout}; //! # use watchexec::handler::Handler; //! # fn on_data>>(_: T) {} //! on_data(|data: Vec| async move { //! stdout().write_all(&data) //! }); //! ``` //! //! Sync closure with wrapper: //! //! ``` //! use std::io::{Write, stdout}; //! # use watchexec::handler::{Handler, SyncFnHandler}; //! # fn on_data>>(_: T) {} //! on_data(SyncFnHandler::from(|data: Vec| { //! stdout().write_all(&data) //! })); //! ``` //! //! Std channel: //! //! ``` //! use std::sync::mpsc; //! # use watchexec::handler::Handler; //! # fn on_data>>(_: T) {} //! let (s, r) = mpsc::channel(); //! on_data(s); //! ``` //! //! Tokio channel: //! //! ``` //! use tokio::sync::mpsc; //! # use watchexec::handler::Handler; //! # fn on_data>>(_: T) {} //! let (s, r) = mpsc::channel(123); //! on_data(s); //! ``` //! //! Printing to console: //! //! ``` //! use std::io::{Write, stderr, stdout}; //! # use watchexec::handler::{Handler, PrintDebug, PrintDisplay}; //! # fn on_data>(_: T) {} //! on_data(PrintDebug(stdout())); //! on_data(PrintDisplay(stderr())); //! ``` use std::{error::Error, future::Future, io::Write, marker::PhantomData}; use tokio::{runtime::Handle, task::block_in_place}; use crate::error::RuntimeError; /// A callable that can be used to hook into watchexec. pub trait Handler { /// Call the handler with the given data. fn handle(&mut self, _data: T) -> Result<(), Box>; } pub(crate) fn rte(ctx: &'static str, err: Box) -> RuntimeError { RuntimeError::Handler { ctx, err: err.to_string(), } } /// Wrapper for [`Handler`]s that are non-future [`FnMut`]s. /// /// Construct using [`Into::into`]: /// /// ``` /// # use watchexec::handler::{Handler as _, SyncFnHandler}; /// # let f: SyncFnHandler<(), std::io::Error, _> = /// (|data| { dbg!(data); Ok(()) }).into() /// # ; /// ``` /// /// or [`From::from`]: /// /// ``` /// # use watchexec::handler::{Handler as _, SyncFnHandler}; /// # let f: SyncFnHandler<(), std::io::Error, _> = /// SyncFnHandler::from(|data| { dbg!(data); Ok(()) }); /// ``` pub struct SyncFnHandler where E: Error + 'static, F: FnMut(T) -> Result<(), E> + Send + 'static, { inner: F, _t: PhantomData, _e: PhantomData, } impl From for SyncFnHandler where E: Error + 'static, F: FnMut(T) -> Result<(), E> + Send + 'static, { fn from(inner: F) -> Self { Self { inner, _t: PhantomData, _e: PhantomData, } } } impl Handler for SyncFnHandler where E: Error + 'static, F: FnMut(T) -> Result<(), E> + Send + 'static, { fn handle(&mut self, data: T) -> Result<(), Box> { (self.inner)(data).map_err(|e| Box::new(e) as _) } } impl Handler for F where E: Error + 'static, F: FnMut(T) -> U + Send + 'static, U: Future>, { fn handle(&mut self, data: T) -> Result<(), Box> { // this will always be called within watchexec context, which runs within tokio block_in_place(|| { Handle::current() .block_on((self)(data)) .map_err(|e| Box::new(e) as _) }) } } impl Handler for () { fn handle(&mut self, _data: T) -> Result<(), Box> { Ok::<(), std::convert::Infallible>(()).map_err(|e| Box::new(e) as _) } } impl Handler for std::sync::mpsc::Sender where T: Send + 'static, { fn handle(&mut self, data: T) -> Result<(), Box> { self.send(data).map_err(|e| Box::new(e) as _) } } impl Handler for tokio::sync::mpsc::Sender where T: std::fmt::Debug + 'static, { fn handle(&mut self, data: T) -> Result<(), Box> { self.try_send(data).map_err(|e| Box::new(e) as _) } } /// A handler implementation to print to any [`Write`]r (e.g. stdout) in `Debug` format. pub struct PrintDebug(pub W); impl Handler for PrintDebug where T: std::fmt::Debug, W: Write, { fn handle(&mut self, data: T) -> Result<(), Box> { writeln!(self.0, "{:?}", data).map_err(|e| Box::new(e) as _) } } /// A handler implementation to print to any [`Write`]r (e.g. stdout) in `Display` format. pub struct PrintDisplay(pub W); impl Handler for PrintDisplay where T: std::fmt::Display, W: Write, { fn handle(&mut self, data: T) -> Result<(), Box> { writeln!(self.0, "{}", data).map_err(|e| Box::new(e) as _) } }