Skip to main content

brb_cli/
runner.rs

1use chrono::{DateTime, Utc};
2use std::process::{Command, Stdio};
3use std::time::{Duration, Instant};
4
5/// Captured result from executing a wrapped command.
6#[derive(Debug, Clone)]
7pub struct RunResult {
8    /// Command argv used for execution.
9    pub command: Vec<String>,
10
11    /// UTC timestamp for command start.
12    pub started_at: DateTime<Utc>,
13
14    /// UTC timestamp for command finish.
15    pub finished_at: DateTime<Utc>,
16
17    /// Total command execution duration.
18    pub duration: Duration,
19
20    /// Final process exit code (`127` when spawn fails).
21    pub exit_code: i32,
22
23    /// Spawn-time error message if the command failed to start.
24    pub spawn_error: Option<String>,
25}
26
27/// Runs a command with inherited stdio and returns completion metadata.
28pub fn run_command(command: &[String]) -> RunResult {
29    let started_at = Utc::now();
30    let started = Instant::now();
31
32    if command.is_empty() {
33        let finished_at = Utc::now();
34        return RunResult {
35            command: vec![],
36            started_at,
37            finished_at,
38            duration: started.elapsed(),
39            exit_code: 2,
40            spawn_error: Some("no command provided".to_string()),
41        };
42    }
43
44    let status = Command::new(&command[0])
45        .args(&command[1..])
46        .stdin(Stdio::inherit())
47        .stdout(Stdio::inherit())
48        .stderr(Stdio::inherit())
49        .status();
50
51    match status {
52        Ok(status) => {
53            let finished_at = Utc::now();
54            RunResult {
55                command: command.to_vec(),
56                started_at,
57                finished_at,
58                duration: started.elapsed(),
59                exit_code: status.code().unwrap_or(1),
60                spawn_error: None,
61            }
62        }
63        Err(error) => {
64            let finished_at = Utc::now();
65            RunResult {
66                command: command.to_vec(),
67                started_at,
68                finished_at,
69                duration: started.elapsed(),
70                exit_code: 127,
71                spawn_error: Some(format!("failed to start `{}`: {error}", command[0])),
72            }
73        }
74    }
75}