waddling-errors 0.4.0

Ultra-minimal error code standard for the Waddling ecosystem
Documentation

waddling-errors đŸĻ†

Type-safe diagnostic code system

Crates.io Documentation License


Overview

waddling-errors provides structured diagnostic codes with a consistent 4-part format: SEVERITY.COMPONENT.PRIMARY.SEQUENCE

E.AUTH.TOKEN.001    // Error in Auth component, Token category, sequence 001
W.PARSER.SYNTAX.003 // Warning in Parser component, Syntax category, sequence 003
S.BUILD.DONE.999    // Success in Build component, Done category, sequence 999

Key Features

  • ✅ Type-safe - Define your own component/primary enums with compile-time checking
  • ✅ Zero dependencies by default - Modular features (doc-gen, hash, serde, emoji, ansi-colors)
  • ✅ Semantic methods - is_blocking(), is_positive(), priority() for intelligent error handling
  • ✅ Documentation generation - Auto-generate JSON & interactive HTML with role-based filtering
  • ✅ Hash codes - Optional 5-character base62 hashes for compact logging
  • ✅ no_std compatible - Works in embedded and WASM environments

Installation

[dependencies]
# Minimal
waddling-errors = "0.4"

# With features
waddling-errors = { version = "0.4", features = ["doc-gen", "hash", "serde"] }

Available features: doc-gen, hash, metadata, ansi-colors, emoji, serde, std


Quick Start

use waddling_errors::prelude::*;

// 1. Define your error structure
#[derive(Debug, Copy, Clone)]
enum Component { Auth, Parser, Database }

impl ComponentId for Component {
    fn as_str(&self) -> &'static str {
        match self {
            Component::Auth => "AUTH",
            Component::Parser => "PARSER",
            Component::Database => "DB",
        }
    }
}

#[derive(Debug, Copy, Clone)]
enum Primary { Token, Syntax, Connection }

impl PrimaryId for Primary {
    fn as_str(&self) -> &'static str {
        match self {
            Primary::Token => "TOKEN",
            Primary::Syntax => "SYNTAX",
            Primary::Connection => "CONN",
        }
    }
}

// 2. Create error codes
const ERR_TOKEN_MISSING: Code<Component, Primary> = error(Component::Auth, Primary::Token, 1);
const ERR_SYNTAX_INVALID: Code<Component, Primary> = error(Component::Parser, Primary::Syntax, 3);
const SUCCESS_CONNECTED: Code<Component, Primary> = success(Component::Database, Primary::Connection, 999);

// 3. Use them
fn authenticate(token: Option<&str>) -> Result<(), Code<Component, Primary>> {
    token.ok_or(ERR_TOKEN_MISSING)?;
    Ok(())
}

fn main() {
    println!("{}", ERR_TOKEN_MISSING.code());  // "E.AUTH.TOKEN.001"
    println!("Priority: {}", ERR_TOKEN_MISSING.severity().priority());  // 8
    println!("Blocking? {}", ERR_TOKEN_MISSING.severity().is_blocking());  // true
}

Severity Levels

Nine severity levels with semantic methods:

Severity Code Emoji Priority Blocking Use Case
Error E ❌ 8 Yes Invalid input, logic errors
Blocked B đŸšĢ 7 Yes Deadlock, I/O wait, network down
Critical C đŸ”Ĩ 6 No Data corruption, resource exhaustion
Warning W âš ī¸ 5 No Deprecated API, edge cases
Help H 💡 4 No Helpful suggestions, tips
Success S ✅ 3 No Operation succeeded
Completed K âœ”ī¸ 2 No Task/phase finished
Info I â„šī¸ 1 No Events, milestones, status
Trace T 🔍 0 No Execution traces, probes, timing
let severity = Severity::Error;

// Semantic methods
severity.is_blocking();   // true - stops execution
severity.is_negative();   // true - indicates failure
severity.is_positive();   // false
severity.priority();      // 8 (0-8 scale)

// Visual representation (requires features)
#[cfg(feature = "emoji")]
severity.emoji();         // "❌"

#[cfg(feature = "ansi-colors")]
severity.ansi_color();    // "\x1b[1;31m" (bold red)

Error Registry Pattern

Organize errors in a central registry:

// errors.rs
pub mod errors {
    use waddling_errors::prelude::*;
    
    #[derive(Debug, Copy, Clone)]
    pub enum Component { Auth, Database }
    
    impl ComponentId for Component {
        fn as_str(&self) -> &'static str {
            match self { Component::Auth => "AUTH", Component::Database => "DB" }
        }
    }
    
    #[derive(Debug, Copy, Clone)]
    pub enum Primary { Token, Connection }
    
    impl PrimaryId for Primary {
        fn as_str(&self) -> &'static str {
            match self { Primary::Token => "TOKEN", Primary::Connection => "CONN" }
        }
    }
    
    // Error codes
    pub const TOKEN_MISSING: Code<Component, Primary> = error(Component::Auth, Primary::Token, 1);
    pub const TOKEN_INVALID: Code<Component, Primary> = error(Component::Auth, Primary::Token, 3);
    pub const DB_CONN_FAILED: Code<Component, Primary> = error(Component::Database, Primary::Connection, 21);
}

// Use across your project
use errors::{Component, Primary, TOKEN_MISSING};

fn validate(token: Option<&str>) -> Result<(), Code<Component, Primary>> {
    token.ok_or(TOKEN_MISSING)?;
    Ok(())
}

Documentation Generation

Generate searchable HTML documentation with role-based filtering:

Error Browser with Search: Generated HTML Documentation

Interactive Query Builder: Query Builder

use waddling_errors::doc_generator::DocGenerator;
use waddling_errors::Role;

let mut gen = DocGenerator::new("My API", "1.0.0");

// Register components, primaries, sequences for rich context
gen.register_component("AUTH", Some("Authentication system"), &[], &["security"]);
gen.register_primary("TOKEN", Some("Token validation errors"), &[], &[]);
gen.register_sequence("MISSING", 1, Some("Required item not provided"), Some("Error"), &[]);

// Register error codes
gen.register_code_extended(
    &ERR_TOKEN_MISSING,
    "JWT token missing from Authorization header",
    &["Include Authorization: Bearer <token> header"],
    &["auth", "security"],
    Some(Role::Public),
    &[]
);

// Generate documentation
gen.generate_json("target/errors.json")?;
gen.generate_html_for_role("target/public.html", Role::Public)?;
gen.generate_html_for_role("target/internal.html", Role::Internal)?;

Three documentation roles:

  • Role::Public - End-user facing errors
  • Role::Developer - For contributors
  • Role::Internal - Full team visibility

JSON-Only Workflow (Custom Frontends)

For teams preferring their own frontend stack, use JSON-only output:

// Generate only JSON data
doc_gen.generate_json("target/errors.json")?;

// Use with React/Vue/Svelte/etc
// Your frontend fetches errors.json and builds custom UI

Trade-offs:

  • ✅ Full UI control - Use modern frameworks, hot reload, TypeScript
  • ❌ Requires build tooling - Node.js, npm, bundler
  • ❌ Distribution complexity - Need HTTP server (file:// has CORS issues)

Default HTML generation:

  • ✅ Zero dependencies - No Node.js or build tools needed
  • ✅ Single-file distribution - Double-click to open, share via email
  • ✅ Offline-first - Works in air-gapped environments

Sequence Conventions

Semantic sequence numbers provide consistency:

Sequence Meaning Example
001 MISSING Required item not provided
002 MISMATCH Values don't match expected type
003 INVALID Format/validation failed
007 DUPLICATE Rate limit or duplicate entry
008 DENIED Permission/access denied
021 NOTFOUND Resource not found
025 CORRUPTED Data corruption detected
999 COMPLETE Full completion

See docs/SEQUENCE-CONVENTIONS.md for the complete list.


Hash Codes

Enable 5-character base62 hashes for compact logging:

// Cargo.toml
waddling-errors = { version = "0.4", features = ["hash"] }
const ERR: Code<Component, Primary> = error(Component::Auth, Primary::Token, 1);

println!("{} → #{}", ERR.code(), ERR.hash());
// E.AUTH.TOKEN.001 → #a7Kx2

// Use in logs
log::error!("#{} Authentication failed", ERR.hash());
// Search logs: grep "#a7Kx2" app.log

Examples

Basic usage:

cargo run --example trait_based_enums

With documentation metadata:

cargo run --example trait_based_documented

Production pattern (6 components, 31 errors, role-based docs):

cargo run --example complete_system --features "doc-gen,hash"

WASM/no_std:

cargo run --example wasm_minimal --target wasm32-unknown-unknown

Documentation


License

Dual-licensed under MIT or Apache-2.0.