Skip to content

Instantly share code, notes, and snippets.

@aturon
Created April 30, 2014 00:03
Show Gist options
  • Save aturon/1a17763ab8d8aa3f1124 to your computer and use it in GitHub Desktop.
Save aturon/1a17763ab8d8aa3f1124 to your computer and use it in GitHub Desktop.
draft ProcessBuilder implementation
/// This process builder type provides fine-grained control over how a new
/// process should be spawned. A default configuration can be generated using
/// `ProcessBuilder::new(program)`, where `program` gives a path to the program
/// to be executed. Additional builder methods allow the configuration to be
/// changed (for example, by adding arguments) prior to spawning:
///
/// ```
/// use std:io::ProcessBuilder;
///
/// let mut process = match ProcessBuilder.new("sh").arg("-c").arg("echo hello").spawn() {
/// Ok(p) => p
/// Err(e) => fail!("failed to execute process: {}", e),
/// };
///
/// let output = process.stdout.get_mut_ref().read_to_end();
/// ```
pub struct ProcessBuilder {
// The meaning of these configuration options is documented by the
// constructor and builder methods below.
program: CString,
args: Vec<CString>,
inherit_env: bool,
env: Vec<(CString, CString)>,
cwd: Option<Path>,
stdin: StdioContainer,
stdout: StdioContainer,
stderr: StdioContainer,
extra_io: Vec<StdioContainer>,
uid: Option<uint>,
gid: Option<uint>,
detach: bool,
}
impl ProcessBuilder {
/// Constructs a new `ProcessBuilder` for launching the program at
/// path `program`, with the following default configuration:
///
/// * No arguments to the program
/// * Inherit the current process's environment
/// * Inherit the current process's working directory
/// * A readable pipe for stdin (file descriptor 0)
/// * A writeable pipe for stdour and stderr (file descriptors 1 and 2)
///
/// Builder methods are provided to change these defaults and
/// otherwise configure the process.
fn new<T:ToCStr>(program: T) -> ProcessBuilder {
ProcessBuilder {
program: program,
args: Vec::new(),
inherit_env: true,
env: Vec::new(),
cwd: None,
stdin: CreatePipe(true, false),
stdout: CreatePipe(false, true),
stderr: CreatePipe(false, true),
extra_io: Vec::new(),
uid: None,
gid: None,
detach: false,
}
}
/// Add an argument to pass to the program
fn arg<T:ToCStr>(mut self, arg: &T) -> ProcessBuilder {
self.args.push(arg.to_c_str());
self
}
/// Determines whether the current process's environment is inherited by the
/// child process
fn inherit_env(mut self, inherit: bool) -> ProcessBuilder {
self.inheric_env = inherit;
self
}
/// Add an environment variable to the child process
fn env<T:ToCStr>(mut self, name: &T, val: &T) -> ProcessBuilder {
self.env.push((name.to_c_str(), val.to_c_str()));
self
}
/// Set the working directory for the child process
fn cwd(mut self, dir: Path) -> ProcessBuilder {
self.cwd = Some(dir);
self
}
/// Configuration for the child process's stdin handle (file descriptor 0).
/// Defaults to `CreatePipe(true, false)` so the input can be written to.
fn stdin(mut self, cfg: StdioContainer) -> ProcessBuilder {
self.stdin = cfg;
self
}
/// Configuration for the child process's stdout handle (file descriptor 1).
/// Defaults to `CreatePipe(false, true)` so the output can be collected.
fn stdout(mut self, cfg: StdioContainer) -> ProcessBuilder {
self.stdout = cfg;
self
}
/// Configuration for the child process's stderr handle (file descriptor 2).
/// Defaults to `CreatePipe(false, true)` so the output can be collected.
fn stderr(mut self, cfg: StdioContainer) -> ProcessBuilder {
self.stderr = cfg;
self
}
/// Attaches a stream/file descriptor/pipe to the child process. Inherited
/// file descriptors are numbered consecutively, starting at 3; the first
/// three file descriptors (stdin/stdout/stderr) are configured with the
/// `stdin`, `stdout`, and `stderr` methods.
fn extra_io(mut self, cfg: StdioContainer) -> ProcessBuilder {
self.extra_io.push(cfg);
self
}
/// Sets the child process's user id. This translates to a `setuid` call in
/// the child process. Setting this value on windows will cause the spawn to
/// fail. Failure in the `setuid` call on unix will also cause the spawn to
/// fail.
fn uid(mut self, id: uint) -> ProcessBuilder {
self.uid = Some(id);
self
}
/// Similar to `uid`, but sets the group id of the child process. This has
/// the same semantics as the `uid` field.
fn gid(mut self, id: uint) -> ProcessBuilder {
self.gid = Some(id);
self
}
/// Sets the child process to be spawned in a detached state. On unix, this
/// means that the child is the leader of a new process group.
fn detached(mut self) -> ProcessBuilder {
self.detach = true;
self
}
/// Actually spawn the process, while consuming the builder.
fn spawn(self) -> IOResult<Process> {
let mut config = Some(self);
LocalIo::maybe_raise(|io| {
io.spawn(config.take_unwrap()).map(|(p, io)| {
let mut io = io.move_iter().map(|p| {
p.map(|p| io::PipeStream::new(p))
});
Process {
handle: p,
stdin: io.next().unwrap(),
stdout: io.next().unwrap(),
stderr: io.next().unwrap(),
extra_io: io.collect(),
}
})
})
}
// todo: add output and status
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment