aboutsummaryrefslogtreecommitdiffstats
path: root/src/command.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/command.rs')
-rw-r--r--src/command.rs172
1 files changed, 82 insertions, 90 deletions
diff --git a/src/command.rs b/src/command.rs
index 303b885..b3d1104 100644
--- a/src/command.rs
+++ b/src/command.rs
@@ -1,6 +1,4 @@
-use crate::pty::Pty as _;
-
-use ::std::os::unix::io::AsRawFd as _;
+use ::std::os::unix::io::{AsRawFd as _, FromRawFd as _};
#[cfg(any(feature = "backend-async-std", feature = "backend-smol"))]
mod async_process;
@@ -9,47 +7,59 @@ mod std;
#[cfg(feature = "backend-tokio")]
mod tokio;
-/// Adds methods to the existing `Command` struct.
-///
-/// This trait is automatically implemented for a backend's `Command` struct
-/// when that backend's feature is enabled.
-pub trait Command {
- type Child;
- type Pty;
+pub struct Command<C: Impl, P: crate::pty::Impl> {
+ inner: C,
+ stdin_set: bool,
+ stdout_set: bool,
+ stderr_set: bool,
- /// Creates a new pty, associates the command's stdin/stdout/stderr with
- /// that pty, and then calls `spawn`. This will override any previous
- /// calls to `stdin`/`stdout`/`stderr`.
- ///
- /// # Errors
- /// * `Error::CreatePty`: error creating pty
- /// * `Error::SetTermSize`: error setting terminal size
- /// * `Error::Spawn`: error spawning subprocess
- fn spawn_pty(
- &mut self,
- size: Option<&crate::pty::Size>,
- ) -> crate::error::Result<Child<Self::Child, Self::Pty>>;
+ _phantom: ::std::marker::PhantomData<P>,
}
-impl<T> Command for T
-where
- T: Impl,
- T::Pty: crate::pty::Pty,
- <<T as Impl>::Pty as crate::pty::Pty>::Pt: ::std::os::unix::io::AsRawFd,
-{
- type Child = T::Child;
- type Pty = T::Pty;
+impl<C: Impl, P: crate::pty::Impl> Command<C, P> {
+ pub fn new<S: AsRef<::std::ffi::OsStr>>(program: S) -> Self {
+ Self {
+ inner: C::new_impl(program.as_ref()),
+ stdin_set: false,
+ stdout_set: false,
+ stderr_set: false,
+
+ _phantom: ::std::marker::PhantomData,
+ }
+ }
+
+ pub fn stdin<T: Into<::std::process::Stdio>>(&mut self, cfg: T) {
+ self.stdin_set = true;
+ self.inner.stdin_impl(cfg.into());
+ }
+
+ pub fn stdout<T: Into<::std::process::Stdio>>(&mut self, cfg: T) {
+ self.stdout_set = true;
+ self.inner.stdout_impl(cfg.into());
+ }
+
+ pub fn stderr<T: Into<::std::process::Stdio>>(&mut self, cfg: T) {
+ self.stderr_set = true;
+ self.inner.stderr_impl(cfg.into());
+ }
- fn spawn_pty(
+ pub fn spawn(
&mut self,
- size: Option<&crate::pty::Size>,
- ) -> crate::error::Result<Child<Self::Child, Self::Pty>> {
- let (pty, pts, stdin, stdout, stderr) = setup_pty::<Self::Pty>(size)?;
+ pty: crate::Pty<P>,
+ ) -> crate::Result<Child<<C as Impl>::Child, P>> {
+ let (pts, stdin, stdout, stderr) = pty.setup()?;
let pt_fd = pty.pt().as_raw_fd();
let pts_fd = pts.as_raw_fd();
- self.std_fds(stdin, stdout, stderr);
+ self.inner
+ .stdin_impl(unsafe { ::std::process::Stdio::from_raw_fd(stdin) });
+ self.inner.stdout_impl(unsafe {
+ ::std::process::Stdio::from_raw_fd(stdout)
+ });
+ self.inner.stderr_impl(unsafe {
+ ::std::process::Stdio::from_raw_fd(stderr)
+ });
let pre_exec = move || {
nix::unistd::setsid()?;
@@ -75,30 +85,44 @@ where
// safe because setsid() and close() are async-signal-safe functions
// and ioctl() is a raw syscall (which is inherently
// async-signal-safe).
- unsafe { self.pre_exec_impl(pre_exec) };
+ unsafe { self.inner.pre_exec_impl(pre_exec) };
+
+ let child = self.inner.spawn_impl()?;
+
+ Ok(Child::new(child, pty))
+ }
+}
+
+impl<C: Impl, P: crate::pty::Impl> ::std::ops::Deref for Command<C, P> {
+ type Target = C;
- let child = self.spawn_impl().map_err(crate::error::spawn)?;
+ fn deref(&self) -> &Self::Target {
+ &self.inner
+ }
+}
- Ok(Child { child, pty })
+impl<C: Impl, P: crate::pty::Impl> ::std::ops::DerefMut for Command<C, P> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.inner
}
}
-/// Wrapper struct adding pty methods to the normal `Child` struct.
-pub struct Child<C, P> {
- child: C,
- pty: P,
+pub struct Child<C, P: crate::pty::Impl> {
+ inner: C,
+ pty: crate::Pty<P>,
}
-impl<C, P> Child<C, P>
-where
- P: crate::pty::Pty,
-{
+impl<C, P: crate::pty::Impl> Child<C, P> {
+ fn new(inner: C, pty: crate::Pty<P>) -> Self {
+ Self { inner, pty }
+ }
+
/// Returns a reference to the pty.
///
/// The underlying pty instance is guaranteed to implement
/// [`AsRawFd`](::std::os::unix::io::AsRawFd), as well as the appropriate
/// `Read` and `Write` traits for the associated backend.
- pub fn pty(&self) -> &P::Pt {
+ pub fn pty(&self) -> &P {
self.pty.pt()
}
@@ -111,7 +135,7 @@ where
/// This method is primarily useful for the tokio backend, since tokio's
/// `AsyncRead` and `AsyncWrite` traits have methods which take mutable
/// references.
- pub fn pty_mut(&mut self) -> &mut P::Pt {
+ pub fn pty_mut(&mut self) -> &mut P {
self.pty.pt_mut()
}
@@ -124,70 +148,38 @@ where
/// * `Error::SetTermSize`: error setting terminal size
pub fn resize_pty(
&self,
- size: &crate::pty::Size,
+ size: crate::pty::Size,
) -> crate::error::Result<()> {
self.pty.resize(size)
}
}
-impl<C, P> ::std::ops::Deref for Child<C, P> {
+impl<C, P: crate::pty::Impl> ::std::ops::Deref for Child<C, P> {
type Target = C;
fn deref(&self) -> &Self::Target {
- &self.child
+ &self.inner
}
}
-impl<C, P> ::std::ops::DerefMut for Child<C, P> {
+impl<C, P: crate::pty::Impl> ::std::ops::DerefMut for Child<C, P> {
fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.child
+ &mut self.inner
}
}
-// XXX shouldn't be pub?
pub trait Impl {
type Child;
- type Pty;
- fn std_fds(
- &mut self,
- stdin: ::std::os::unix::io::RawFd,
- stdout: ::std::os::unix::io::RawFd,
- stderr: ::std::os::unix::io::RawFd,
- );
+ fn new_impl(program: &::std::ffi::OsStr) -> Self;
+ fn stdin_impl(&mut self, cfg: ::std::process::Stdio);
+ fn stdout_impl(&mut self, cfg: ::std::process::Stdio);
+ fn stderr_impl(&mut self, cfg: ::std::process::Stdio);
unsafe fn pre_exec_impl<F>(&mut self, f: F)
where
F: FnMut() -> ::std::io::Result<()> + Send + Sync + 'static;
- fn spawn_impl(&mut self) -> ::std::io::Result<Self::Child>;
-}
-
-fn setup_pty<P>(
- size: Option<&crate::pty::Size>,
-) -> crate::error::Result<(
- P,
- ::std::fs::File,
- ::std::os::unix::io::RawFd,
- ::std::os::unix::io::RawFd,
- ::std::os::unix::io::RawFd,
-)>
-where
- P: crate::pty::Pty,
-{
- let pty = P::new()?;
- if let Some(size) = size {
- pty.resize(size)?;
- }
-
- let pts = pty.pts()?;
- let pts_fd = pts.as_raw_fd();
-
- let stdin = nix::unistd::dup(pts_fd).map_err(crate::error::create_pty)?;
- let stdout =
- nix::unistd::dup(pts_fd).map_err(crate::error::create_pty)?;
- let stderr =
- nix::unistd::dup(pts_fd).map_err(crate::error::create_pty)?;
- Ok((pty, pts, stdin, stdout, stderr))
+ fn spawn_impl(&mut self) -> crate::Result<Self::Child>;
}
fn set_controlling_terminal(