# 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.001` → `hCF3I`). 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**
```rust
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**
```rust
#[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**
```json
// 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:
```rust
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)
```rust
// 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
```rust
use waddling_errors_hash::compute_hash;
fn log_error(code: &str) {
let hash = compute_hash(code);
println!("[{}] Error: {}", hash, code);
}
```
### Verification
```rust
use waddling_errors_hash::verify_hash;
assert!(verify_hash("E.AUTH.TOKEN.001", "hCF3I"));
```
## Testing
```bash
# 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
- [`waddling-errors`](../waddling-errors/) - Main error handling library
- [`waddling-errors-macros`](../waddling-errors-macros/) - Procedural macros