use crate::config::BrowserConfig;
use crate::session::BrowserSession;
use adk_core::{AdkError, Result};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
pub struct BrowserSessionPool {
config: BrowserConfig,
sessions: RwLock<HashMap<String, Arc<BrowserSession>>>,
max_sessions: usize,
}
impl BrowserSessionPool {
pub fn new(config: BrowserConfig, max_sessions: usize) -> Self {
Self { config, sessions: RwLock::new(HashMap::new()), max_sessions }
}
pub async fn get_or_create(&self, user_id: &str) -> Result<Arc<BrowserSession>> {
{
let sessions = self.sessions.read().await;
if let Some(session) = sessions.get(user_id) {
if session.is_active().await {
return Ok(session.clone());
}
}
}
let mut sessions = self.sessions.write().await;
if let Some(session) = sessions.get(user_id) {
if session.is_active().await {
return Ok(session.clone());
}
sessions.remove(user_id);
}
if sessions.len() >= self.max_sessions {
return Err(AdkError::tool(format!(
"Browser session pool full ({} sessions). Release unused sessions or increase max_sessions.",
self.max_sessions
)));
}
let session = Arc::new(BrowserSession::new(self.config.clone()));
session.start().await?;
sessions.insert(user_id.to_string(), session.clone());
Ok(session)
}
pub async fn release(&self, user_id: &str) -> Result<()> {
let mut sessions = self.sessions.write().await;
if let Some(session) = sessions.remove(user_id) {
session.stop().await.ok(); }
Ok(())
}
pub async fn cleanup_all(&self) {
let mut sessions = self.sessions.write().await;
for (_, session) in sessions.drain() {
session.stop().await.ok();
}
}
pub async fn active_count(&self) -> usize {
self.sessions.read().await.len()
}
pub async fn active_users(&self) -> Vec<String> {
self.sessions.read().await.keys().cloned().collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pool_creation() {
let pool = BrowserSessionPool::new(BrowserConfig::default(), 5);
assert_eq!(pool.max_sessions, 5);
}
#[tokio::test]
async fn test_pool_active_count_starts_zero() {
let pool = BrowserSessionPool::new(BrowserConfig::default(), 5);
assert_eq!(pool.active_count().await, 0);
}
#[tokio::test]
async fn test_pool_active_users_starts_empty() {
let pool = BrowserSessionPool::new(BrowserConfig::default(), 5);
assert!(pool.active_users().await.is_empty());
}
#[tokio::test]
async fn test_pool_release_nonexistent_user() {
let pool = BrowserSessionPool::new(BrowserConfig::default(), 5);
let result = pool.release("nonexistent").await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_pool_cleanup_all_empty() {
let pool = BrowserSessionPool::new(BrowserConfig::default(), 5);
pool.cleanup_all().await;
assert_eq!(pool.active_count().await, 0);
}
}