Expand description

A simple library for working with composable errors.

Examples

Basic usage:

use std::{
    num::{IntErrorKind, ParseIntError},
    iter::Sum,
    str::FromStr
};

use fused_error::{Accumulator, FusedResult, IteratorExt};

/// Take an iterator of textual data, adding up all of the parsed numbers.
///
/// Unlike the standard way of returning a `Result<N, N::Err>`, this doesn't
/// short-circuit, it keeps track of the current sum, and reports any
/// further diagnostics past the first failure.
fn calculate_sum<N, E, I>(iter: I) -> FusedResult<N, N::Err>
where
    N: FromStr + Sum,
    E: AsRef<str>,
    I: IntoIterator<Item = E>,
{
    // Error accumulators collect errors to defer handling them, providing
    // more holistic diagnostics:
    let mut acc = Accumulator::new();
    let sum = iter
        .into_iter()
        .map(|item| item.as_ref().parse::<N>())
        // `fused_error` adds certain methods to iterators; no more
        // disrupting iterator chains and `collect` hells for results!
        .accumulate(&mut acc)
        .sum();
    // fused results let you easily pass around error accumulators and
    // are perfect for cases where a yielded "ok" value and an error case
    // aren't mutually exclusive.
    FusedResult::new(sum, acc)
}

let result: FusedResult<i32, _> = calculate_sum(["1", "2", "3", "4"]);
assert_eq!(result.value(), &10);
assert_eq!(result.errors(), []);

let result: FusedResult<i8, _> = calculate_sum(["", "-129", "foo", "128"]);
assert_eq!(result.value(), &0);
assert_eq!(
    result
        .errors()
        .into_iter()
        .map(|err| err.kind().clone())
        .collect::<Vec<_>>(),
    [
        IntErrorKind::Empty,
        IntErrorKind::NegOverflow,
        IntErrorKind::InvalidDigit,
        IntErrorKind::PosOverflow,
    ],
);

let result: FusedResult<u8, _> = calculate_sum(["-1", "", "0", "1"]);
assert_eq!(result.value(), &1);
assert_eq!(
    result
        .errors()
        .into_iter()
        .map(|err| err.kind().clone())
        .collect::<Vec<_>>(),
    [IntErrorKind::InvalidDigit, IntErrorKind::Empty],
);

Features

So far, there is only one opt-in feature: syn. Enabling this feature implements FusedError on syn::Error, as that was one of the main motivations for creating this library.

Motivation

syn already has great composable errors that you combine with syn::Error::combine. Also, darling has a great system that was the primary inspiration for error accumulators and their drop mechanic. However, of course, darling’s accumulators are only to be used with darling errors and their accumulator API is far more limited to reflect this.

The original use case for this crate, deferring and collecting multiple errors, is primarily helpful in parsing: the more diagnostics you can provide in one pass limits the need of frequently changing something, building, fixing the one error, and trying again.

Modules

Working with remote error types using Accumulated<E>.
Error accumulation via Accumulator<E>.
Extending iterator functionality with IteratorExt.
A prelude to import the main items exported by this library:
Assigning error accumulators to working values with FusedResult<T, E>.

Structs

A thin wrapper to handle remote error types as if they implemented FusedError.
An error accumulator.
A result type that links an Accumulator<E> with a persistently “ok” value of T.

Traits

Interface for error types that can store multiple error messages within one instance.
A trait for splitting a result value into its “ok” and “error” parts.
Extends Iterator with methods for complex error handling:

Type Definitions

A FusedResult<T, E> that can short-circuit with an error, dropping the “ok” value.