Skip to content

fused_error

A simple library for working with composable errors.

Build StatusLatest Version

Documentation

Module documentation with examples.

Usage

Add this to your Cargo.toml:

[dependencies]
fused_error = "0.1"

Here's a simple example to demonstrate the power of composable errors:

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.fncalculate_sum<N,E,I>(iter:I) -> FusedResult<N,N::Err>whereN:FromStr + Sum,E:AsRef<str>,I:IntoIterator<Item = E>,{// Error accumulators collect errors to defer handling them, providing// more holistic diagnostics:letmut 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],);

Or, when using the syn feature for increased interoperability, here's an example of fused_error assisting in procedural macros:

use fused_error::{Accumulator,FusedError};use proc_macro::TokenStream;use proc_macro2::TokenStreamasTokenStream2;use syn::{AttributeArgs,DeriveInput,ItemFn};#[proc_macro_attribute]pubfnmy_attribute(args:TokenStream,input:TokenStream) -> TokenStream{fninner(args:TokenStream,input:TokenStream) -> syn::Result<TokenStream2>{letmut acc = Accumulator::<syn::Error>::new();// fn(TokenStream) -> syn::Result<AttributeArgs>let args = acc.handle(parse_args(args));let item = acc.handle(syn::parse::<ItemFn>(input));// Throw all of the collected parse errors, or continue:
acc = acc.checkpoint()?;// SAFETY: any `None` value would short-circuit at the last// checkpoint.letmut args = unsafe{ args.unwrap_unchecked()};letmut item = unsafe{ item.unwrap_unchecked()};// fn(&AttributeArgs) -> syn::Result<()>
acc.handle(validate_args(&args));// fn(&ItemFn) -> syn::Result<()>
acc.handle(validate_item(&item));// Throw all of the validation parse errors, or continue:
acc = acc.checkpoint()?;// Do multiple steps that can short-circuit:let tokens = acc.handle_in(|| {// fn(&mut AttributeArgs, &mut ItemFn) -> syn::Result<()>prepare(&mut args,&mut item)?;// fn(AttributeArgs, ItemFn) -> syn::Result<TokenStream2>expand(args, item)});// If this closure is called, we know `tokens` is `Some`:
acc.err_or_else(|| unsafe{ tokens.unwrap_unchecked()})}inner(args, input).unwrap_or_else(syn::Error::into_compile_error).into()}

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

About

A simple library for working with composable errors.

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages