# rich-error

This crate exposes a single derive, `#[derive(RichError)]`, which turns an `enum` error type into a location-aware outer
error wrapper.

## Why RichError?

By default `RichError` generates an `Error` wrapper that stores both the inner enum and
the `std::panic::Location` where the error was constructed. This keeps log
lines actionable without needing to thread location metadata yourself:

```
database error: connection refused (at crates/my-crate/src/db.rs:42)
```

## Installation

Add the crate as a procedural-macro dependency. The crate follows the rest of
the hummingbird bridge workspace, so you normally depend on it via the
workspace `Cargo.toml`, but for illustration:

```toml
[dependencies]
rich-error = { path = "crates/rich-error" }
thiserror = "1"
```

## Usage

Derive `RichError` on an enum that already implements `std::error::Error`
(e.g. via `thiserror::Error`). Each enum lives inside its module; the derive
will place the generated `Error` wrapper in the same module.

```rust
use thiserror::Error;
use rich_error::RichError;

#[derive(Debug, Error, RichError)]
pub enum DbInner {
    #[error("database error: {0}")]
    Database(#[from] sqlx::Error),

    #[error("invalid transition from {kind}")]
    InvalidTransition { kind: StatusKind },

    #[error("simple unit error")]
    SimpleUnit,
}
```

Now you can return `Result<T, Error>` from the module and construct errors with
location-aware helpers:

```rust
pub type Result<T> = std::result::Result<T, Error>;

fn do_work() -> Result<()> {
    some_inner_operation()?;               // From<DbInner> for Error
    Err(Error::invalid_transition(kind))?; // struct variant ctor
}
```

### Custom outer type names

If you prefer a module-specific name for the generated wrapper, add the
`#[rich_error(outer = YourType)]` helper attribute alongside the derive:

```rust
#[derive(Debug, Error, RichError)]
#[rich_error(outer = BridgeError)]
pub enum BridgeInner {
    #[error("bridge error: {0}")]
    Tuple(sqlx::Error),
}

pub type Result<T> = std::result::Result<T, BridgeError>;

fn do_work() -> Result<()> {
    Err(BridgeError::tuple(sqlx::Error::Protocol("oops".into())))?
}
```

## Generated API (per inner enum)

`RichError` emits the following items next to the enum:

- `#[derive(Debug)] pub struct Error` (or your configured outer type) wrapping the inner enum plus a
  `&'static Location`.
- `impl Display` that prints `<inner display> (at file:line)`.
- `impl std::error::Error` delegating `source()` to the inner enum.
- `impl Error { #[track_caller] pub fn new(inner: Inner) -> Self }`.
- Per-variant constructors (`fn database(...)`, `fn invalid_transition(...)`, …),
  each marked `#[track_caller]` so they record the caller’s location.
- `impl From<Inner> for Error` so `?` converts automatically.
- For struct or tuple variants with exactly one field,
  `impl From<FieldTy> for Error`, meaning external error types can be `?`-propagated
  straight into the outer error.

## Requirements and Caveats

- Only enums are supported; deriving on structs will emit a compile error.
- The outer wrapper defaults to `Error` and is generated in the enum’s module, but you can override it
  with `#[rich_error(outer = MyError)]`.
- Because the constructors are plain functions, passing them by name into
  combinators like `Result::map_err(Error::database)` records the standard
  library callsite. Prefer `?` or closures (e.g. `map_err(|e| Error::database(e))`).

## Development

`cargo test` is sufficient: the crate contains only compile-time code and
relies on doc-tests for examples.
