diff options
Diffstat (limited to 'src/blocking/command.rs')
-rw-r--r-- | src/blocking/command.rs | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/src/blocking/command.rs b/src/blocking/command.rs new file mode 100644 index 0000000..3de0f3e --- /dev/null +++ b/src/blocking/command.rs @@ -0,0 +1,156 @@ +use std::os::unix::process::CommandExt as _; + +pub struct Command { + inner: std::process::Command, + stdin: Option<std::process::Stdio>, + stdout: Option<std::process::Stdio>, + stderr: Option<std::process::Stdio>, +} + +impl Command { + pub fn new<S: AsRef<std::ffi::OsStr>>(program: S) -> Self { + Self { + inner: std::process::Command::new(program), + stdin: None, + stdout: None, + stderr: None, + } + } + + pub fn arg<S: AsRef<std::ffi::OsStr>>(&mut self, arg: S) -> &mut Self { + self.inner.arg(arg); + self + } + + pub fn args<I, S>(&mut self, args: I) -> &mut Self + where + I: IntoIterator<Item = S>, + S: AsRef<std::ffi::OsStr>, + { + self.inner.args(args); + self + } + + pub fn env<K, V>(&mut self, key: K, val: V) -> &mut Self + where + K: AsRef<std::ffi::OsStr>, + V: AsRef<std::ffi::OsStr>, + { + self.inner.env(key, val); + self + } + + pub fn envs<I, K, V>(&mut self, vars: I) -> &mut Self + where + I: IntoIterator<Item = (K, V)>, + K: AsRef<std::ffi::OsStr>, + V: AsRef<std::ffi::OsStr>, + { + self.inner.envs(vars); + self + } + + pub fn env_remove<K: AsRef<std::ffi::OsStr>>( + &mut self, + key: K, + ) -> &mut Self { + self.inner.env_remove(key); + self + } + + pub fn env_clear(&mut self) -> &mut Self { + self.inner.env_clear(); + self + } + + pub fn current_dir<P: AsRef<std::path::Path>>( + &mut self, + dir: P, + ) -> &mut Self { + self.inner.current_dir(dir); + self + } + + pub fn stdin<T: Into<std::process::Stdio>>( + &mut self, + cfg: Option<T>, + ) -> &mut Self { + self.stdin = cfg.map(Into::into); + self + } + + pub fn stdout<T: Into<std::process::Stdio>>( + &mut self, + cfg: Option<T>, + ) -> &mut Self { + self.stdout = cfg.map(Into::into); + self + } + + pub fn stderr<T: Into<std::process::Stdio>>( + &mut self, + cfg: Option<T>, + ) -> &mut Self { + self.stderr = cfg.map(Into::into); + self + } + + pub fn spawn( + &mut self, + pty: crate::blocking::Pty, + ) -> crate::Result<Child> { + let (stdin, stdout, stderr, pre_exec) = crate::sys::setup_subprocess( + &pty, + pty.pts().map_err(crate::error::spawn)?, + ) + .map_err(crate::error::spawn)?; + + self.inner.stdin(self.stdin.take().unwrap_or(stdin)); + self.inner.stdout(self.stdout.take().unwrap_or(stdout)); + self.inner.stderr(self.stderr.take().unwrap_or(stderr)); + + // safe because setsid() and close() are async-signal-safe functions + // and ioctl() is a raw syscall (which is inherently + // async-signal-safe). + unsafe { self.inner.pre_exec(pre_exec) }; + + let child = self.inner.spawn().map_err(crate::error::spawn)?; + + Ok(Child::new(child, pty)) + } +} + +pub struct Child { + inner: std::process::Child, + pty: crate::blocking::Pty, +} + +impl Child { + fn new(inner: std::process::Child, pty: crate::blocking::Pty) -> Self { + Self { inner, pty } + } + + #[must_use] + pub fn pty(&self) -> &crate::blocking::Pty { + &self.pty + } + + #[must_use] + pub fn pty_mut(&mut self) -> &mut crate::blocking::Pty { + &mut self.pty + } +} + +impl std::ops::Deref for Child { + type Target = std::process::Child; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl std::ops::DerefMut for Child { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} |