supabase_realtime_rs/client/
state.rs

1use super::connection::ConnectionState;
2use crate::channel::RealtimeChannel;
3use crate::infrastructure::TaskManager;
4use std::sync::Arc;
5use tokio::sync::watch;
6
7/// Consolidated mutable state for RealtimeClient
8/// Using a single struct reduces lock contention
9pub struct ClientState {
10    /// Current ref counter for message IDs
11    pub ref_counter: u64,
12
13    /// Pending heartbeat ref (if any)
14    pub pending_heartbeat_ref: Option<String>,
15
16    /// All channels managed by this client
17    pub channels: Vec<Arc<RealtimeChannel>>,
18
19    /// Background task manager
20    pub task_manager: TaskManager,
21
22    /// Whether the disconnect was manual (prevents auto-reconnect)
23    pub was_manual_disconnect: bool,
24
25    /// Sender for state change notifications
26    pub state_change_tx: Option<watch::Sender<(ConnectionState, bool)>>,
27}
28
29impl ClientState {
30    pub fn new() -> Self {
31        Self {
32            ref_counter: 0,
33            pending_heartbeat_ref: None,
34            channels: Vec::new(),
35            task_manager: TaskManager::new(),
36            was_manual_disconnect: false,
37            state_change_tx: None,
38        }
39    }
40
41    /// Generate next message reference
42    pub fn make_ref(&mut self) -> String {
43        self.ref_counter += 1;
44        self.ref_counter.to_string()
45    }
46
47    /// Notify state change watchers
48    pub fn notify_state_change(&self, state: ConnectionState, manual: bool) {
49        if let Some(tx) = &self.state_change_tx
50            && tx.send((state, manual)).is_err()
51        {
52            tracing::debug!(
53                "State change watcher disconnected, could not notify state: {:?}",
54                state
55            );
56        }
57    }
58}
59
60impl Default for ClientState {
61    fn default() -> Self {
62        Self::new()
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*; // Import items from parent module
69
70    #[test]
71    fn test_make_ref_increments() {
72        let mut state = ClientState::default();
73        assert_eq!(state.ref_counter, 0);
74
75        let ref1 = state.make_ref();
76        assert_eq!(state.ref_counter, 1);
77        assert_eq!(ref1, "1");
78
79        let ref2 = state.make_ref();
80        assert_eq!(state.ref_counter, 2);
81        assert_eq!(ref2, "2");
82
83        assert_ne!(ref1, ref2);
84    }
85
86    #[test]
87    fn test_make_ref_start_at_zero() {
88        let state = ClientState::default();
89        assert_eq!(state.ref_counter, 0);
90    }
91}