hugo-server 0.3.0

A hugo server
Documentation
use std::path::Path;
use std::time::Duration;

use axum::error_handling::HandleErrorLayer;
use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::{BoxError, Router};
use tower::ServiceBuilder;
use tower_http::compression::CompressionLayer;
use tower_http::decompression::RequestDecompressionLayer;
use tower_http::services::{ServeDir, ServeFile};
use tower_http::trace::TraceLayer;

use crate::ServerError;

pub fn router<T>(path: T) -> Router
where
    T: AsRef<Path>,
{
    Router::new()
        .fallback_service(
            ServeDir::new(&path).not_found_service(ServeFile::new(path.as_ref().join("404.html"))),
        )
        .layer(
            ServiceBuilder::new()
                .layer(HandleErrorLayer::new(handle_error))
                .layer(RequestDecompressionLayer::new())
                .layer(CompressionLayer::new())
                .load_shed()
                .concurrency_limit(1024)
                .timeout(Duration::from_secs(10))
                .layer(TraceLayer::new_for_http()),
        )
}

async fn handle_error(error: BoxError) -> impl IntoResponse {
    if error.is::<tower::timeout::error::Elapsed>() {
        return ServerError(
            StatusCode::REQUEST_TIMEOUT,
            anyhow::anyhow!("request timed out"),
        );
    }

    if error.is::<tower::load_shed::error::Overloaded>() {
        return ServerError(
            StatusCode::SERVICE_UNAVAILABLE,
            anyhow::anyhow!("service is overloaded, try again later"),
        );
    }

    ServerError(
        StatusCode::INTERNAL_SERVER_ERROR,
        anyhow::anyhow!("unhandled internal error: {error}"),
    )
}