arcly-http 0.2.1

Enterprise-grade NestJS-inspired web framework on axum: zero-lock DI, declarative controllers, multi-tenant data routing, transactional outbox, ABAC, and a self-documenting OpenAPI surface
Documentation
//! Route-level deadline enforcement for `#[Timeout("…")]`.
//!
//! Wraps the whole handler thunk (guards + extraction + body) in
//! `tokio::time::timeout`: on expiry the future is **dropped** — the worker
//! is released immediately — and the client receives `504 Gateway Timeout`
//! as RFC-7807 ProblemDetails. This is the backstop that keeps a slow
//! dependency from holding request tasks hostage past their SLA.

use std::time::Duration;

use axum::response::Response;

use crate::web::error::{GatewayTimeout, HttpException};

/// Called by the `#[Timeout]` macro expansion. Not part of the public API.
#[doc(hidden)]
pub async fn run_with_timeout<F>(millis: u64, route: &'static str, fut: F) -> Response
where
    F: std::future::Future<Output = Response>,
{
    match tokio::time::timeout(Duration::from_millis(millis), fut).await {
        Ok(resp) => resp,
        Err(_elapsed) => {
            metrics::counter!("handler_timeouts_total", "route" => route).increment(1);
            crate::http::IntoResponse::into_response(HttpException::from(GatewayTimeout::new(
                "handler exceeded its deadline",
            )))
        }
    }
}

/// Parse `"250ms"`, `"2s"`, `"1m"` (used at macro-expansion time via the
/// macro crate's own copy; kept here for runtime construction too).
pub fn parse_duration_millis(s: &str) -> Option<u64> {
    let s = s.trim();
    if let Some(v) = s.strip_suffix("ms") {
        return v.trim().parse().ok();
    }
    if let Some(v) = s.strip_suffix('s') {
        return v.trim().parse::<u64>().ok().map(|n| n * 1_000);
    }
    if let Some(v) = s.strip_suffix('m') {
        return v.trim().parse::<u64>().ok().map(|n| n * 60_000);
    }
    s.parse().ok()
}