pub mod config;
pub mod metrics;
pub mod metrics_recording;
pub mod network;
pub mod pool;
pub mod protocol;
pub mod tui;
pub mod validated;
pub use config::{
BackendReadTimeout, BufferSize, CacheCapacity, CommandExecutionTimeout, ConnectionTimeout,
HealthCheckTimeout, MaxConnections, MaxErrors, Port, ThreadCount, WindowSize, duration_serde,
option_duration_serde,
};
pub use metrics::{
ArticleBytesTotal, BackendToClientBytes, BytesPerSecondRate, BytesReceived, BytesSent,
ClientBytes, ClientToBackendBytes, TimingMeasurementCount, TotalConnections, TransferMetrics,
};
pub use metrics_recording::{
DirectionalBytes, MetricsBytes, Recorded, RecordingState, TransferDirection, Unrecorded,
};
pub use network::ClientAddress;
pub use pool::{
AvailableConnections, CreatedConnections, InUseConnections, MaxPoolSize, PoolUtilization,
};
pub use protocol::MessageId;
pub use validated::{ConfigPath, HostName, Password, ServerName, Username, ValidationError};
use nutype::nutype;
use std::fmt;
use uuid::Uuid;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct ClientId(Uuid);
impl ClientId {
#[inline]
#[must_use]
pub fn new() -> Self {
Self(Uuid::new_v4())
}
#[inline]
#[must_use]
pub const fn as_uuid(&self) -> &Uuid {
&self.0
}
}
impl Default for ClientId {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl fmt::Display for ClientId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[nutype(derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
Hash,
PartialOrd,
Ord,
From,
Serialize,
Deserialize
))]
pub struct BackendId(usize);
impl BackendId {
#[inline]
#[must_use]
pub fn from_index(index: usize) -> Self {
Self::new(index)
}
#[inline]
#[must_use]
pub fn as_index(&self) -> usize {
self.into_inner()
}
#[inline]
#[must_use]
pub(crate) fn availability_bit(self) -> u8 {
let idx = self.as_index();
u32::try_from(idx)
.ok()
.and_then(|shift| 1u8.checked_shl(shift))
.unwrap_or_else(|| panic!("Backend index {idx} exceeds MAX_BACKENDS"))
}
}
impl fmt::Display for BackendId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Backend({})", self.into_inner())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_client_id_unique() {
let id1 = ClientId::new();
let id2 = ClientId::new();
assert_ne!(id1, id2);
}
#[test]
fn test_client_id_default() {
let id1 = ClientId::default();
let id2 = ClientId::default();
assert_ne!(id1, id2); }
#[test]
fn test_client_id_as_uuid() {
let id = ClientId::new();
let uuid = id.as_uuid();
assert_eq!(uuid.get_version(), Some(uuid::Version::Random));
}
#[test]
fn test_client_id_display() {
let id = ClientId::new();
let display = format!("{id}");
assert!(!display.is_empty());
assert_eq!(display.len(), 36);
assert_eq!(display.chars().filter(|&c| c == '-').count(), 4);
}
#[test]
fn test_client_id_debug() {
let id = ClientId::new();
let debug = format!("{id:?}");
assert!(debug.contains("ClientId"));
}
#[test]
fn test_client_id_clone() {
let id1 = ClientId::new();
let id2 = id1;
assert_eq!(id1, id2);
}
#[test]
fn test_client_id_equality() {
let id1 = ClientId::new();
let id2 = id1;
let id3 = ClientId::new();
assert_eq!(id1, id2);
assert_ne!(id1, id3);
}
#[test]
fn test_client_id_hash() {
use std::collections::HashSet;
let id1 = ClientId::new();
let id2 = id1;
let id3 = ClientId::new();
let mut set = HashSet::new();
set.insert(id1);
set.insert(id2); set.insert(id3);
assert_eq!(set.len(), 2);
}
#[test]
fn test_client_id_ordering() {
let id1 = ClientId::new();
let id2 = ClientId::new();
let _ = id1 < id2;
let _ = id1 > id2;
let id3 = id1;
assert_eq!(id1, id3);
}
#[test]
fn test_backend_id() {
let id1 = BackendId::from_index(0);
let id2 = BackendId::from_index(1);
assert_ne!(id1, id2);
assert_eq!(id1.as_index(), 0);
assert_eq!(id2.as_index(), 1);
}
#[test]
fn test_backend_id_from_usize() {
let id: BackendId = 42.into();
assert_eq!(id.as_index(), 42);
}
#[test]
fn test_backend_id_const_fn() {
let id = BackendId::from_index(10);
assert_eq!(id.as_index(), 10);
}
#[test]
fn test_backend_id_display() {
let id = BackendId::from_index(5);
assert_eq!(format!("{id}"), "Backend(5)");
}
#[test]
fn test_backend_id_availability_bit() {
assert_eq!(BackendId::from_index(0).availability_bit(), 0b0000_0001);
assert_eq!(BackendId::from_index(7).availability_bit(), 0b1000_0000);
}
#[test]
#[should_panic(expected = "Backend index 8 exceeds MAX_BACKENDS")]
fn test_backend_id_availability_bit_panics_when_index_exceeds_bitset() {
let _ = BackendId::from_index(8).availability_bit();
}
#[test]
fn test_backend_id_debug() {
let id = BackendId::from_index(7);
let debug = format!("{id:?}");
assert!(debug.contains("BackendId"));
assert!(debug.contains('7'));
}
#[test]
fn test_backend_id_clone() {
let id1 = BackendId::from_index(15);
let id2 = id1;
assert_eq!(id1, id2);
}
#[test]
fn test_backend_id_equality() {
let id1 = BackendId::from_index(10);
let id2 = BackendId::from_index(10);
let id3 = BackendId::from_index(20);
assert_eq!(id1, id2);
assert_ne!(id1, id3);
}
#[test]
fn test_backend_id_hash() {
use std::collections::HashSet;
let id1 = BackendId::from_index(1);
let id2 = BackendId::from_index(1);
let id3 = BackendId::from_index(2);
let mut set = HashSet::new();
set.insert(id1);
set.insert(id2); set.insert(id3);
assert_eq!(set.len(), 2);
}
#[test]
fn test_backend_id_ordering() {
let id1 = BackendId::from_index(1);
let id2 = BackendId::from_index(2);
let id3 = BackendId::from_index(3);
assert!(id1 < id2);
assert!(id2 < id3);
assert!(id1 < id3);
assert!(id2 > id1);
}
#[test]
fn test_backend_id_zero() {
let id = BackendId::from_index(0);
assert_eq!(id.as_index(), 0);
}
#[test]
fn test_backend_id_large_index() {
let id = BackendId::from_index(usize::MAX);
assert_eq!(id.as_index(), usize::MAX);
}
#[test]
fn test_display() {
let client_id = ClientId::new();
let backend_id = BackendId::from_index(5);
assert!(!format!("{client_id}").is_empty());
assert_eq!(format!("{backend_id}"), "Backend(5)");
}
}