Skip to content
use actix_web::{
    HttpRequest, HttpResponse, Responder, get,
    http::header::{CACHE_CONTROL, HeaderValue},
    web,
};

use crate::config::Config;

const HTMX: &str = include_str!("../assets/h.js");
const HTMX_WS: &str = include_str!("../assets/hx-ws.js");
const DAISYUI: &str = include_str!("../assets/daisyui.css");
const APP_CSS: &str = include_str!("../assets/app.css");

#[get("/assets/{filename:.*}")]
pub async fn serve_asset(req: HttpRequest, config: web::Data<Config>) -> impl Responder {
    let filename = req.match_info().query("filename");
    let cache_control = config
        .cache_control
        .clone()
        .unwrap_or_else(|| HeaderValue::from_static(crate::config::DEFAULT_CACHE_CONTROL));

    match filename {
        "h.js" => HttpResponse::Ok()
            .content_type("application/javascript; charset=utf-8")
            .insert_header((CACHE_CONTROL, cache_control))
            .body(HTMX),
        "hx-ws.js" => HttpResponse::Ok()
            .content_type("application/javascript; charset=utf-8")
            .insert_header((CACHE_CONTROL, cache_control))
            .body(HTMX_WS),
        "daisyui.css" | "daisyui" => HttpResponse::Ok()
            .content_type("text/css; charset=utf-8")
            .insert_header((CACHE_CONTROL, cache_control))
            .body(DAISYUI),
        "app.css" => HttpResponse::Ok()
            .content_type("text/css; charset=utf-8")
            .insert_header((CACHE_CONTROL, cache_control))
            .body(APP_CSS),
        _ => HttpResponse::NotFound().body("Asset not found"),
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use actix_web::{App, test as aw_test};

    #[actix_web::test]
    async fn test_serve_known_assets() {
        let config = Config::new(
            8090,
            None,
            "test.duckdb".into(),
            "test.db".into(),
            "1234".into(),
        );
        let app = aw_test::init_service(
            App::new()
                .app_data(web::Data::new(config))
                .service(serve_asset),
        )
        .await;

        for (path, expected_type) in [
            ("/assets/h.js", "application/javascript"),
            ("/assets/hx-ws.js", "application/javascript"),
            ("/assets/daisyui.css", "text/css"),
            ("/assets/app.css", "text/css"),
        ] {
            let req = aw_test::TestRequest::get().uri(path).to_request();
            let resp = aw_test::call_service(&app, req).await;
            assert!(resp.status().is_success(), "{path} should succeed");
            let ctype = resp
                .headers()
                .get("content-type")
                .unwrap()
                .to_str()
                .unwrap();
            assert!(
                ctype.starts_with(expected_type),
                "{path} wrong content type {ctype}"
            );
        }
    }

    #[actix_web::test]
    async fn test_serve_unknown_asset() {
        let config = Config::new(
            8090,
            None,
            "test.duckdb".into(),
            "test.db".into(),
            "1234".into(),
        );
        let app = aw_test::init_service(
            App::new()
                .app_data(web::Data::new(config))
                .service(serve_asset),
        )
        .await;

        let req = aw_test::TestRequest::get()
            .uri("/assets/missing.js")
            .to_request();
        let resp = aw_test::call_service(&app, req).await;
        assert_eq!(resp.status(), actix_web::http::StatusCode::NOT_FOUND);
    }
}