1use chrono::{DateTime, Utc};
2use std::process::{Command, Stdio};
3use std::time::{Duration, Instant};
4
5#[derive(Debug, Clone)]
7pub struct RunResult {
8 pub command: Vec<String>,
10
11 pub started_at: DateTime<Utc>,
13
14 pub finished_at: DateTime<Utc>,
16
17 pub duration: Duration,
19
20 pub exit_code: i32,
22
23 pub spawn_error: Option<String>,
25}
26
27pub 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}