You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
yuurei/src/service.rs

148 lines
3.8 KiB

//! 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<dyn std::any::Any + Send + Sync + 'static>;
/// A handle to a service.
pub trait Service<T=ExitStatus>
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<T>;
/// 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<Self::Message>;
/// The message pipe for sending messages to the service.
fn message_in(&mut self) -> &mut mpsc::Sender<Self::Message>;
/// The message pipe for receiving messages from the service.
fn message_out(&mut self) -> &mut broadcast::Receiver<Self::Response>;
/// 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<bool>
{
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<T=()>
{
/// 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<T,E: Into<eyre::Report>> From<Result<T, E>> for ExitStatus<T>
{
fn from(from: Result<T, E>) -> 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<U, T: error::Error + Send+Sync+'static> From<T> for ExitStatus<U>
{
fn from(from: T) -> Self
{
Self::Error(from.into())
}
}*/
impl<T: Default> Default for ExitStatus<T>
{
#[inline]
fn default() -> Self
{
Self::Graceful(T::default())
}
}
impl<T> From<ExitStatus<T>> for eyre::Result<T>
{
fn from(from: ExitStatus<T>) -> 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),
}
}
}