puerto 0.10.0

Puerto — Rust full-stack DDD framework CLI. Scaffolds Clean Architecture projects with auto-wired DI, SQLx support, and zero manual wiring. Formerly puerto-framework.
mod api;
mod generated;
mod health;

use std::time::Duration;

use poem::{Server, listener::TcpListener};

/// How long in-flight requests get to finish once a shutdown signal arrives. Keep it below the
/// orchestrator's kill timeout (`docker stop` waits 10s by default) so requests drain instead of
/// being cut.
const SHUTDOWN_DEADLINE: Duration = Duration::from_secs(8);

const BANNER: &str = r#"
    ***********   ++++++++++++   =============
    ***********   ++++++++++++   =============
    ***********   ++++++++++++   =============
    ***********   ++++++++++++   =============
    ***********   ++++++++++++   =============
    ***********   ++++++++++++   =============
    ***********   ++++++++++++   =============
    ***********   ++++++++++++   =============
    ***********   ++++++++++++   =============
    ***********   ++++++++++++   =============
    ***********   ++++++++++++   =============
    ***********   ++++++++++++   =============
    ***********   ++++++++++++   =============
    %#*******##%%%#**********#%%%%++++++++++*%
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
     %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
      ****###########%%%%%%%%%%%%%%%%%%%%%%%
  *****************************        ****
  ***********     ***********************
   *                     ***************
                                *****

    H  A  R  B  O  R"#;

#[tokio::main]
async fn main() -> Result<(), std::io::Error> {
    dotenvy::dotenv().ok();

    tracing_subscriber::fmt()
        .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
        .init();

    if std::env::var("HARBOR_BANNER").as_deref() != Ok("false") {
        println!("{}", BANNER);
        println!("    v{}\n", env!("CARGO_PKG_VERSION"));
    }

    let port = std::env::var("SERVICE_PORT")
        .ok()
        .and_then(|p| p.parse::<u16>().ok())
        .unwrap_or(8080);

    Server::new(TcpListener::bind(format!("0.0.0.0:{port}")))
        .run_with_graceful_shutdown(
            generated::bootstrap::build_app().await,
            shutdown_signal(),
            Some(SHUTDOWN_DEADLINE),
        )
        .await
}

/// Resolves on Ctrl-C or SIGTERM — what `docker stop` and Kubernetes send. The listener then stops
/// accepting and in-flight requests get `SHUTDOWN_DEADLINE` to finish.
async fn shutdown_signal() {
    let ctrl_c = async {
        let _ = tokio::signal::ctrl_c().await;
    };

    #[cfg(unix)]
    let terminate = async {
        match tokio::signal::unix::signal(tokio::signal::unix::SignalKind::terminate()) {
            Ok(mut signal) => {
                signal.recv().await;
            }
            Err(_) => std::future::pending::<()>().await,
        }
    };
    #[cfg(not(unix))]
    let terminate = std::future::pending::<()>();

    tokio::select! {
        _ = ctrl_c => {},
        _ = terminate => {},
    }
    tracing::info!("shutdown signal received, draining in-flight requests");
}