rustlift 2.0.2

A typestate-driven deployment agent for Azure Web Apps
Documentation
// Copyright (c) 2026 Hamze Ghalebi. All rights reserved.
// Licensed under the Rustlift Non-Commercial Licence v1.0.

//! Axum HTTP server deployed to Azure Web Apps.
//!
//! This is the **runtime binary** that Rustlift deploys. It is a minimal
//! Axum application exposing two endpoints:
//!
//! | Route     | Method | Status | Purpose                          |
//! |-----------|--------|--------|----------------------------------|
//! | `/`       | GET    | `200`  | Welcome message                  |
//! | `/health` | GET    | `200`  | Liveness probe for Azure and LB  |
//!
//! # Environment
//!
//! | Variable | Required | Default | Description                      |
//! |----------|----------|---------|----------------------------------|
//! | `PORT`   | no       | `8080`  | TCP port to bind                 |
//!
//! Azure App Service injects `WEBSITES_PORT=8080` and routes external
//! traffic through its load balancer. For local development, the server
//! defaults to port `8080` when `PORT` is not set.
//!
//! # Learning: Axum Basics
//!
//! [Axum](https://docs.rs/axum) is a Rust web framework built on top of
//! [`hyper`](https://docs.rs/hyper) and [`tokio`](https://docs.rs/tokio).
//!
//! The core idea is simple:
//!
//! 1. Create a [`Router`] — this maps URL paths to handler
//!    functions.
//! 2. Define **handler functions** — these are plain async functions whose
//!    return type implements `IntoResponse`.
//! 3. Bind to a [`TcpListener`](tokio::net::TcpListener) and serve.
//!
//! Axum does not use macros for route definitions (unlike Actix Web).
//! Routes are composed via method chaining, which makes them easy to
//! read and refactor.

use axum::{routing::get, Router};
use std::net::SocketAddr;
use tracing::info;

/// Entry point for the deployed server binary.
///
/// Initialises logging, builds the router, and binds to the configured
/// port. The function blocks until the server is shut down.
///
/// # Panics
///
/// - If `PORT` is set to a value that cannot be parsed as a `u16`.
/// - If the TCP listener fails to bind (e.g. port already in use).
/// - If the server exits unexpectedly (all three are fatal for a
///   deployment binary — there is no meaningful recovery).
#[tokio::main]
async fn main() {
    tracing_subscriber::fmt::init();

    // Learning: `Router::new()` creates an empty router. `.route()`
    // adds a path + method + handler. Axum uses the `get()`, `post()`,
    // etc. helpers from `axum::routing` to bind HTTP methods.
    let app = Router::new()
        .route("/", get(root_handler))
        .route("/health", get(health_handler));

    // Read the port from the environment, defaulting to 8080.
    // Azure App Service sets `WEBSITES_PORT` and maps it to `PORT`.
    let port: u16 = std::env::var("PORT")
        .unwrap_or_else(|_| "8080".to_string())
        .parse()
        .expect("PORT must be a valid u16");

    let addr = SocketAddr::from(([0, 0, 0, 0], port));
    info!("🚀 Server listening on {}", addr);

    let listener = tokio::net::TcpListener::bind(addr)
        .await
        .expect("Failed to bind TCP listener");

    axum::serve(listener, app)
        .await
        .expect("Server exited unexpectedly");
}

/// Root handler — returns a simple welcome message.
///
/// # Learning: Handler Return Types
///
/// In Axum, any type that implements `IntoResponse` can be returned from
/// a handler. `&'static str` implements this trait, so a plain string
/// literal becomes an HTTP 200 response with `text/plain` content type.
async fn root_handler() -> &'static str {
    "Hello, Azure!"
}

/// Health probe endpoint consumed by Azure's startup probe and the
/// platform load balancer.
///
/// Returning a short `"OK"` string keeps response sizes minimal. The
/// deployment agent's health-check step polls this endpoint after
/// deploying to verify that the application started successfully.
async fn health_handler() -> &'static str {
    "OK"
}