io_process/runtimes/
std.rs

1//! The standard, blocking process runtime.
2
3use std::{
4    io,
5    process::{Command as StdCommand, Output},
6};
7
8use crate::{command::Command, io::ProcessIo, status::SpawnStatus};
9
10/// The standard, blocking process runtime.
11///
12/// This handler makes use of the standard module [`std::process`] to
13/// spawn processes and wait for exit status or output.
14pub fn handle(io: ProcessIo) -> io::Result<ProcessIo> {
15    match io {
16        ProcessIo::SpawnThenWait(io) => spawn_then_wait(io),
17        ProcessIo::SpawnThenWaitWithOutput(io) => spawn_then_wait_with_output(io),
18    }
19}
20
21/// Spawns a process then wait for its child's exit status.
22///
23/// This function builds a [`std::process::Command`] from the flow's
24/// command builder, spawns a process, collects std{in,out,err} then
25/// waits for the exit status.
26pub fn spawn_then_wait(input: Result<SpawnStatus, Command>) -> io::Result<ProcessIo> {
27    let Err(command) = input else {
28        let kind = io::ErrorKind::InvalidInput;
29        return Err(io::Error::new(kind, "missing command"));
30    };
31
32    let mut command = StdCommand::from(command);
33    let mut child = command.spawn()?;
34
35    let stdin = child.stdin.take();
36    let stdout = child.stdout.take();
37    let stderr = child.stderr.take();
38
39    let output = SpawnStatus {
40        status: child.wait()?,
41        stdin: stdin.map(Into::into),
42        stdout: stdout.map(Into::into),
43        stderr: stderr.map(Into::into),
44    };
45
46    Ok(ProcessIo::SpawnThenWait(Ok(output)))
47}
48
49/// Spawns a process then wait for its child's output.
50///
51/// This function builds a [`std::process::Command`] from the flow's
52/// command builder, spawns a process, then waits for the output.
53pub fn spawn_then_wait_with_output(input: Result<Output, Command>) -> io::Result<ProcessIo> {
54    let Err(command) = input else {
55        let kind = io::ErrorKind::InvalidInput;
56        return Err(io::Error::new(kind, "missing command"));
57    };
58
59    let mut command = StdCommand::from(command);
60    let output = command.output()?;
61
62    Ok(ProcessIo::SpawnThenWaitWithOutput(Ok(output)))
63}
64
65/// Converts a [`Command`] builder to a [`std::process::Command`].
66impl From<Command> for StdCommand {
67    fn from(builder: Command) -> Self {
68        let mut command = StdCommand::new(&*builder.get_program());
69
70        if let Some(args) = builder.get_args() {
71            for arg in args {
72                command.arg(&*arg);
73            }
74        }
75
76        if let Some(envs) = builder.envs {
77            for (key, val) in envs {
78                command.env(key, val);
79            }
80        }
81
82        if let Some(dir) = builder.current_dir {
83            command.current_dir(dir);
84        }
85
86        if let Some(cfg) = builder.stdin {
87            command.stdin(cfg);
88        }
89
90        if let Some(cfg) = builder.stdout {
91            command.stdout(cfg);
92        }
93
94        if let Some(cfg) = builder.stderr {
95            command.stderr(cfg);
96        }
97
98        command
99    }
100}