use std::sync::{Arc, Mutex};
use std::time::Instant;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConnectionState {
Disconnected,
Connecting,
Connected,
Disconnecting,
Failed,
Reconnecting,
}
impl ConnectionState {
pub fn can_send(&self) -> bool {
matches!(self, ConnectionState::Connected)
}
pub fn can_connect(&self) -> bool {
matches!(self, ConnectionState::Disconnected | ConnectionState::Failed)
}
pub fn is_connecting(&self) -> bool {
matches!(self, ConnectionState::Connecting | ConnectionState::Reconnecting)
}
}
pub struct ConnectionStateManager {
state: Arc<Mutex<ConnectionState>>,
state_changed_at: Arc<Mutex<Instant>>,
connect_started_at: Arc<Mutex<Option<Instant>>>,
}
impl Clone for ConnectionStateManager {
fn clone(&self) -> Self {
Self {
state: Arc::clone(&self.state),
state_changed_at: Arc::clone(&self.state_changed_at),
connect_started_at: Arc::clone(&self.connect_started_at),
}
}
}
impl ConnectionStateManager {
pub fn new() -> Self {
Self {
state: Arc::new(Mutex::new(ConnectionState::Disconnected)),
state_changed_at: Arc::new(Mutex::new(Instant::now())),
connect_started_at: Arc::new(Mutex::new(None)),
}
}
pub fn set_state(&self, new_state: ConnectionState) {
if let Ok(mut state) = self.state.lock() {
*state = new_state;
}
if let Ok(mut changed_at) = self.state_changed_at.lock() {
*changed_at = Instant::now();
}
}
pub fn get_state(&self) -> ConnectionState {
self.state.lock().map(|s| *s).unwrap_or(ConnectionState::Disconnected)
}
pub fn start_connecting(&self) {
self.set_state(ConnectionState::Connecting);
if let Ok(mut started_at) = self.connect_started_at.lock() {
*started_at = Some(Instant::now());
}
}
pub fn set_connected(&self) {
self.set_state(ConnectionState::Connected);
if let Ok(mut started_at) = self.connect_started_at.lock() {
*started_at = None;
}
}
pub fn set_disconnected(&self) {
self.set_state(ConnectionState::Disconnected);
if let Ok(mut started_at) = self.connect_started_at.lock() {
*started_at = None;
}
}
pub fn set_failed(&self) {
self.set_state(ConnectionState::Failed);
if let Ok(mut started_at) = self.connect_started_at.lock() {
*started_at = None;
}
}
pub fn set_reconnecting(&self) {
self.set_state(ConnectionState::Reconnecting);
if let Ok(mut started_at) = self.connect_started_at.lock() {
*started_at = Some(Instant::now());
}
}
pub fn state_changed_at(&self) -> Instant {
self.state_changed_at.lock().map(|t| *t).unwrap_or_else(|_| Instant::now())
}
pub fn connect_duration(&self) -> Option<std::time::Duration> {
self.connect_started_at
.lock()
.ok()
.and_then(|started_at| {
started_at.map(|start| start.elapsed())
})
}
}
impl Default for ConnectionStateManager {
fn default() -> Self {
Self::new()
}
}