Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mutate command args #87420

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions library/std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
69 changes: 69 additions & 0 deletions library/std/src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<S: AsRef<OsStr>>(&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,
Expand Down
62 changes: 62 additions & 0 deletions library/std/src/process/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
17 changes: 17 additions & 0 deletions library/std/src/sys/unix/process/process_common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
Expand Down
8 changes: 8 additions & 0 deletions library/std/src/sys/windows/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down