Skip to main content

solverforge_ui/
lib.rs

1use axum::{
2    extract::Path,
3    http::{header, StatusCode},
4    response::{IntoResponse, Response},
5    routing::get,
6    Router,
7};
8use include_dir::{include_dir, Dir};
9
10static ASSETS: Dir = include_dir!("$CARGO_MANIFEST_DIR/static/sf");
11
12pub fn routes() -> Router {
13    Router::new().route("/sf/{*path}", get(serve_asset))
14}
15
16async fn serve_asset(Path(path): Path<String>) -> Response {
17    let Some(file) = ASSETS.get_file(&path) else {
18        return StatusCode::NOT_FOUND.into_response();
19    };
20
21    let mime = mime_from_path(&path);
22    let cache = if is_immutable(&path) {
23        "public, max-age=31536000, immutable"
24    } else {
25        "public, max-age=3600"
26    };
27
28    (
29        StatusCode::OK,
30        [(header::CONTENT_TYPE, mime), (header::CACHE_CONTROL, cache)],
31        file.contents(),
32    )
33        .into_response()
34}
35
36fn mime_from_path(path: &str) -> &'static str {
37    match path.rsplit('.').next() {
38        Some("css") => "text/css; charset=utf-8",
39        Some("js") => "application/javascript; charset=utf-8",
40        Some("svg") => "image/svg+xml",
41        Some("woff2") => "font/woff2",
42        Some("woff") => "font/woff",
43        Some("ttf") => "font/ttf",
44        Some("eot") => "application/vnd.ms-fontobject",
45        Some("png") => "image/png",
46        Some("jpg" | "jpeg") => "image/jpeg",
47        Some("ico") => "image/x-icon",
48        Some("json") => "application/json",
49        Some("html") => "text/html; charset=utf-8",
50        Some("map") => "application/json",
51        _ => "application/octet-stream",
52    }
53}
54
55fn is_immutable(path: &str) -> bool {
56    path.starts_with("fonts/") || path.starts_with("vendor/") || path.starts_with("img/")
57}