nestum 0.2.4

Proc-macro for nested enum paths like Enum1::Variant1::VariantA
Documentation
<div align="center">
  <img alt="nestum logo" src="https://raw.githubusercontent.com/eboody/nestum/main/nestum.png" width="360">
  <p>Nestum lets nested enums read like nested paths in Rust.</p>
  <p>
    <a href="https://github.com/eboody/nestum/actions/workflows/ci.yml"><img src="https://github.com/eboody/nestum/actions/workflows/ci.yml/badge.svg?branch=main&event=push" alt="build status" /></a>
    <a href="https://crates.io/crates/nestum"><img src="https://img.shields.io/crates/v/nestum.svg?logo=rust" alt="crates.io" /></a>
    <a href="https://docs.rs/nestum"><img src="https://docs.rs/nestum/badge.svg" alt="docs.rs" /></a>
  </p>
</div>

# Nestum

Construct:

```rust
Event::Document::Created
```

instead of:

```rust
Event::Document(DocumentEvent::Created)
```

and match the same way with `nested!`.

```rust
use nestum::{nestum, nested};

#[nestum]
enum DocumentEvent {
    Created,
    Deleted,
}

#[nestum]
enum Event {
    Document(DocumentEvent),
}

let inner: DocumentEvent::Enum = DocumentEvent::Created;
let event: Event::Enum = Event::Document::Created;

nested! {
    match event {
        Event::Document::Created => {}
        Event::Document::Deleted => {}
    }
}

let _ = inner;
```

## Mental Model

- `#[nestum]` on an enum turns the enum name into a namespace for nested-path constructors.
- `nested! { ... }` rewrites nested constructors and nested patterns where Rust syntax needs help.
- `#[nestum_scope]` rewrites a whole function, impl, method, or inline module body so you do not need as many local `nested!` wrappers.
- If you need the concrete enum type itself, use `Outer::Enum<T>`.

That namespace tradeoff is what makes `Event::Document::Created` possible in ordinary Rust syntax.

## What This Path Means

- `DocumentEvent::Created` constructs a `DocumentEvent::Enum`.
- `Event::Document::Created` constructs an `Event::Enum`.
- `Event::Document` is a namespace branch, not a completed `Event` value.

Good fits for nestum look like:

- `Event::Document::Created`
- `Command::User::Create`
- `Message::Billing::Paid`

Weak fits usually look like:

- a one-off wrapper where helper functions already hide the wrapping noise
- a hierarchy you would have to invent just to get prettier syntax
- names like `Document::Event::Created` that read like inner type namespaces instead of outer envelope values

`nestum` is strongest when the outer enum is already a real envelope over event, command, or message families.

## Quick Start

```bash
cargo add nestum
```

1. Add `#[nestum]` to each enum in the hierarchy.
2. Construct wrapped values with nested paths like `Event::Document::Created`.
3. Use `nested! { ... }` for focused rewrites, or put `#[nestum_scope]` on the enclosing function, impl, method, or inline module to rewrite a wider scope at once.

## Real-World Showcases

The [`nestum-examples`](./nestum-examples) workspace crate shows the macro against real libraries instead of toy enums.

- `todo_api`: Axum + in-memory SQLite + broadcast events. This proves nested command trees, nested domain errors, and nested events can survive a normal web stack.
- `ops_cli`: Clap subcommands with nested dispatch. This proves nestum can sit on top of derive-heavy command surfaces without turning the type tree into boilerplate.

Run them with:

```bash
cargo run -p nestum-examples --bin todo_api
cargo run -p nestum-examples --bin ops_cli -- users create dev@example.com
```

## Coding Agents

If you use coding agents, see [`docs/agents/`](./docs/agents). It includes copyable instruction templates, an opportunity-signals guide, an audit playbook, and prompts for audits, greenfield design, review, and targeted refactors.

## Examples

### Basic Nesting

```rust
#[nestum]
enum DocumentEvent {
    Created,
    Deleted,
}

#[nestum]
enum ImageEvent {
    Uploaded,
    Archived,
}

#[nestum]
enum Event {
    Document(DocumentEvent),
    Image(ImageEvent),
}

let _ = Event::Document::Created;
let _ = Event::Image::Archived;
```

### Named-Field Constructors

Use `nested!` when the nested leaf is a named-field variant:

```rust
use nestum::{nestum, nested};

#[nestum]
enum Inner {
    Struct { x: i32 },
}

#[nestum]
enum Outer {
    Wrap(Inner),
}

let value: Outer::Enum = nested! { Outer::Wrap::Struct { x: 5 } };
```

### Scope-Level Rewriting

Use `#[nestum_scope]` when a function or impl body has several nested constructors or patterns:

```rust
use nestum::{nestum, nestum_scope};

#[nestum]
enum DocumentEvent {
    Created,
    Renamed { title: &'static str },
}

#[nestum]
enum Event {
    Document(DocumentEvent),
}

#[nestum_scope]
fn handle(event: Event::Enum) {
    let renamed = Event::Document::Renamed { title: "scope" };
    assert!(matches!(
        renamed,
        Event::Document::Renamed { title } if title == "scope"
    ));

    match event {
        Event::Document::Created => {}
        Event::Document::Renamed { title } => {
            let _ = title;
        }
    }
}
```

### Cross-Module Nesting

Use `#[nestum(external = "...")]` when the inner enum lives in another module file:

```rust
mod inner;

#[nestum]
enum Outer {
    #[nestum(external = "crate::inner::Inner")]
    Wrap(Inner),
}

let _ = Outer::Wrap::A;
```

### Ecosystem Compatibility

`nestum` still works with common derive-heavy Rust crates. The test suite covers:

- `serde` round trips for wrapped outer enums
- `thiserror` derives with transparent outer error envelopes
- common assertion macros, including `assert!(matches!(...))`

## Core Rules

- Only enums are supported.
- Both the outer enum and the nested inner enum need `#[nestum]`.
- Use `nested!` for focused rewrites, or `#[nestum_scope]` for function-, impl-, method-, or inline-module-level rewrites.

## Advanced Notes

- In type positions, use `Outer::Enum<T>` for the enum type itself.
- Put `#[nestum]` before `#[derive(...)]` so derive macros see the rewritten enum shape.
- `serde`, `thiserror`, and common assertion macros are covered by tests.
- Generic outer enums use functions for unit constructors, so `Outer::Other()` or `Outer::Wrap::Ready()` may be function calls instead of constants.
- Plain local inner enum paths, `self::...`, `super::...`, and qualified crate-local inner enum paths are supported, including generic arguments.
- Cross-module nesting is explicit with `#[nestum(external = "crate::path::Enum")]`.
- For nested variants, the raw root constructor path like `Outer::Wrap(inner)` is no longer part of the public surface; use `Outer::Enum::Wrap(inner)` if you need the explicit underlying constructor.
- `#[nestum_scope]` rewrites normal Rust AST inside the annotated item body and also handles `matches!`, `assert!`, `debug_assert!`, `assert_eq!`, `assert_ne!`, and their debug variants.

## Limitations

- Most other outer macro token trees are still opaque to `#[nestum_scope]`.
- qself or associated paths are rejected for nested field detection.
- `#[path = "..."]`, `include!()`, and complex `cfg` module layouts may not resolve.
- External crates are not supported because proc macros cannot reliably inspect dependency sources.

## API

### `#[nestum]`

Marks an enum so nested enum-wrapping variants can be constructed through path-shaped syntax.

```rust
use nestum::nestum;

#[nestum]
enum Inner {
    A,
    B,
}

#[nestum]
enum Outer {
    Wrap(Inner),
}

let _ = Outer::Wrap::A;
```

### `nested! { ... }`

Rewrites nested constructors and nested patterns into ordinary Rust enum syntax.
Use it for `match`, `if let`, `while let`, `let-else`, `matches!`, common assertion macros, and named-field nested construction.

```rust
use nestum::{nestum, nested};

#[nestum]
enum Inner {
    A,
    B(u8),
}

#[nestum]
enum Outer {
    Wrap(Inner),
}

let value = Outer::Wrap::B(3);
let ok = nested! { matches!(value, Outer::Wrap::B(n) if n > 0) };
```

### `#[nestum_scope]`

Rewrites nested constructors and nested patterns across a wider body.
Use it on functions, impl methods, impl blocks, or inline modules when local `nested!` wrappers would get noisy.

```rust
use nestum::{nestum, nestum_scope};

#[nestum]
enum Inner {
    A,
    B(u8),
}

#[nestum]
enum Outer {
    Wrap(Inner),
}

#[nestum_scope]
fn demo(value: Outer::Enum) -> bool {
    let created = Outer::Wrap::B(3);
    matches!(value, Outer::Wrap::A) && matches!(created, Outer::Wrap::B(n) if n > 0)
}
```

### `#[nestum(external = "path::to::Enum")]`

Marks a variant as wrapping a nested enum defined in another module file.

```rust
use nestum::nestum;

mod inner;

#[nestum]
enum Outer {
    #[nestum(external = "crate::inner::Inner")]
    Wrap(Inner),
}
```

### `nestum_match! { match value { ... } }`

Match-only compatibility macro. Prefer `nested!` unless you specifically want a `match`-only entry point.

## License

MIT