Skip to main content

Crate resext

Crate resext 

Source
Expand description

Context-rich error handling for Rust with zero-cost abstractions and zero allocations

This is the primary interface for ResExt. It re-exports the proc-macro as well as other helpers provided by ResExt.

§Quick Start

use resext::resext;

#[resext]
enum ConfigError {
    Io(std::io::Error),
    Utf8(std::string::FromUtf8Error),
}

fn load_config(path: &str) -> Res<String> {
    let content = std::fs::read(path)
        .context("Failed to read config")?;
     
    std::string::String::from_utf8(content)
        .context("Failed to parse config")
}

§Proc Macro

The proc macro provides clean syntax with full customization:

use resext::resext;

#[resext(
    prefix = "ERROR: ",
    delimiter = " -> ",
    include_variant = true,
)]
enum MyError {
    Io(std::io::Error),
    Fmt { error: std::fmt::Error },
}

§Attribute Options

  • prefix - String prepended to entire error message
  • suffix - String appended to entire error message
  • msg_prefix - String prepended to each context message
  • msg_suffix - String appended to each context message
  • delimiter - Separator between context messages (default: “ - “)
  • source_prefix - String prepended to source error (default: “Error: “)
  • include_variant - Include variant name in Display output (default: false)
  • alias - Custom type alias name which is used for getting the names for other items generated by the proc-macro (default: Res)
  • buf_size - Size for the context message byte buffer (default: 64)
  • alloc Enable heap-spilling if context exceeds buf_size

§.context() Method

Add static context to an error.

Accepts &str or ctx!() macro which outputs a lazily evaluated closure with usage similar to old format_args!() API

§Example

std::fs::read("file.txt")
    .context("Failed to read file")?;

§Error Display Format

Errors are displayed with context chains:

Failed to load application
 - Failed to read config file
 - Failed to open file
Error: No such file or directory

With include_variant = true:

Failed to load application
 - Failed to read config file
Error: Io: No such file or directory

§Examples

§Error Handling

use resext::ctx;
use resext::resext;

use std::io::{Error, ErrorKind};

#[resext]
enum AppError {
    Io(Error),
    Parse { error: std::num::ParseIntError },
}

fn read_config(path: &str) -> Res<String> {
    let content: String = std::fs::read_to_string(path)
        .context(ctx!("Failed to read file: {}", path))?;

    if content.is_empty() {
        return Err(ResErr::new(
            "Content is is empty",
            Error::new(ErrorKind::UnexpectedEof, "Data is empty"),
        ));
    }

    let value = content
        .trim()
        .parse::<i32>()
        .context(ctx!("Failed to parse config value: {}", &content))?;

    if value < 32 {
        return Err(ResErr::from_args(
            ctx!("Value: {} is less than 32", value),
            Error::new(ErrorKind::InvalidData, "Data is less than 32"),
        ));
    }

    Ok(content)
}

§Multiple Error Types

Note: This example is not tested as it’s an example of errors from external crates

use resext::resext;
use resext::ctx;

#[resext(alias = ApiResult)]
enum ApiError {
    Network(reqwest::Error),
    Database(sqlx::Error),
    Json(serde_json::Error),
}

async fn fetch_user(id: u64) -> ApiResult<User> {
    let response = reqwest::get(format!("/users/{}", id))
        .await
        .context(ctx!("Failed to fetch user: {}", id))?;
     
    let user = response.json()
        .await
        .context("Failed to parse user data")?;
     
    Ok(user)
}

Macros§

ctx
Creates a lazily-evaluated context message for use with .context().

Attribute Macros§

resext
Generate error handling boilerplate for an enum.