1pub mod config;
6pub mod metrics;
7pub mod metrics_recording;
8pub mod network;
9pub mod pool;
10pub mod protocol;
11pub mod tui;
12pub mod validated;
13
14pub use config::{
15 BackendReadTimeout, BufferSize, CacheCapacity, CommandExecutionTimeout, ConnectionTimeout,
16 HealthCheckTimeout, MaxConnections, MaxErrors, Port, ThreadCount, WindowSize, duration_serde,
17 option_duration_serde,
18};
19pub use metrics::{
20 ArticleBytesTotal, BackendToClientBytes, BytesPerSecondRate, BytesReceived, BytesSent,
21 ClientBytes, ClientToBackendBytes, TimingMeasurementCount, TotalConnections, TransferMetrics,
22};
23pub use metrics_recording::{
24 DirectionalBytes, MetricsBytes, Recorded, RecordingState, TransferDirection, Unrecorded,
25};
26pub use network::ClientAddress;
27pub use pool::{
28 AvailableConnections, CreatedConnections, InUseConnections, MaxPoolSize, PoolUtilization,
29};
30pub use protocol::MessageId;
31pub use validated::{ConfigPath, HostName, Password, ServerName, Username, ValidationError};
32
33use serde::{Deserialize, Deserializer, Serialize};
34use std::fmt;
35use uuid::Uuid;
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
42pub struct ClientId(Uuid);
43
44impl ClientId {
45 #[inline]
47 #[must_use]
48 pub fn new() -> Self {
49 Self(Uuid::new_v4())
50 }
51
52 #[inline]
54 #[must_use]
55 pub const fn as_uuid(&self) -> &Uuid {
56 &self.0
57 }
58}
59
60impl Default for ClientId {
61 #[inline]
62 fn default() -> Self {
63 Self::new()
64 }
65}
66
67impl fmt::Display for ClientId {
68 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69 write!(f, "{}", self.0)
70 }
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize)]
78pub struct BackendId(usize);
79
80impl BackendId {
81 pub const MAX_COUNT: usize = usize::BITS as usize;
82
83 #[inline]
85 #[must_use]
86 pub fn from_index(index: usize) -> Self {
87 Self::try_from_index(index).unwrap_or_else(|| {
88 panic!(
89 "Backend index {index} exceeds maximum backend count ({})",
90 Self::MAX_COUNT
91 )
92 })
93 }
94
95 #[inline]
96 #[must_use]
97 pub const fn try_from_index(index: usize) -> Option<Self> {
98 if index < Self::MAX_COUNT {
99 Some(Self(index))
100 } else {
101 None
102 }
103 }
104
105 #[inline]
107 #[must_use]
108 pub const fn as_index(&self) -> usize {
109 self.0
110 }
111
112 #[inline]
114 #[must_use]
115 pub(crate) fn availability_bit(self) -> usize {
116 1usize << self.as_index()
117 }
118}
119
120impl fmt::Display for BackendId {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 write!(f, "Backend({})", self.0)
123 }
124}
125
126impl<'de> Deserialize<'de> for BackendId {
127 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
128 where
129 D: Deserializer<'de>,
130 {
131 let index = usize::deserialize(deserializer)?;
132 Self::try_from_index(index).ok_or_else(|| {
133 serde::de::Error::custom(format!(
134 "backend index {index} exceeds maximum backend count ({})",
135 Self::MAX_COUNT
136 ))
137 })
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144
145 #[test]
147 fn test_client_id_unique() {
148 let id1 = ClientId::new();
149 let id2 = ClientId::new();
150 assert_ne!(id1, id2);
151 }
152
153 #[test]
154 fn test_client_id_default() {
155 let id1 = ClientId::default();
156 let id2 = ClientId::default();
157 assert_ne!(id1, id2); }
159
160 #[test]
161 fn test_client_id_as_uuid() {
162 let id = ClientId::new();
163 let uuid = id.as_uuid();
164 assert_eq!(uuid.get_version(), Some(uuid::Version::Random));
165 }
166
167 #[test]
168 fn test_client_id_display() {
169 let id = ClientId::new();
170 let display = format!("{id}");
171 assert!(!display.is_empty());
172 assert_eq!(display.len(), 36);
174 assert_eq!(display.chars().filter(|&c| c == '-').count(), 4);
175 }
176
177 #[test]
178 fn test_client_id_debug() {
179 let id = ClientId::new();
180 let debug = format!("{id:?}");
181 assert!(debug.contains("ClientId"));
182 }
183
184 #[test]
185 fn test_client_id_clone() {
186 let id1 = ClientId::new();
187 let id2 = id1;
188 assert_eq!(id1, id2);
189 }
190
191 #[test]
192 fn test_client_id_equality() {
193 let id1 = ClientId::new();
194 let id2 = id1;
195 let id3 = ClientId::new();
196
197 assert_eq!(id1, id2);
198 assert_ne!(id1, id3);
199 }
200
201 #[test]
202 fn test_client_id_hash() {
203 use std::collections::HashSet;
204
205 let id1 = ClientId::new();
206 let id2 = id1;
207 let id3 = ClientId::new();
208
209 let mut set = HashSet::new();
210 set.insert(id1);
211 set.insert(id2); set.insert(id3);
213
214 assert_eq!(set.len(), 2);
215 }
216
217 #[test]
218 fn test_client_id_ordering() {
219 let id1 = ClientId::new();
220 let id2 = ClientId::new();
221
222 let _ = id1 < id2;
225 let _ = id1 > id2;
226
227 let id3 = id1;
228 assert_eq!(id1, id3);
229 }
230
231 #[test]
233 fn test_backend_id() {
234 let id1 = BackendId::from_index(0);
235 let id2 = BackendId::from_index(1);
236 assert_ne!(id1, id2);
237 assert_eq!(id1.as_index(), 0);
238 assert_eq!(id2.as_index(), 1);
239 }
240
241 #[test]
242 fn test_backend_id_const_fn() {
243 let id = BackendId::from_index(10);
244 assert_eq!(id.as_index(), 10);
245 }
246
247 #[test]
248 fn test_backend_id_display() {
249 let id = BackendId::from_index(5);
250 assert_eq!(format!("{id}"), "Backend(5)");
251 }
252
253 #[test]
254 fn test_backend_id_availability_bit() {
255 assert_eq!(BackendId::from_index(0).availability_bit(), 0b0000_0001);
256 assert_eq!(BackendId::from_index(7).availability_bit(), 0b1000_0000);
257 assert_eq!(BackendId::from_index(8).availability_bit(), 0b1_0000_0000);
258 }
259
260 #[test]
261 fn test_backend_id_rejects_index_outside_bitset() {
262 assert!(BackendId::try_from_index(usize::BITS as usize).is_none());
263 }
264
265 #[test]
266 fn test_backend_id_debug() {
267 let id = BackendId::from_index(7);
268 let debug = format!("{id:?}");
269 assert!(debug.contains("BackendId"));
270 assert!(debug.contains('7'));
271 }
272
273 #[test]
274 fn test_backend_id_clone() {
275 let id1 = BackendId::from_index(15);
276 let id2 = id1;
277 assert_eq!(id1, id2);
278 }
279
280 #[test]
281 fn test_backend_id_equality() {
282 let id1 = BackendId::from_index(10);
283 let id2 = BackendId::from_index(10);
284 let id3 = BackendId::from_index(20);
285
286 assert_eq!(id1, id2);
287 assert_ne!(id1, id3);
288 }
289
290 #[test]
291 fn test_backend_id_hash() {
292 use std::collections::HashSet;
293
294 let id1 = BackendId::from_index(1);
295 let id2 = BackendId::from_index(1);
296 let id3 = BackendId::from_index(2);
297
298 let mut set = HashSet::new();
299 set.insert(id1);
300 set.insert(id2); set.insert(id3);
302
303 assert_eq!(set.len(), 2);
304 }
305
306 #[test]
307 fn test_backend_id_ordering() {
308 let id1 = BackendId::from_index(1);
309 let id2 = BackendId::from_index(2);
310 let id3 = BackendId::from_index(3);
311
312 assert!(id1 < id2);
313 assert!(id2 < id3);
314 assert!(id1 < id3);
315 assert!(id2 > id1);
316 }
317
318 #[test]
319 fn test_backend_id_zero() {
320 let id = BackendId::from_index(0);
321 assert_eq!(id.as_index(), 0);
322 }
323
324 #[test]
325 #[should_panic(expected = "exceeds maximum backend count")]
326 fn test_backend_id_from_index_panics_for_out_of_range_index() {
327 let _ = BackendId::from_index(usize::MAX);
328 }
329
330 #[test]
331 fn test_display() {
332 let client_id = ClientId::new();
333 let backend_id = BackendId::from_index(5);
334
335 assert!(!format!("{client_id}").is_empty());
336 assert_eq!(format!("{backend_id}"), "Backend(5)");
337 }
338}