waddling-errors 0.7.3

Structured, secure-by-default diagnostic codes for distributed systems with no_std and role-based documentation
Documentation
//! API Integration Component
//!
//! External API integrations - third-party service calls, webhooks, rate limiting.

use crate::primaries::Primary;
use waddling_errors::ComponentIdDocumented;
use waddling_errors::prelude::*;

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Api;

impl ComponentId for Api {
    fn as_str(&self) -> &'static str {
        "Api"
    }
}

impl ComponentIdDocumented for Api {
    fn description(&self) -> Option<&'static str> {
        Some(
            "External API integrations. Manages third-party service calls, webhook handling, \
              and API rate limiting.",
        )
    }

    fn examples(&self) -> &'static [&'static str] {
        &[
            "B.Api.RateLimit.007: Rate limit exceeded (100 req/min)",
            "E.Api.Connection.028: Third-party API unreachable",
            "W.Api.Validation.003: Response schema validation warning",
        ]
    }

    fn tags(&self) -> &'static [&'static str] {
        &["integration", "http", "webhooks"]
    }
}

// ============================================================================
// Error Code Definitions
// ============================================================================

/// API rate limit exceeded (.007 = DUPLICATE/rate hit)
pub const BLOCKED_RATELIMIT: Code<Api, Primary> = Code::blocked(Api, Primary::RateLimit, 7);

/// Third-party API unreachable (.028 = UNREACHABLE)
pub const ERR_CONNECTION_UNREACHABLE: Code<Api, Primary> =
    Code::error(Api, Primary::Connection, 28);

/// Third-party API temporarily unavailable (.027 = UNAVAILABLE)
pub const ERR_CONNECTION_UNAVAILABLE: Code<Api, Primary> =
    Code::error(Api, Primary::Connection, 27);

/// API response validation warning (.003 = INVALID)
pub const WARN_VALIDATION_INVALID: Code<Api, Primary> = Code::warning(Api, Primary::Validation, 3);

/// API request timeout (.017 = TIMEOUT)
pub const ERR_TIMEOUT: Code<Api, Primary> = Code::error(Api, Primary::Timeout, 17);

// ============================================================================
// Component Registration
// ============================================================================

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

    generator.register_component(
        "Api",
        Some("External API integrations. Manages third-party service calls, webhook handling, and API rate limiting. Implements circuit breaker pattern, retry logic with exponential backoff, and request/response logging."),
        &[
            "RESTful API client with retries",
            "Webhook signature verification",
            "Rate limit tracking (token bucket)",
            "Circuit breaker for fault tolerance",
            "Request timeout and cancellation",
            "Response caching and validation",
        ],
        &["Api", "integration", "http", "webhooks", "rest", "circuit-breaker"]);

    // Register component locations with role-based visibility
    // Demonstrates mixed visibility: Public examples alongside Internal implementation details

    // PUBLIC: Integration examples for external developers
    generator.register_component_location_with_role(
        "Api",
        "examples/complete_system/components/api.rs",
        Some(Role::Public),
    );
    generator.register_component_location_with_role(
        "Api",
        "examples/api_integration_guide.rs",
        Some(Role::Public),
    );
    generator.register_component_location_with_role(
        "Api",
        "examples/webhook_examples.rs",
        Some(Role::Public),
    );

    // DEVELOPER: Testing and debugging utilities
    generator.register_component_location_with_role(
        "Api",
        "src/clients/mock_api.rs",
        Some(Role::Developer),
    );
    generator.register_component_location_with_role(
        "Api",
        "src/middleware/request_logger.rs",
        Some(Role::Developer),
    );

    // INTERNAL: Production implementation details
    generator.register_component_location_with_role(
        "Api",
        "src/clients/external_api.rs",
        Some(Role::Internal),
    );
    generator.register_component_location_with_role(
        "Api",
        "src/middleware/rate_limiter.rs",
        Some(Role::Internal),
    );
    generator.register_component_location_with_role(
        "Api",
        "src/handlers/webhook_handler.rs",
        Some(Role::Internal),
    );
    generator.register_component_location_with_role(
        "Api",
        "src/clients/circuit_breaker.rs",
        Some(Role::Internal),
    );
    generator.register_component_location_with_role(
        "Api",
        "src/clients/api_secrets.rs",
        Some(Role::Internal),
    );
}

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

    let errors = vec![
        (
            "B.Api.RateLimit.007",
            "API rate limit exceeded",
            &[
                "Implement backoff",
                "Check X-RateLimit-Reset",
                "Cache responses",
            ][..],
            &["Api", "rate-limiting"][..],
            Role::Public,
        ),
        (
            "E.Api.Connection.028",
            "Third-party API unreachable",
            &[
                "Check network",
                "Verify endpoint URL",
                "Check service status",
            ],
            &["Api", "network", "external"],
            Role::Developer,
        ),
        (
            "E.Api.Connection.027",
            "Third-party API temporarily unavailable",
            &[
                "Retry after delay",
                "Enable circuit breaker",
                "Use fallback",
            ],
            &["Api", "resilience"],
            Role::Developer,
        ),
        (
            "W.Api.Validation.003",
            "API response validation warning",
            &[
                "Check API version",
                "Review schema changes",
                "Update client",
            ],
            &["Api", "validation", "compatibility"],
            Role::Developer,
        ),
        (
            "E.Api.Timeout.017",
            "API request timeout",
            &[
                "Increase timeout",
                "Optimize payload size",
                "Use async endpoints",
            ],
            &["Api", "performance"],
            Role::Developer,
        ),
    ];

    for (code, desc, hints, tags, role) in &errors {
        let _ = generator.register(*code, *desc, hints, tags);

        let snippet = match role {
            Role::Internal => format!(
                "// Internal debugging\nlog::error!(\"[{}] {}\");",
                code, desc
            ),
            Role::Developer => format!(
                "// Developer API usage\nif let Err(e) = operation() {{\n    eprintln!(\"Error: {}\");\n}}",
                code
            ),
            Role::Public => format!(
                "// Public error handling\nmatch result {{\n    Err(_) => println!(\"Operation failed: {}\"),\n    Ok(_) => println!(\"Success\"),\n}}",
                desc
            ),
        };

        generator.add_snippet_for_role(
            format!("{:?} - Usage Example", role),
            "rust",
            snippet,
            *role,
        );
    }
}

// ============================================================================
// Demo Functions
// ============================================================================

pub fn demo() {
    println!("🌐 API Component Errors:\n");
    println!("  {} - Rate limit exceeded", BLOCKED_RATELIMIT.code());
    println!("  {} - API unreachable", ERR_CONNECTION_UNREACHABLE.code());
    println!("  {} - API unavailable", ERR_CONNECTION_UNAVAILABLE.code());
    println!("  {} - Validation warning", WARN_VALIDATION_INVALID.code());
    println!("  {} - Request timeout", ERR_TIMEOUT.code());
}