use std::collections::{HashMap, HashSet};
use std::path::PathBuf;
use std::sync::Arc;
use crate::command::agents::locks::{AgentLock, AgentLockMap};
pub struct AgentInstanceRegistry {
state_dir: PathBuf,
agent_locks: Arc<AgentLockMap>,
open: HashMap<String, AgentLock>,
attempted: HashSet<String>,
tag_claims: Vec<AgentLock>,
}
impl AgentInstanceRegistry {
pub fn new(state_dir: PathBuf, agent_locks: Arc<AgentLockMap>) -> Self {
Self {
state_dir,
agent_locks,
open: HashMap::new(),
attempted: HashSet::new(),
tag_claims: Vec::new(),
}
}
pub fn aih(&self) -> Option<&str> {
self.open.keys().next().map(String::as_str)
}
pub fn preseed(&mut self, hier: String, claim: AgentLock) {
self.attempted.insert(hier.clone());
self.announce_active(&hier);
self.open.insert(hier, claim);
}
pub fn hold_tag_claims(&mut self, claims: Vec<AgentLock>) {
self.tag_claims.extend(claims);
}
pub async fn observe(&mut self, hier: &str) {
if !self.attempted.insert(hier.to_string()) {
return;
}
let (dir, key) = crate::command::agents::locks::agent_instance_lock(&self.state_dir, hier);
if let Some(claim) =
crate::command::agents::locks::try_acquire(&self.agent_locks, &dir, &key).await
{
self.open.insert(hier.to_string(), claim);
self.announce_active(hier);
}
}
fn announce_active(&self, hier: &str) {
if tokio::runtime::Handle::try_current().is_err() {
return;
}
let state_dir = self.state_dir.clone();
let hier = hier.to_string();
tokio::spawn(async move {
super::websocket_agents::announce_active(&state_dir, &hier).await;
});
}
pub fn destroy(&mut self, hier: &str) {
if let Some(claim) = self.open.remove(hier) {
let _ = claim.release();
}
}
}
impl Drop for AgentInstanceRegistry {
fn drop(&mut self) {
for (_, claim) in self.open.drain() {
let _ = claim.release();
}
for claim in self.tag_claims.drain(..) {
let _ = claim.release();
}
}
}