proxit — Unix-style error messages in Rust

proxit is a super simple Rust library that allows you to have consistent, UNIX-style error messages with a little bit less boilerplate by implementing the Termination trait for you.

Why?

All UNIX commandline utilities have displayed their errors in more-or-less the exact same way since the beginning of time:

<utility name>: <error message>

and sometimes:

Usage: <utility name> <utility arguments>
# or
usage: <utility name> <utility arguments>

For example:

$ grep -fakeflags
grep: akeflags: No such file or directory

$ mandoc
bash: mandoc: command not found

$ doas
usage: doas [-Lns] [-C config] [-u user] command [args]

So how did the genius Rust team decide to do it? Let’s check the code:

rust impl<T: Termination, E: fmt::Debug> Termination for Result<T, E> { fn report(self) -> ExitCode { match self { Ok(val) => val.report(), Err(err) => { io::attempt_print_to_stderr(format_args_nl!("Error: {err:?}")); ExitCode::FAILURE } } } }

Wait… did I see that right?

rust … format_args_nl!("Error: {err:?}"));

Bruh.

How Proxit Works

Using proxit is super simple:

```rust use proxit::MainResult;

fn main() -> MainResult { work().into(); }

fn work() -> Result<(), Error> { /* … */ } ```

A MainResult is just a wrapper around a Result<T, E> where T implements Termination, and E implements Display. In practice, T will basically always be () which does implement Termination.

When the main function returns, if the Result within the MainResult is an Ok variant, then the .report() method of T is invoked. Otherwise, E is printed out to standard error (this is why you need to implement Display) prefixed by the string "{argv0}: " where argv0 is the program name.

What if you want to print a usage string though? Well… because simpler and simpler is always better, just call eprintln!() and process::exit(). Printing the usage string yourself also gives you the flexibility to decide between GNU- and FreeBSD-style usage strings ("Usage: {s}") and OpenBSD-style usage strings ("usage: {s}").

```rust use std::process;

use proxit::{self, MainResult, Usage};

fn main() -> MainResult { work.into(); }

fn work() -> Result<(), Error> { if usagewrong() { eprintln!("Usage: [-abc] requiredarg"); process::exit(1); } } ```

Additional Note

When a process using this library is spawned, the parent process calling exec() could potentially not provide the program name in argv[0]. In this case, we default to using the Rust style of "Error: {s}".