use std::{path::Path, time::Duration};
use tokio::task::JoinHandle;
use crate::{
domain::{
errors::AgentResult,
pi_rpc::{PiRpcCommand, PiRpcResponse},
},
infrastructure::pi_rpc_process::{PiProcessOptions, PiRpcProcess, PiSessionMode},
};
#[derive(Clone)]
pub(crate) struct PiRpcProbe {
command: Vec<String>,
request_timeout: Duration,
}
impl PiRpcProbe {
pub(crate) fn new(command: Vec<String>, request_timeout: Duration) -> Self {
Self {
command,
request_timeout,
}
}
pub(crate) fn is_configured(&self) -> bool {
!self.command.is_empty()
}
pub(crate) async fn request(
&self,
cwd: &Path,
command: PiRpcCommand,
) -> AgentResult<PiRpcResponse> {
let spawned = PiRpcProcess::spawn(PiProcessOptions {
command: self.command.clone(),
cwd: cwd.to_path_buf(),
session: PiSessionMode::Ephemeral,
})
.await?;
let process = spawned.process;
let mut events = spawned.events;
let mut drain = EventDrainGuard::new(tokio::spawn(async move {
while events.recv().await.is_some() {}
}));
let response = process.request(command, self.request_timeout).await;
let shutdown = process.shutdown().await;
drain.finish().await;
let response = response?;
shutdown?;
Ok(response)
}
}
struct EventDrainGuard {
task: Option<JoinHandle<()>>,
}
impl EventDrainGuard {
fn new(task: JoinHandle<()>) -> Self {
Self { task: Some(task) }
}
async fn finish(&mut self) {
if let Some(task) = self.task.take() {
let _ = task.await;
}
}
}
impl Drop for EventDrainGuard {
fn drop(&mut self) {
if let Some(task) = self.task.take() {
task.abort();
}
}
}