user-error 1.0.4

Simple error type that display in a clear and beautiful manner to the user of a CLI application.
Documentation

UserError

UserError is an error type that helps you format and print good looking error messages for users of your CLI application. These errors are intended for consumption by a human, not your program. They are divided into 3 parts: summary, reasons and subtleties.

Summary: A String representing a one-line description of your error. A summary is mandatory and is printed boldly in red.

Reasons: A vector of Strings explaining in more detail why this error occured. Reasons are optional and if the terminal supports color, the bullet point ('-') will be colored yellow. Each reason will be printed on its own line.

Sublteties: A vector of Strings explaining additional information, including what the user can do about the error, or where to file a bug. Sublteties are optionals and if the terminal supports color, subtleties will be printed dimly. Each subtly will be printed on its own line

Coercion

std::io::Error, Err(String) and Err(&str) can all be coerced into a UserError. See the docs for examples.

Docs

The docs are really good for this crate. In addition to reading this README you can find tons of examples in the documentation generated by Cargo.

Quickstart

Add the following to your Cargo.toml:

[dependencies]
user-error = "1.0.4"

Add the following to your main.rs/lib.rs:

use user_error::UserError;

fn main() {
    let e = UserError::hardcoded("Failed to build project", 
                                    &[  "Database could not be parsed", 
                                        "File \"main.db\" not found"], 
                                    &[  "Try: touch main.db", 
                                        "This command will create and empty database file the program can use"]);
    eprintln!("{}", e);
}

This prints:

Error: Failed to build project
- Database could not be parsed
- File "main.db" not found
Try: touch main.db
This command will create and empty database file the program can use 

If the user has colors enabled on their terminal, it may look something like this: Quickstart example of user-error library for Rust

Examples

Creating Errors

If you're a flawless being like me, you are probably wondering how one makes a UserError, here are examples of the different ways:

Simple Error

Creates an error with only a summary.

use user_error::UserError;

fn main() {
    let e = UserError::simple("Failed to build project");
    eprintln!("{}", e);
}

This prints:

Error: Failed to build project

Static Error

Creates an error from hardcoded &str's

use user_error::UserError;

fn main() {
    let e = UserError::hardcoded("Failed to build project", 
                                    &[  "Database could not be parsed", 
                                        "File \"main.db\" not found"], 
                                    &[  "Try: touch main.db", 
                                        "This command will create and empty database file the program can use"]);
    eprintln!("{}", e);
}

This prints:

Error: Failed to build project
- Database could not be parsed
- File "main.db" not found
Try: touch main.db
This command will create and empty database file the program can use 

Dynamic Error

Creates an error using heap allocated structures.

use user_error::UserError;

fn main() {
    let error_summary    = String::from("Failed to build project");
    let error_reasons    = vec![String::from("Database could not be parsed"), 
                                String::from("File \"main.db\" not found")];
    let error_subtleties = vec![String::from("Try: touch main.db"), 
                                String::from("This command will create and empty database file the program can use ")];
    let e = UserError::new(error_summary, error_reasons, error_subtleties);
    eprintln!("{}", e);
}

This prints:

Error: Failed to build project
- Database could not be parsed
- File "main.db" not found
Try: touch main.db
This command will create and empty database file the program can use 

From Other Errors

Certain types of errors will be automagically coerced into a UserError - and will preserve a pointer to the original error (should you need it) Creates an error using heap allocated structures.

use std::fs::File;
use user_error::UserError;

// A really redundant way to open a file
fn open_file(path: &str) -> Result<File, UserError> {
    let f = File::open(path)?; // std::io::Error is coerced into a UserError here
    Ok(f)
}

fn main() {
    match open_file("does_not_exist.txt") {
        Err(e) => eprintln!("{}", e),
        Ok(_) => ()
    }
}

This prints:

Error: No such file or directory (os error 2)

Convenience Methods

These methods could save you some boilerplate in the same way unwrap() does.

Print

If for some reason you don't want to follow the format!() convention, you can call print() on a UserError and it will pretty print itself to stderr

use user_error::UserError;

fn main() {
    let e = UserError::simple("Critical Failure!");
    e.print();
}

This prints:

Error: Critical Failure!

Print and Exit

Since this error is likely the last thing your program will run you can use this shortcut to print the error and exit the process in an immediate, albeit ungraceful manner. Returns error code 1 to the OS.

use user_error::UserError;

fn main() {
    let e = UserError::simple("Critical Failure!");
    e.print_and_exit();
    eprintln("I am never printed!");
}

This prints:

Error: Critical Failure!

Modifying the Error

There are a few methods for modifying the UserError after it has been made:

  • clear_reasons(): empties the reasons array
  • clear_subtleties(): empties the subtleties array
  • add_reason(): add another reason to the reasons array
  • add_subtly(): add another subtly to the subtleties array

Additional information can be found in the documentation.

Future Work

  • Adding more types that a UserError can be coerced from
  • Figuring out how to implement source() for an arbitrary type that implements Error
  • Printing out a link to the issue tracker