titan_client/tcp/
connection_status.rs1use std::sync::{Arc, RwLock};
2use tracing::error;
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum ConnectionStatus {
7 Disconnected,
9 Connecting,
11 Connected,
13 Reconnecting,
15}
16
17pub trait StatusListener: Send + Sync {
19 fn notify(&self, status: ConnectionStatus) -> bool;
20}
21
22impl StatusListener for std::sync::mpsc::Sender<ConnectionStatus> {
24 fn notify(&self, status: ConnectionStatus) -> bool {
25 self.send(status).is_ok()
26 }
27}
28
29impl StatusListener for tokio::sync::mpsc::Sender<ConnectionStatus> {
31 fn notify(&self, status: ConnectionStatus) -> bool {
32 self.try_send(status).is_ok()
33 }
34}
35
36#[derive(Clone)]
38pub struct ConnectionStatusTracker {
39 status: Arc<RwLock<ConnectionStatus>>,
40 listeners: Arc<RwLock<Vec<Box<dyn StatusListener>>>>,
41}
42
43impl ConnectionStatusTracker {
44 pub fn new() -> Self {
46 Self {
47 status: Arc::new(RwLock::new(ConnectionStatus::Disconnected)),
48 listeners: Arc::new(RwLock::new(Vec::new())),
49 }
50 }
51
52 pub fn with_status(initial_status: ConnectionStatus) -> Self {
54 Self {
55 status: Arc::new(RwLock::new(initial_status)),
56 listeners: Arc::new(RwLock::new(Vec::new())),
57 }
58 }
59
60 pub fn get_status(&self) -> ConnectionStatus {
62 match self.status.read() {
63 Ok(status) => *status,
64 Err(_) => {
65 error!("Failed to read connection status due to poisoned lock");
67 ConnectionStatus::Disconnected
68 }
69 }
70 }
71
72 pub fn register_listener<L: StatusListener + 'static>(&self, listener: L) {
74 if let Ok(mut listeners) = self.listeners.write() {
75 listeners.push(Box::new(listener));
76 } else {
77 error!("Failed to register status listener due to poisoned lock");
78 }
79 }
80
81 pub fn update_status(&self, new_status: ConnectionStatus) {
83 if let Ok(mut status_guard) = self.status.write() {
84 *status_guard = new_status;
85
86 if let Ok(mut listeners) = self.listeners.write() {
88 listeners.retain(|listener| listener.notify(new_status));
90 }
91 } else {
92 error!("Failed to update connection status due to poisoned lock");
93 }
94 }
95
96 pub fn create_updater<'a>(&'a self) -> impl Fn(ConnectionStatus) + 'a {
98 let status = Arc::clone(&self.status);
99 let listeners = Arc::<RwLock<Vec<Box<dyn StatusListener>>>>::clone(&self.listeners);
100
101 move |new_status| {
102 if let Ok(mut status_guard) = status.write() {
103 *status_guard = new_status;
104
105 if let Ok(mut listeners_guard) = listeners.write() {
107 listeners_guard.retain(|listener| listener.notify(new_status));
108 }
109 } else {
110 error!("Failed to update connection status due to poisoned lock");
111 }
112 }
113 }
114
115 pub fn get_inner(&self) -> Arc<RwLock<ConnectionStatus>> {
118 Arc::clone(&self.status)
119 }
120}
121
122impl Default for ConnectionStatusTracker {
123 fn default() -> Self {
124 Self::new()
125 }
126}