use std::fmt;
use tokio::sync::{broadcast, mpsc};
use mermaid_domain::Msg;
#[derive(Debug)]
pub struct EngineGone(Box<Msg>);
impl EngineGone {
#[must_use]
pub fn message(&self) -> &Msg {
&self.0
}
#[must_use]
pub fn into_message(self) -> Msg {
*self.0
}
}
impl fmt::Display for EngineGone {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "the engine is no longer running")
}
}
impl std::error::Error for EngineGone {}
pub struct EngineHandle<E> {
inbox: mpsc::Sender<Msg>,
events: broadcast::Sender<E>,
}
impl<E> Clone for EngineHandle<E> {
fn clone(&self) -> Self {
Self {
inbox: self.inbox.clone(),
events: self.events.clone(),
}
}
}
impl<E> EngineHandle<E> {
#[must_use]
pub const fn new(inbox: mpsc::Sender<Msg>, events: broadcast::Sender<E>) -> Self {
Self { inbox, events }
}
#[must_use]
pub fn with_capacity(inbox: mpsc::Sender<Msg>, capacity: usize) -> Self
where
E: Clone,
{
Self::new(inbox, broadcast::channel(capacity).0)
}
pub async fn send(&self, msg: Msg) -> Result<(), EngineGone> {
self.inbox
.send(msg)
.await
.map_err(|e| EngineGone(Box::new(e.0)))
}
pub fn try_send(&self, msg: Msg) -> Result<(), EngineGone> {
use mpsc::error::TrySendError;
match self.inbox.try_send(msg) {
Ok(()) => Ok(()),
Err(TrySendError::Full(m) | TrySendError::Closed(m)) => Err(EngineGone(Box::new(m))),
}
}
#[must_use]
pub fn subscribe(&self) -> broadcast::Receiver<E> {
self.events.subscribe()
}
#[must_use]
pub const fn publisher(&self) -> &broadcast::Sender<E> {
&self.events
}
#[must_use]
pub fn is_running(&self) -> bool {
!self.inbox.is_closed()
}
}
impl<E> fmt::Debug for EngineHandle<E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EngineHandle")
.field("running", &self.is_running())
.field("subscribers", &self.events.receiver_count())
.finish_non_exhaustive()
}
}