ccxt_core/ws_client/
state.rs

1//! WebSocket connection state and statistics.
2
3use std::sync::atomic::{AtomicI64, AtomicU32, AtomicU64, Ordering};
4
5/// WebSocket connection state.
6#[repr(u8)]
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum WsConnectionState {
9    /// Not connected
10    Disconnected = 0,
11    /// Establishing connection
12    Connecting = 1,
13    /// Successfully connected
14    Connected = 2,
15    /// Attempting to reconnect
16    Reconnecting = 3,
17    /// Error state
18    Error = 4,
19}
20
21impl WsConnectionState {
22    /// Converts a `u8` value to `WsConnectionState`.
23    #[inline]
24    pub fn from_u8(value: u8) -> Self {
25        match value {
26            0 => Self::Disconnected,
27            1 => Self::Connecting,
28            2 => Self::Connected,
29            3 => Self::Reconnecting,
30            _ => Self::Error,
31        }
32    }
33
34    /// Converts the `WsConnectionState` to its `u8` representation.
35    #[inline]
36    pub fn as_u8(self) -> u8 {
37        self as u8
38    }
39}
40
41/// WebSocket connection statistics (lock-free).
42#[derive(Debug)]
43pub struct WsStats {
44    messages_received: AtomicU64,
45    messages_sent: AtomicU64,
46    bytes_received: AtomicU64,
47    bytes_sent: AtomicU64,
48    last_message_time: AtomicI64,
49    last_ping_time: AtomicI64,
50    last_pong_time: AtomicI64,
51    connected_at: AtomicI64,
52    reconnect_attempts: AtomicU32,
53}
54
55impl WsStats {
56    /// Creates a new `WsStats` instance with all counters initialized to zero.
57    pub fn new() -> Self {
58        Self {
59            messages_received: AtomicU64::new(0),
60            messages_sent: AtomicU64::new(0),
61            bytes_received: AtomicU64::new(0),
62            bytes_sent: AtomicU64::new(0),
63            last_message_time: AtomicI64::new(0),
64            last_ping_time: AtomicI64::new(0),
65            last_pong_time: AtomicI64::new(0),
66            connected_at: AtomicI64::new(0),
67            reconnect_attempts: AtomicU32::new(0),
68        }
69    }
70
71    /// Records a received message.
72    pub fn record_received(&self, bytes: u64) {
73        self.messages_received.fetch_add(1, Ordering::Relaxed);
74        self.bytes_received.fetch_add(bytes, Ordering::Relaxed);
75        self.last_message_time
76            .store(chrono::Utc::now().timestamp_millis(), Ordering::Relaxed);
77    }
78
79    /// Records a sent message.
80    pub fn record_sent(&self, bytes: u64) {
81        self.messages_sent.fetch_add(1, Ordering::Relaxed);
82        self.bytes_sent.fetch_add(bytes, Ordering::Relaxed);
83    }
84
85    /// Records a ping sent.
86    pub fn record_ping(&self) {
87        self.last_ping_time
88            .store(chrono::Utc::now().timestamp_millis(), Ordering::Relaxed);
89    }
90
91    /// Records a pong received.
92    pub fn record_pong(&self) {
93        self.last_pong_time
94            .store(chrono::Utc::now().timestamp_millis(), Ordering::Relaxed);
95    }
96
97    /// Records a connection established.
98    pub fn record_connected(&self) {
99        self.connected_at
100            .store(chrono::Utc::now().timestamp_millis(), Ordering::Relaxed);
101    }
102
103    /// Increments the reconnection attempt counter.
104    pub fn increment_reconnect_attempts(&self) -> u32 {
105        self.reconnect_attempts.fetch_add(1, Ordering::Relaxed) + 1
106    }
107
108    /// Resets the reconnection attempt counter to zero.
109    pub fn reset_reconnect_attempts(&self) {
110        self.reconnect_attempts.store(0, Ordering::Relaxed);
111    }
112
113    /// Returns the last pong timestamp.
114    pub fn last_pong_time(&self) -> i64 {
115        self.last_pong_time.load(Ordering::Relaxed)
116    }
117
118    /// Returns the last ping timestamp.
119    pub fn last_ping_time(&self) -> i64 {
120        self.last_ping_time.load(Ordering::Relaxed)
121    }
122
123    /// Creates an immutable snapshot of current statistics.
124    pub fn snapshot(&self) -> WsStatsSnapshot {
125        WsStatsSnapshot {
126            messages_received: self.messages_received.load(Ordering::Relaxed),
127            messages_sent: self.messages_sent.load(Ordering::Relaxed),
128            bytes_received: self.bytes_received.load(Ordering::Relaxed),
129            bytes_sent: self.bytes_sent.load(Ordering::Relaxed),
130            last_message_time: self.last_message_time.load(Ordering::Relaxed),
131            last_ping_time: self.last_ping_time.load(Ordering::Relaxed),
132            last_pong_time: self.last_pong_time.load(Ordering::Relaxed),
133            connected_at: self.connected_at.load(Ordering::Relaxed),
134            reconnect_attempts: self.reconnect_attempts.load(Ordering::Relaxed),
135        }
136    }
137
138    /// Resets all statistics to their default values.
139    pub fn reset(&self) {
140        self.messages_received.store(0, Ordering::Relaxed);
141        self.messages_sent.store(0, Ordering::Relaxed);
142        self.bytes_received.store(0, Ordering::Relaxed);
143        self.bytes_sent.store(0, Ordering::Relaxed);
144        self.last_message_time.store(0, Ordering::Relaxed);
145        self.last_ping_time.store(0, Ordering::Relaxed);
146        self.last_pong_time.store(0, Ordering::Relaxed);
147        self.connected_at.store(0, Ordering::Relaxed);
148        self.reconnect_attempts.store(0, Ordering::Relaxed);
149    }
150}
151
152impl Default for WsStats {
153    fn default() -> Self {
154        Self::new()
155    }
156}
157
158/// Immutable snapshot of WebSocket connection statistics.
159#[derive(Debug, Clone, Default)]
160pub struct WsStatsSnapshot {
161    /// Total messages received
162    pub messages_received: u64,
163    /// Total messages sent
164    pub messages_sent: u64,
165    /// Total bytes received
166    pub bytes_received: u64,
167    /// Total bytes sent
168    pub bytes_sent: u64,
169    /// Last message timestamp in milliseconds
170    pub last_message_time: i64,
171    /// Last ping timestamp in milliseconds
172    pub last_ping_time: i64,
173    /// Last pong timestamp in milliseconds
174    pub last_pong_time: i64,
175    /// Connection established timestamp in milliseconds
176    pub connected_at: i64,
177    /// Number of reconnection attempts
178    pub reconnect_attempts: u32,
179}