arcly-http 0.3.0

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
//! Mounts plugin-registered routes as axum handlers.
//!
//! Lives in the `web` layer (not `core`) so the DI engine and plugin
//! lifecycle stay HTTP-agnostic. Reuses `boundary::assemble_context` —
//! plugin routes get exactly the same body cap, trace propagation, and
//! credential extraction as macro routes, from the same code path.

use axum::routing::{on, MethodFilter, MethodRouter};

use crate::core::engine::FrozenDiContainer;
use crate::core::plugins::PluginRoute;

#[doc(hidden)]
pub fn build_plugin_route(
    container: &'static FrozenDiContainer,
    route: &PluginRoute,
    globals: &'static [&'static dyn crate::web::interceptors::Interceptor],
    filters: &'static [&'static dyn crate::web::boundary::BoundaryFilter],
) -> MethodRouter {
    let filter = MethodFilter::try_from(axum::http::Method::from(route.method))
        .expect("supported HTTP method");
    let chain = crate::web::interceptors::compose_chain(globals, route.handler.clone());
    // Plugin paths are mounted verbatim (no params), so the path doubles as
    // the metrics route-pattern label with no cardinality risk.
    let pattern = route.path;

    let handler = move |req: axum::extract::Request| {
        let chain = chain.clone();
        async move {
            let (parts, body) = req.into_parts();
            crate::web::boundary::run_entry(
                parts,
                body,
                Default::default(),
                container,
                pattern,
                None,
                filters,
                &chain,
            )
            .await
        }
    };

    on(filter, handler)
}