calm_io 0.1.1

Calms a panicky I/O stream.
# Calm I/O

Writing to standard output and error streams is easy in Rust: use one of the
`print!`, `println!` (stdout), `eprint!`, or `eprintln!` (stderr) macros to
format a message and print it directly to the appropriate file descriptor.

But these macros return `()`, not an `io::Result`, even though they perform I/O.
That’s because if the write fails, they panic. Normally, this is fine: the
standard streams should basically always be there.

Here’s an example where they aren’t:

```sh
prints_more_than_ten_lines | head
```

The Unix `head` program reads ten lines (by default) from its standard input,
prints them to its standard output, and then exits. When it exits, it closes its
standard streams.

The `stdout` stream of `prints_more_than_ten_lines` is connected to a kernel
pipe, whose other end is the `stdin` stream of `head`. When `head` exits, it
closes the read end of the pipe. When the kernel processes `close()` calls for
the read ends of pipes, it sends `SIGPIPE` to the process holding the write side
of the pipe (which the C runtime `crt0` catches and terminates, but the Rust
runtime ignores), and then any future `write()` calls to the pipe are
immediately returned with `-EPIPE`.

Rust’s `std::io::Write` function correctly translates this into an `Err`, which
`println!` unwraps, beginning a panic.

The calm I/O crate does not panic in the face of closed streams: it propagates
the error, and allows the caller to gracefully unwind and exit.

This crate exposes four macros: `stdout!`, `stdoutln!`, `stderr!`, and
`stderrln!`. These behave exactly like the macros listed above, except that they
return the `io::Result` from `write!` and `writeln!` rather than unwrapping it
and potentially panicking.

In addition, this crate exports a function attribute, `pipefail`, which
suppresses only the `BrokenPipe` error. It can be attached to any function which
returns `io::Result` (but should only be attached to `main`). Functions
decorated with `#[pipefail]` have a `match` shim wrapped around their body,
which replaces both `Ok(_)` and `Err(io::ErrorKind::BrokenPipe)` with `Ok(())`,
and leaves all other errors as they were.

```rust
use calm_io::*;

#[pipefail]
fn main() -> std::io::Result<()> {
    stdoutln!("Hello stdout from Rust")?;
    stderrln!("Hello stderr from Rust")?;
}
```

As an example, consider this reimplementation of `yes | head`:

```rust
//  examples/yeah.rs
use calm_io::*;

#[pipefail]
fn main () -> std::io::Result<!> {
    let text = std::env::args().nth(1).unwrap_or("y".to_string());
    loop {
        stdoutln!("{}", text)?;
    }
}
```

Try running these commands in your shell!

```sh
$ cargo run --example good_yes | head > /dev/null
$ echo "${PIPESTATUS[@]}"
# The name is `PIPESTATUS` in bash, but `pipestatus` (lowercase!) in zsh
0 0
# yeah exits successfully, head exits successfully
$ yes | head > /dev/null
$ echo "${PIPESTATUS[@]}"
141 0
# yes crashes due to SIGPIPE, head exits successfully
$ cargo run --example bad_yes | head > /dev/null
thread 'main' panicked at 'failed printing to stdout: Broken pipe (os error 32)', src/libstd/io/stdio.rs:792:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
```

In the future, other suppression attributes may be added, or a general
suppression attribute may be created that takes a list of errors to suppress.