about summary refs log tree commit diff
path: root/library/std/src
diff options
context:
space:
mode:
authorEric Huss <eric@huss.org>2020-09-21 11:32:06 -0700
committerEric Huss <eric@huss.org>2020-09-26 18:58:38 -0700
commitc297e20e03452c659b6d3f026ce4beee42ed8738 (patch)
tree0c20b7dc582d780b8a00cdbb2965767b24cf3ce9 /library/std/src
parent623fb90b5a1f324e0ec44085116bf858cef19a00 (diff)
downloadrust-c297e20e03452c659b6d3f026ce4beee42ed8738.tar.gz
rust-c297e20e03452c659b6d3f026ce4beee42ed8738.zip
Add accessors to Command.
Diffstat (limited to 'library/std/src')
-rw-r--r--library/std/src/process.rs125
-rw-r--r--library/std/src/sys/unix/process/mod.rs3
-rw-r--r--library/std/src/sys/unix/process/process_common.rs53
-rw-r--r--library/std/src/sys/unix/process/process_fuchsia.rs2
-rw-r--r--library/std/src/sys/unix/process/process_unix.rs4
-rw-r--r--library/std/src/sys/unsupported/process.rs39
-rw-r--r--library/std/src/sys/windows/process.rs48
-rw-r--r--library/std/src/sys_common/process.rs37
8 files changed, 303 insertions, 8 deletions
diff --git a/library/std/src/process.rs b/library/std/src/process.rs
index 9f3796e11ed..3d238b7f764 100644
--- a/library/std/src/process.rs
+++ b/library/std/src/process.rs
@@ -110,6 +110,8 @@ use crate::path::Path;
 use crate::str;
 use crate::sys::pipe::{read2, AnonPipe};
 use crate::sys::process as imp;
+#[unstable(feature = "command_access", issue = "44434")]
+pub use crate::sys_common::process::CommandEnvs;
 use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
 
 /// Representation of a running or exited child process.
@@ -894,6 +896,98 @@ impl Command {
             .map(Child::from_inner)
             .and_then(|mut p| p.wait())
     }
+
+    /// Returns the path to the program that was given to [`Command::new`].
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # #![feature(command_access)]
+    /// use std::process::Command;
+    ///
+    /// let cmd = Command::new("echo");
+    /// assert_eq!(cmd.get_program(), "echo");
+    /// ```
+    #[unstable(feature = "command_access", issue = "44434")]
+    pub fn get_program(&self) -> &OsStr {
+        self.inner.get_program()
+    }
+
+    /// Returns an iterator of the arguments that will be passed to the program.
+    ///
+    /// This does not include the path to the program as the first argument;
+    /// it only includes the arguments specified with [`Command::arg`] and
+    /// [`Command::args`].
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # #![feature(command_access)]
+    /// use std::ffi::OsStr;
+    /// use std::process::Command;
+    ///
+    /// let mut cmd = Command::new("echo");
+    /// cmd.arg("first").arg("second");
+    /// let args: Vec<&OsStr> = cmd.get_args().collect();
+    /// assert_eq!(args, &["first", "second"]);
+    /// ```
+    #[unstable(feature = "command_access", issue = "44434")]
+    pub fn get_args(&self) -> CommandArgs<'_> {
+        CommandArgs { inner: self.inner.get_args() }
+    }
+
+    /// Returns an iterator of the environment variables that will be set when
+    /// the process is spawned.
+    ///
+    /// Each element is a tuple `(&OsStr, Option<&OsStr>)`, where the first
+    /// value is the key, and the second is the value, which is [`None`] if
+    /// the environment variable is to be explicitly removed.
+    ///
+    /// This only includes environment variables explicitly set with
+    /// [`Command::env`], [`Command::envs`], and [`Command::env_remove`]. It
+    /// does not include environment variables that will be inherited by the
+    /// child process.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # #![feature(command_access)]
+    /// use std::ffi::OsStr;
+    /// use std::process::Command;
+    ///
+    /// let mut cmd = Command::new("ls");
+    /// cmd.env("TERM", "dumb").env_remove("TZ");
+    /// let envs: Vec<(&OsStr, Option<&OsStr>)> = cmd.get_envs().collect();
+    /// assert_eq!(envs, &[
+    ///     (OsStr::new("TERM"), Some(OsStr::new("dumb"))),
+    ///     (OsStr::new("TZ"), None)
+    /// ]);
+    /// ```
+    #[unstable(feature = "command_access", issue = "44434")]
+    pub fn get_envs(&self) -> CommandEnvs<'_> {
+        self.inner.get_envs()
+    }
+
+    /// Returns the working directory for the child process.
+    ///
+    /// This returns [`None`] if the working directory will not be changed.
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// # #![feature(command_access)]
+    /// use std::path::Path;
+    /// use std::process::Command;
+    ///
+    /// let mut cmd = Command::new("ls");
+    /// assert_eq!(cmd.get_current_dir(), None);
+    /// cmd.current_dir("/bin");
+    /// assert_eq!(cmd.get_current_dir(), Some(Path::new("/bin")));
+    /// ```
+    #[unstable(feature = "command_access", issue = "44434")]
+    pub fn get_current_dir(&self) -> Option<&Path> {
+        self.inner.get_current_dir()
+    }
 }
 
 #[stable(feature = "rust1", since = "1.0.0")]
@@ -918,6 +1012,37 @@ impl AsInnerMut<imp::Command> for Command {
     }
 }
 
+/// An iterator over the command arguments.
+///
+/// This struct is created by [`Command::get_args`]. See its documentation for
+/// more.
+#[unstable(feature = "command_access", issue = "44434")]
+#[derive(Debug)]
+pub struct CommandArgs<'a> {
+    inner: imp::CommandArgs<'a>,
+}
+
+#[unstable(feature = "command_access", issue = "44434")]
+impl<'a> Iterator for CommandArgs<'a> {
+    type Item = &'a OsStr;
+    fn next(&mut self) -> Option<&'a OsStr> {
+        self.inner.next()
+    }
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.inner.size_hint()
+    }
+}
+
+#[unstable(feature = "command_access", issue = "44434")]
+impl<'a> ExactSizeIterator for CommandArgs<'a> {
+    fn len(&self) -> usize {
+        self.inner.len()
+    }
+    fn is_empty(&self) -> bool {
+        self.inner.is_empty()
+    }
+}
+
 /// The output of a finished process.
 ///
 /// This is returned in a Result by either the [`output`] method of a
diff --git a/library/std/src/sys/unix/process/mod.rs b/library/std/src/sys/unix/process/mod.rs
index 553e980f08e..1b7b93f9d4a 100644
--- a/library/std/src/sys/unix/process/mod.rs
+++ b/library/std/src/sys/unix/process/mod.rs
@@ -1,6 +1,7 @@
-pub use self::process_common::{Command, ExitCode, Stdio, StdioPipes};
+pub use self::process_common::{Command, CommandArgs, ExitCode, Stdio, StdioPipes};
 pub use self::process_inner::{ExitStatus, Process};
 pub use crate::ffi::OsString as EnvKey;
+pub use crate::sys_common::process::CommandEnvs;
 
 mod process_common;
 #[cfg(not(target_os = "fuchsia"))]
diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs
index f8666485eec..9ddd4ad4000 100644
--- a/library/std/src/sys/unix/process/process_common.rs
+++ b/library/std/src/sys/unix/process/process_common.rs
@@ -7,11 +7,12 @@ use crate::collections::BTreeMap;
 use crate::ffi::{CStr, CString, OsStr, OsString};
 use crate::fmt;
 use crate::io;
+use crate::path::Path;
 use crate::ptr;
 use crate::sys::fd::FileDesc;
 use crate::sys::fs::File;
 use crate::sys::pipe::{self, AnonPipe};
-use crate::sys_common::process::CommandEnv;
+use crate::sys_common::process::{CommandEnv, CommandEnvs};
 
 #[cfg(not(target_os = "fuchsia"))]
 use crate::sys::fs::OpenOptions;
@@ -184,11 +185,30 @@ impl Command {
     pub fn saw_nul(&self) -> bool {
         self.saw_nul
     }
+
+    pub fn get_program(&self) -> &OsStr {
+        OsStr::from_bytes(self.program.as_bytes())
+    }
+
+    pub fn get_args(&self) -> CommandArgs<'_> {
+        let mut iter = self.args.iter();
+        iter.next();
+        CommandArgs { iter }
+    }
+
+    pub fn get_envs(&self) -> CommandEnvs<'_> {
+        self.env.iter()
+    }
+
+    pub fn get_current_dir(&self) -> Option<&Path> {
+        self.cwd.as_ref().map(|cs| Path::new(OsStr::from_bytes(cs.as_bytes())))
+    }
+
     pub fn get_argv(&self) -> &Vec<*const c_char> {
         &self.argv.0
     }
 
-    pub fn get_program(&self) -> &CStr {
+    pub fn get_program_cstr(&self) -> &CStr {
         &*self.program
     }
 
@@ -402,3 +422,32 @@ impl ExitCode {
         self.0 as i32
     }
 }
+
+pub struct CommandArgs<'a> {
+    iter: crate::slice::Iter<'a, CString>,
+}
+
+impl<'a> Iterator for CommandArgs<'a> {
+    type Item = &'a OsStr;
+    fn next(&mut self) -> Option<&'a OsStr> {
+        self.iter.next().map(|cs| OsStr::from_bytes(cs.as_bytes()))
+    }
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.iter.size_hint()
+    }
+}
+
+impl<'a> ExactSizeIterator for CommandArgs<'a> {
+    fn len(&self) -> usize {
+        self.iter.len()
+    }
+    fn is_empty(&self) -> bool {
+        self.iter.is_empty()
+    }
+}
+
+impl<'a> fmt::Debug for CommandArgs<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_list().entries(self.iter.clone()).finish()
+    }
+}
diff --git a/library/std/src/sys/unix/process/process_fuchsia.rs b/library/std/src/sys/unix/process/process_fuchsia.rs
index fab27cd9f70..b64636c3f3d 100644
--- a/library/std/src/sys/unix/process/process_fuchsia.rs
+++ b/library/std/src/sys/unix/process/process_fuchsia.rs
@@ -120,7 +120,7 @@ impl Command {
                 | FDIO_SPAWN_CLONE_NAMESPACE
                 | FDIO_SPAWN_CLONE_ENVIRON // this is ignored when envp is non-null
                 | FDIO_SPAWN_CLONE_UTC_CLOCK,
-            self.get_program().as_ptr(),
+            self.get_program_cstr().as_ptr(),
             self.get_argv().as_ptr(),
             envp,
             actions.len() as size_t,
diff --git a/library/std/src/sys/unix/process/process_unix.rs b/library/std/src/sys/unix/process/process_unix.rs
index 08efe154e4c..50f5e78cf2a 100644
--- a/library/std/src/sys/unix/process/process_unix.rs
+++ b/library/std/src/sys/unix/process/process_unix.rs
@@ -245,7 +245,7 @@ impl Command {
             *sys::os::environ() = envp.as_ptr();
         }
 
-        libc::execvp(self.get_program().as_ptr(), self.get_argv().as_ptr());
+        libc::execvp(self.get_program_cstr().as_ptr(), self.get_argv().as_ptr());
         Err(io::Error::last_os_error())
     }
 
@@ -383,7 +383,7 @@ impl Command {
             let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::os::environ() as *const _);
             let ret = libc::posix_spawnp(
                 &mut p.pid,
-                self.get_program().as_ptr(),
+                self.get_program_cstr().as_ptr(),
                 file_actions.0.as_ptr(),
                 attrs.0.as_ptr(),
                 self.get_argv().as_ptr() as *const _,
diff --git a/library/std/src/sys/unsupported/process.rs b/library/std/src/sys/unsupported/process.rs
index 7156c9ab92f..3ede2291d5a 100644
--- a/library/std/src/sys/unsupported/process.rs
+++ b/library/std/src/sys/unsupported/process.rs
@@ -1,10 +1,12 @@
 use crate::ffi::OsStr;
 use crate::fmt;
 use crate::io;
+use crate::marker::PhantomData;
+use crate::path::Path;
 use crate::sys::fs::File;
 use crate::sys::pipe::AnonPipe;
 use crate::sys::{unsupported, Void};
-use crate::sys_common::process::CommandEnv;
+use crate::sys_common::process::{CommandEnv, CommandEnvs};
 
 pub use crate::ffi::OsString as EnvKey;
 
@@ -49,6 +51,22 @@ impl Command {
 
     pub fn stderr(&mut self, _stderr: Stdio) {}
 
+    pub fn get_program(&self) -> &OsStr {
+        panic!("unsupported")
+    }
+
+    pub fn get_args(&self) -> CommandArgs<'_> {
+        CommandArgs { _p: PhantomData }
+    }
+
+    pub fn get_envs(&self) -> CommandEnvs<'_> {
+        self.env.iter()
+    }
+
+    pub fn get_current_dir(&self) -> Option<&Path> {
+        None
+    }
+
     pub fn spawn(
         &mut self,
         _default: Stdio,
@@ -147,3 +165,22 @@ impl Process {
         match self.0 {}
     }
 }
+
+pub struct CommandArgs<'a> {
+    _p: PhantomData<&'a ()>,
+}
+
+impl<'a> Iterator for CommandArgs<'a> {
+    type Item = &'a OsStr;
+    fn next(&mut self) -> Option<&'a OsStr> {
+        None
+    }
+}
+
+impl<'a> ExactSizeIterator for CommandArgs<'a> {}
+
+impl<'a> fmt::Debug for CommandArgs<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_list().finish()
+    }
+}
diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs
index e18521bb30d..243065b94b1 100644
--- a/library/std/src/sys/windows/process.rs
+++ b/library/std/src/sys/windows/process.rs
@@ -22,7 +22,7 @@ use crate::sys::handle::Handle;
 use crate::sys::mutex::Mutex;
 use crate::sys::pipe::{self, AnonPipe};
 use crate::sys::stdio;
-use crate::sys_common::process::CommandEnv;
+use crate::sys_common::process::{CommandEnv, CommandEnvs};
 use crate::sys_common::AsInner;
 
 use libc::{c_void, EXIT_FAILURE, EXIT_SUCCESS};
@@ -134,6 +134,23 @@ impl Command {
         self.flags = flags;
     }
 
+    pub fn get_program(&self) -> &OsStr {
+        &self.program
+    }
+
+    pub fn get_args(&self) -> CommandArgs<'_> {
+        let iter = self.args.iter();
+        CommandArgs { iter }
+    }
+
+    pub fn get_envs(&self) -> CommandEnvs<'_> {
+        self.env.iter()
+    }
+
+    pub fn get_current_dir(&self) -> Option<&Path> {
+        self.cwd.as_ref().map(|cwd| Path::new(cwd))
+    }
+
     pub fn spawn(
         &mut self,
         default: Stdio,
@@ -529,3 +546,32 @@ fn make_dirp(d: Option<&OsString>) -> io::Result<(*const u16, Vec<u16>)> {
         None => Ok((ptr::null(), Vec::new())),
     }
 }
+
+pub struct CommandArgs<'a> {
+    iter: crate::slice::Iter<'a, OsString>,
+}
+
+impl<'a> Iterator for CommandArgs<'a> {
+    type Item = &'a OsStr;
+    fn next(&mut self) -> Option<&'a OsStr> {
+        self.iter.next().map(|s| s.as_ref())
+    }
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.iter.size_hint()
+    }
+}
+
+impl<'a> ExactSizeIterator for CommandArgs<'a> {
+    fn len(&self) -> usize {
+        self.iter.len()
+    }
+    fn is_empty(&self) -> bool {
+        self.iter.is_empty()
+    }
+}
+
+impl<'a> fmt::Debug for CommandArgs<'a> {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_list().entries(self.iter.clone()).finish()
+    }
+}
diff --git a/library/std/src/sys_common/process.rs b/library/std/src/sys_common/process.rs
index f3a2962098b..fe89b11043c 100644
--- a/library/std/src/sys_common/process.rs
+++ b/library/std/src/sys_common/process.rs
@@ -92,4 +92,41 @@ impl CommandEnv {
             self.saw_path = true;
         }
     }
+
+    pub fn iter(&self) -> CommandEnvs<'_> {
+        let iter = self.vars.iter();
+        CommandEnvs { iter }
+    }
+}
+
+/// An iterator over the command environment variables.
+///
+/// This struct is created by
+/// [`Command::get_envs`][crate::process::Command::get_envs]. See its
+/// documentation for more.
+#[unstable(feature = "command_access", issue = "44434")]
+#[derive(Debug)]
+pub struct CommandEnvs<'a> {
+    iter: crate::collections::btree_map::Iter<'a, EnvKey, Option<OsString>>,
+}
+
+#[unstable(feature = "command_access", issue = "44434")]
+impl<'a> Iterator for CommandEnvs<'a> {
+    type Item = (&'a OsStr, Option<&'a OsStr>);
+    fn next(&mut self) -> Option<Self::Item> {
+        self.iter.next().map(|(key, value)| (key.as_ref(), value.as_deref()))
+    }
+    fn size_hint(&self) -> (usize, Option<usize>) {
+        self.iter.size_hint()
+    }
+}
+
+#[unstable(feature = "command_access", issue = "44434")]
+impl<'a> ExactSizeIterator for CommandEnvs<'a> {
+    fn len(&self) -> usize {
+        self.iter.len()
+    }
+    fn is_empty(&self) -> bool {
+        self.iter.is_empty()
+    }
 }