use std::fmt;
use std::future::Future;
use std::sync::Arc;
use arc_swap::ArcSwapOption;
use lapin::Channel;
use tokio::sync::Mutex;
use crate::error::AmqpError;
pub(crate) trait Holds: Clone + Send + Sync {
fn channel(&self) -> &Channel;
}
impl Holds for Channel {
fn channel(&self) -> &Self {
self
}
}
pub(crate) struct ChannelCell<T> {
held: ArcSwapOption<T>,
opening: Mutex<()>,
}
impl<T: Holds> ChannelCell<T> {
pub(crate) fn new() -> Self {
Self {
held: ArcSwapOption::empty(),
opening: Mutex::new(()),
}
}
pub(crate) fn holding(value: T) -> Self {
Self {
held: ArcSwapOption::from_pointee(value),
opening: Mutex::new(()),
}
}
pub(crate) fn live(&self) -> Option<T> {
let held = self.held.load();
let value = held.as_deref()?;
value.channel().status().connected().then(|| value.clone())
}
pub(crate) fn held(&self) -> Option<T> {
self.held.load().as_deref().cloned()
}
pub(crate) async fn get<Open, Opening>(&self, open: Open) -> Result<T, AmqpError>
where
Open: FnOnce() -> Opening + Send,
Opening: Future<Output = Result<T, AmqpError>> + Send,
{
if let Some(live) = self.live() {
return Ok(live);
}
let _opening = self.opening.lock().await;
if let Some(live) = self.live() {
return Ok(live);
}
let opened = open().await?;
self.held.store(Some(Arc::new(opened.clone())));
Ok(opened)
}
}
impl<T> fmt::Debug for ChannelCell<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ChannelCell").finish_non_exhaustive()
}
}