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());
}