Skip to main content

holodeck_simctl_core/
process_runner.rs

1use async_trait::async_trait;
2
3#[derive(Debug, Clone)]
4pub struct ProcessResult {
5    pub stdout: Vec<u8>,
6    pub stderr: Vec<u8>,
7    pub exit_code: i32,
8}
9
10/// Injectable process execution seam — the Rust analogue of Swift's
11/// `ProcessRunning` protocol witness, kept so `SimctlClient` tests can pin
12/// exact argv without spawning real processes.
13#[async_trait]
14pub trait ProcessRunning: Send + Sync {
15    async fn run(&self, launch_path: &str, arguments: &[String]) -> std::io::Result<ProcessResult>;
16}
17
18/// `tokio::process::Command::output()` drains stdout and stderr concurrently
19/// by construction, so the pipe-drain deadlock the Swift `ProcessRunner` had
20/// to guard against (see CLAUDE.md) cannot happen here.
21#[derive(Debug, Default, Clone, Copy)]
22pub struct TokioProcessRunner;
23
24#[async_trait]
25impl ProcessRunning for TokioProcessRunner {
26    async fn run(&self, launch_path: &str, arguments: &[String]) -> std::io::Result<ProcessResult> {
27        let output = tokio::process::Command::new(launch_path).args(arguments).output().await?;
28        Ok(ProcessResult { stdout: output.stdout, stderr: output.stderr, exit_code: output.status.code().unwrap_or(-1) })
29    }
30}