use std::time::{Duration, Instant};
#[derive(Debug, Clone)]
pub enum ConnState {
Healthy,
Unhealthy { since: Instant },
Reconnecting,
}
impl ConnState {
pub fn is_healthy(&self) -> bool {
matches!(self, ConnState::Healthy)
}
pub fn is_reconnecting(&self) -> bool {
matches!(self, ConnState::Reconnecting)
}
}
#[derive(Debug, Clone)]
pub enum Circuit {
Closed,
Open { until: Instant },
HalfOpen,
}
impl Circuit {
pub fn should_allow(&self) -> bool {
match self {
Circuit::Closed => true,
Circuit::Open { until } => Instant::now() >= *until,
Circuit::HalfOpen => true,
}
}
pub fn open(&mut self, duration: Duration) {
*self = Circuit::Open {
until: Instant::now() + duration,
};
}
#[allow(dead_code)]
pub fn half_open(&mut self) {
*self = Circuit::HalfOpen;
}
pub fn close(&mut self) {
*self = Circuit::Closed;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ExhaustionPolicy {
#[default]
FailFast,
Block { timeout: Duration },
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum ValidationMode {
#[default]
FailFast,
BestEffort,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PoolPhase {
Running,
Shutdown,
}
#[derive(Debug, Clone)]
pub struct PoolStats {
pub size: usize,
pub healthy: usize,
pub total_requests: usize,
pub is_shutdown: bool,
}
#[macro_export]
macro_rules! define_pool_logging_macros {
($prefix:ident) => {
#[cfg(feature = "tracing")]
macro_rules! $prefix_trace {
($($arg:tt)*) => { tracing::trace!($($arg)*) };
}
#[cfg(not(feature = "tracing"))]
macro_rules! $prefix_trace {
($($arg:tt)*) => {};
}
#[cfg(feature = "tracing")]
macro_rules! $prefix_debug {
($($arg:tt)*) => { tracing::debug!($($arg)*) };
}
#[cfg(not(feature = "tracing"))]
macro_rules! $prefix_debug {
($($arg:tt)*) => {};
}
#[cfg(feature = "tracing")]
macro_rules! $prefix_info {
($($arg:tt)*) => { tracing::info!($($arg)*) };
}
#[cfg(not(feature = "tracing"))]
macro_rules! $prefix_info {
($($arg:tt)*) => {};
}
#[cfg(feature = "tracing")]
macro_rules! $prefix_warn {
($($arg:tt)*) => { tracing::warn!($($arg)*) };
}
#[cfg(not(feature = "tracing"))]
macro_rules! $prefix_warn {
($($arg:tt)*) => {};
}
#[cfg(feature = "tracing")]
macro_rules! $prefix_error {
($($arg:tt)*) => { tracing::error!($($arg)*) };
}
#[cfg(not(feature = "tracing"))]
macro_rules! $prefix_error {
($($arg:tt)*) => {};
}
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_conn_state_is_healthy() {
assert!(ConnState::Healthy.is_healthy());
assert!(
!ConnState::Unhealthy {
since: Instant::now()
}
.is_healthy()
);
assert!(!ConnState::Reconnecting.is_healthy());
}
#[test]
fn test_conn_state_is_reconnecting() {
assert!(!ConnState::Healthy.is_reconnecting());
assert!(
!ConnState::Unhealthy {
since: Instant::now()
}
.is_reconnecting()
);
assert!(ConnState::Reconnecting.is_reconnecting());
}
#[test]
fn test_circuit_should_allow_closed() {
let circuit = Circuit::Closed;
assert!(circuit.should_allow());
}
#[test]
fn test_circuit_should_allow_open_not_expired() {
let circuit = Circuit::Open {
until: Instant::now() + Duration::from_secs(60),
};
assert!(!circuit.should_allow());
}
#[test]
fn test_circuit_should_allow_open_expired() {
let circuit = Circuit::Open {
until: Instant::now() - Duration::from_secs(1),
};
assert!(circuit.should_allow());
}
#[test]
fn test_circuit_should_allow_half_open() {
let circuit = Circuit::HalfOpen;
assert!(circuit.should_allow());
}
#[test]
fn test_circuit_transitions() {
let mut circuit = Circuit::Closed;
circuit.open(Duration::from_secs(30));
assert!(matches!(circuit, Circuit::Open { .. }));
circuit.half_open();
assert!(matches!(circuit, Circuit::HalfOpen));
circuit.close();
assert!(matches!(circuit, Circuit::Closed));
}
#[test]
fn test_exhaustion_policy_default() {
let policy = ExhaustionPolicy::default();
assert_eq!(policy, ExhaustionPolicy::FailFast);
}
#[test]
fn test_validation_mode_default() {
let mode = ValidationMode::default();
assert_eq!(mode, ValidationMode::FailFast);
}
#[test]
fn test_pool_stats() {
let stats = PoolStats {
size: 10,
healthy: 8,
total_requests: 100,
is_shutdown: false,
};
assert_eq!(stats.size, 10);
assert_eq!(stats.healthy, 8);
assert_eq!(stats.total_requests, 100);
assert!(!stats.is_shutdown);
}
#[test]
fn test_pool_phase() {
assert_eq!(PoolPhase::Running, PoolPhase::Running);
assert_ne!(PoolPhase::Running, PoolPhase::Shutdown);
}
}