waddling-errors-hash 0.5.0

Hash computation utilities for waddling-errors (shared between runtime and proc-macros)
Documentation

waddling-errors-hash

Deterministic hash computation for waddling-errors diagnostic codes

This is a shared utility crate that provides hash computation functionality for the waddling-errors ecosystem. It exists to enable compile-time hash embedding in procedural macros while keeping the same hash algorithm available at runtime.

Why Does This Crate Exist?

The waddling-errors system needs to compute short, deterministic hashes for error codes (e.g., E.AUTH.TOKEN.001hCF3I). These hashes are used for:

  • Compact logging: Save space in logs with 5-char identifiers
  • Network protocols: Efficient error transmission
  • Quick lookups: Fast error code search and correlation
  • URL-safe identifiers: Safe for all systems (alphanumeric only)

The Problem

Procedural macros (like diag!) run in a separate compilation context from your runtime code. They can't directly use your runtime library's functions. So if we want to compute hashes at compile time (for zero runtime cost), we need the hash logic in a place both can access.

The Solution

waddling-errors-hash is a small, standalone crate that:

  1. ✅ Can be used by proc macros at compile time
  2. ✅ Can be used by the runtime library
  3. ✅ Keeps the hash algorithm consistent between both
  4. ✅ Has minimal dependencies (ahash only)
  5. ✅ Is no_std compatible

Architecture

┌─────────────────────────────────┐
│  waddling-errors-macros         │
│  (proc macro - compile time)    │
│                                 │
│  uses hash to embed constants   │
└────────────┬────────────────────┘
             │
             │  depends on
             ▼
┌─────────────────────────────────┐
│  waddling-errors-hash           │◄─────────┐
│  (pure computation)             │          │
│                                 │          │ depends on
│  • compute_hash()               │          │
│  • verify_hash()                │          │
└─────────────────────────────────┘          │
             ▲                               │
             │  depends on                   │
             │                               │
┌────────────┴────────────────────┐          │
│  waddling-errors                │──────────┘
│  (runtime library)              │
│                                 │
│  uses hash for doc generation   │
└─────────────────────────────────┘

What Features Does This Give Users?

1. Zero Runtime Cost Hashes

use waddling_errors_macros::diag;

diag! {
    E.AUTH.TOKEN.001: {
        message: "Token expired",
    }
}

fn main() {
    // Hash was computed at compile time!
    println!("{}", E_AUTH_TOKEN_001_HASH); // "hCF3I"
    
    // No runtime computation needed ✅
    // No ahash dependency in binary (unless you enable hash feature)
}

Benefit: Every error code gets a hash constant for free, computed once at build time.

2. Deterministic Across Builds

The same error code always produces the same hash, regardless of:

  • Platform (Windows, Linux, macOS)
  • Rust version
  • Build configuration
  • Time of compilation

Benefit: Hashes can be used in documentation, APIs, and protocols without changing.

3. Optional Runtime Verification

#[cfg(feature = "hash")]
{
    use waddling_errors_hash::{compute_hash, verify_hash};
    
    // Verify hash matches code
    assert!(verify_hash("E.AUTH.TOKEN.001", "hCF3I"));
    
    // Compute new hash if needed
    let hash = compute_hash("E.AUTH.TOKEN.002");
}

Benefit: Can validate hashes at runtime if needed, but not required.

4. Compact Error Representation

// Instead of sending full error in network protocol:
{
  "error": "E.AUTH.TOKEN.001",
  "message": "The JWT token has expired..."
}

// Send compact version:
{
  "h": "hCF3I",
  "f": { "user_id": 123 }
}

Client looks up the hash in a catalog to get full message/docs.

Benefit: Reduced bandwidth, i18n on client, security (no sensitive info in errors).

What Burden Does This Put on Users?

For End Users: Zero Burden 🎉

If you just use waddling-errors:

  • ✅ Hash computation happens automatically at compile time
  • ✅ Hash constants are always available (e.g., E_AUTH_TOKEN_001_HASH)
  • ✅ No extra dependencies in your binary (unless you enable hash feature)
  • ✅ No performance overhead
  • ✅ Nothing to configure

Example:

use waddling_errors_macros::diag;

diag! {
    E.AUTH.TOKEN.001: { message: "Token expired" }
}

fn main() {
    println!("{}", E_AUTH_TOKEN_001_HASH); // Just works!
}

For Library Maintainers: Minimal

The only "burden" is having one extra crate in the workspace:

Pros:

  • ✅ Clean separation of concerns
  • ✅ Proc macros can compute hashes at compile time
  • ✅ Runtime library can use same algorithm
  • ✅ Easy to test in isolation
  • ✅ Can be versioned independently if needed

Cons:

  • ❌ One extra crate in workspace (adds ~200 lines of code)
  • ❌ Slightly more complex dependency graph

But this complexity is internal - users never see it!

Algorithm Details

  • Hash Function: ahash with fixed seed ("Waddling")
  • Output Length: 5 characters
  • Character Set: Base62 (0-9, A-Z, a-z)
  • Collision Space: 916,132,832 combinations (62^5)
  • Deterministic: Yes - same input always produces same output
  • Performance: ~50ns per hash on modern CPUs

Usage Examples

In Proc Macros (Compile Time)

// In waddling-errors-macros/src/diag.rs
use waddling_errors_hash::compute_hash;

let code = "E.AUTH.TOKEN.001";
let hash = compute_hash(code); // Computed at compile time
let hash_const = quote! {
    pub const E_AUTH_TOKEN_001_HASH: &str = #hash;
};

At Runtime

use waddling_errors_hash::compute_hash;

fn log_error(code: &str) {
    let hash = compute_hash(code);
    println!("[{}] Error: {}", hash, code);
}

Verification

use waddling_errors_hash::verify_hash;

assert!(verify_hash("E.AUTH.TOKEN.001", "hCF3I"));

Testing

# Run all tests

cargo test -p waddling-errors-hash


# Test with no_std

cargo test -p waddling-errors-hash --no-default-features

Feature Flags

  • default = ["std"]
  • std: Enable standard library support (disable for no_std)

License

MIT OR Apache-2.0

See Also