[][src]Crate tokio_process

An implementation of asynchronous process management for Tokio.

This crate provides a CommandExt trait to enhance the functionality of the Command type in the standard library. The three methods provided by this trait mirror the "spawning" methods in the standard library. The CommandExt trait in this crate, though, returns "future aware" types that interoperate with Tokio. The asynchronous process support is provided through signal handling on Unix and system APIs on Windows.

Examples

Here's an example program which will spawn echo hello world and then wait for it using an event loop.

extern crate futures;
extern crate tokio;
extern crate tokio_process;

use std::process::Command;

use futures::Future;
use tokio_process::CommandExt;

fn main() {
    // Use the standard library's `Command` type to build a process and
    // then execute it via the `CommandExt` trait.
    let child = Command::new("echo").arg("hello").arg("world")
                        .spawn_async();

    // Make sure our child succeeded in spawning and process the result
    let future = child.expect("failed to spawn")
        .map(|status| println!("exit status: {}", status))
        .map_err(|e| panic!("failed to wait for exit: {}", e));

    // Send the future to the tokio runtime for execution
    tokio::run(future)
}

Next, let's take a look at an example where we not only spawn echo hello world but we also capture its output.

extern crate futures;
extern crate tokio;
extern crate tokio_process;

use std::process::Command;

use futures::Future;
use tokio_process::CommandExt;

fn main() {
    // Like above, but use `output_async` which returns a future instead of
    // immediately returning the `Child`.
    let output = Command::new("echo").arg("hello").arg("world")
                        .output_async();

    let future = output.map_err(|e| panic!("failed to collect output: {}", e))
        .map(|output| {
            assert!(output.status.success());
            assert_eq!(output.stdout, b"hello world\n");
        });

    tokio::run(future);
}

We can also read input line by line.

extern crate failure;
extern crate futures;
extern crate tokio;
extern crate tokio_process;
extern crate tokio_io;

use failure::Error;
use futures::{Future, Stream};
use std::io::BufReader;
use std::process::{Command, Stdio};
use tokio_process::{Child, ChildStdout, CommandExt};

fn lines_stream(child: &mut Child) -> impl Stream<Item = String, Error = Error> + Send + 'static {
    let stdout = child.stdout().take()
        .expect("child did not have a handle to stdout");

    tokio_io::io::lines(BufReader::new(stdout))
        // Convert any io::Error into a failure::Error for better flexibility
        .map_err(|e| Error::from(e))
        // We print each line we've received here as an example of a way we can
        // do something with the data. This can be changed to map the data to
        // something else, or to consume it differently.
        .inspect(|line| println!("Line: {}", line))
}

fn main() {
    // Lazily invoke any code so it can run directly within the tokio runtime
    tokio::run(futures::lazy(|| {
        let mut cmd = Command::new("cat");

        // Specify that we want the command's standard output piped back to us.
        // By default, standard input/output/error will be inherited from the
        // current process (for example, this means that standard input will
        // come from the keyboard and standard output/error will go directly to
        // the terminal if this process is invoked from the command line).
        cmd.stdout(Stdio::piped());

        let mut child = cmd.spawn_async()
            .expect("failed to spawn command");

        let lines = lines_stream(&mut child);

        // Spawning into the tokio runtime requires that the future's Item and
        // Error are both `()`. This is because tokio doesn't know what to do
        // with any results or errors, so it requires that we've handled them!
        //
        // We can replace these sample usages of the child's exit status (or
        // an encountered error) perform some different actions if needed!
        // For example, log the error, or send a message on a channel, etc.
        let child_future = child
                .map(|status| println!("child status was: {}", status))
                .map_err(|e| panic!("error while running child: {}", e));

        // Ensure the child process can live on within the runtime, otherwise
        // the process will get killed if this handle is dropped
        tokio::spawn(child_future);

        // Return a future to tokio. This is the same as calling using
        // `tokio::spawn` above, but without having to return a dummy future
        // here.
        lines
            // Convert the stream of values into a future which will resolve
            // once the entire stream has been consumed. In this example we
            // don't need to do anything with the data within the `for_each`
            // call, but you can extend this to do something else (keep in mind
            // that the stream will not produce items until the future returned
            // from the closure resolves).
            .for_each(|_| Ok(()))
            // Similarly we "handle" any errors that arise, as required by tokio.
            .map_err(|e| panic!("error while processing lines: {}", e))
    }));
}

Caveats

While similar to the standard library, this crate's Child type differs importantly in the behavior of drop. In the standard library, a child process will continue running after the instance of std::process::Child is dropped. In this crate, however, because tokio_process::Child is a future of the child's ExitStatus, a child process is terminated if tokio_process::Child is dropped. The behavior of the standard library can be regained with the Child::forget method.

Structs

Child

Representation of a child process spawned onto an event loop.

ChildStderr

The standard error stream for spawned children.

ChildStdin

The standard input stream for spawned children.

ChildStdout

The standard output stream for spawned children.

OutputAsync

Future returned by the CommandExt::output_async method.

StatusAsync

Future returned by the CommandExt::status_async method.

WaitWithOutput

Future returned from the Child::wait_with_output method.

Traits

CommandExt

Extensions provided by this crate to the Command type in the standard library.