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
//! The convenience runner that reads argv and builds the certified Tokio
//! runtime (AP2.1-10).
//!
//! [`run`] is the one-line app-`main` entry: it reads `std::env::args()`,
//! parses the subcommand via [`super::parse`], dispatches via
//! [`super::dispatch`] on the certified Tokio multi-thread runtime, and
//! returns the process exit code. The app's `main` is:
//!
//! ```ignore
//! use arcature::cli::{Operations, run};
//!
//! # struct AppOperations;
//! # impl Operations for AppOperations { /* … */ }
//! fn main() -> std::process::ExitCode {
//!     run(AppOperations)
//! }
//! ```
//!
//! Gated by the `macros` feature, which brings the certified Tokio runtime
//! (`rt-multi-thread` + `macros` + `net` + `signal`). An expert user on a
//! custom runtime parses with [`super::parse`] and dispatches with
//! [`super::dispatch`] themselves.
//!
//! # Honest exit codes
//!
//! A successful operation returns `ExitCode::SUCCESS`. A parse error prints
//! the typed diagnostic to stderr and returns `ExitCode::FAILURE`. An
//! operation that returns `Err` prints the engine error and returns
//! `ExitCode::FAILURE` (the operator sees *which* subsystem failed —
//! AGENTS.md §18). The runner never panics on an operation failure
//! (AGENTS.md §17): it surfaces the error and exits.

use std::process::ExitCode;

use super::{Operations, SubcommandError, parse};

/// Parse argv, dispatch to `operations` on the certified Tokio runtime, and
/// return the process exit code.
///
/// Reads `std::env::args()` skipping the program name. The first positional
/// argument is the subcommand; the rest are forwarded to the operation as a
/// `&[String]`. Returns `ExitCode::SUCCESS` on a successful operation,
/// `ExitCode::FAILURE` on a parse error, a runtime error, or an operation
/// error (the diagnostic is printed to stderr).
#[must_use = "the exit code must be returned from main"]
pub fn run(operations: impl Operations) -> ExitCode {
    let args: Vec<String> = std::env::args().skip(1).collect();
    let subcommand = match args.split_first() {
        None => {
            eprintln!("error: {}", SubcommandError::Missing);
            return ExitCode::FAILURE;
        }
        Some((sub, trailing)) => {
            let subcommand = match parse(std::iter::once(sub.clone())) {
                Ok(sub) => sub,
                Err(error) => {
                    eprintln!("error: {error}");
                    return ExitCode::FAILURE;
                }
            };
            let trailing: Vec<String> = trailing.to_vec();
            (subcommand, trailing)
        }
    };
    // Build the certified Tokio multi-thread runtime. `enable_all` turns on
    // the I/O + time drivers the serve path (and the worker/scheduler) need.
    let runtime = tokio::runtime::Builder::new_multi_thread()
        .enable_all()
        .build();
    let handle = match runtime {
        Ok(handle) => handle,
        Err(error) => {
            eprintln!("error: cannot start the Tokio runtime: {error}");
            return ExitCode::FAILURE;
        }
    };
    let result = handle.block_on(super::dispatch::dispatch(
        &operations,
        subcommand.0,
        &subcommand.1,
    ));
    match result {
        Ok(()) => ExitCode::SUCCESS,
        Err(error) => {
            eprintln!("error: {error}");
            ExitCode::FAILURE
        }
    }
}