//! 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, sync::Arc}; use tokio::{runtime::Handle, sync::Mutex, 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>; } /// A shareable wrapper for a [`Handler`]. /// /// Internally this is a Tokio [`Mutex`]. pub struct HandlerLock(Arc + Send>>>); impl HandlerLock where T: Send, { /// Wrap a [`Handler`] into a lock. #[must_use] pub fn new(handler: Box + Send>) -> Self { Self(Arc::new(Mutex::new(handler))) } /// Replace the handler with a new one. pub async fn replace(&self, new: Box + Send>) { let mut handler = self.0.lock().await; *handler = new; } /// Call the handler. pub async fn call(&self, data: T) -> Result<(), Box> { let mut handler = self.0.lock().await; handler.handle(data) } } impl Clone for HandlerLock { fn clone(&self) -> Self { Self(Arc::clone(&self.0)) } } impl Default for HandlerLock where T: Send, { fn default() -> Self { Self::new(Box::new(())) } } pub(crate) fn rte(ctx: &'static str, err: &dyn Error) -> 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 _) } }