esox_objectpool 1.1.2

High-performance, thread-safe object pool with async support, circuit breaker, eviction, health monitoring and Prometheus metrics
Documentation
//! Pool configuration options

use std::time::Duration;

/// Configuration for object pool behavior
///
/// # Examples
///
/// ```
/// use esox_objectpool::PoolConfiguration;
/// use std::time::Duration;
///
/// let config = PoolConfiguration::<i32>::new()
///     .with_max_pool_size(100)
///     .with_max_active_objects(50)
///     .with_timeout(Duration::from_secs(30))
///     .with_ttl(Duration::from_secs(3600));
///
/// assert_eq!(config.max_pool_size, 100);
/// assert_eq!(config.max_active_objects, Some(50));
/// ```
#[derive(Debug, Clone)]
pub struct PoolConfiguration<T> {
    /// Maximum number of objects that can exist in the pool
    pub max_pool_size: usize,
    
    /// Maximum number of objects that can be active (checked out) simultaneously
    pub max_active_objects: Option<usize>,
    
    /// Whether to validate objects when they are returned to the pool
    pub validate_on_return: bool,
    
    /// Custom validation function
    pub validation_function: Option<fn(&T) -> bool>,
    
    /// Timeout for async operations
    pub operation_timeout: Option<Duration>,
    
    /// Time-to-live for objects (eviction policy)
    pub time_to_live: Option<Duration>,
    
    /// Idle timeout for objects (eviction policy)
    pub idle_timeout: Option<Duration>,
    
    /// Whether to pre-populate the pool on creation
    pub warmup_size: Option<usize>,
    
    /// Enable circuit breaker protection
    pub enable_circuit_breaker: bool,
    
    /// Circuit breaker failure threshold
    pub circuit_breaker_threshold: usize,
    
    /// Circuit breaker reset timeout
    pub circuit_breaker_timeout: Duration,
}

impl<T> Default for PoolConfiguration<T> {
    fn default() -> Self {
        Self {
            max_pool_size: 100,
            max_active_objects: None,
            validate_on_return: false,
            validation_function: None,
            operation_timeout: Some(Duration::from_secs(30)),
            time_to_live: None,
            idle_timeout: None,
            warmup_size: None,
            enable_circuit_breaker: false,
            circuit_breaker_threshold: 5,
            circuit_breaker_timeout: Duration::from_secs(60),
        }
    }
}

impl<T> PoolConfiguration<T> {
    /// Create a new configuration with default values
    pub fn new() -> Self {
        Self::default()
    }
    
    /// Set the maximum pool size
    ///
    /// # Examples
    ///
    /// ```
    /// use esox_objectpool::PoolConfiguration;
    ///
    /// let config = PoolConfiguration::<i32>::new()
    ///     .with_max_pool_size(50);
    ///
    /// assert_eq!(config.max_pool_size, 50);
    /// ```
    pub fn with_max_pool_size(mut self, size: usize) -> Self {
        self.max_pool_size = size;
        self
    }
    
    /// Set the maximum active objects
    pub fn with_max_active_objects(mut self, count: usize) -> Self {
        self.max_active_objects = Some(count);
        self
    }
    
    /// Enable validation on return
    pub fn with_validation(mut self, func: fn(&T) -> bool) -> Self {
        self.validate_on_return = true;
        self.validation_function = Some(func);
        self
    }
    
    /// Set operation timeout
    pub fn with_timeout(mut self, timeout: Duration) -> Self {
        self.operation_timeout = Some(timeout);
        self
    }
    
    /// Set time-to-live for objects
    pub fn with_ttl(mut self, ttl: Duration) -> Self {
        self.time_to_live = Some(ttl);
        self
    }
    
    /// Set idle timeout for objects
    pub fn with_idle_timeout(mut self, timeout: Duration) -> Self {
        self.idle_timeout = Some(timeout);
        self
    }
    
    /// Set warm-up size
    pub fn with_warmup(mut self, size: usize) -> Self {
        self.warmup_size = Some(size);
        self
    }
    
    /// Enable circuit breaker
    ///
    /// # Examples
    ///
    /// ```
    /// use esox_objectpool::PoolConfiguration;
    /// use std::time::Duration;
    ///
    /// let config = PoolConfiguration::<i32>::new()
    ///     .with_circuit_breaker(5, Duration::from_secs(60));
    ///
    /// assert!(config.enable_circuit_breaker);
    /// assert_eq!(config.circuit_breaker_threshold, 5);
    /// ```
    pub fn with_circuit_breaker(mut self, threshold: usize, timeout: Duration) -> Self {
        self.enable_circuit_breaker = true;
        self.circuit_breaker_threshold = threshold;
        self.circuit_breaker_timeout = timeout;
        self
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn default_values_are_sensible() {
        let cfg = PoolConfiguration::<i32>::default();
        assert_eq!(cfg.max_pool_size, 100);
        assert_eq!(cfg.max_active_objects, None);
        assert!(!cfg.validate_on_return);
        assert!(cfg.validation_function.is_none());
        assert!(cfg.operation_timeout.is_some());
        assert!(cfg.time_to_live.is_none());
        assert!(cfg.idle_timeout.is_none());
        assert!(cfg.warmup_size.is_none());
        assert!(!cfg.enable_circuit_breaker);
        assert_eq!(cfg.circuit_breaker_threshold, 5);
    }

    #[test]
    fn with_max_pool_size() {
        let cfg = PoolConfiguration::<i32>::new().with_max_pool_size(42);
        assert_eq!(cfg.max_pool_size, 42);
    }

    #[test]
    fn with_max_active_objects() {
        let cfg = PoolConfiguration::<i32>::new().with_max_active_objects(7);
        assert_eq!(cfg.max_active_objects, Some(7));
    }

    #[test]
    fn with_validation() {
        let cfg = PoolConfiguration::<i32>::new().with_validation(|x| *x > 0);
        assert!(cfg.validate_on_return);
        assert!(cfg.validation_function.is_some());
        // Verify the stored function works.
        assert!(cfg.validation_function.unwrap()(&1));
        assert!(!cfg.validation_function.unwrap()(&-1));
    }

    #[test]
    fn with_timeout() {
        let cfg = PoolConfiguration::<i32>::new().with_timeout(Duration::from_secs(5));
        assert_eq!(cfg.operation_timeout, Some(Duration::from_secs(5)));
    }

    #[test]
    fn with_ttl() {
        let cfg = PoolConfiguration::<i32>::new().with_ttl(Duration::from_secs(60));
        assert_eq!(cfg.time_to_live, Some(Duration::from_secs(60)));
    }

    #[test]
    fn with_idle_timeout() {
        let cfg = PoolConfiguration::<i32>::new().with_idle_timeout(Duration::from_secs(30));
        assert_eq!(cfg.idle_timeout, Some(Duration::from_secs(30)));
    }

    #[test]
    fn with_warmup() {
        let cfg = PoolConfiguration::<i32>::new().with_warmup(20);
        assert_eq!(cfg.warmup_size, Some(20));
    }

    #[test]
    fn with_circuit_breaker() {
        let cfg = PoolConfiguration::<i32>::new()
            .with_circuit_breaker(3, Duration::from_secs(45));
        assert!(cfg.enable_circuit_breaker);
        assert_eq!(cfg.circuit_breaker_threshold, 3);
        assert_eq!(cfg.circuit_breaker_timeout, Duration::from_secs(45));
    }

    #[test]
    fn builder_is_chainable() {
        let cfg = PoolConfiguration::<i32>::new()
            .with_max_pool_size(10)
            .with_max_active_objects(5)
            .with_timeout(Duration::from_secs(1))
            .with_ttl(Duration::from_secs(60))
            .with_idle_timeout(Duration::from_secs(30))
            .with_warmup(3)
            .with_circuit_breaker(2, Duration::from_secs(15));

        assert_eq!(cfg.max_pool_size, 10);
        assert_eq!(cfg.max_active_objects, Some(5));
        assert_eq!(cfg.operation_timeout, Some(Duration::from_secs(1)));
        assert_eq!(cfg.time_to_live, Some(Duration::from_secs(60)));
        assert_eq!(cfg.idle_timeout, Some(Duration::from_secs(30)));
        assert_eq!(cfg.warmup_size, Some(3));
        assert!(cfg.enable_circuit_breaker);
        assert_eq!(cfg.circuit_breaker_threshold, 2);
    }
}