//! Session services use super::*; use tokio::{ task::JoinHandle, sync::{ mpsc, broadcast, }, }; use std::{fmt,error}; use std::marker::{Send,Sync}; ///// A boxed message that can be downcasted. //pub type BoxedMessage = Box; /// A handle to a service. pub trait Service where T: Send + 'static { /// The message type to send to the service. type Message: Send + Sync + 'static; /// The response to expect from the service. type Response: Send + Sync + 'static; /// Return the wait handle. /// /// This method should drop the message pipeline. fn wait_on(self) -> JoinHandle; /// An immutable reference to the pipe for sending message. Useful for service handle detaching (i.e. cloning the message input pipe). fn message_in_ref(&self) -> &mpsc::Sender; /// The message pipe for sending messages to the service. fn message_in(&mut self) -> &mut mpsc::Sender; /// The message pipe for receiving messages from the service. fn message_out(&mut self) -> &mut broadcast::Receiver; /// Is the service alive? A `None` value means 'maybe', and is the default return. /// /// # Note /// This should not be considered an infallible indicator of if the service has crashed or not. A better method is attempting to send or receive a message and the sender/receiver returning an error. #[inline] fn is_alive(&self) -> Option { None } } /// Error returned when subscribing to a service #[derive(Debug)] #[non_exhaustive] pub enum SubscribeError { /// The service's receive has already been dropped. SenderDropped, /// The service dropped the response oneshot channel. NoResponse, } impl error::Error for SubscribeError{} impl fmt::Display for SubscribeError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::SenderDropped => write!(f, "the service has already stopped"), Self::NoResponse => write!(f, "the service declined to, or was unable to respond"), } } } #[macro_export] macro_rules! join_service { ($serv:expr) => ($serv.await.map_err(|_| $crate::service::ExitStatus::Abnormal)?) } /// How a service exited #[derive(Debug)] #[non_exhaustive] pub enum ExitStatus { /// A graceful exit with value Graceful(T), /// An abnormal exit (counted as error) /// /// # Usage /// Usually for panicked services, otherwise use `Self::Error`. /// The macro `join_service!()` can be used to convert handle join errors into this Abnormal, /// Exit on an error report (counted as error) Error(eyre::Report), } impl> From> for ExitStatus { fn from(from: Result) -> Self { match from { Ok(v) => Self::Graceful(v), Err(e) => Self::Error(e.into()) } } } #[derive(Debug)] /// The error `ExitStatus::Abnormal` converts to. pub struct AbnormalExitError; impl error::Error for AbnormalExitError{} impl fmt::Display for AbnormalExitError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "service terminated in an abnormal way") } } /* impl From for ExitStatus { fn from(from: T) -> Self { Self::Error(from.into()) } }*/ impl Default for ExitStatus { #[inline] fn default() -> Self { Self::Graceful(T::default()) } } impl From> for eyre::Result { fn from(from: ExitStatus) -> Self { match from { ExitStatus::Abnormal => Err(AbnormalExitError).with_note(|| "The background worker likely panicked"), ExitStatus::Graceful(t) => Ok(t), ExitStatus::Error(rep) => Err(rep).wrap_err(AbnormalExitError), } } }