waddling-errors-macros 0.7.3

Procedural macros for structured error codes with compile-time validation and taxonomy enforcement
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
//! Integration tests for hash verification and reproducibility
//!
//! These tests verify that:
//! 1. Hashes computed by the macro are deterministic
//! 2. Hashes are reproducible using the hash library
//! 3. WDP-conformant hashing (xxHash3 + WDP seed + normalization) works correctly
//! 4. Hashes match manual computation with the WDP-conformant function
//!
//! Note: The diag! macro generates compile-time hash constants using WDP-conformant hashing.
//! These tests verify the hashes are correct and reproducible.

#![allow(non_upper_case_globals)] // Intentional: testing various naming patterns

use waddling_errors_hash::{HashConfig, compute_hash_with_config};
use waddling_errors_macros::{diag, setup};

// Define test components
pub mod components {
    use waddling_errors_macros::component;

    component! {
        Auth {
            docs: "Authentication component",
            tags: ["test"],
        },
        Database {
            docs: "Database component",
            tags: ["test"],
        },
    }
}

// Define test primaries
pub mod primaries {
    use waddling_errors_macros::primary;

    primary! {
        Token {
            docs: "Token category",
        },
        Connection {
            docs: "Connection category",
        },
    }
}

// Define test sequences
pub mod sequences {
    use waddling_errors_macros::sequence;

    sequence! {
        TEST001(1) {
            description: "Test sequence 001",
            typical_severity: "Error",
        },
        TEST002(2) {
            description: "Test sequence 002",
            typical_severity: "Error",
        },
        TEST003(3) {
            description: "Test sequence 003",
            typical_severity: "Error",
        },
        TEST010(10) {
            description: "Test sequence 010",
            typical_severity: "Error",
        },
        TEST020(20) {
            description: "Test sequence 020",
            typical_severity: "Error",
        },
        TEST030(30) {
            description: "Test sequence 030",
            typical_severity: "Error",
        },
    }
}

setup! {
    components = crate::components,
    primaries = crate::primaries,
    sequences = crate::sequences,
}

// ============================================================================
// Test 1: WDP-conformant hash configuration (xxHash64 + "wdp-v1")
// ============================================================================

diag! {
    E.AUTH.TOKEN.TEST001: {
        message: "Test error with default hash",
        fields: [],
        'R role: "Public",
    },
}

#[test]
fn test_default_hash_is_reproducible() {
    // The macro generates a hash constant at compile time using WDP-conformant hashing
    let macro_hash = E_AUTH_TOKEN_TEST001_HASH;

    // Manually compute the same hash using WDP-conformant function
    // The macro uses sequence numeric value (1), not the NAME (TEST001)
    let hash_bytes = waddling_errors_hash::const_wdp_hash_from_parts(
        b'E', b"AUTH", b"TOKEN", 1, // TEST001 = 1
    );
    let manual_hash = std::str::from_utf8(&hash_bytes).unwrap();

    println!("Macro hash:  {}", macro_hash);
    println!("Manual hash: {}", manual_hash);

    assert_eq!(
        macro_hash, manual_hash,
        "WDP hash mismatch! Macro generated '{}' but manual computation gave '{}'",
        macro_hash, manual_hash
    );
}

#[test]
fn test_default_hash_explicit_config() {
    let macro_hash = E_AUTH_TOKEN_TEST001_HASH;

    // Manually compute using WDP-conformant function (equivalent to macro behavior)
    // The macro uses sequence numeric value (1), not the NAME (TEST001)
    let hash_bytes = waddling_errors_hash::const_wdp_hash_from_parts(
        b'E', b"AUTH", b"TOKEN", 1, // TEST001 = 1
    );
    let manual_hash = std::str::from_utf8(&hash_bytes).unwrap();

    println!("Macro hash:    {}", macro_hash);
    println!("Expected hash: {}", manual_hash);

    assert_eq!(
        macro_hash, manual_hash,
        "Hash should match WDP-conformant computation"
    );
}

#[test]
fn test_default_hash_format() {
    let hash = E_AUTH_TOKEN_TEST001_HASH;

    // Should be 5 characters
    assert_eq!(
        hash.len(),
        5,
        "Hash should be 5 characters, got {}",
        hash.len()
    );

    // Should be base62 (alphanumeric)
    assert!(
        hash.chars().all(|c| c.is_ascii_alphanumeric()),
        "Hash should be base62 alphanumeric, got '{}'",
        hash
    );

    println!(
        "Hash format valid: {} (length={}, base62=yes)",
        hash,
        hash.len()
    );
}

// ============================================================================
// Test 2: Multiple errors have different hashes
// ============================================================================

diag! {
    E.AUTH.TOKEN.TEST002: {
        message: "Second test error",
        fields: [],
        'R role: "Public",
    },

    E.AUTH.TOKEN.TEST003: {
        message: "Third test error",
        fields: [],
        'R role: "Developer",
    },
}

#[test]
fn test_different_errors_different_hashes() {
    let hash001 = E_AUTH_TOKEN_TEST001_HASH;
    let hash002 = E_AUTH_TOKEN_TEST002_HASH;
    let hash003 = E_AUTH_TOKEN_TEST003_HASH;

    println!("E.AUTH.TOKEN.001: {}", hash001);
    println!("E.AUTH.TOKEN.002: {}", hash002);
    println!("E.AUTH.TOKEN.003: {}", hash003);

    assert_ne!(
        hash001, hash002,
        "Different errors should have different hashes"
    );
    assert_ne!(
        hash002, hash003,
        "Different errors should have different hashes"
    );
    assert_ne!(
        hash001, hash003,
        "Different errors should have different hashes"
    );
}

#[test]
fn test_different_errors_match_manual_computation() {
    let hash001 = E_AUTH_TOKEN_TEST001_HASH;
    let hash002 = E_AUTH_TOKEN_TEST002_HASH;
    let hash003 = E_AUTH_TOKEN_TEST003_HASH;

    // The macro uses sequence numeric values, not names
    let bytes001 = waddling_errors_hash::const_wdp_hash_from_parts(b'E', b"AUTH", b"TOKEN", 1);
    let bytes002 = waddling_errors_hash::const_wdp_hash_from_parts(b'E', b"AUTH", b"TOKEN", 2);
    let bytes003 = waddling_errors_hash::const_wdp_hash_from_parts(b'E', b"AUTH", b"TOKEN", 3);

    let expected001 = std::str::from_utf8(&bytes001).unwrap();
    let expected002 = std::str::from_utf8(&bytes002).unwrap();
    let expected003 = std::str::from_utf8(&bytes003).unwrap();

    assert_eq!(hash001, expected001, "Hash 001 mismatch");
    assert_eq!(hash002, expected002, "Hash 002 mismatch");
    assert_eq!(hash003, expected003, "Hash 003 mismatch");
}

// ============================================================================
// Test 3: Different severities produce different hashes
// ============================================================================

diag! {
    E.AUTH.TOKEN.TEST010: {
        message: "Error severity test",
        fields: [],
        'R role: "Public",
    },
}

diag! {
    W.AUTH.TOKEN.TEST010: {
        message: "Warning severity test",
        fields: [],
        'R role: "Public",
    },
}

diag! {
    C.AUTH.TOKEN.TEST010: {
        message: "Critical severity test",
        fields: [],
        'R role: "Public",
    },
}

#[test]
fn test_different_severities_different_hashes() {
    let error_hash = E_AUTH_TOKEN_TEST010_HASH;
    let warning_hash = W_AUTH_TOKEN_TEST010_HASH;
    let critical_hash = C_AUTH_TOKEN_TEST010_HASH;

    println!("Error hash:    {}", error_hash);
    println!("Warning hash:  {}", warning_hash);
    println!("Critical hash: {}", critical_hash);

    assert_ne!(error_hash, warning_hash, "E vs W should differ");
    assert_ne!(error_hash, critical_hash, "E vs C should differ");
    assert_ne!(warning_hash, critical_hash, "W vs C should differ");
}

#[test]
fn test_severity_hashes_reproducible() {
    let error_hash = E_AUTH_TOKEN_TEST010_HASH;
    let warning_hash = W_AUTH_TOKEN_TEST010_HASH;

    // The macro uses sequence numeric values (10), not names (TEST010)
    let bytes_error = waddling_errors_hash::const_wdp_hash_from_parts(b'E', b"AUTH", b"TOKEN", 10);
    let bytes_warning =
        waddling_errors_hash::const_wdp_hash_from_parts(b'W', b"AUTH", b"TOKEN", 10);

    let expected_error = std::str::from_utf8(&bytes_error).unwrap();
    let expected_warning = std::str::from_utf8(&bytes_warning).unwrap();

    assert_eq!(
        error_hash, expected_error,
        "Error hash should be reproducible"
    );
    assert_eq!(
        warning_hash, expected_warning,
        "Warning hash should be reproducible"
    );
}

// ============================================================================
// Test 4: Different components produce different hashes
// ============================================================================

diag! {
    E.AUTH.TOKEN.TEST020: {
        message: "Auth component test",
        fields: [],
        'R role: "Public",
    },
}

diag! {
    E.DATABASE.TOKEN.TEST020: {
        message: "Database component test",
        fields: [],
        'R role: "Public",
    },
}

#[test]
fn test_different_components_different_hashes() {
    let auth_hash = E_AUTH_TOKEN_TEST020_HASH;
    let db_hash = E_DATABASE_TOKEN_TEST020_HASH;

    println!("AUTH component: {}", auth_hash);
    println!("DATABASE component:   {}", db_hash);

    assert_ne!(
        auth_hash, db_hash,
        "Different components should have different hashes"
    );

    // Verify with manual computation - uses numeric sequence value (20)
    let bytes_auth = waddling_errors_hash::const_wdp_hash_from_parts(b'E', b"AUTH", b"TOKEN", 20);
    let bytes_db = waddling_errors_hash::const_wdp_hash_from_parts(b'E', b"DATABASE", b"TOKEN", 20);

    let expected_auth = std::str::from_utf8(&bytes_auth).unwrap();
    let expected_db = std::str::from_utf8(&bytes_db).unwrap();

    assert_eq!(auth_hash, expected_auth);
    assert_eq!(db_hash, expected_db);
}

// ============================================================================
// Test 5: Different primaries produce different hashes
// ============================================================================

diag! {
    E.AUTH.TOKEN.TEST030: {
        message: "Token primary test",
        fields: [],
        'R role: "Public",
    },
}

diag! {
    E.AUTH.CONNECTION.TEST030: {
        message: "Connection primary test",
        fields: [],
        'R role: "Public",
    },
}

#[test]
fn test_different_primaries_different_hashes() {
    let token_hash = E_AUTH_TOKEN_TEST030_HASH;
    let conn_hash = E_AUTH_CONNECTION_TEST030_HASH;

    println!("TOKEN primary: {}", token_hash);
    println!("CONNECTION primary:  {}", conn_hash);

    assert_ne!(
        token_hash, conn_hash,
        "Different primaries should have different hashes"
    );

    // Verify with manual computation - uses numeric sequence value (30)
    let bytes_token = waddling_errors_hash::const_wdp_hash_from_parts(b'E', b"AUTH", b"TOKEN", 30);
    let bytes_conn =
        waddling_errors_hash::const_wdp_hash_from_parts(b'E', b"AUTH", b"CONNECTION", 30);

    let expected_token = std::str::from_utf8(&bytes_token).unwrap();
    let expected_conn = std::str::from_utf8(&bytes_conn).unwrap();

    assert_eq!(token_hash, expected_token);
    assert_eq!(conn_hash, expected_conn);
}

// ============================================================================
// Test 6: Verify hash with xxHash3 (WDP-mandated algorithm)
// ============================================================================

#[test]
fn test_xxhash3_produces_valid_hashes() {
    let code = "E.AUTH.TOKEN.999";

    let config = HashConfig::wdp_compliant();
    let hash = compute_hash_with_config(code, &config);

    println!("\nxxHash3 (WDP-mandated) produces valid 5-char base62 hash:");
    assert_eq!(hash.len(), 5, "xxHash3 hash should be 5 chars");
    assert!(
        hash.chars().all(|c| c.is_ascii_alphanumeric()),
        "xxHash3 hash should be base62"
    );

    println!("  xxHash3 → {}", hash);
}

#[test]
fn test_hash_determinism() {
    let code = "E.AUTH.TOKEN.999";

    let config = HashConfig::wdp_compliant();

    // Compute hash multiple times
    let hash1 = compute_hash_with_config(code, &config);
    let hash2 = compute_hash_with_config(code, &config);
    let hash3 = compute_hash_with_config(code, &config);

    println!("Hash 1: {}", hash1);
    println!("Hash 2: {}", hash2);
    println!("Hash 3: {}", hash3);

    // Same code should produce same hash every time
    assert_eq!(hash1, hash2, "Hash should be deterministic");
    assert_eq!(hash2, hash3, "Hash should be deterministic");
}

#[test]
fn test_different_seeds_different_hashes() {
    let code = "E.AUTH.TOKEN.999";

    let hash_seed1 = compute_hash_with_config(code, &HashConfig::new(1));
    let hash_seed2 = compute_hash_with_config(code, &HashConfig::new(2));
    let hash_seed3 = compute_hash_with_config(code, &HashConfig::new(3));

    println!("Seed 1: {}", hash_seed1);
    println!("Seed 2: {}", hash_seed2);
    println!("Seed 3: {}", hash_seed3);

    // Different seeds should produce different hashes
    assert_ne!(hash_seed1, hash_seed2);
    assert_ne!(hash_seed2, hash_seed3);
    assert_ne!(hash_seed1, hash_seed3);
}

// ============================================================================
// Summary Test: Print all hashes for manual inspection
// ============================================================================

#[test]
fn summary_all_macro_hashes() {
    println!("\n╔════════════════════════════════════════════════════════╗");
    println!("║        Hash Verification Summary (Macro-Generated)     ║");
    println!("╚════════════════════════════════════════════════════════╝\n");

    println!("WDP-conformant configuration (xxHash3 + WDP seed + normalization):");
    println!("  E.AUTH.TOKEN.001: {}", E_AUTH_TOKEN_TEST001_HASH);
    println!("  E.AUTH.TOKEN.002: {}", E_AUTH_TOKEN_TEST002_HASH);
    println!("  E.AUTH.TOKEN.003: {}", E_AUTH_TOKEN_TEST003_HASH);

    println!("\nDifferent severities (same component/primary/sequence):");
    println!("  E.AUTH.TOKEN.010: {}", E_AUTH_TOKEN_TEST010_HASH);
    println!("  W.AUTH.TOKEN.010: {}", W_AUTH_TOKEN_TEST010_HASH);
    println!("  C.AUTH.TOKEN.010: {}", C_AUTH_TOKEN_TEST010_HASH);

    println!("\nDifferent components:");
    println!("  E.AUTH.TOKEN.020:     {}", E_AUTH_TOKEN_TEST020_HASH);
    println!("  E.DATABASE.TOKEN.020: {}", E_DATABASE_TOKEN_TEST020_HASH);

    println!("\nDifferent primaries:");
    println!("  E.AUTH.TOKEN.030:      {}", E_AUTH_TOKEN_TEST030_HASH);
    println!(
        "  E.AUTH.CONNECTION.030: {}",
        E_AUTH_CONNECTION_TEST030_HASH
    );

    println!("\n✅ All hashes are 5-character base62 strings");
    println!("✅ All hashes are deterministic and reproducible");
    println!("✅ Manual computation matches macro-generated hashes\n");
}