Skip to main content

zbus/abstractions/
process.rs

1#[cfg(feature = "async-io")]
2use async_process::{Child, unix::CommandExt};
3#[cfg(unix)]
4use std::process::Output;
5use std::{ffi::OsStr, io::Error, process::Stdio};
6#[cfg(all(feature = "tokio", not(feature = "async-io")))]
7use tokio::process::Child;
8
9use crate::address::transport::Unixexec;
10
11/// A wrapper around the command API of the underlying async runtime.
12///
13/// Unlike the socket transports, `unixexec` isn't run-time selected: `async-process` is used
14/// whenever it's compiled in (its pipes are `Async<_>`, which any runtime can drive), so with both
15/// backends a tokio app connecting over `unixexec:` picks up async-io's reactor.
16pub struct Command(
17    #[cfg(feature = "async-io")] async_process::Command,
18    #[cfg(all(feature = "tokio", not(feature = "async-io")))] tokio::process::Command,
19);
20
21impl Command {
22    /// Constructs a new `Command` for launching the program at path `program`.
23    pub fn new<S>(program: S) -> Self
24    where
25        S: AsRef<OsStr>,
26    {
27        #[cfg(feature = "async-io")]
28        return Self(async_process::Command::new(program));
29
30        #[cfg(all(feature = "tokio", not(feature = "async-io")))]
31        return Self(tokio::process::Command::new(program));
32    }
33
34    /// Constructs a new `Command` from a `unixexec` address.
35    pub fn for_unixexec(unixexec: &Unixexec) -> Self {
36        let mut command = Self::new(unixexec.path());
37        command.args(unixexec.args());
38
39        if let Some(arg0) = unixexec.arg0() {
40            command.arg0(arg0);
41        }
42
43        command
44    }
45
46    /// Sets executable argument.
47    ///
48    /// Set the first process argument, `argv[0]`, to something other than the
49    /// default executable path.
50    pub fn arg0<S>(&mut self, arg: S) -> &mut Self
51    where
52        S: AsRef<OsStr>,
53    {
54        self.0.arg0(arg);
55        self
56    }
57
58    /// Adds multiple arguments to pass to the program.
59    pub fn args<I, S>(&mut self, args: I) -> &mut Self
60    where
61        I: IntoIterator<Item = S>,
62        S: AsRef<OsStr>,
63    {
64        self.0.args(args);
65        self
66    }
67
68    /// Executes the command as a child process, waiting for it to finish and
69    /// collecting all of its output.
70    #[cfg(unix)]
71    pub async fn output(&mut self) -> Result<Output, Error> {
72        self.0.output().await
73    }
74
75    /// Sets configuration for the child process's standard input (stdin) handle.
76    pub fn stdin<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Self {
77        self.0.stdin(cfg);
78        self
79    }
80
81    /// Sets configuration for the child process's standard output (stdout) handle.
82    pub fn stdout<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Self {
83        self.0.stdout(cfg);
84        self
85    }
86
87    /// Sets configuration for the child process's standard error (stderr) handle.
88    pub fn stderr<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Self {
89        self.0.stderr(cfg);
90        self
91    }
92
93    /// Executes the command as a child process, returning a handle to it.
94    pub fn spawn(&mut self) -> Result<Child, Error> {
95        self.0.spawn()
96    }
97}
98
99/// An asynchronous wrapper around running and getting command output
100#[cfg(unix)]
101pub async fn run<I, S>(program: S, args: I) -> Result<Output, Error>
102where
103    I: IntoIterator<Item = S>,
104    S: AsRef<OsStr>,
105{
106    Command::new(program).args(args).output().await
107}