Skip to main content

Crate errortools

Crate errortools 

Source
Expand description

§errortools

crates doc

Tired of writing this in every project?

fn main() {
    if let Err(e) = run() {
        eprintln!("error: {e}");
        std::process::exit(1);
    }
}

fn run() -> Result<(), MyError> { todo!() }

Because returning Result from main uses Debug, which gives you this:

Error: Outer(Inner(Io(Os { code: 2, kind: NotFound, message: "No such file or directory" })))

We have a solution: MainResult.

§Example

use errortools::MainResult;
use std::{fs, io};

#[derive(Debug, thiserror::Error)]
enum Error {
    #[error("failed to load config")]
    Config(#[source] io::Error),
}

fn main() -> MainResult<Error> {
    fs::read_to_string("missing.toml").map_err(Error::Config)?;
    Ok(())
}

Output:

Error: failed to load config: No such file or directory (os error 2)

The error and its full source chain are joined with ": " — no boilerplate, no run() wrapper, no manual loop.

§Tree format

Prefer a multi-line view? Swap the format strategy:

use errortools::{MainResult, Tree};
use std::{fs, io};

#[derive(Debug, thiserror::Error)]
enum AppError {
    #[error("failed to load config")]
    Config(#[source] io::Error),
}

fn main() -> MainResult<AppError, Tree> {
    let _ = fs::read_to_string("missing.toml").map_err(AppError::Config)?;
    Ok(())
}
Error: failed to load config
└── No such file or directory (os error 2)

§Logging in place

Sometimes you cannot return and need to log the full source chain right where the error happens. The FormatError extension trait works on any error:

use errortools::FormatError;

if let Err(e) = do_thing() {
    tracing::error!("do_thing failed: {}", e.one_line());
    // do_thing failed: outer: middle: inner
}

For ad-hoc strategies, pick the format inline with formatted::<F>():

use errortools::{FormatError, Tree};

if let Err(e) = do_thing() {
    eprintln!("{}", e.formatted::<Tree>());
    // outer
    // └── middle
    //     └── inner
}

§Custom formats

Implement the Format trait on a unit type:

use core::{error::Error, fmt};
use errortools::{Format, FormatError, chain};
use itertools::Itertools;

struct Arrow;
impl Format for Arrow {
    fn fmt(error: &dyn Error, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", chain(error).format(" -> "))
    }
}

println!("{}", my_error.formatted::<Arrow>()); // outer -> middle -> inner

§How it works

MainResult<E, F> is a type alias:

use errortools::{DisplaySwapDebug, Formatted, OneLine};

pub type MainResult<E, F = OneLine> = Result<(), DisplaySwapDebug<Formatted<E, F>>>;

DisplaySwapDebug swaps the Debug and Display impls of its inner type, so when main prints the error via Debug, you actually get its Display output — formatted by the chosen strategy. ? converts your error automatically via the blanket From impl.

§Examples

Runnable examples in examples/:

ExampleWhat it shows
one_lineMainResult with default OneLine format
treeMainResult<E, Tree> for indented multi-line output
format_errorFormatError trait for ad-hoc formatting
custom_formatA custom Format strategy
transparent#[error(transparent)] pass-through with #[from]

Run with: cargo run --example <name>.

§Features

FeatureDefaultEffect
stdyesEnables itertools/use_std. Disable for no_std.

Structs§

DisplaySwapDebug
Wrapper that swaps an inner type’s fmt::Debug and fmt::Display impls.
Formatted
An error wrapper that uses a static Format strategy for fmt::Display.
OneLine
One-line format. Joins the error and its sources with ": ".
Tree
Tree format with a configurable marker and indent.
TreeIndent
Default tree indent: four spaces.
TreeMarker
Default tree branch marker: "└── ".

Traits§

Format
A static strategy for formatting an error and its source chain.
FormatError
A helper trait to format errors.

Functions§

chain
Iterator over an error and its source chain.

Type Aliases§

FormatOneLine
One-line error wrapper. Alias for Formatted<E, OneLine>.
MainResult
A result type that wraps an error with Formatted and DisplaySwapDebug to output from the main function.