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;