waddling-errors 0.1.0

Ultra-minimal error code standard for the Waddling ecosystem
Documentation

waddling-errors 🦆

Ultra-minimal diagnostic code system

Crates.io Documentation 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:

Severity Code Use Case
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:

[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

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:

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:

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:

use waddling_errors::{Code, Severity};

const ERR: Code = Code::new(Severity::Error, "CRYPTO", "SALT", 1);

Standalone Usage (No Dependencies)

Return diagnostic codes directly:

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:

// 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:

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):

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:

Severity Code Description
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

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

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

Sequence Meaning Example
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

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
Enforcement strategies: docs/ENFORCEMENT.md



API

ErrorCode Struct

pub struct ErrorCode {
    component: &'static str,
    primary: &'static str,
    sequence: u32,
}

Methods

// 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.

[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.

[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:

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):

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:

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:

Approach Dependencies Complexity Use Case
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

Configuration Binary Size Use Case
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

  • 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.