Skip to content

Commit 094d67a

Browse files
committed
Add accessors to Command.
1 parent 7467d17 commit 094d67a

File tree

7 files changed

+302
-7
lines changed

7 files changed

+302
-7
lines changed

library/std/src/process.rs

+125
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,8 @@ use crate::path::Path;
110110
use crate::str;
111111
use crate::sys::pipe::{read2, AnonPipe};
112112
use crate::sys::process as imp;
113+
#[unstable(feature = "command_access", issue = "44434")]
114+
pub use crate::sys_common::process::CommandEnvs;
113115
use crate::sys_common::{AsInner, AsInnerMut, FromInner, IntoInner};
114116

115117
/// Representation of a running or exited child process.
@@ -875,6 +877,98 @@ impl Command {
875877
.map(Child::from_inner)
876878
.and_then(|mut p| p.wait())
877879
}
880+
881+
/// Returns the path to the program that was given to [`Command::new`].
882+
///
883+
/// # Examples
884+
///
885+
/// ```
886+
/// # #![feature(command_access)]
887+
/// use std::process::Command;
888+
///
889+
/// let cmd = Command::new("echo");
890+
/// assert_eq!(cmd.get_program(), "echo");
891+
/// ```
892+
#[unstable(feature = "command_access", issue = "44434")]
893+
pub fn get_program(&self) -> &OsStr {
894+
self.inner.get_program()
895+
}
896+
897+
/// Returns an iterator of the arguments that will be passed to the program.
898+
///
899+
/// This does not include the path to the program as the first argument;
900+
/// it only includes the arguments specified with [`Command::arg`] and
901+
/// [`Command::args`].
902+
///
903+
/// # Examples
904+
///
905+
/// ```
906+
/// # #![feature(command_access)]
907+
/// use std::ffi::OsStr;
908+
/// use std::process::Command;
909+
///
910+
/// let mut cmd = Command::new("echo");
911+
/// cmd.arg("first").arg("second");
912+
/// let args: Vec<&OsStr> = cmd.get_args().collect();
913+
/// assert_eq!(args, &["first", "second"]);
914+
/// ```
915+
#[unstable(feature = "command_access", issue = "44434")]
916+
pub fn get_args(&self) -> CommandArgs<'_> {
917+
CommandArgs { inner: self.inner.get_args() }
918+
}
919+
920+
/// Returns an iterator of the environment variables that will be set when
921+
/// the process is spawned.
922+
///
923+
/// Each element is a tuple `(&OsStr, Option<&OsStr>)`, where the first
924+
/// value is the key, and the second is the value, which is [`None`] if
925+
/// the environment variable is to be explicitly removed.
926+
///
927+
/// This only includes environment variables explicitly set with
928+
/// [`Command::env`], [`Command::envs`], and [`Command::env_remove`]. It
929+
/// does not include environment variables that will be inherited by the
930+
/// child process.
931+
///
932+
/// # Examples
933+
///
934+
/// ```
935+
/// # #![feature(command_access)]
936+
/// use std::ffi::OsStr;
937+
/// use std::process::Command;
938+
///
939+
/// let mut cmd = Command::new("ls");
940+
/// cmd.env("TERM", "dumb").env_remove("TZ");
941+
/// let envs: Vec<(&OsStr, Option<&OsStr>)> = cmd.get_envs().collect();
942+
/// assert_eq!(envs, &[
943+
/// (OsStr::new("TERM"), Some(OsStr::new("dumb"))),
944+
/// (OsStr::new("TZ"), None)
945+
/// ]);
946+
/// ```
947+
#[unstable(feature = "command_access", issue = "44434")]
948+
pub fn get_envs(&self) -> CommandEnvs<'_> {
949+
self.inner.get_envs()
950+
}
951+
952+
/// Returns the working directory for the child process.
953+
///
954+
/// This returns [`None`] if the working directory will not be changed.
955+
///
956+
/// # Examples
957+
///
958+
/// ```
959+
/// # #![feature(command_access)]
960+
/// use std::path::Path;
961+
/// use std::process::Command;
962+
///
963+
/// let mut cmd = Command::new("ls");
964+
/// assert_eq!(cmd.get_current_dir(), None);
965+
/// cmd.current_dir("/bin");
966+
/// assert_eq!(cmd.get_current_dir(), Some(Path::new("/bin")));
967+
/// ```
968+
#[unstable(feature = "command_access", issue = "44434")]
969+
pub fn get_current_dir(&self) -> Option<&Path> {
970+
self.inner.get_current_dir()
971+
}
878972
}
879973

880974
#[stable(feature = "rust1", since = "1.0.0")]
@@ -899,6 +993,37 @@ impl AsInnerMut<imp::Command> for Command {
899993
}
900994
}
901995

996+
/// An iterator over the command arguments.
997+
///
998+
/// This struct is created by [`Command::get_args`]. See its documentation for
999+
/// more.
1000+
#[unstable(feature = "command_access", issue = "44434")]
1001+
#[derive(Debug)]
1002+
pub struct CommandArgs<'a> {
1003+
inner: imp::CommandArgs<'a>,
1004+
}
1005+
1006+
#[unstable(feature = "command_access", issue = "44434")]
1007+
impl<'a> Iterator for CommandArgs<'a> {
1008+
type Item = &'a OsStr;
1009+
fn next(&mut self) -> Option<&'a OsStr> {
1010+
self.inner.next()
1011+
}
1012+
fn size_hint(&self) -> (usize, Option<usize>) {
1013+
self.inner.size_hint()
1014+
}
1015+
}
1016+
1017+
#[unstable(feature = "command_access", issue = "44434")]
1018+
impl<'a> ExactSizeIterator for CommandArgs<'a> {
1019+
fn len(&self) -> usize {
1020+
self.inner.len()
1021+
}
1022+
fn is_empty(&self) -> bool {
1023+
self.inner.is_empty()
1024+
}
1025+
}
1026+
9021027
/// The output of a finished process.
9031028
///
9041029
/// This is returned in a Result by either the [`output`] method of a

library/std/src/sys/unix/process/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
pub use self::process_common::{Command, ExitCode, Stdio, StdioPipes};
1+
pub use self::process_common::{Command, CommandArgs, ExitCode, Stdio, StdioPipes};
22
pub use self::process_inner::{ExitStatus, Process};
33
pub use crate::ffi::OsString as EnvKey;
4+
pub use crate::sys_common::process::CommandEnvs;
45

56
mod process_common;
67
#[cfg(not(target_os = "fuchsia"))]

library/std/src/sys/unix/process/process_common.rs

+51-2
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@ use crate::collections::BTreeMap;
77
use crate::ffi::{CStr, CString, OsStr, OsString};
88
use crate::fmt;
99
use crate::io;
10+
use crate::path::Path;
1011
use crate::ptr;
1112
use crate::sys::fd::FileDesc;
1213
use crate::sys::fs::File;
1314
use crate::sys::pipe::{self, AnonPipe};
14-
use crate::sys_common::process::CommandEnv;
15+
use crate::sys_common::process::{CommandEnv, CommandEnvs};
1516

1617
#[cfg(not(target_os = "fuchsia"))]
1718
use crate::sys::fs::OpenOptions;
@@ -184,11 +185,30 @@ impl Command {
184185
pub fn saw_nul(&self) -> bool {
185186
self.saw_nul
186187
}
188+
189+
pub fn get_program(&self) -> &OsStr {
190+
OsStr::from_bytes(self.program.as_bytes())
191+
}
192+
193+
pub fn get_args(&self) -> CommandArgs<'_> {
194+
let mut iter = self.args.iter();
195+
iter.next();
196+
CommandArgs { iter }
197+
}
198+
199+
pub fn get_envs(&self) -> CommandEnvs<'_> {
200+
self.env.iter()
201+
}
202+
203+
pub fn get_current_dir(&self) -> Option<&Path> {
204+
self.cwd.as_ref().map(|cs| Path::new(OsStr::from_bytes(cs.as_bytes())))
205+
}
206+
187207
pub fn get_argv(&self) -> &Vec<*const c_char> {
188208
&self.argv.0
189209
}
190210

191-
pub fn get_program(&self) -> &CStr {
211+
pub fn get_program_cstr(&self) -> &CStr {
192212
&*self.program
193213
}
194214

@@ -402,3 +422,32 @@ impl ExitCode {
402422
self.0 as i32
403423
}
404424
}
425+
426+
pub struct CommandArgs<'a> {
427+
iter: crate::slice::Iter<'a, CString>,
428+
}
429+
430+
impl<'a> Iterator for CommandArgs<'a> {
431+
type Item = &'a OsStr;
432+
fn next(&mut self) -> Option<&'a OsStr> {
433+
self.iter.next().map(|cs| OsStr::from_bytes(cs.as_bytes()))
434+
}
435+
fn size_hint(&self) -> (usize, Option<usize>) {
436+
self.iter.size_hint()
437+
}
438+
}
439+
440+
impl<'a> ExactSizeIterator for CommandArgs<'a> {
441+
fn len(&self) -> usize {
442+
self.iter.len()
443+
}
444+
fn is_empty(&self) -> bool {
445+
self.iter.is_empty()
446+
}
447+
}
448+
449+
impl<'a> fmt::Debug for CommandArgs<'a> {
450+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
451+
f.debug_list().entries(self.iter.clone()).finish()
452+
}
453+
}

library/std/src/sys/unix/process/process_unix.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ impl Command {
245245
*sys::os::environ() = envp.as_ptr();
246246
}
247247

248-
libc::execvp(self.get_program().as_ptr(), self.get_argv().as_ptr());
248+
libc::execvp(self.get_program_cstr().as_ptr(), self.get_argv().as_ptr());
249249
Err(io::Error::last_os_error())
250250
}
251251

@@ -383,7 +383,7 @@ impl Command {
383383
let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::os::environ() as *const _);
384384
let ret = libc::posix_spawnp(
385385
&mut p.pid,
386-
self.get_program().as_ptr(),
386+
self.get_program_cstr().as_ptr(),
387387
file_actions.0.as_ptr(),
388388
attrs.0.as_ptr(),
389389
self.get_argv().as_ptr() as *const _,

library/std/src/sys/unsupported/process.rs

+38-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
use crate::ffi::OsStr;
22
use crate::fmt;
33
use crate::io;
4+
use crate::marker::PhantomData;
5+
use crate::path::Path;
46
use crate::sys::fs::File;
57
use crate::sys::pipe::AnonPipe;
68
use crate::sys::{unsupported, Void};
7-
use crate::sys_common::process::CommandEnv;
9+
use crate::sys_common::process::{CommandEnv, CommandEnvs};
810

911
pub use crate::ffi::OsString as EnvKey;
1012

@@ -49,6 +51,22 @@ impl Command {
4951

5052
pub fn stderr(&mut self, _stderr: Stdio) {}
5153

54+
pub fn get_program(&self) -> &OsStr {
55+
panic!("unsupported")
56+
}
57+
58+
pub fn get_args(&self) -> CommandArgs<'_> {
59+
CommandArgs { _p: PhantomData }
60+
}
61+
62+
pub fn get_envs(&self) -> CommandEnvs<'_> {
63+
self.env.iter()
64+
}
65+
66+
pub fn get_current_dir(&self) -> Option<&Path> {
67+
None
68+
}
69+
5270
pub fn spawn(
5371
&mut self,
5472
_default: Stdio,
@@ -147,3 +165,22 @@ impl Process {
147165
match self.0 {}
148166
}
149167
}
168+
169+
pub struct CommandArgs<'a> {
170+
_p: PhantomData<&'a ()>,
171+
}
172+
173+
impl<'a> Iterator for CommandArgs<'a> {
174+
type Item = &'a OsStr;
175+
fn next(&mut self) -> Option<&'a OsStr> {
176+
None
177+
}
178+
}
179+
180+
impl<'a> ExactSizeIterator for CommandArgs<'a> {}
181+
182+
impl<'a> fmt::Debug for CommandArgs<'a> {
183+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184+
f.debug_list().finish()
185+
}
186+
}

library/std/src/sys/windows/process.rs

+47-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use crate::sys::handle::Handle;
2222
use crate::sys::mutex::Mutex;
2323
use crate::sys::pipe::{self, AnonPipe};
2424
use crate::sys::stdio;
25-
use crate::sys_common::process::CommandEnv;
25+
use crate::sys_common::process::{CommandEnv, CommandEnvs};
2626
use crate::sys_common::AsInner;
2727

2828
use libc::{c_void, EXIT_FAILURE, EXIT_SUCCESS};
@@ -134,6 +134,23 @@ impl Command {
134134
self.flags = flags;
135135
}
136136

137+
pub fn get_program(&self) -> &OsStr {
138+
&self.program
139+
}
140+
141+
pub fn get_args(&self) -> CommandArgs<'_> {
142+
let iter = self.args.iter();
143+
CommandArgs { iter }
144+
}
145+
146+
pub fn get_envs(&self) -> CommandEnvs<'_> {
147+
self.env.iter()
148+
}
149+
150+
pub fn get_current_dir(&self) -> Option<&Path> {
151+
self.cwd.as_ref().map(|cwd| Path::new(cwd))
152+
}
153+
137154
pub fn spawn(
138155
&mut self,
139156
default: Stdio,
@@ -529,3 +546,32 @@ fn make_dirp(d: Option<&OsString>) -> io::Result<(*const u16, Vec<u16>)> {
529546
None => Ok((ptr::null(), Vec::new())),
530547
}
531548
}
549+
550+
pub struct CommandArgs<'a> {
551+
iter: crate::slice::Iter<'a, OsString>,
552+
}
553+
554+
impl<'a> Iterator for CommandArgs<'a> {
555+
type Item = &'a OsStr;
556+
fn next(&mut self) -> Option<&'a OsStr> {
557+
self.iter.next().map(|s| s.as_ref())
558+
}
559+
fn size_hint(&self) -> (usize, Option<usize>) {
560+
self.iter.size_hint()
561+
}
562+
}
563+
564+
impl<'a> ExactSizeIterator for CommandArgs<'a> {
565+
fn len(&self) -> usize {
566+
self.iter.len()
567+
}
568+
fn is_empty(&self) -> bool {
569+
self.iter.is_empty()
570+
}
571+
}
572+
573+
impl<'a> fmt::Debug for CommandArgs<'a> {
574+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
575+
f.debug_list().entries(self.iter.clone()).finish()
576+
}
577+
}

0 commit comments

Comments
 (0)