backerror-macros 0.1.1

Java style backtrace for Rust
Documentation
# backerror

`backerror` is a Rust library that enhances error handling by automatically capturing location information and stack traces for errors. It provides a seamless integration with [thiserror](https://github.com/dtolnay/thiserror) to improve debugging capabilities by tracking where errors originate in your code.
When an error is thrown, error chain and stack trace are captured and displayed in a user-friendly format.

## Features

* **Automatic Location Tracking**: Automatically captures the source location (file, line, column) where an error originates
* **Stack Trace Capture**: Optionally captures full stack traces for better debugging
* **Seamless Integration**: Works with existing `thiserror`-based error types
* **Zero-cost Abstraction**: Only incurs runtime cost when enabled (can be disabled in release builds)
* **Transparent Wrapping**: Preserves the original error type while adding location metadata
* **Conditional Compilation**: Can be disabled in release builds to minimize overhead

## Installation

Execute the following command to add `backerror` to your project:

```bash
cargo add backerror
```

Or add this to your `Cargo.toml`:

```toml
[dependencies]
backerror = "0.1"
thiserror = "2.0"
```

## Usage

### Example Codes

```rust
    use backerror::backerror;
    use thiserror::Error;

    #[backerror]
    #[derive(Debug, Error)]
    #[error(transparent)]
    pub struct MyError1(#[from] std::io::Error);

    #[backerror]
    #[derive(Debug, Error)]
    pub enum MyError2 {
        #[error("By MyError2: {0}")]
        MyError1(#[from] MyError1),
    }

    #[backerror]
    #[derive(Debug, Error)]
    pub enum MyError3 {
        #[error("By MyError3: {0}")]
        MyError2(#[from] MyError2),
    }

    fn throw_error1() -> Result<(), MyError1> {
        std::fs::File::open("blurb.txt")?;
        Ok(())
    }

    fn throw_error2() -> Result<(), MyError2> {
        Ok(throw_error1()?)
    }
    fn throw_error3() -> Result<(), MyError3> {
        Ok(throw_error2()?)
    }

    #[test]
    fn test_display() {
        if let Err(err) = throw_error3() {
            println!("{}", err);
        }
    }

    #[test]
    fn test_debug() {
        if let Err(e) = throw_error3() {
            println!("{:?}", e);
        }
    }
```

### Example Output

#### Display Output(`to_string`)

```text
By MyError3: By MyError2: The system cannot find the file specified. (os error 2); Caused by example::tests::MyError2 (backerror\tests\example.rs:36:12); Caused by example::tests::MyError1 (backerror\tests\example.rs:33:12); Caused by std::io::error::Error(backerror\tests\example.rs:28:9);
```

#### Debug Output

```text
MyError2(MyError1(MyError1(Os { code: 2, kind: NotFound, message: "The system cannot find the file specified." } (backerror\tests\example.rs:27:9)
Caused by: example::tests::MyError2: By MyError2: The system cannot find the file specified. (os error 2)
        at example::tests::impl$10::from (.\tests\example.rs:19)
        at core::result::impl$28::from_residual<tuple$<>,enum2$<example::tests::MyError2>,enum2$<example::tests::MyError3> > (C:\Users\admin\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\result.rs:2177)
Caused by: example::tests::MyError1: The system cannot find the file specified. (os error 2)
        at example::tests::impl$5::from (.\tests\example.rs:12)
        at core::result::impl$28::from_residual<tuple$<>,example::tests::MyError1,enum2$<example::tests::MyError2> > (C:\Users\admin\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\result.rs:2177)
Caused by: std::io::error::Error: The system cannot find the file specified. (os error 2)
        at example::tests::impl$0::from (.\tests\example.rs:7)
        at core::result::impl$28::from_residual<tuple$<>,std::io::error::Error,example::tests::MyError1> (C:\Users\admin\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\result.rs:2177)
        at example::tests::throw_error1 (.\tests\example.rs:27)
        at example::tests::throw_error2 (.\tests\example.rs:32)
        at example::tests::throw_error3 (.\tests\example.rs:35)
        at example::tests::test_debug (.\tests\example.rs:47)
        at example::tests::test_debug::closure$0 (.\tests\example.rs:46)
        at core::ops::function::FnOnce::call_once<example::tests::test_debug::closure_env$0,tuple$<> > (C:\Users\admin\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\ops\function.rs:250)
        at core::ops::function::FnOnce::call_once (/rustc/f8297e351a40c1439a467bbbb6879088047f50b3/library\core\src\ops\function.rs:250)
        at test::__rust_begin_short_backtrace<enum2$<core::result::Result<tuple$<>,alloc::string::String> >,enum2$<core::result::Result<tuple$<>,alloc::string::String> > (*)()> (/rustc/f8297e351a40c1439a467bbbb6879088047f50b3/library\test\src\lib.rs:663)
        at test::run_test_in_process (/rustc/f8297e351a40c1439a467bbbb6879088047f50b3/library\test\src\lib.rs:686)
        at test::run_test::closure$0 (/rustc/f8297e351a40c1439a467bbbb6879088047f50b3/library\test\src\lib.rs:607)
        at test::run_test::closure$1 (/rustc/f8297e351a40c1439a467bbbb6879088047f50b3/library\test\src\lib.rs:637)
        at std::sys::backtrace::__rust_begin_short_backtrace<test::run_test::closure_env$1,tuple$<> > (/rustc/f8297e351a40c1439a467bbbb6879088047f50b3/library\std\src\sys\backtrace.rs:158)
        at core::ops::function::FnOnce::call_once<std::thread::impl$0::spawn_unchecked_::closure_env$1<test::run_test::closure_env$1,tuple$<> >,tuple$<> > (/rustc/f8297e351a40c1439a467bbbb6879088047f50b3/library\core\src\ops\function.rs:250)
        at alloc::boxed::impl$29::call_once (/rustc/f8297e351a40c1439a467bbbb6879088047f50b3/library\alloc\src\boxed.rs:1985)
        at alloc::boxed::impl$29::call_once (/rustc/f8297e351a40c1439a467bbbb6879088047f50b3/library\alloc\src\boxed.rs:1985)
        at std::sys::thread::windows::impl$0::new::thread_start (/rustc/f8297e351a40c1439a467bbbb6879088047f50b3/library\std\src\sys\thread\windows.rs:60)
        at BaseThreadInitThunk
        at RtlUserThreadStart
) (backerror\tests\example.rs:32:12)) (backerror\tests\example.rs:35:12))
```

## Rust Features

The crate provides several optional features:

* `backtrace`: Enables backtrace capture only when `RUST_BACKTRACE` environment variable is set
* `force_backtrace`: Forces backtrace capture regardless of environment variables (enabled by default)
* `release_off`: Disables the backerror transformation in release builds (enabled by default)

To customize features:

```toml
[dependencies]
backerror = { version = "...", default-features = false, features = ["force_backtrace"] }
```

## How It Works

The `backerror` crate works by:

1. Providing a `#[backerror]` attribute macro that transforms your error types
2. Converting `#[from] T` attributes to `#[from] LocatedError<T>`
3. Using Rust's `#[track_caller]` attribute to capture the location where errors are converted
4. Optionally capturing a full backtrace when the error is created

When an error occurs, it gets wrapped in a `LocatedError<T>` struct that preserves the original error while adding location metadata.

## License

Apache License, Version 2.0 [LICENSE-APACHE](http://www.apache.org/licenses/LICENSE-2.0)