| Crates.io | proxit |
| lib.rs | proxit |
| version | 1.0.1 |
| created_at | 2023-08-01 20:45:19.916245+00 |
| updated_at | 2023-08-01 21:36:19.6123+00 |
| description | UNIX-style error messages in Rust |
| homepage | |
| repository | https://git.thomasvoss.com/proxit |
| max_upload_size | |
| id | 932200 |
| size | 6,041 |
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.
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:
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?
… format_args_nl!("Error: {err:?}"));
Bruh.
Using proxit is super simple:
use proxit::MainResult;
fn main() -> MainResult<(), Error> {
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}").
use std::process;
use proxit::{self, MainResult, Usage};
fn main() -> MainResult<(), Error> {
work.into();
}
fn work() -> Result<(), Error> {
if usage_wrong() {
eprintln!("Usage: [-abc] required_arg");
process::exit(1);
}
}
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}".