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.
148 lines
3.8 KiB
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),
|
|
}
|
|
}
|
|
}
|
|
|
|
|