hermes_tokio_runtime_components/impls/os/
exec_command.rs

1use core::str;
2use core::str::Utf8Error;
3use std::ffi::OsStr;
4use std::io::Error as IoError;
5
6use cgp::prelude::*;
7use hermes_runtime_components::traits::fs::file_path::HasFilePathType;
8use hermes_runtime_components::traits::os::exec_command::{CommandWithEnvsExecutor, ExecOutput};
9use tokio::process::Command;
10
11pub struct TokioExecCommand;
12
13pub struct ExecCommandFailure {
14    pub command: String,
15    pub exit_code: Option<i32>,
16    pub stdout: String,
17    pub stderr: String,
18}
19
20impl<Runtime> CommandWithEnvsExecutor<Runtime> for TokioExecCommand
21where
22    Runtime: HasFilePathType
23        + CanRaiseError<IoError>
24        + CanRaiseError<Utf8Error>
25        + CanRaiseError<ExecCommandFailure>,
26    Runtime::FilePath: AsRef<OsStr>,
27{
28    async fn exec_command_with_envs(
29        _runtime: &Runtime,
30        command_path: &Runtime::FilePath,
31        args: &[&str],
32        envs: &[(&str, &str)],
33    ) -> Result<ExecOutput, Runtime::Error> {
34        let output = Command::new(command_path)
35            .args(args)
36            .envs(Vec::from(envs))
37            .kill_on_drop(true)
38            .output()
39            .await
40            .map_err(Runtime::raise_error)?;
41
42        let stdout = str::from_utf8(&output.stdout).map_err(Runtime::raise_error)?;
43
44        let stderr = str::from_utf8(&output.stderr).map_err(Runtime::raise_error)?;
45
46        if output.status.success() {
47            Ok(ExecOutput {
48                stdout: stdout.to_owned(),
49                stderr: stderr.to_owned(),
50            })
51        } else {
52            Err(Runtime::raise_error(ExecCommandFailure {
53                command: Runtime::file_path_to_string(command_path),
54                exit_code: output.status.code(),
55                stdout: stdout.to_owned(),
56                stderr: stderr.to_owned(),
57            }))
58        }
59    }
60}