# waddling-errors 🦆
**Ultra-minimal diagnostic code system**
[](https://crates.io/crates/waddling-errors)
[](https://docs.rs/waddling-errors)
[](LICENSE)
---
## Overview
`waddling-errors` provides a **super ergonomic diagnostic code system** with consistent error code format generation.
### Key Features
- ✅ **Tiny** - Only ~500 bytes without hashing, ~30KB with SHA256 (optional)
- ✅ **Zero dependencies by default** - No forced dependencies
- ✅ **Flexible** - Enum-based error codes with optional SHA256 hashing
- ✅ **Consistent** - Same error format for diagnostic tooling integration
- ✅ **`no_std` compatible** - Works in embedded and WASM environments
- ✅ **Const-friendly** - Error codes can be defined as constants
---
## Why This Exists
Projects need **consistent error code formats** for diagnostic tooling integration.
### Standard Format
**4-Part Format** - consistent diagnostic codes:
```
SEVERITY.COMPONENT.PRIMARY.SEQUENCE → E.CRYPTO.SALT.001
```
Where SEVERITY can be:
| **Error** | `E` | Invalid input, logic errors |
| **Warning** | `W` | Deprecated API, edge cases |
| **Critical** | `C` | Data corruption, security breach |
| **Blocked** | `B` | Deadlock, I/O wait, network down |
| **Success** | `S` | Operation succeeded ✅ |
| **Completed**| `K` | Task/phase finished ✅ |
| **Info** | `I` | Events, milestones, status |
| **Trace** | `T` | Execution traces, probes, timing |
**Solution:** `waddling-errors` provides a **single, minimal, flexible** implementation.
---
## Installation
Add to your `Cargo.toml`:
```toml
[dependencies]
# Minimal (no hashing) - ultra lightweight
waddling-errors = { version = "0.1", default-features = false }
# With ahash hashing - fast deterministic hashing
waddling-errors = { version = "0.1", features = ["hash"] }
```
---
## Quick Start
```rust
use waddling_errors::prelude::*;
// Ultra-clean error definitions
const ERR_SALT: Code = error("CRYPTO", "SALT", 1);
const WARN_DEPRECATED: Code = warning("API", "FUNC", 10);
const SUCCESS_BUILD: Code = success("BUILD", "DONE", 999);
fn validate(data: &[u8]) -> Result<(), Code> {
if data.is_empty() {
return Err(ERR_SALT);
}
Ok(())
}
fn main() {
println!("{}", ERR_SALT.code()); // "E.CRYPTO.SALT.001"
}
```
---
## Usage Patterns
waddling-errors provides **multiple API styles** - choose what fits your project:
### 1. Convenience Functions (Recommended)
The cleanest, most ergonomic approach:
```rust
use waddling_errors::prelude::*;
// All 8 severity levels
const ERR: Code = error("CRYPTO", "SALT", 1);
const WARN: Code = warning("API", "FUNC", 10);
const CRIT: Code = critical("MEM", "CORRUPT", 23);
const BLOCK: Code = blocked("THREAD", "LOCK", 24);
const SUCCESS: Code = success("BUILD", "DONE", 999);
const COMPLETE: Code = completed("PARSE", "DONE", 999);
const INFO: Code = info("SERVER", "START", 1);
const TRACE: Code = trace("PROBE", "THREAD", 1);
```
### 2. Method Style
Object-oriented approach:
```rust
use waddling_errors::Code;
const ERR: Code = Code::error("CRYPTO", "SALT", 1);
const WARN: Code = Code::warning("API", "FUNC", 10);
```
### 3. Explicit Severity
Full control when needed:
```rust
use waddling_errors::{Code, Severity};
const ERR: Code = Code::new(Severity::Error, "CRYPTO", "SALT", 1);
```
### Standalone Usage (No Dependencies)
Return diagnostic codes directly:
```rust
use waddling_errors::prelude::*;
const ERR_INVALID_SALT: Code = error("CRYPTO", "SALT", 1);
fn validate(data: &[u8]) -> Result<(), Code> {
if data.is_empty() {
return Err(ERR_INVALID_SALT);
}
Ok(())
}
// Or wrap in your own error type
#[derive(Debug)]
struct MyError {
code: Code,
message: String,
}
impl std::fmt::Display for MyError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "[MYAPP.{}] {}", self.code.code(), self.message)
}
}
```
See `examples/standalone.rs` for a complete example with no external dependencies.
---
### Error Registry Pattern (Recommended)
For larger projects, create a central error registry:
```rust
// errors.rs - Your project's error registry
pub mod errors {
use waddling_errors::prelude::*;
// Crypto errors (E.CRYPTO.*)
pub const SALT_MISSING: Code = error("CRYPTO", "SALT", 1);
pub const KEY_LENGTH: Code = error("CRYPTO", "LENGTH", 2);
pub const HMAC_INVALID: Code = error("CRYPTO", "HMAC", 3);
// Parser warnings (W.PARSE.*)
pub const DEPRECATED_SYNTAX: Code = warning("PARSE", "DEPR", 1);
// Build success (S.BUILD.*)
pub const BUILD_COMPLETE: Code = success("BUILD", "DONE", 999);
}
// Use across your project
use errors::SALT_MISSING;
fn validate_salt(salt: &[u8]) -> Result<(), Code> {
if salt.len() != 32 {
return Err(SALT_MISSING);
}
Ok(())
}
```
**Benefits:**
- ✅ Centralized error definitions
- ✅ Easy to document and maintain
- ✅ Prevents duplicate sequence numbers
- ✅ IDE autocomplete for all error codes
---
### Optional Integration: thiserror
If you're already using `thiserror`, waddling-errors integrates seamlessly:
```rust
use waddling_errors::prelude::*;
use thiserror::Error; // Optional - not required!
// Define codes in your error registry (errors.rs)
const ERR_INVALID_SALT: Code = error("CRYPTO", "SALT", 1);
const ERR_MAC_FAILED: Code = error("CRYPTO", "MAC", 2);
// Wrap with thiserror in your error type
#[derive(Error, Debug)]
pub enum MyError {
#[error("[{0}] Invalid salt length: expected {1} bytes, got {2} bytes")]
InvalidSaltLength(Code, usize, usize),
#[error("[{0}] MAC verification failed")]
MacVerificationFailed(Code),
}
// Usage in your functions
fn validate_salt(salt: &[u8]) -> Result<(), MyError> {
if salt.len() != 32 {
return Err(MyError::InvalidSaltLength(ERR_INVALID_SALT, 32, salt.len()));
}
Ok(())
}
// Error output:
// [E.CRYPTO.SALT.001] Invalid salt length: expected 32 bytes, got 16 bytes
```
**Note**: `thiserror` is **completely optional**. waddling-errors works standalone or with any error handling library (anyhow, eyre, custom types, etc.).
See `examples/integration_thiserror.rs` for a complete integration example.
---
### Advanced Usage (With Hashing)
Enable fast deterministic hashing with the `hash` feature (uses ahash with "Waddling" salt):
```rust
use waddling_errors::prelude::*;
const ERR: Code = error("CRYPTO", "SALT", 1);
println!("Code: {}", ERR.code()); // E.CRYPTO.SALT.001
println!("Hash: {}", ERR.hash()); // 5-char base62 hash (alphanumeric, safe for logging)
```
**Hash features:**
- ✅ Base62 encoding (0-9, A-Z, a-z only)
- ✅ Safe for all logging systems (no special characters)
- ✅ 916M combinations (~14,000x better than 4-char hex)
- ✅ Deterministic and fast
---
## Severity Levels
`waddling-errors` provides 8 severity levels for comprehensive diagnostic coverage:
| **Error** | `E` | Operation failed |
| **Warning** | `W` | Potential issue or caveat |
| **Critical** | `C` | Severe issue requiring attention |
| **Blocked** | `B` | Execution blocked/waiting |
| **Success** | `S` | Operation succeeded |
| **Completed**| `K` | Task or phase completed |
| **Info** | `I` | Events, milestones, status |
| **Trace** | `T` | Execution traces, probes, timing |
### Severity Helpers
```rust
use waddling_errors::{Code, Severity};
const ERR: Code = Code::error("io", "FILE", 1);
// Get severity level (0-7, higher = more severe)
let level = ERR.severity().level(); // 7 for Error
// Check if positive outcome (Success/Completed)
let is_good = ERR.severity().is_positive(); // false
```
### Example: Running the Severity Matrix Demo
```bash
cargo run --example severity_matrix
```
This shows all severity levels with real-world use cases.
---
## Sequence Conventions 🔢
To maximize consistency, certain **sequence numbers have reserved semantic meanings**.
### Quick Reference
| **001** | MISSING | `E.CRYPTO.SALT.001` - Salt missing |
| **002** | MISMATCH | `E.CRYPTO.LENGTH.002` - Length mismatch |
| **003** | INVALID | `E.PATTERN.REGEX.003` - Invalid format |
| **021** | NOTFOUND | `E.DATA.KEY.021` - Resource not found |
| **025** | CORRUPTED | `C.CRYPTO.MAC.025` - Data corrupted |
| **031-897** | *(project-specific)* | Domain logic errors |
| **999** | COMPLETE | `S.BUILD.DONE.999` - Full completion |
**Benefits:**
- ✅ **Instant Recognition**: `.001` always means "missing" across projects
- ✅ **Cross-Project Learning**: Consistent patterns improve understanding
- ✅ **Searchability**: Search `.001` finds all "missing" errors
### Example Usage
```rust
use waddling_errors::{ErrorCode, Severity};
// Following conventions (recommended)
const ERR_MISSING_SALT: ErrorCode =
ErrorCode::new(Severity::Error, "CRYPTO", "SALT", 1); // .001 = MISSING ✅
const ERR_LENGTH_MISMATCH: ErrorCode =
ErrorCode::new(Severity::Error, "CRYPTO", "LENGTH", 2); // .002 = MISMATCH ✅
// Domain-specific (project-specific range)
const ERR_HMAC_COMPUTE: ErrorCode =
ErrorCode::new(Severity::Error, "CRYPTO", "HMAC", 31); // 031-897 range ✅
```
### Convention Status
These are **SHOULD** guidelines (RFC 2119), **not requirements**. Projects are encouraged but not forced to follow them.
**Full documentation**: [docs/SEQUENCE-CONVENTIONS.md](docs/SEQUENCE-CONVENTIONS.md)
**Enforcement strategies**: [docs/ENFORCEMENT.md](docs/ENFORCEMENT.md)
---
---
## API
### `ErrorCode` Struct
```rust
pub struct ErrorCode {
component: &'static str,
primary: &'static str,
sequence: u32,
}
```
### Methods
```rust
// Create a new error code (const fn - can be used in constants!)
pub const fn new(component: &'static str, primary: &'static str, sequence: u32) -> Self;
// Get the full error code string (e.g., "CRYPTO.SALT.001")
pub fn code(&self) -> String;
// Get component (e.g., "CRYPTO")
pub const fn component(&self) -> &'static str;
// Get primary category (e.g., "SALT")
pub const fn primary(&self) -> &'static str;
// Get sequence number (e.g., 1)
pub const fn sequence(&self) -> u32;
// Get 4-character SHA256 hash (requires "hash" feature)
#[cfg(feature = "hash")]
pub fn hash(&self) -> String;
```
---
## Features
### Default Features
By default, **no features are enabled** for minimal binary size.
```toml
[dependencies]
waddling-errors = { version = "0.1", default-features = false }
```
**Size:** ~500 bytes
---
### Optional Features
#### `hash` - SHA256-based error code hashing
Adds a 4-character hash based on the error code for stable error identification.
```toml
[dependencies]
waddling-errors = { version = "0.1", features = ["hash"] }
```
**Size:** ~30KB (includes SHA256 implementation from `sha2` crate)
**Use when:**
- Building diagnostic/compiler tools
- Need stable error identifiers for documentation
- Want to link errors to docs pages
---
## Examples
### Standalone (No Dependencies)
See `examples/standalone.rs` for pure waddling-errors usage with zero external dependencies:
```bash
cargo run --example standalone
```
Shows:
- ✅ Direct ErrorCode returns
- ✅ Custom DIY error types
- ✅ No thiserror/anyhow needed
- ✅ Works in no_std environments
### Optional: Integration with thiserror
See `examples/integration_thiserror.rs` for integration with `thiserror` (if you're already using it):
```bash
cargo run --example integration_thiserror
```
Shows:
- ✅ Wrapping error codes in thiserror enums
- ✅ Professional error messages
- ✅ **Note**: thiserror is completely optional!
### Severity Matrix Demo
See `examples/severity_matrix.rs` for a visual demonstration of all severity levels:
```bash
cargo run --example severity_matrix
```
Shows all 8 severity levels with real-world use cases and the blocking/actionable matrix.
---
## Error Handling Approaches
waddling-errors supports multiple approaches - choose what fits your project:
| **Direct ErrorCode** | None | Minimal | Simple libraries, no_std |
| **DIY Error Type** | None | Low | Custom error handling |
| **With thiserror** | thiserror | Medium | Professional errors |
| **With anyhow** | anyhow | Low | Applications |
**All approaches work!** Choose based on your needs, not waddling-errors requirements.
---
## Format Convention
Consistent error code format for better diagnostics:
**4-Part Format**
```
SEVERITY.COMPONENT.PRIMARY.SEQUENCE
E.CRYPTO.SALT.001
```
**Examples:**
- `E.CRYPTO.SALT.001` - Invalid salt length
- `W.API.DEPR.001` - Deprecated API usage
- `E.parser.SYNTAX.001` - Syntax error
- `S.BUILD.DONE.001` - Build successful
This consistency allows:
- ✅ Instantly recognize error types
- ✅ Search documentation effectively
- ✅ Track error patterns with analytics
- ✅ Reference stable error codes in issues/docs
---
## Binary Size Comparison
| **No waddling-errors** | 0 bytes | No error codes |
| **waddling-errors (default)** | ~500 bytes | Minimal libraries |
| **waddling-errors (hash)** | ~30KB | Diagnostic tools |
| **DIY implementation** | ~500 bytes | But duplicated across projects |
**Key insight:** The ~500 byte cost is **amortized across projects** since they share the same crate. SHA256 is **deduplicated** by Cargo if multiple projects enable the `hash` feature.
---
## Roadmap
- [x] **v0.1.0** - Initial release with `ErrorCode` struct
- [ ] **v0.1.1** - Add error code registry (optional feature)
- [ ] **v0.2.0** - Add error code documentation generator
- [ ] **v0.3.0** - Add error analytics helpers
---
## Contributing
Contributions welcome! This is a **minimal, stable** crate, so new features should:
1. Be **opt-in** via feature flags
2. Not increase default binary size
3. Maintain `no_std` compatibility
Please open an issue before submitting large PRs.
---
## License
Dual-licensed under MIT or Apache-2.0.