waddling-errors-macros 0.6.0

Procedural macros for waddling-errors
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
# Compile-Time Validation System


Complete guide to the `diag!` macro's validation capabilities in `waddling-errors-macros`.

---

## Table of Contents


1. [Overview]#overview
2. [Validation Modes]#validation-modes
3. [Usage Examples]#usage-examples
4. [Error Messages]#error-messages
5. [Best Practices]#best-practices
6. [Testing]#testing

---

## Overview


The `diag!` macro provides **7 granular validation modes** to catch errors at compile time:

| Validation | What It Checks | When to Use |
|------------|---------------|-------------|
| `sequence` | Sequence constant exists | Always (catches typos) |
| `primary` | Primary constant exists | When using primary hub |
| `component` | Component enum variant exists | When using component hub |
| `naming` | UPPER_SNAKE_CASE / PascalCase | To enforce conventions |
| `duplicates` | No duplicate codes in block | To prevent conflicts |
| `sequence_values` | Sequence is u16 type | Type safety |
| `string_values` | Primary is &str, component is enum | Type safety |

### Philosophy


- **Fail fast**: Catch errors at compile time, not runtime
- **Granular control**: Enable only the validations you need
- **Clear messages**: Helpful error messages with suggestions
- **Zero overhead**: All checks happen at compile time

---

## Validation Modes


### 1. Sequence Validation (`sequence`)


**Validates:** The sequence constant exists in `crate::sequences`

```rust
diag! {
    strict(sequence),
    
    E.Auth.Token.EXPIRED: {
        message: "Token expired",
    }
}
```

**What it does:**
- ✅ Checks that `crate::sequences::EXPIRED` exists
- ✅ Prevents typos like `EXPRIED` or `EXIRED`
- ✅ Generates compile-time reference: `let _ = crate::sequences::EXPIRED;`

**Error example:**
```
error[E0425]: cannot find value `EXPRIED` in module `crate::sequences`

  --> src/diagnostics.rs:42:18
   |
42 |     E.Auth.Token.EXPRIED: {
   |                  ^^^^^^^ not found in `crate::sequences`
```

---

### 2. Primary Validation (`primary`)


**Validates:** The primary constant exists in `crate::primaries`

```rust
diag! {
    strict(primary),
    
    E.Auth.Token.EXPIRED: {
        message: "Token expired",
    }
}
```

**What it does:**
- ✅ Checks that `crate::primaries::Token` exists
- ✅ Prevents typos like `Tokne` or `Toekn`
- ✅ Generates compile-time reference: `let _ = crate::primaries::Token;`

**Error example:**
```
error[E0425]: cannot find value `Tokne` in module `crate::primaries`

  --> src/diagnostics.rs:42:12
   |
42 |     E.Auth.Tokne.EXPIRED: {
   |            ^^^^^ not found in `crate::primaries`
```

---

### 3. Component Validation (`component`)


**Validates:** The component enum variant exists in `crate::components`

```rust
diag! {
    strict(component),
    
    E.Auth.Token.EXPIRED: {
        message: "Token expired",
    }
}
```

**What it does:**
- ✅ Checks that `crate::components::Auth` exists
- ✅ Prevents typos like `Auht` or `Atuh`
- ✅ Generates compile-time reference: `crate::components::Auth;`

**Error example:**
```
error[E0425]: cannot find value `Auht` in module `crate::components`

  --> src/diagnostics.rs:42:7
   |
42 |     E.Auht.Token.EXPIRED: {
   |       ^^^^ not found in `crate::components`
```

---

### 4. Naming Convention Validation (`naming`)


**Validates:** Naming follows standard conventions

```rust
diag! {
    strict(naming),
    
    E.Auth.Token.EXPIRED: {
        message: "Token expired",
    }
}
```

**What it checks:**
- **Sequences**: UPPER_SNAKE_CASE (e.g., `EXPIRED`, `MISSING`, `NOT_FOUND`)
-**Primaries**: PascalCase (e.g., `Token`, `Permission`, `Connection`)
-**Components**: PascalCase (e.g., `Auth`, `Database`, `ApiGateway`)

**Error examples:**

Lowercase sequence:
```
error: Sequence 'expired' should be UPPER_SNAKE_CASE (e.g., 'MISSING', 'INVALID', 'EXPIRED')
  --> src/diagnostics.rs:42:18
   |
42 |     E.Auth.Token.expired: {
   |                  ^^^^^^^
```

Lowercase primary:
```
error: Primary 'token' should be PascalCase (e.g., 'Token', 'Permission', 'Connection')
  --> src/diagnostics.rs:42:12
   |
42 |     E.Auth.token.EXPIRED: {
   |            ^^^^^
```

**Rules:**
- Sequences must not start/end with underscore
- Sequences must not have consecutive underscores
- PascalCase must start with uppercase letter
- PascalCase must contain only letters and digits (no underscores)

---

### 5. Duplicate Detection (`duplicates`)


**Validates:** No duplicate diagnostic codes within the same block

```rust
diag! {
    strict(duplicates),
    
    E.Auth.Token.MISSING: {
        message: "First definition",
    },
    
    E.Auth.Token.MISSING: {  // ❌ Error: Duplicate!
        message: "Second definition",
    }
}
```

**What it does:**
- ✅ Tracks all diagnostic codes in the block
- ✅ Detects exact duplicates (Severity.Component.Primary.Sequence)
- ✅ Shows both occurrences in error message

**Error example:**
```
error: Duplicate diagnostic code 'E.Auth.Token.MISSING'. This code was already defined earlier in this block.
       Each diagnostic code must be unique within a block.
  --> src/diagnostics.rs:47:5
   |
47 |     E.Auth.Token.MISSING: {
   |     ^

error: First occurrence of 'E.Auth.Token.MISSING' is here
  --> src/diagnostics.rs:42:5
   |
42 |     E.Auth.Token.MISSING: {
   |     ^
```

**Important:** Duplicates are checked **per block**, not across files. Different `diag!` blocks can have the same code.

---

### 6. Sequence Value Validation (`sequence_values`)


**Validates:** Sequence constants are of type `u16`

```rust
diag! {
    strict(sequence_values),
    
    E.Auth.Token.EXPIRED: {
        message: "Token expired",
    }
}
```

**What it does:**
- ✅ Type-checks that `crate::sequences::EXPIRED` is `u16`
- ✅ Ensures sequences are valid numeric constants
- ✅ Generates: `let _seq_value: u16 = crate::sequences::EXPIRED;`

**Purpose:**
- Catch accidental string constants (`const EXPIRED: &str = "17"`)
- Ensure numeric types are correct
- Validate that sequences are numbers, not other types

**Note:** The actual numeric value (e.g., 17) is enforced by the `sequence!` macro, not by `diag!`. This validation ensures the **type** is correct.

---

### 7. String Value Validation (`string_values`)


**Validates:** Primaries are `&str` constants and components are enum variants

```rust
diag! {
    strict(string_values),
    
    E.Auth.Token.EXPIRED: {
        message: "Token expired",
    }
}
```

**What it does:**
- ✅ Type-checks that `crate::primaries::Token` is `&str`
- ✅ Verifies that `crate::components::Auth` is an enum variant
- ✅ Generates: `let _primary: &str = crate::primaries::Token;`

**Purpose:**
- Catch accidental numeric constants (`const Token: u16 = 1`)
- Ensure string types are correct
- Validate that primaries are strings, not other types

**Note:** The actual string values (e.g., "TOKEN") are enforced by the `primary!` and `component!` macros. This validation ensures the **types** are correct.

---

## Usage Examples


### Recommended: Full Validation


```rust
diag! {
    strict(sequence, primary, component, naming, duplicates, sequence_values, string_values),
    
    E.Auth.Token.EXPIRED: {
        message: "Authentication token has expired",
        hints: ["Refresh your authentication token"],
    },
    
    E.Auth.Permission.DENIED: {
        message: "Permission denied for this operation",
        hints: ["Check your user permissions"],
    }
}
```

**Best for:** Production code, shared libraries, public APIs

---

### Common: Basic Validation


```rust
diag! {
    strict(sequence, primary, component),
    
    E.Auth.Token.MISSING: {
        message: "Token is missing",
    }
}
```

**Best for:** Most projects, catches the most common errors

---

### Minimal: Sequence Only


```rust
diag! {
    strict(sequence),
    
    E.Auth.Token.INVALID: {
        message: "Token is invalid",
    }
}
```

**Best for:** Rapid prototyping, when hubs aren't fully set up

---

### Flexible: Relaxed Mode


```rust
diag! {
    relaxed,  // or omit strict() entirely
    
    E.AnyComponent.AnyPrimary.ANY_SEQUENCE: {
        message: "Maximum flexibility - no validation",
    }
}
```

**Best for:** Experimentation, dynamic code generation, when references aren't available yet

---

### Selective: Choose Your Validations


```rust
// Only validate naming and duplicates
diag! {
    strict(naming, duplicates),
    
    E.Auth.Token.EXPIRED: { ... }
}

// Only validate existence, skip naming
diag! {
    strict(sequence, primary, component),
    
    E.Auth.Token.EXPIRED: { ... }
}

// Only validate types, skip existence checks
diag! {
    strict(sequence_values, string_values),
    
    E.Auth.Token.EXPIRED: { ... }
}
```

**Best for:** Custom workflows, incremental adoption, specific requirements

---

## Error Messages


### Error Message Anatomy


All validation errors follow this pattern:

```
error: [Brief description]
  --> [file path]:[line]:[column]
   |
[line] | [code snippet]
   |     [pointer to error location]
   |
= note: [Additional context or suggestions]
```

### Common Errors and Solutions


#### Missing Sequence


**Error:**
```
error[E0425]: cannot find value `EXPIERD` in module `crate::sequences`

```

**Solution:** Fix the typo or add the sequence to your sequences module
```rust
sequence! {
    EXPIRED(17) { ... }  // ← Add this
}
```

#### Missing Primary


**Error:**
```
error[E0425]: cannot find value `Tokne` in module `crate::primaries`

```

**Solution:** Fix the typo or add the primary
```rust
primary! {
    pub enum Primary {
        Token { ... }  // ← Use correct spelling
    }
}
```

#### Invalid Naming


**Error:**
```
error: Sequence 'Missing' should be UPPER_SNAKE_CASE
```

**Solution:** Use uppercase with underscores
```rust
E.Auth.Token.MISSING  // ✅ Correct
E.Auth.Token.Missing  // ❌ Wrong
```

#### Duplicate Code


**Error:**
```
error: Duplicate diagnostic code 'E.Auth.Token.MISSING'
```

**Solution:** Use a different sequence or wrap in modules
```rust
// Option 1: Use different sequence
E.Auth.Token.MISSING: { ... }
E.Auth.Token.INVALID: { ... }  // ✅ Different

// Option 2: Separate blocks
mod block1 {
    diag! {
        E.Auth.Token.MISSING: { ... }
    }
}
mod block2 {
    diag! {
        E.Auth.Token.MISSING: { ... }  // ✅ Different block
    }
}
```

---

## Best Practices


### 1. Start Strict, Relax as Needed


```rust
// Start with full validation
diag! {
    strict(sequence, primary, component, naming, duplicates),
    // ...
}

// Relax if blocking development
diag! {
    strict(sequence),  // Minimal but useful
    // ...
}

// Relax further if needed
diag! {
    relaxed,
    // ...
}
```

### 2. Use Full Validation in CI


```yaml
# .github/workflows/ci.yml

- name: Test with full validation
  run: cargo test --features metadata
  env:
    RUSTFLAGS: "-D warnings"
```

All tests use `strict(...)` by default, ensuring your CI catches validation errors.

### 3. Enable All Validations for Public APIs

```rust
// src/errors.rs (public API)
diag! {
    strict(sequence, primary, component, naming, duplicates, sequence_values, string_values),
    
    E.Auth.Token.EXPIRED: {
        message: "Token has expired",
    }
}
```

Public APIs benefit most from strict validation.

### 4. Document Your Validation Policy


```rust
//! # Validation Policy
//!
//! All diagnostics use `strict(sequence, primary, component, naming, duplicates)`.
//!
//! This ensures:
//! - No typos in sequence/primary/component references
//! - Consistent naming conventions
//! - No duplicate diagnostic codes
```

### 5. Use Relaxed Mode for Generated Code


```rust
// build.rs or code generation
println!(r#"
diag! {{
    relaxed,  // Generated code doesn't need validation
    
    E.Generated.Code.{}: {{
        message: "{}",
    }}
}}
"#, sequence, message);
```

### 6. Validate in Development, Document in Production


```rust
#[cfg(debug_assertions)]

diag! {
    strict(sequence, primary, component, naming, duplicates, sequence_values, string_values),
    E.Auth.Token.EXPIRED: { ... }
}

#[cfg(not(debug_assertions))]

diag! {
    strict(sequence),  // Minimal for release builds
    E.Auth.Token.EXPIRED: { ... }
}
```

Though validation is compile-time, you can conditionally compile different validation levels.

---

## Testing


### Running Validation Tests


```bash
# Run all validation tests

cargo test -p waddling-errors-macros --test compile_fail --features metadata

# Run only compile-fail tests

cargo test -p waddling-errors-macros compile_fail_tests --features metadata

# Run only compile-pass tests

cargo test -p waddling-errors-macros compile_pass_tests --features metadata
```

### Test Coverage


| Validation | Compile-Fail Test | Compile-Pass Test |
|------------|------------------|-------------------|
| `sequence` |`invalid_sequence.rs` |`strict_mode_with_valid_refs.rs` |
| `primary` |`invalid_primary.rs` |`strict_mode_with_valid_refs.rs` |
| `component` |`invalid_component.rs` |`strict_mode_with_valid_refs.rs` |
| `naming` |`invalid_naming_*.rs` (3 tests) |`valid_naming_conventions.rs` |
| `duplicates` |`duplicate_diagnostic_codes.rs` |`no_duplicate_codes.rs` |
| `sequence_values` | N/A (type check) |`validates_sequence_values.rs` |
| `string_values` | N/A (type check) |`validates_string_values.rs` |
| `relaxed` | N/A |`relaxed_mode_allows_invalid.rs` |

### Adding Custom Validation Tests


See `tests/README.md` for detailed instructions on adding tests.

---

## Migration Guide


### From No Validation to Basic Validation


```rust
// Before
diag! {
    E.Auth.Token.EXPIRED: { message: "..." }
}

// After
diag! {
    strict(sequence),
    E.Auth.Token.EXPIRED: { message: "..." }
}
```

**Impact:** May reveal typos or missing sequences. Fix incrementally.

### From Basic to Full Validation


```rust
// Before
diag! {
    strict(sequence),
    E.Auth.Token.EXPIRED: { message: "..." }
}

// After
diag! {
    strict(sequence, primary, component, naming, duplicates, sequence_values, string_values),
    E.Auth.Token.EXPIRED: { message: "..." }
}
```

**Impact:** May reveal naming issues or duplicates. Fix before enabling.

---

## Performance


### Compile-Time Impact


- **Minimal**: Validation runs during macro expansion
- **No runtime cost**: All checks happen at compile time
- **Incremental**: Only validates when `diag!` blocks change

### Build Time


| Validation Mode | Overhead |
|----------------|----------|
| `relaxed` | 0 ms (baseline) |
| `strict(sequence)` | +1-2 ms per block |
| Full validation | +3-5 ms per block |

**For 100 diagnostic blocks:** ~300-500 ms total overhead (negligible in most projects)

---

## FAQ


### Q: Should I always use full validation?


**A:** In production code, yes. For prototyping, start with `strict(sequence)` and add more as needed.

### Q: Does validation affect runtime performance?


**A:** No. All validation happens at compile time. Zero runtime overhead.

### Q: Can I disable validation for specific diagnostics?


**A:** Yes, use separate `diag!` blocks with different validation modes:

```rust
// Strict validation for public APIs
diag! {
    strict(sequence, primary, component, naming),
    E.Auth.Token.EXPIRED: { ... }
}

// Relaxed for internal/dynamic code
diag! {
    relaxed,
    E.Internal.Dynamic.CODE: { ... }
}
```

### Q: What if I have false positives?


**A:** Use relaxed mode or selective validation:

```rust
diag! {
    strict(sequence),  // Only validate what you need
    E.Auth.Token.EXPIRED: { ... }
}
```

### Q: Can validation check actual numeric values?


**A:** Not directly. `sequence_values` validates the **type** (u16), but the actual numeric value is enforced by the `sequence!` macro. This separation keeps validation simple and fast.

### Q: Can validation check actual string values?


**A:** Not directly. `string_values` validates the **type** (&str), but the actual string value is enforced by the `primary!` and `component!` macros. The identifier name should match the value by convention.

---

## Summary


The validation system provides **7 granular modes** to catch errors at compile time:

1. **`sequence`** - Catch typos in sequence names
2.**`primary`** - Catch typos in primary names
3.**`component`** - Catch typos in component names
4.**`naming`** - Enforce naming conventions
5.**`duplicates`** - Prevent duplicate codes
6.**`sequence_values`** - Type-check sequences (u16)
7.**`string_values`** - Type-check primaries (&str)

**Start with basic validation and add more as needed. Full validation provides maximum safety with zero runtime cost.**

For more details, see:
- `tests/README.md` - Test documentation
- `examples/strict_validation_demo.rs` - Usage examples
- `tests/compile-fail/*.rs` - Error examples
- `tests/compile-pass/*.rs` - Valid usage examples