Skip to main content

Crate errgonomic

Crate errgonomic 

Source
Expand description

Macros for ergonomic error handling with thiserror.

§Example

#[derive(Serialize, Deserialize)]
struct Config {/* some fields */}

// bad: doesn't return the path to config (the user won't be able to fix it)
fn parse_config_v1(path: PathBuf) -> io::Result<Config> {
    let contents = read_to_string(&path)?;
    let config = from_str(&contents).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
    Ok(config)
}

// good: returns the path to config & the underlying deserialization error (the user will be able fix it)
fn parse_config_v2(path: PathBuf) -> Result<Config, ParseConfigError> {
    use ParseConfigError::*;
    let contents = handle!(read_to_string(&path), ReadToStringFailed, path);
    let config = handle!(from_str(&contents), DeserializeFailed, path, contents);
    Ok(config)
}

#[derive(Error, Debug)]
enum ParseConfigError {
    #[error("failed to read file to string: '{path}'")]
    ReadToStringFailed { path: PathBuf, source: std::io::Error },
    #[error("failed to parse the file contents into config: '{path}'")]
    DeserializeFailed { path: PathBuf, contents: String, source: serde_json::Error }
}

Advantages:

  • parse_config_v2 allows you to determine exactly what error has occurred
  • parse_config_v2 provides you with all information needed to fix the underlying issue
  • parse_config_v2 allows you to retry the call by reusing the path (avoiding unnecessary clones)

Disadvantages:

  • parse_config_v2 is longer

That means parse_config_v2 is strictly better but requires writing more code. However, with LLMs, writing more code is not an issue. Therefore, it’s better to use a more verbose approach v2, which provides you with better errors.

This crates provides the handle family of macros to simplify the error handling code.

§Better debugging

To improve your debugging experience: call exit_result in main right before return, and it will display all information necessary to understand the root cause of the error:

pub fn main() -> ExitCode {
    exit_result(run())
}

This will produce a nice “error trace” like below:

- failed to run CLI command
- failed to run i18n update command
- failed to update 2 rows
- encountered 2 errors
  * - failed to send an i18n request for row 'Foo'
    - failed to construct a JSON schema
    - input must be a JSON object
  * - failed to send an i18n request for row 'Bar'
    - failed to send a request
    - server at 239.143.73.1 did not respond

Macros§

handle
handle! is a better alternative to map_err because it doesn’t capture any variables from the environment if the result is Ok, only when the result is Err. By contrast, a closure passed to map_err always captures the variables from environment, regardless of whether the result is Ok or Err Use handle! if you need to pass owned variables to an error variant (which is returned only in case when result is Err) In addition, this macro captures the original error in the source variable, and sets it as the source key of the error variant
handle_bool
Returns an error when the condition is true.
handle_discard
handle_discard should only be used when you want to discard the source error. This is discouraged. Prefer other handle-family macros that preserve the source error.
handle_into_iter
Collects results from any IntoIterator, wrapping all errors into one variant.
handle_iter
Collects results from an iterator, returning a variant that wraps all errors.
handle_iter_of_refs
Collects results while keeping the corresponding input items, returning (outputs, items) on success.
handle_opt
See also: handle_opt_take!
handle_opt_take
This macro is an opposite of handle_opt! - it returns an error if the option contains a Some variant.
map_err
map_err should be used only when the error variant doesn’t capture any owned variables (which is very rare), or exactly at the end of the block (in the position of returned expression).

Structs§

DebugAsDisplay
A wrapper that renders Debug using the inner type’s Display implementation. This wrapper is needed for types that have an easy-to-understand Display impl but hard-to-understand Debug impl.
DisplayAsDebug
A wrapper that renders Display using the inner type’s Debug implementation.
ErrVec
An owned collection of errors
ErrorDisplayer
ItemError
Associates an error with the item that caused it.

Enums§

WriteToNamedTempFileError
Errors returned by write_to_named_temp_file.
WritelnErrorToWriterAndFileError
Errors returned by writeln_error_to_writer_and_file.

Functions§

eprintln_error
Writes an error trace to stderr and, if possible, includes a path to the full error report.
exit_iterator_of_results_print_first
Converts an impl IntoIterator<Item = Result<(), E>> into an ExitCode, printing a detailed error trace on the first failure.
exit_result
Converts a Result into an ExitCode, printing a detailed error trace on failure.
get_root_source
Returns the deepest source error in the error chain (the root cause).
write_to_named_temp_file
Writes the provided buffer to a named temporary file and persists it to disk.
writeln_error_to_formatter
Writes a human-readable error trace to the provided formatter.
writeln_error_to_writer_and_file
Writes a human-readable error trace to the provided writer and persists the full debug output to a temp file.

Type Aliases§

PathBufDisplay
A PathBuf that returns a Debug representation in Display impl.