use std::collections::HashMap;
use std::sync::{Arc, Mutex, PoisonError};
use tokio::sync::{mpsc, oneshot};
use crate::env::SpawnedProcess;
#[derive(Debug)]
pub(crate) struct DetachRequest {
pub(crate) task_id: String,
pub(crate) reason: String,
pub(crate) reply: oneshot::Sender<DetachedProcess>,
}
#[derive(Debug)]
pub(crate) enum CallMessage {
Detach(DetachRequest),
Pid(oneshot::Sender<Option<u32>>),
}
#[derive(Debug)]
pub struct DetachedProcess {
pub process: SpawnedProcess,
pub output_so_far: String,
pub full_output_path: Option<String>,
}
#[derive(Clone, Debug, Default)]
pub struct BashDetachRegistry {
calls: Arc<Mutex<HashMap<String, mpsc::UnboundedSender<CallMessage>>>>,
}
pub(crate) struct Registration {
registry: BashDetachRegistry,
id: String,
}
impl Drop for Registration {
fn drop(&mut self) {
self.registry.lock().remove(&self.id);
}
}
impl BashDetachRegistry {
fn lock(
&self,
) -> std::sync::MutexGuard<'_, HashMap<String, mpsc::UnboundedSender<CallMessage>>> {
self.calls.lock().unwrap_or_else(PoisonError::into_inner)
}
pub(crate) fn register(
&self,
tool_call_id: &str,
) -> (Registration, mpsc::UnboundedReceiver<CallMessage>) {
let (tx, rx) = mpsc::unbounded_channel();
self.lock().insert(tool_call_id.to_owned(), tx);
(
Registration {
registry: self.clone(),
id: tool_call_id.to_owned(),
},
rx,
)
}
#[must_use]
pub fn is_running(&self, tool_call_id: &str) -> bool {
self.lock().contains_key(tool_call_id)
}
pub async fn detach(
&self,
tool_call_id: &str,
task_id: impl Into<String>,
reason: impl Into<String>,
) -> Option<DetachedProcess> {
let sender = self.lock().get(tool_call_id).cloned()?;
let (reply, response) = oneshot::channel();
sender
.send(CallMessage::Detach(DetachRequest {
task_id: task_id.into(),
reason: reason.into(),
reply,
}))
.ok()?;
response.await.ok()
}
pub async fn pid(&self, tool_call_id: &str) -> Option<u32> {
let sender = self.lock().get(tool_call_id).cloned()?;
let (reply, response) = oneshot::channel();
sender.send(CallMessage::Pid(reply)).ok()?;
response.await.ok().flatten()
}
#[must_use]
pub fn running(&self) -> Vec<String> {
self.lock().keys().cloned().collect()
}
}