use super::tokio_client::Client;
use anyhow::Result;
use std::sync::Arc;
use std::time::Duration;
use tokio::sync::RwLock;
use tracing::{debug, trace};
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
struct ConnectionKey {
host: String,
port: u16,
user: String,
}
pub struct ConnectionPool {
_connections: Arc<RwLock<Vec<ConnectionKey>>>,
#[allow(dead_code)]
ttl: Duration,
#[allow(dead_code)]
enabled: bool,
#[allow(dead_code)]
max_connections: usize,
}
impl ConnectionPool {
pub fn new(ttl: Duration, max_connections: usize, enabled: bool) -> Self {
Self {
_connections: Arc::new(RwLock::new(Vec::new())),
ttl,
enabled,
max_connections,
}
}
pub fn disabled() -> Self {
const DISABLED_POOL_TTL_SECS: u64 = 0;
const DISABLED_POOL_CAPACITY: usize = 0;
Self::new(
Duration::from_secs(DISABLED_POOL_TTL_SECS),
DISABLED_POOL_CAPACITY,
false,
)
}
pub fn with_defaults() -> Self {
const DEFAULT_POOL_TTL_SECS: u64 = 300;
const DEFAULT_POOL_CAPACITY: usize = 50;
Self::new(
Duration::from_secs(DEFAULT_POOL_TTL_SECS),
DEFAULT_POOL_CAPACITY,
false, )
}
pub async fn get_or_create<F>(
&self,
host: &str,
port: u16,
user: &str,
create_fn: F,
) -> Result<Client>
where
F: FnOnce() -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Client>> + Send>>,
{
let _key = ConnectionKey {
host: host.to_string(),
port,
user: user.to_string(),
};
if self.enabled {
trace!("Connection pooling enabled (placeholder mode)");
} else {
trace!("Connection pooling disabled");
}
debug!("Creating new SSH connection to {}@{}:{}", user, host, port);
create_fn().await
}
pub async fn return_connection(&self, _host: &str, _port: u16, _user: &str, _client: Client) {
if self.enabled {
trace!("Connection return requested (no-op in placeholder mode)");
}
}
pub async fn cleanup_expired(&self) {
if self.enabled {
trace!("Cleanup requested (no-op in placeholder mode)");
}
}
pub async fn clear(&self) {
if self.enabled {
trace!("Clear requested (no-op in placeholder mode)");
}
}
pub async fn size(&self) -> usize {
0 }
pub fn is_enabled(&self) -> bool {
self.enabled
}
pub fn enable(&mut self) {
self.enabled = true;
debug!("Connection pooling enabled");
}
pub fn disable(&mut self) {
self.enabled = false;
debug!("Connection pooling disabled");
}
}
impl Default for ConnectionPool {
fn default() -> Self {
Self::with_defaults()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_pool_disabled_by_default() {
let pool = ConnectionPool::with_defaults();
assert!(!pool.is_enabled());
assert_eq!(pool.size().await, 0);
}
#[tokio::test]
async fn test_pool_cleanup() {
let pool = ConnectionPool::new(Duration::from_millis(100), 10, true);
assert_eq!(pool.size().await, 0);
pool.cleanup_expired().await;
assert_eq!(pool.size().await, 0);
}
#[tokio::test]
async fn test_pool_clear() {
let pool = ConnectionPool::new(Duration::from_secs(60), 10, true);
pool.clear().await;
assert_eq!(pool.size().await, 0);
}
}