# Macro Crate Architecture
This document describes the architecture of `waddling-errors-macros`.
## Overview
The macro crate provides 7 procedural macros for defining WDP error codes with rich metadata.
---
## Macro Dependency Chain
Macros must be used in a specific order:
```
┌─────────────────────────────────────────────────────────────────────┐
│ │
│ setup! component! primary! sequence! │
│ │ │ │ │ │
│ │ (configures │ (defines │ (defines │ │
│ │ path │ Component │ Primary │ │
│ │ resolution) │ enum) │ enum) │ │
│ │ │ │ │ │
│ └────────────────┴──────────────────┴────────────────┘ │
│ │ │
│ ▼ │
│ diag! │
│ (uses all above) │
│ │
└─────────────────────────────────────────────────────────────────────┘
Order: setup! → component! → primary! → sequence! → diag!
```
---
## The 7 Macros
| `setup!` | proc-macro | Configure path resolution for diag! |
| `component!` | proc-macro | Define Component enum + traits |
| `primary!` | proc-macro | Define Primary enum + traits |
| `sequence!` | proc-macro | Define sequence constants (001-999) |
| `diag!` | proc-macro | Define full error codes with metadata |
| `component_location!` | proc-macro | Register file/folder as belonging to component |
| `#[in_component]` | attribute | Mark modules as belonging to component |
---
## Data Flow
### Phase 1: Setup
```rust
setup! {
components = crate::my_components,
primaries = crate::my_primaries,
sequences = crate::my_sequences,
}
```
**Generates:**
```rust
#[doc(hidden)]
pub mod __wd_paths {
pub use crate::my_components as components;
pub use crate::my_primaries as primaries;
pub use crate::my_sequences as sequences;
}
```
This allows `diag!` to find definitions regardless of where they're placed.
### Phase 2: Building Blocks (parallel)
**component!**
```rust
component! {
Auth { description: "Authentication" },
Database,
}
```
**Generates:**
```rust
pub enum Component { Auth, Database }
impl ComponentId for Component { ... }
impl ComponentIdDocumented for Component { ... }
```
**primary!**
```rust
primary! {
Token { description: "Token errors" },
Query,
}
```
**Generates:**
```rust
pub enum Primary { Token, Query }
impl PrimaryId for Primary { ... }
impl PrimaryIdDocumented for Primary { ... }
```
**sequence!**
```rust
sequence! {
MISSING(001) { description: "Item missing" },
EXPIRED(031),
}
```
**Generates:**
```rust
pub const MISSING: u16 = 1;
pub const EXPIRED: u16 = 31;
// + SequenceMeta structs for doc generation
```
### Phase 3: Error Definition
```rust
diag! {
<json, html>, // Auto-register for doc generation
E.Auth.Token.MISSING: {
message: "Token missing",
hints: ["Check Authorization header"],
},
}
```
**Processing:**
1. **Parse**: Split `E.Auth.Token.MISSING` into parts
2. **Resolve**: Look up `Auth` in components, `Token` in primaries, `MISSING` in sequences
3. **Validate**: Check all exist and sequence is 1-999
4. **Hash**: Compute WDP hash at compile time via `waddling-errors-hash`
5. **Generate**: Const definition + metadata + optional doc registration
---
## File Structure
```
waddling-errors-macros/src/
├── lib.rs # Macro exports (entry points)
├── setup.rs # setup! implementation
├── component.rs # component! implementation
├── primary.rs # primary! implementation
├── sequence.rs # sequence! implementation
├── diag.rs # diag! implementation (largest file)
├── in_component.rs # #[in_component] attribute
├── component_location.rs # component_location! macro
├── doc_gen_attr.rs # Doc generation attribute parsing
└── doc_gen_macro.rs # Doc generation helpers
```
### Key Types by File
| `setup.rs` | `SetupInput` |
| `component.rs` | `ComponentInput`, `ComponentVariant`, `ComponentMetadata` |
| `primary.rs` | `PrimaryInput`, `PrimaryVariant`, `PrimaryMetadata` |
| `sequence.rs` | `SequenceInput`, `SequenceItem`, `SequenceMetadata` |
| `diag.rs` | `DiagInput`, `DiagEntry`, `FieldVisibility`, `FieldRole` |
---
## diag! Field System
The `diag!` macro supports a rich marker system for fields:
### Visibility Markers
Controls where the field appears:
| `'C` | Compile-time only | Documentation, not in binary |
| `'R` | Runtime only | In binary, not in docs |
| `'CR` | Both (default) | Everywhere |
### Role Markers
Controls who sees the field:
| `'Pub` | Public (default) | End users |
| `'Dev` | Developer | Internal developers |
| `'Int` | Internal | SRE/Operations |
### Combined Example
```rust
diag! {
E.Auth.Token.001: {
// Both contexts, public (default)
message: "Token is missing",
// Docs only, public
'C 'Pub description: "The authentication token was not found in the request",
// Docs only, developer
'C 'Dev hints: ["Check the Authorization header format"],
// Both contexts, developer
'CR 'Dev debug_info: "Token parsing failed at step 1",
},
}
```
---
## Supported diag! Fields
| `message` | String | Primary error message (supports `{{field}}` interpolation) |
| `description` | String | Detailed explanation |
| `hints` | [String] | Array of helpful hints |
| `tags` | [String] | Category tags |
| `see_also` | [String] | Related error codes |
| `related_codes` | [String] | Alternative error codes |
| `examples` | [String] | Usage examples |
| `code_snippets` | [Snippet] | Wrong/correct code examples |
| `introduced` | String | Version introduced |
| `deprecated` | String | Deprecation notice |
| `role` | Ident | Default role for error |
| `fields` | [Ident] | Message template fields |
---
## Hash Computation
Hashes are computed at **compile time** using `waddling-errors-hash`:
```
diag! input: "E.Auth.Token.001"
│
▼
┌─────────────────────────────────┐
│ waddling_errors_hash │
│ │
│ 1. Format: "E.AUTH.TOKEN.001" │
│ 2. xxHash3 with WDP seed │
│ 3. Extract 40 bits │
│ 4. Base62 encode │
│ │
│ Output: "V6a0B" (5 chars) │
└─────────────────────────────────┘
│
▼
Generated: const HASH: &str = "V6a0B";
```
No runtime cost - hash is a string literal in the binary.
---
## Location Tracking
Two macros for associating code locations with components:
### #[in_component(Auth)]
Attribute on modules:
```rust
#[in_component(Auth)]
mod auth_handlers {
// ...
}
```
### component_location!(Auth)
Standalone macro:
```rust
// Mark this file as Auth component
component_location!(Auth);
// With role
component_location!(Auth, role = developer);
```
Both generate metadata that can be collected for documentation.
---
## Extension Points
### Custom Metadata Fields
The parsing is extensible - new fields can be added to `diag.rs` by:
1. Adding to the field enum
2. Adding parsing logic
3. Adding code generation
### Integration with Doc Generator
When `<json, html>` is specified:
```rust
diag! {
<json, html>,
E.Auth.Token.001: { ... },
}
```
The macro generates registration calls:
```rust
// Auto-generated
inventory::submit! {
ErrorDocRegistration { ... }
}
```
These are collected at link time by the doc generator.
---
## Related Documents
- [MACRO_GUIDE.md](MACRO_GUIDE.md) - Complete usage guide for all macros
- [../DESIGN_RATIONALE.md](../../DESIGN_RATIONALE.md) - Why design decisions were made
- [../../waddling-errors/docs/ARCHITECTURE.md](../../waddling-errors/docs/ARCHITECTURE.md) - Core library architecture