1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
use std::time::Duration;

use axum::{
    extract::Path,
    response::{IntoResponse, Response},
};
use axum_extra::{
    headers::{CacheControl, ContentType, ETag, IfNoneMatch},
    TypedHeader,
};
use http::StatusCode;

#[tracing::instrument(level = "Debug", ret)]
#[allow(clippy::expect_used)]
pub async fn static_files(
    Path(path): Path<String>,
    if_none_match: Option<TypedHeader<IfNoneMatch>>,
) -> Response {
    match files::STATIC_FILES.get(&path) {
        None => {
            (
                StatusCode::NOT_FOUND,
                TypedHeader(CacheControl::new().with_max_age(Duration::from_secs(60))),
                "404 - Not Found",
            )
                .into_response()
        },
        Some((content, hash, mime)) => {
            let etag: ETag = format_etag(hash)
                .expect("Failed to generate ETag for static value, this should never happen")
                .parse()
                .expect("Failed to generate ETag for static value, this should never happen");
            if let Some(TypedHeader(if_none_match)) = if_none_match {
                if !if_none_match.precondition_passes(&etag) {
                    return (
                        StatusCode::NOT_MODIFIED,
                        TypedHeader(CacheControl::new().with_max_age(Duration::from_secs(60))),
                        TypedHeader(etag),
                    )
                        .into_response();
                }
            }
            (
                StatusCode::OK,
                TypedHeader(CacheControl::new().with_max_age(Duration::from_secs(60))),
                TypedHeader(etag),
                TypedHeader(ContentType::from(mime.clone())),
                *content,
            )
                .into_response()
        },
    }
}

fn format_etag(etag: &[u8; 16]) -> anyhow::Result<String> {
    use core::fmt::Write;

    let mut s = String::with_capacity(2 * 16 + 2);
    write!(s, "\"")?;
    for byte in etag {
        write!(s, "{byte:02x}")?;
    }
    write!(s, "\"")?;
    Ok(s)
}


mod files {
    use mime::Mime;

    pub const STATIC_FILES: phf::Map<&str, (&str, [u8; 16], Mime)> = phf::phf_map! {
        "app.js" => (APP_JS_CONTENT, APP_JS_HASH, mime::APPLICATION_JAVASCRIPT),
        "style.css" => (STYLE_CSS_CONTENT, STYLE_CSS_HASH, mime::TEXT_CSS),
    };

    const APP_JS_CONTENT: &str = include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/../../assets/html/static/app.js"
    ));
    const APP_JS_HASH: [u8; 16] = extendhash::md5::compute_hash(APP_JS_CONTENT.as_bytes());

    const STYLE_CSS_CONTENT: &str = include_str!(concat!(
        env!("CARGO_MANIFEST_DIR"),
        "/../../assets/html/static/style.css"
    ));
    const STYLE_CSS_HASH: [u8; 16] = extendhash::md5::compute_hash(STYLE_CSS_CONTENT.as_bytes());
}