amagi 0.1.4

Rust SDK, CLI, and Web API service skeleton for multi-platform social web adapters.
Documentation
//! HTTP server bootstrap, routing, and request handlers.

mod handlers;
mod router;
mod runtime;
mod state;

use tokio::net::TcpListener;
use tracing::info;

use crate::APP_NAME;
use crate::client::AmagiClient;
use crate::config::ServeConfig;
use crate::error::AppError;
use crate::output::Printer;
use runtime::ServerRuntimeConfig;

/// Bind the configured address, print startup output, and serve the HTTP app.
///
/// This function blocks until a shutdown signal is received or the server
/// returns an error.
///
/// # Errors
///
/// Returns an error if the TCP listener cannot be bound, startup output cannot
/// be written, or the HTTP server fails while serving requests.
pub async fn serve(
    config: ServeConfig,
    client: AmagiClient,
    printer: &Printer,
) -> Result<(), AppError> {
    let bind_addr = config.bind_addr();
    let listener = TcpListener::bind(&bind_addr).await?;
    let local_addr = listener.local_addr()?.to_string();
    let runtime =
        ServerRuntimeConfig::from_env_with_overrides(|name| config.runtime_override(name))?;

    let state = state::AppState::new(
        APP_NAME,
        env!("CARGO_PKG_VERSION"),
        config.clone(),
        client,
        runtime,
    )?;
    let app = router::build(state);

    printer.print_banner(APP_NAME, env!("CARGO_PKG_VERSION"))?;
    printer.print_server_ready(APP_NAME, env!("CARGO_PKG_VERSION"), &local_addr)?;

    info!(
        app = APP_NAME,
        mode = "server",
        addr = %local_addr,
        "http server listening"
    );

    axum::serve(listener, app)
        .with_graceful_shutdown(shutdown_signal())
        .await?;

    Ok(())
}

async fn shutdown_signal() {
    if tokio::signal::ctrl_c().await.is_ok() {
        info!("shutdown signal received");
    }
}