[][src]Crate io_mux

A Mux provides a single receive end and multiple send ends. Data sent to any of the send ends comes out the receive end, in order, tagged by the sender.

Each send end works as a file descriptor. For instance, with io-mux you can collect stdout and stderr from a process, and highlight any error output from stderr, while preserving the relative order of data across both stdout and stderr.

Example

let mut mux = io_mux::Mux::new()?;

let mut child = std::process::Command::new("sh")
    .arg("-c")
    .arg("echo out1 && echo err1 1>&2 && echo out2")
    .stdout(mux.make_untagged_sender()?)
    .stderr(mux.make_tagged_sender("e")?)
    .spawn()?;

let mut done_sender = mux.make_tagged_sender("d")?;
std::thread::spawn(move || match child.wait() {
    Ok(status) if status.success() => {
        let _ = write!(done_sender, "Done\n");
    }
    Ok(status) => {
        let _ = write!(done_sender, "Child process failed\n");
    }
    Err(e) => {
        let _ = write!(done_sender, "Error: {:?}\n", e);
    }
});

loop {
    let tagged_data = mux.read()?;
    if let Some(tag) = tagged_data.tag {
        print!("{}: ", tag);
        if tag == "d" {
            break;
        }
    }
    std::io::stdout().write_all(tagged_data.data)?;
}

Internals

Internally, Mux creates a UNIX datagram socket for the receive end, and a separate UNIX datagram socket for each sender. Datagram sockets support recvfrom, which provides the address of the sender, so Mux::read can use the sender address as the tag for the packet received.

However, datagram sockets require reading an entire datagram with each recvfrom call, so Mux::read needs to find out the size of the next datagram before calling recvfrom. Finding the next datagram size requires an OS-specific mechanism, currently only implemented on Linux systems.

Mux creates UNIX sockets within a temporary directory, removed when dropping the Mux.

Note that Mux::read cannot provide any indication of end-of-file. When using Mux, you will need to have some other indication that no further output will arrive, such as the exit of the child process producing output.

Structs

Mux

A Mux provides a single receive end and multiple send ends. Data sent to any of the send ends comes out the receive end, in order, tagged by the sender.

MuxSender

A send end of a Mux. You can convert a MuxSender to a std::process::Stdio for use with a child process, obtain the underlying file descriptor using IntoRawFd, or send data using std::io::Write.

TaggedData

Data received through a mux, along with the tag if any.