arcature 2026.2.1

Arcature application framework: a high-level Application facade over the certified Arcature subsystems, with the low-level Axum/Tower escape hatch preserved.
Documentation
//! The `run` helper — bind a `TcpListener` and serve the application on the
//! certified Tokio runtime.
//!
//! Gated by the `macros` feature, which brings in the already-vetted `tokio`
//! runtime with `macros`, `rt-multi-thread`, `net`, and `signal`. A normal
//! application pairs `#[arcature::main]` with `Application::run`:
//!
//! ```ignore
//! use arcature::prelude::*;
//!
//! #[arcature::main]
//! async fn main() -> Result<()> {
//!     Application::new()
//!         .routes(Routes::new().route("/", get(|| async { "ok" })))
//!         .run()
//!         .await
//! }
//! ```
//!
//! Expert users who want their own runtime, listener, or shutdown signal can
//! skip this helper and call `Application::serve` or
//! `Application::serve_with_shutdown` directly (engine spec §21/§23).

use crate::application::ty::Application;
use crate::application::{EngineError, Result};

/// Bind-and-serve on the certified Tokio runtime.
impl Application<()> {
    /// Bind a `TcpListener` on the configured address/port and serve until
    /// `SIGINT` (Ctrl-C). The bind address defaults to `127.0.0.1` and the
    /// port to `3000`; configure them via `ApplicationBuilder::bind` /
    /// `ApplicationBuilder::port`.
    ///
    /// Bind failures return `EngineError::BindListener` (preserving the I/O
    /// source and the address that failed); serve failures return
    /// `EngineError::Serve`.
    pub async fn run(self) -> Result<()> {
        let bind_addr = format!("{}:{}", self.bind_address, self.port);
        let listener = tokio::net::TcpListener::bind(&bind_addr)
            .await
            .map_err(|source| EngineError::BindListener {
                address: bind_addr.clone(),
                source,
            })?;
        // `ctrl_c()` resolves to `Result<(), io::Error>`; the shutdown signal
        // for `serve_with_shutdown` needs `Future<Output = ()>`, so discard the
        // result. The engine does not surface the signal-read failure here — a
        // failed Ctrl-C read is not actionable above the server lifecycle
        // (the OS will still deliver SIGINT and the runtime exits).
        self.serve_with_shutdown(listener, async {
            let _ = tokio::signal::ctrl_c().await;
        })
        .await
    }
}