diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 67846e7883570..6d612abcc98a2 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -306,6 +306,7 @@ #![feature(min_specialization)] #![feature(mixed_integer_ops)] #![feature(must_not_suspend)] +#![feature(mutate_command_args)] #![feature(needs_panic_runtime)] #![feature(negative_impls)] #![feature(never_type)] diff --git a/library/std/src/process.rs b/library/std/src/process.rs index 4e9fd51f28229..a629a94d496a2 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -648,6 +648,75 @@ impl Command { self } + /// Clears the argument array. + /// + /// When one wants to restart a Command again with different + /// arguments the argument list can to be cleared first. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// #![feature(mutate_command_args)] + /// use std::process::Command; + /// + /// let mut lsdir = Command::new("ls"); + /// + /// lsdir + /// .arg("target") + /// .spawn() + /// .expect("ls command failed to start"); + /// + /// lsdir + /// .args_clear() + /// .arg("tests") + /// .spawn() + /// .expect("ls command failed to start"); + /// ``` + #[unstable(feature = "mutate_command_args", issue = "87379")] + pub fn args_clear(&mut self) -> &mut Command { + self.inner.args_clear(); + self + } + + /// Sets the argument at index to a new value. + /// + /// An existing argument at the given index will be replaced with a new value. + /// + /// # Panics + /// + /// May panic if the index is 0 or out of bounds. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ```no_run + /// #![feature(mutate_command_args)] + /// use std::process::Command; + /// + /// // Prepare a command + /// let mut command = Command::new("ls"); + /// command.args(["-l", "-a", "FILE"]); + /// + /// // Run it with mutated 3rd parameter + /// ["foo", "bar", "baz"].iter() + /// .for_each( + /// |file| { + /// command + /// .arg_set(3, file) + /// .spawn() + /// .unwrap(); + /// } + /// ); + /// ``` + #[unstable(feature = "mutate_command_args", issue = "87379")] + pub fn arg_set>(&mut self, index: usize, value: S) -> &mut Command { + self.inner.arg_set(index, value.as_ref()); + self + } + /// Inserts or updates an environment variable mapping. /// /// Note that environment variable names are case-insensitive (but case-preserving) on Windows, diff --git a/library/std/src/process/tests.rs b/library/std/src/process/tests.rs index 67b747e410732..f46b3cfc7730e 100644 --- a/library/std/src/process/tests.rs +++ b/library/std/src/process/tests.rs @@ -211,6 +211,68 @@ fn test_wait_with_output_once() { assert_eq!(stderr, Vec::new()); } +#[test] +fn test_args_clear() { + let mut prog = Command::new("echo"); + + if cfg!(target_os = "windows") { + prog.args(&["/C", "echo reset_me"]) + } else { + prog.arg("reset_me") + }; + + prog.args_clear(); + + if cfg!(target_os = "windows") { + prog.args(&["/C", "echo hello"]); + } else { + prog.arg("hello"); + }; + + let Output { status, stdout, stderr } = prog.output().unwrap(); + let output_str = str::from_utf8(&stdout).unwrap(); + + assert!(status.success()); + assert_eq!(output_str.trim().to_string(), "hello"); + assert_eq!(stderr, Vec::new()); +} + +#[test] +fn test_arg_set() { + let mut prog = Command::new("echo"); + + if cfg!(target_os = "windows") { + prog.args(&["/C", "echo set_me"]) + } else { + prog.arg("set_me") + }; + + if cfg!(target_os = "windows") { + prog.arg_set(2, "echo hello"); + } else { + prog.arg_set(1, "hello"); + }; + + let Output { status, stdout, stderr } = prog.output().unwrap(); + let output_str = str::from_utf8(&stdout).unwrap(); + + assert!(status.success()); + assert_eq!(output_str.trim().to_string(), "hello"); + assert_eq!(stderr, Vec::new()); +} + +#[test] +#[should_panic] +fn test_arg_set_fail() { + let mut prog = Command::new("echo"); + + if cfg!(target_os = "windows") { + prog.arg_set(1, "echo hello"); + } else { + prog.arg_set(1, "hello"); + }; +} + #[cfg(all(unix, not(target_os = "android")))] pub fn env_cmd() -> Command { Command::new("env") diff --git a/library/std/src/sys/unix/process/process_common.rs b/library/std/src/sys/unix/process/process_common.rs index 7ac2f9d8af75a..62e60c26e7a17 100644 --- a/library/std/src/sys/unix/process/process_common.rs +++ b/library/std/src/sys/unix/process/process_common.rs @@ -190,6 +190,23 @@ impl Command { self.args.push(arg); } + pub fn args_clear(&mut self) { + // resize `argv` to 2 (argv[0] and NULL). + self.argv.0.truncate(2); + self.argv.0[1] = ptr::null(); + + // drop all excess args. + self.args.truncate(1); + } + + pub fn arg_set(&mut self, index: usize, arg: &OsStr) { + assert!(index >= 1, "Index must be >= 1"); + debug_assert!(index < self.args.len(), "Index out of range"); + let arg = os2c(arg, &mut self.saw_nul); + self.argv.0[index] = arg.as_ptr(); + self.args[index] = arg; + } + pub fn cwd(&mut self, dir: &OsStr) { self.cwd = Some(os2c(dir, &mut self.saw_nul)); } diff --git a/library/std/src/sys/windows/process.rs b/library/std/src/sys/windows/process.rs index 66b210ce1bfb3..bf3dad8d0086b 100644 --- a/library/std/src/sys/windows/process.rs +++ b/library/std/src/sys/windows/process.rs @@ -208,6 +208,14 @@ impl Command { pub fn arg(&mut self, arg: &OsStr) { self.args.push(Arg::Regular(arg.to_os_string())) } + pub fn args_clear(&mut self) { + self.args.truncate(1); + } + pub fn arg_set(&mut self, index: usize, arg: &OsStr) { + assert!(index >= 1, "Index must be >= 1"); + debug_assert!(index < self.args.len(), "Index out of range"); + self.args[index] = Arg::Regular(arg.to_os_string()); + } pub fn env_mut(&mut self) -> &mut CommandEnv { &mut self.env }