waddling-errors 0.7.3

Structured, secure-by-default diagnostic codes for distributed systems with no_std and role-based documentation
Documentation
//! Authentication Metadata - Manual DiagnosticRuntime Creation
//!
//! This module demonstrates how to manually create `DiagnosticRuntime` structures.
//! This is the EXACT structure that the proc-macros generate automatically.
//!
//! ## Why This Matters
//!
//! Understanding `DiagnosticRuntime` is crucial because:
//! - It shows what happens "under the hood" when using macros
//! - Allows manual control when macros aren't suitable
//! - Demonstrates the const-compatible diagnostic pattern
//! - Shows the separation between runtime and compile-time metadata
//!
//! ## DiagnosticRuntime vs Code<C, P>
//!
//! - `Code<C, P>`: Type-safe, generic, compile-time checked
//! - `DiagnosticRuntime`: String-based, runtime-friendly, macro output
//!
//! Both represent the same error codes, just different representations.

use waddling_errors::Severity;
use waddling_errors::metadata::DiagnosticRuntime;

// ============================================================================
// Manual DiagnosticRuntime Creation
// ============================================================================

/// JWT token missing - Manual DiagnosticRuntime definition
///
/// This is EXACTLY what the `#[diagnostic]` macro generates.
/// Compare this verbose manual definition with the clean macro syntax.
pub const ERR_TOKEN_MISSING_METADATA: DiagnosticRuntime = DiagnosticRuntime {
    severity: Severity::Error,
    namespace: None,
    namespace_hash: None,
    component: "Auth",
    primary: "Token",
    sequence: "MISSING",
    sequence_value: 1,
    code: "E.Auth.Token.001",
    message: "Authentication token is missing from the Authorization header",
    fields: &[],
    pii_fields: &[],
    contains_pii: false,
    role: Some("Public"),
    tags: &["security", "authentication", "http"],
    tags_gated: &[],
    related_codes: &["E.Auth.Token.003", "E.Auth.Token.017"],
    related_codes_gated: &[],
    hints_runtime: &[
        "Include the Authorization header with a valid JWT token",
        "Check that your authentication middleware is properly configured",
    ],
    hints_runtime_gated: &[],
    hints_both: &[
        "Ensure the client includes: Authorization: Bearer <token>",
        "Verify token is not being stripped by proxies or load balancers",
    ],
    hints_both_gated: &[],
    hash: "xxxxx", // Placeholder - actual hash computed by macro
};

/// JWT token invalid - Demonstrating Developer role
pub const ERR_TOKEN_INVALID_METADATA: DiagnosticRuntime = DiagnosticRuntime {
    severity: Severity::Error,
    namespace: None,
    namespace_hash: None,
    component: "Auth",
    primary: "Token",
    sequence: "INVALID",
    sequence_value: 3,
    code: "E.Auth.Token.003",
    message: "JWT token signature validation failed",
    fields: &[],
    pii_fields: &[],
    contains_pii: false,
    role: Some("Developer"),
    tags: &["security", "authentication", "cryptography", "jwt"],
    tags_gated: &[],
    related_codes: &["E.Auth.Token.001", "E.Auth.Token.017"],
    related_codes_gated: &[],
    hints_runtime: &[
        "Verify the JWT_SECRET environment variable matches between services",
        "Check that the token hasn't been modified in transit",
    ],
    hints_runtime_gated: &[],
    hints_both: &[
        "Use a consistent signing algorithm (HS256, RS256, etc.)",
        "Ensure clock synchronization between auth server and validators",
    ],
    hints_both_gated: &[],
    hash: "xxxxx", // Placeholder - actual hash computed by macro
};

/// JWT token expired - Demonstrating timeout sequence
pub const ERR_TOKEN_EXPIRED_METADATA: DiagnosticRuntime = DiagnosticRuntime {
    severity: Severity::Error,
    namespace: None,
    namespace_hash: None,
    component: "Auth",
    primary: "Token",
    sequence: "TIMEOUT",
    sequence_value: 17,
    code: "E.Auth.Token.017",
    message: "JWT token has expired and requires refresh",
    fields: &[],
    pii_fields: &[],
    contains_pii: false,
    role: Some("Public"),
    tags: &["security", "authentication", "session"],
    tags_gated: &[],
    related_codes: &["E.Auth.Token.001", "E.Auth.Token.003"],
    related_codes_gated: &[],
    hints_runtime: &[
        "Use the refresh token endpoint to obtain a new access token",
        "Implement automatic token refresh in your client application",
    ],
    hints_runtime_gated: &[],
    hints_both: &[
        "Check token expiration time (exp claim) before making requests",
        "Configure appropriate token lifetimes (e.g., 15 minutes for access tokens)",
    ],
    hints_both_gated: &[],
    hash: "xxxxx", // Placeholder - actual hash computed by macro
};

/// Permission denied - Demonstrating authorization error
pub const ERR_PERMISSION_DENIED_METADATA: DiagnosticRuntime = DiagnosticRuntime {
    severity: Severity::Error,
    namespace: None,
    namespace_hash: None,
    component: "Auth",
    primary: "Permission",
    sequence: "DENIED",
    sequence_value: 8,
    code: "E.Auth.Permission.008",
    message: "User lacks required permissions for this operation",
    fields: &[],
    pii_fields: &[],
    contains_pii: false,
    role: Some("Public"),
    tags: &["authorization", "rbac", "permissions"],
    tags_gated: &[],
    related_codes: &["W.Auth.Permission.008"],
    related_codes_gated: &[],
    hints_runtime: &[
        "Check that the user has the required role assigned",
        "Request elevated permissions from your administrator",
    ],
    hints_runtime_gated: &[],
    hints_both: &[
        "Review the resource's access control list (ACL)",
        "Verify role-based access control (RBAC) configuration",
    ],
    hints_both_gated: &[],
    hash: "xxxxx", // Placeholder - actual hash computed by macro
};

/// Limited permissions - Warning variant
pub const WARN_PERMISSION_LIMITED_METADATA: DiagnosticRuntime = DiagnosticRuntime {
    severity: Severity::Warning,
    namespace: None,
    namespace_hash: None,
    component: "Auth",
    primary: "Permission",
    sequence: "DENIED",
    sequence_value: 8,
    code: "W.Auth.Permission.008",
    message: "User has limited permissions - some features unavailable",
    fields: &[],
    pii_fields: &[],
    contains_pii: false,
    role: Some("Public"),
    tags: &["authorization", "account", "limits"],
    tags_gated: &[],
    related_codes: &["E.Auth.Permission.008"],
    related_codes_gated: &[],
    hints_runtime: &[
        "Upgrade your account tier for full feature access",
        "Contact support to request additional permissions",
    ],
    hints_runtime_gated: &[],
    hints_both: &[
        "Check your subscription plan details",
        "Review available features for your current tier",
    ],
    hints_both_gated: &[],
    hash: "xxxxx", // Placeholder - actual hash computed by macro
};

/// Deprecated token malformed error - Demonstrating Internal role
pub const ERR_TOKEN_MALFORMED_METADATA: DiagnosticRuntime = DiagnosticRuntime {
    severity: Severity::Error,
    namespace: None,
    namespace_hash: None,
    component: "Auth",
    primary: "Token",
    sequence: "MALFORMED",
    sequence_value: 4,
    code: "E.Auth.Token.004",
    message: "JWT token is malformed (DEPRECATED - use E.Auth.Token.003)",
    fields: &[],
    pii_fields: &[],
    contains_pii: false,
    role: Some("Internal"),
    tags: &["authentication", "deprecated", "legacy"],
    tags_gated: &[],
    related_codes: &["E.Auth.Token.003"],
    related_codes_gated: &[],
    hints_runtime: &[],
    hints_runtime_gated: &[],
    hints_both: &[
        "Migrate to E.Auth.Token.003 for new code",
        "This error code is maintained for backward compatibility only",
    ],
    hints_both_gated: &[],
    hash: "xxxxx", // Placeholder - actual hash computed by macro
};

// ============================================================================
// Demonstration Functions
// ============================================================================

/// Print all metadata examples
pub fn demo_metadata() {
    println!("📋 DiagnosticRuntime Manual Creation Demo\n");
    println!("This demonstrates what proc-macros generate automatically.\n");

    println!("1️⃣  ERR_TOKEN_MISSING (Public role):");
    print_diagnostic(&ERR_TOKEN_MISSING_METADATA);
    println!();

    println!("2️⃣  ERR_TOKEN_INVALID (Developer role):");
    print_diagnostic(&ERR_TOKEN_INVALID_METADATA);
    println!();

    println!("3️⃣  ERR_TOKEN_EXPIRED (Timeout sequence .017):");
    print_diagnostic(&ERR_TOKEN_EXPIRED_METADATA);
    println!();

    println!("4️⃣  ERR_PERMISSION_DENIED (Authorization):");
    print_diagnostic(&ERR_PERMISSION_DENIED_METADATA);
    println!();

    println!("5️⃣  WARN_PERMISSION_LIMITED (Warning severity):");
    print_diagnostic(&WARN_PERMISSION_LIMITED_METADATA);
    println!();

    println!("6️⃣  ERR_TOKEN_MALFORMED (Internal role, deprecated):");
    print_diagnostic(&ERR_TOKEN_MALFORMED_METADATA);
    println!();
}

/// Pretty-print a DiagnosticRuntime
fn print_diagnostic(diag: &DiagnosticRuntime) {
    println!("   Code:     {}", diag.code);
    println!("   Severity: {:?}", diag.severity);
    println!("   Message:  {}", diag.message);
    println!("   Role:     {:?}", diag.role);
    println!("   Tags:     {:?}", diag.tags);
    if !diag.related_codes.is_empty() {
        println!("   Related:  {:?}", diag.related_codes);
    }
    if !diag.hints_runtime.is_empty() {
        println!("   Hints:    {:?}", diag.hints_runtime);
    }
}

// ============================================================================
// Registry Integration
// ============================================================================

#[cfg(feature = "doc-gen")]
pub fn register_metadata_errors(generator: &mut waddling_errors::doc_generator::DocRegistry) {
    use waddling_errors::Role;

    // You can register DiagnosticRuntime structures directly
    // This shows that Code<C, P> and DiagnosticRuntime are interoperable

    println!("   ℹ️  Registering metadata-based diagnostics...");

    // Register each diagnostic by code string (extracted from DiagnosticRuntime)
    let _ = generator.register(
        ERR_TOKEN_MISSING_METADATA.code,
        ERR_TOKEN_MISSING_METADATA.message,
        ERR_TOKEN_MISSING_METADATA.hints_both,
        ERR_TOKEN_MISSING_METADATA.tags,
    );

    let _ = generator.register(
        ERR_TOKEN_INVALID_METADATA.code,
        ERR_TOKEN_INVALID_METADATA.message,
        ERR_TOKEN_INVALID_METADATA.hints_both,
        ERR_TOKEN_INVALID_METADATA.tags,
    );

    let _ = generator.register(
        ERR_TOKEN_EXPIRED_METADATA.code,
        ERR_TOKEN_EXPIRED_METADATA.message,
        ERR_TOKEN_EXPIRED_METADATA.hints_both,
        ERR_TOKEN_EXPIRED_METADATA.tags,
    );

    let _ = generator.register(
        ERR_PERMISSION_DENIED_METADATA.code,
        ERR_PERMISSION_DENIED_METADATA.message,
        ERR_PERMISSION_DENIED_METADATA.hints_both,
        ERR_PERMISSION_DENIED_METADATA.tags,
    );

    let _ = generator.register(
        WARN_PERMISSION_LIMITED_METADATA.code,
        WARN_PERMISSION_LIMITED_METADATA.message,
        WARN_PERMISSION_LIMITED_METADATA.hints_both,
        WARN_PERMISSION_LIMITED_METADATA.tags,
    );

    let _ = generator.register(
        ERR_TOKEN_MALFORMED_METADATA.code,
        ERR_TOKEN_MALFORMED_METADATA.message,
        ERR_TOKEN_MALFORMED_METADATA.hints_both,
        ERR_TOKEN_MALFORMED_METADATA.tags,
    );
}

// ============================================================================
// Comparison: Manual vs Macro
// ============================================================================

#[cfg(doc)]
/// # Manual vs Macro Comparison
///
/// ## Manual (this file):
///
/// ```rust,ignore
/// pub const ERR_TOKEN_MISSING_METADATA: DiagnosticRuntime = DiagnosticRuntime {
///     severity: Severity::Error,
///     component: "Auth",
///     primary: "Token",
///     sequence: "MISSING",
///     sequence_value: 1,
///     code: "E.Auth.Token.001",
///     message: "Authentication token is missing",
///     fields: &[],
///     role: Some("Public"),
///     tags: &["security", "authentication"],
///     related_codes: &[],
///     hints_runtime: &["Include Authorization header"],
///     hints_both: &[],
/// };
/// ```
///
/// ## With Macro (hypothetical - macros generate the above):
///
/// ```rust,ignore
/// #[diagnostic(
///     component = AUTH,
///     primary = TOKEN,
///     sequence = MISSING,
///     role = Public,
///     tags = ["security", "authentication"],
/// )]
/// pub const ERR_TOKEN_MISSING: Diagnostic =
///     error!("Authentication token is missing");
/// ```
///
/// The macro is 90% less code but generates the exact same `DiagnosticRuntime` structure!
const _COMPARISON_DOC: () = ();