nntp_proxy/pool/
connection_pool.rs

1//! Connection pool abstraction for NNTP connections
2//!
3//! This module defines the core `ConnectionPool` trait which provides a generic
4//! interface for managing pooled NNTP connections. This abstraction enables:
5//! - Easy mocking for testing
6//! - Swappable pool implementations
7//! - Testability without real network connections
8
9use crate::stream::ConnectionStream;
10use anyhow::Result;
11use async_trait::async_trait;
12use std::fmt::Debug;
13
14/// Connection pool abstraction for async NNTP connections
15///
16/// This trait provides a generic interface for connection pooling, allowing
17/// different implementations (real pools, mocks, test doubles) to be used
18/// interchangeably throughout the codebase.
19///
20/// # Examples
21///
22/// ```no_run
23/// use nntp_proxy::pool::{ConnectionPool, DeadpoolConnectionProvider};
24/// use async_trait::async_trait;
25///
26/// async fn example(pool: impl ConnectionPool) -> anyhow::Result<()> {
27///     let conn = pool.get().await?;
28///     // Use connection...
29///     Ok(())
30/// }
31/// ```
32#[async_trait]
33pub trait ConnectionPool: Send + Sync + Debug {
34    /// Get a connection from the pool
35    ///
36    /// Returns a connection that will be automatically returned to the pool
37    /// when dropped. May create a new connection if none are available.
38    ///
39    /// # Errors
40    ///
41    /// Returns an error if:
42    /// - Connection creation fails
43    /// - Pool is exhausted and cannot create more connections
44    /// - Network errors occur during connection establishment
45    async fn get(&self) -> Result<ConnectionStream>;
46
47    /// Get the name/identifier of this connection pool
48    ///
49    /// Used for logging and metrics to distinguish between different backend servers.
50    fn name(&self) -> &str;
51
52    /// Get current pool statistics
53    ///
54    /// Returns information about pool usage including available connections,
55    /// maximum size, and total created connections.
56    fn status(&self) -> super::PoolStatus;
57
58    /// Get the backend host this pool connects to
59    fn host(&self) -> &str;
60
61    /// Get the backend port this pool connects to
62    fn port(&self) -> u16;
63}
64
65/// Mock connection pool for testing
66///
67/// This implementation allows tests to inject pre-configured responses
68/// without requiring actual network connections.
69///
70/// # Examples
71///
72/// ```
73/// use nntp_proxy::pool::{ConnectionPool, MockConnectionPool};
74///
75/// #[tokio::test]
76/// async fn test_with_mock_pool() {
77///     let pool = MockConnectionPool::new("test-server");
78///     // Use pool in tests...
79/// }
80/// ```
81#[derive(Debug, Clone)]
82pub struct MockConnectionPool {
83    name: String,
84    host: String,
85    port: u16,
86}
87
88impl MockConnectionPool {
89    /// Create a new mock connection pool
90    ///
91    /// # Arguments
92    /// * `name` - Identifier for this pool (used in logging)
93    pub fn new(name: impl Into<String>) -> Self {
94        Self {
95            name: name.into(),
96            host: "mock.example.com".to_string(),
97            port: 119,
98        }
99    }
100
101    /// Set the mock host
102    #[must_use]
103    pub fn with_host(mut self, host: impl Into<String>) -> Self {
104        self.host = host.into();
105        self
106    }
107
108    /// Set the mock port
109    #[must_use]
110    pub fn with_port(mut self, port: u16) -> Self {
111        self.port = port;
112        self
113    }
114}
115
116#[async_trait]
117impl ConnectionPool for MockConnectionPool {
118    async fn get(&self) -> Result<ConnectionStream> {
119        // For testing: create a mock stream pair
120        // Real tests would inject specific behavior here
121        Err(anyhow::anyhow!(
122            "MockConnectionPool::get() - tests should override this"
123        ))
124    }
125
126    fn name(&self) -> &str {
127        &self.name
128    }
129
130    fn status(&self) -> super::PoolStatus {
131        super::PoolStatus {
132            available: 0,
133            max_size: 0,
134            created: 0,
135        }
136    }
137
138    fn host(&self) -> &str {
139        &self.host
140    }
141
142    fn port(&self) -> u16 {
143        self.port
144    }
145}
146
147#[cfg(test)]
148mod tests;