#[cfg(feature = "testing")]
use std::any::Any;
use std::{
any::type_name,
error::Error as StdError,
future::Future,
pin::Pin,
sync::{Arc, Mutex},
};
use crate::{Broker, ConnectedBroker};
pub(crate) type BoxError = Box<dyn StdError + Send + Sync>;
pub(crate) type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
#[doc(hidden)]
pub type ConnectedSlot<B> = Arc<Mutex<Option<Arc<<B as Broker>::Connected>>>>;
pub(crate) trait BrokerLifecycle: Send + Sync {
fn connect(
self: Box<Self>,
) -> BoxFuture<'static, Result<Box<dyn ConnectedLifecycle>, BoxError>>;
}
pub(crate) trait ConnectedLifecycle: Send + Sync {
fn shutdown(self: Box<Self>) -> BoxFuture<'static, Result<(), BoxError>>;
fn name(&self) -> &'static str;
#[cfg(feature = "testing")]
fn as_any(&self) -> &dyn Any
where
Self: 'static;
}
pub(crate) struct BrokerCell<B: Broker> {
pub(crate) broker: B,
pub(crate) slot: ConnectedSlot<B>,
}
impl<B: Broker + 'static> BrokerLifecycle for BrokerCell<B> {
fn connect(
self: Box<Self>,
) -> BoxFuture<'static, Result<Box<dyn ConnectedLifecycle>, BoxError>> {
Box::pin(async move {
let connected = Arc::new(
self.broker
.connect()
.await
.map_err(|e| Box::new(e) as BoxError)?,
);
*self.slot.lock().expect("connected slot mutex poisoned") =
Some(Arc::clone(&connected));
Ok(Box::new(ConnectedCell::<B> {
connected,
slot: self.slot,
}) as Box<dyn ConnectedLifecycle>)
})
}
}
struct ConnectedCell<B: Broker> {
connected: Arc<B::Connected>,
slot: ConnectedSlot<B>,
}
impl<B: Broker + 'static> ConnectedLifecycle for ConnectedCell<B> {
fn name(&self) -> &'static str {
type_name::<B>()
}
#[cfg(feature = "testing")]
fn as_any(&self) -> &dyn Any
where
Self: 'static,
{
self.connected.as_ref()
}
fn shutdown(self: Box<Self>) -> BoxFuture<'static, Result<(), BoxError>> {
Box::pin(async move {
self.slot
.lock()
.expect("connected slot mutex poisoned")
.take();
let connected = Arc::try_unwrap(self.connected).map_err(|_| {
Box::from(format!(
"connected broker {} still aliased at shutdown",
type_name::<B>(),
)) as BoxError
})?;
connected
.shutdown()
.await
.map(|_closed| ())
.map_err(|e| Box::new(e) as BoxError)
})
}
}