use std::sync::{Arc, OnceLock};
use tokio_util::sync::CancellationToken;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum CloseReason {
Local,
IdleTimeout,
Peer,
Io,
Other,
}
pub(crate) struct ConnLifetime {
closed: CancellationToken,
reason: OnceLock<CloseReason>,
}
impl ConnLifetime {
pub(crate) fn new() -> Arc<Self> {
Arc::new(Self {
closed: CancellationToken::new(),
reason: OnceLock::new(),
})
}
pub(crate) fn close(&self, reason: CloseReason) {
let _ = self.reason.set(reason);
self.closed.cancel();
}
pub(crate) fn is_closed(&self) -> bool {
self.closed.is_cancelled()
}
}
#[derive(Clone)]
pub struct ConnectionToken(Arc<ConnLifetime>);
impl ConnectionToken {
pub(crate) fn new(lifetime: Arc<ConnLifetime>) -> Self {
Self(lifetime)
}
pub async fn closed(&self) -> CloseReason {
self.0.closed.cancelled().await;
self.close_reason().unwrap_or(CloseReason::Other)
}
pub fn is_closed(&self) -> bool {
self.0.is_closed()
}
pub fn close_reason(&self) -> Option<CloseReason> {
self.0.reason.get().copied()
}
}
impl std::fmt::Debug for ConnectionToken {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.close_reason() {
Some(reason) => write!(f, "ConnectionToken(closed: {reason:?})"),
None => write!(f, "ConnectionToken(up)"),
}
}
}