arcature 2026.2.0

Arcature application framework: a high-level Application facade over the certified Arcature subsystems, with the low-level Axum/Tower escape hatch preserved.
Documentation
//! Serving an [`Application`] on a caller-provided listener.
//!
//! State must be resolved to `()` before serving (call `ApplicationBuilder::state`
//! — the engine analogue of `axum::Router::with_state`). These methods compose
//! the full request pipeline (pre-routing layers: request-id, proxy;
//! post-routing layers: maintenance, Inertia, observe; the 404 fallback) then
//! forward to `axum::serve` (engine spec §7).
//!
//! The engine stays runtime-independent here: a normal application uses the
//! `macros` feature's `Application::run` helper, but an expert user can bring
//! their own runtime and call `serve` directly (engine spec §21/§23).

use std::future::Future;

use crate::application::Result;
use crate::application::ty::Application;
use crate::axum::ServiceExt;
use crate::axum::serve::Listener;

/// Serving, once state is resolved to `()`.
impl Application<()> {
    /// Serve the resolved application on `listener`. Consumes `self`.
    ///
    /// The request pipeline is assembled internally: pre-routing
    /// `RequestIdLayer` + `ProxyLayer` wrap `router.into_service()`; post-routing
    /// maintenance / Inertia / observe layers and the 404 fallback are applied to
    /// the router. The composed service is handed to `axum::serve` via
    /// `into_make_service`. A [`crate::EngineError::Serve`] wraps any I/O failure.
    pub async fn serve<L>(self, listener: L) -> Result<()>
    where
        L: Listener,
        L::Addr: std::fmt::Debug,
    {
        let service = crate::pipeline::assemble::into_service(self);
        crate::axum::serve(listener, service.into_make_service())
            .await
            .map_err(|source| crate::EngineError::Serve { source })
    }

    /// Serve with a graceful-shutdown signal. The signal is a caller-provided
    /// `Future<Output = ()>`; the engine does not implement an OS signal
    /// framework (Phase 1 spec §7). The `macros` feature's `Application::run`
    /// helper wires `tokio::signal::ctrl_c()` for the common case.
    ///
    /// The same pipeline assembly as [`serve`](Self::serve) is used; the
    /// graceful-shutdown signal is forwarded to `axum::serve`.
    pub async fn serve_with_shutdown<L, F>(self, listener: L, signal: F) -> Result<()>
    where
        L: Listener,
        L::Addr: std::fmt::Debug,
        F: Future<Output = ()> + Send + 'static,
    {
        let service = crate::pipeline::assemble::into_service(self);
        crate::axum::serve(listener, service.into_make_service())
            .with_graceful_shutdown(signal)
            .await
            .map_err(|source| crate::EngineError::Serve { source })
    }
}