use super::{AgentHealth, AgentStatus, VerificationAgent};
use crate::error::{AgentError, Error, Result};
use dashmap::DashMap;
use std::sync::Arc;
use uuid::Uuid;
#[derive(Clone)]
pub struct AgentPool {
agents: Arc<DashMap<Uuid, VerificationAgent>>,
max_size: usize,
}
impl AgentPool {
pub fn new(size: usize) -> Self {
let agents = Arc::new(DashMap::new());
for _ in 0..size {
let agent = VerificationAgent::new();
agents.insert(agent.id(), agent);
}
Self {
agents,
max_size: size,
}
}
pub fn size(&self) -> usize {
self.agents.len()
}
pub fn max_size(&self) -> usize {
self.max_size
}
pub fn get_agent(&self, id: &Uuid) -> Option<dashmap::mapref::one::Ref<'_, Uuid, VerificationAgent>> {
self.agents.get(id)
}
pub fn get_healthy_agent(&self) -> Result<Uuid> {
for entry in self.agents.iter() {
if entry.value().health().is_healthy() {
return Ok(*entry.key());
}
}
Err(Error::PoolExhausted)
}
pub fn get_healthy_agents(&self) -> Vec<Uuid> {
self.agents
.iter()
.filter(|entry| entry.value().health().is_healthy())
.map(|entry| *entry.key())
.collect()
}
pub fn update_status(&self, agent_id: &Uuid, status: AgentStatus) -> Result<()> {
self.agents
.get_mut(agent_id)
.ok_or_else(|| Error::Agent(AgentError::AgentNotFound {
agent_id: agent_id.to_string(),
}))?
.set_status(status);
Ok(())
}
pub fn get_health(&self, agent_id: &Uuid) -> Result<AgentHealth> {
Ok(self
.agents
.get(agent_id)
.ok_or_else(|| Error::Agent(AgentError::AgentNotFound {
agent_id: agent_id.to_string(),
}))?
.health()
.clone())
}
pub fn add_agent(&self, agent: VerificationAgent) -> Result<()> {
if self.agents.len() >= self.max_size {
return Err(Error::Config("Agent pool is at maximum capacity".to_string()));
}
self.agents.insert(agent.id(), agent);
Ok(())
}
pub fn remove_agent(&self, agent_id: &Uuid) -> Result<VerificationAgent> {
self.agents
.remove(agent_id)
.map(|(_, agent)| agent)
.ok_or_else(|| Error::Agent(AgentError::AgentNotFound {
agent_id: agent_id.to_string(),
}))
}
pub fn health_summary(&self) -> PoolHealthSummary {
let total = self.agents.len();
let mut healthy = 0;
let mut busy = 0;
let mut error = 0;
let mut recovering = 0;
let mut quarantined = 0;
for entry in self.agents.iter() {
match entry.value().health().status {
AgentStatus::Healthy => healthy += 1,
AgentStatus::Busy => busy += 1,
AgentStatus::Error => error += 1,
AgentStatus::Recovering => recovering += 1,
AgentStatus::Quarantined => quarantined += 1,
}
}
PoolHealthSummary {
total,
healthy,
busy,
error,
recovering,
quarantined,
}
}
}
#[derive(Debug, Clone)]
pub struct PoolHealthSummary {
pub total: usize,
pub healthy: usize,
pub busy: usize,
pub error: usize,
pub recovering: usize,
pub quarantined: usize,
}
impl PoolHealthSummary {
pub fn health_percentage(&self) -> f64 {
if self.total == 0 {
0.0
} else {
self.healthy as f64 / self.total as f64
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_pool_creation() {
let pool = AgentPool::new(5);
assert_eq!(pool.size(), 5);
assert_eq!(pool.max_size(), 5);
}
#[test]
fn test_get_healthy_agent() {
let pool = AgentPool::new(5);
let agent_id = pool.get_healthy_agent().unwrap();
assert!(pool.get_agent(&agent_id).is_some());
}
#[test]
fn test_pool_health_summary() {
let pool = AgentPool::new(5);
let summary = pool.health_summary();
assert_eq!(summary.total, 5);
assert_eq!(summary.healthy, 5);
assert_eq!(summary.health_percentage(), 1.0);
}
}