help_probe/
runner.rs

1use std::process::{Output, Stdio};
2use std::time::Duration;
3
4use anyhow::{Context, Result, anyhow};
5use tokio::process::Command;
6use tokio::time;
7
8/// Outcome of running a command with timeout.
9pub enum RunOutcome {
10    /// Command completed (may have non-zero exit code).
11    Completed(Output),
12    /// Command timed out before completion.
13    TimedOut,
14}
15
16/// Run a command with arguments, capturing stdout/stderr, with a timeout.
17pub async fn run_with_timeout(
18    program: &str,
19    args: &[String],
20    timeout: Duration,
21) -> Result<RunOutcome> {
22    let mut cmd = Command::new(program);
23    cmd.args(args)
24        .stdin(Stdio::null())
25        .stdout(Stdio::piped())
26        .stderr(Stdio::piped());
27
28    let child = cmd
29        .spawn()
30        .with_context(|| format!("Failed to spawn command: {} {:?}", program, args))?;
31
32    match time::timeout(timeout, child.wait_with_output()).await {
33        Ok(Ok(output)) => Ok(RunOutcome::Completed(output)),
34        Ok(Err(e)) => Err(anyhow!("Error while running command: {}", e)),
35        Err(_) => Ok(RunOutcome::TimedOut),
36    }
37}