use tokio::sync::watch;
use crate::errors::Error;
use crate::transport::common::SessionState;
#[derive(Debug)]
pub(crate) struct ConnectionSignal {
sender: watch::Sender<SessionState>,
}
impl Default for ConnectionSignal {
fn default() -> Self {
Self {
sender: watch::channel(SessionState::Connected).0,
}
}
}
impl ConnectionSignal {
pub(crate) fn is_connected(&self) -> bool {
*self.sender.borrow() == SessionState::Connected
}
pub(crate) fn set_connected(&self) {
self.set(SessionState::Connected);
}
pub(crate) fn set_disconnected(&self) {
self.set(SessionState::Disconnected);
}
pub(crate) fn shutdown(&self) {
self.set(SessionState::ShutDown);
}
pub(crate) fn fail(&self) {
self.set(SessionState::Failed);
}
pub(crate) fn check_ended(&self) -> Result<(), Error> {
self.sender.borrow().check_ended()
}
pub(crate) fn check(&self) -> Result<(), Error> {
self.sender.borrow().check()
}
pub(crate) async fn wait_connected(&self) -> Result<(), Error> {
let mut receiver = self.sender.subscribe();
loop {
let state = *receiver.borrow_and_update();
if state != SessionState::Disconnected {
return state.check();
}
if receiver.changed().await.is_err() {
return Err(Error::Shutdown);
}
}
}
fn set(&self, new_state: SessionState) {
self.sender.send_if_modified(|state| {
if state.is_terminal() || *state == new_state {
return false;
}
*state = new_state;
true
});
}
}
#[cfg(test)]
#[path = "connection_signal_tests.rs"]
mod tests;