async-err

Crates.ioasync-err
lib.rsasync-err
version0.1.1
created_at2025-08-15 21:34:15.410588+00
updated_at2025-08-15 21:41:58.753843+00
descriptionContextual asynchronous error handling.
homepage
repositoryhttps://github.com/ParkBlake/async-err
max_upload_size
id1797563
size37,857
b!k (ParkBlake)

documentation

https://docs.rs/async-err

README

async-err

Contextual async error handling for Rust.

Overview

async-err provides utilities for adding rich, contextual information to asynchronous errors in Rust. It offers:

  • A wrapper error type AsyncError that attaches lazy, user-defined context strings to any error.
  • Extension traits for async futures that allow chaining and adding context easily.
  • A hook system to log or react to async errors globally.
  • Optional timestamps in hook logs with chrono integration.

Features

  • Error Wrapping: Wrap errors with extra context captured lazily.
  • Async Extensions: .with_context() and .and_then_async() combinators for ergonomic async error flows.
  • Hooks: Register global hooks to log or process errors when they occur.
  • Configurable Timestamping: Optional timestamps with chrono feature support.

Usage

Add async-err to your dependencies:

async-err = { version = "0.1", features = ["hooks", "chrono"] }
  • The hooks feature enables the global hooks system for async error logging and processing.
  • The chrono feature adds timestamp support in hooks output via the chrono crate.

Example usage in async code:

#[cfg(feature = "hooks")]
use async_err::hooks::{enable_hook_timestamps, register_hook, AsyncErrorHookDefault};
use async_err::prelude::*;
use std::{io, sync::Arc};

struct LoggingHook;

#[cfg(feature = "hooks")]
impl AsyncErrorHook<io::Error> for LoggingHook {
    fn on_error(&self, error: &AsyncError<io::Error>) {
        <Self as AsyncErrorHookDefault<io::Error>>::on_error(self, error);
    }
}

async fn step1(val: i32) -> Result<i32, io::Error> {
    Ok(val + 1)
}

async fn step2(val: i32) -> Result<i32, io::Error> {
    if val % 2 == 0 {
        Ok(val * 2)
    } else {
        Err(io::Error::other("Odd value at step2"))
    }
}

async fn step3(val: i32) -> Result<i32, io::Error> {
    if val < 10 {
        Ok(val + 5)
    } else {
        Err(io::Error::other("Value too large at step3"))
    }
}

#[tokio::main]
async fn main() -> Result<(), AsyncError<io::Error>> {
    #[cfg(feature = "hooks")]
    {
        register_hook::<io::Error>(Arc::new(LoggingHook));
        enable_hook_timestamps();
    }

    let result = step1(2)
        .with_context(|_| "Failed at step1".to_string())
        .and_then_async(|v| step2(v).with_context(|_| "Failed at step2".to_string()))
        .and_then_async(|v| step3(v).with_context(|_| "Failed at step3".to_string()))
        .await;

    match &result {
        Ok(val) => println!("Success! Result: {}", val),
        Err(e) => {
            eprintln!("Error occurred:");
            if let Some(ctx) = e.context() {
                eprintln!("  Context: {}", ctx);
            }
            eprintln!("  Error: {}", e.inner_error());
        }
    }

    result.map(|_| ())
}
Commit count: 0

cargo fmt