use axum::{ extract::{Extension, Path}, response::{IntoResponse, Response}, }; use std::{collections::HashMap, sync::Arc}; #[derive(Debug, Clone, Copy)] struct Resource { data: &'static [u8], mime: &'static str, } impl IntoResponse for &Resource { fn into_response(self) -> Response { ( axum::http::StatusCode::OK, [("Content-Type", self.mime)], self.data, ) .into_response() } } // TODO replace with the "phf" crate someday type ResourceTable = Arc>; #[tracing::instrument] async fn map_to_static( Path(name): Path, Extension(resources): Extension, ) -> Response { tracing::debug!("Searching for {} in the resource table...", name); match resources.get(name.as_str()) { Some(res) => res.into_response(), None => { #[cfg(debug_assertions)] tracing::error!("Not found"); ( axum::http::StatusCode::NOT_FOUND, [("Content-Type", "text/plain")], "Not found. Sorry.".as_bytes(), ) .into_response() } } } pub fn router() -> axum::Router { let resources: ResourceTable = { let mut map = HashMap::new(); macro_rules! register_resource { ($map:ident, $prefix:expr, ($filename:literal, $mime:literal)) => {{ $map.insert($filename, Resource { data: include_bytes!(concat!($prefix, $filename)), mime: $mime }) }}; ($map:ident, $prefix:expr, ($filename:literal, $mime:literal), $( ($f:literal, $m:literal) ),+) => {{ register_resource!($map, $prefix, ($filename, $mime)); register_resource!($map, $prefix, $(($f, $m)),+); }}; } register_resource! { map, concat!(env!("OUT_DIR"), "/", "companion", "/"), ("index.html", "text/html; charset=\"utf-8\""), ("main.js", "text/javascript"), ("micropub_api.js", "text/javascript"), ("indieauth.js", "text/javascript"), ("base64.js", "text/javascript"), ("style.css", "text/css") }; Arc::new(map) }; axum::Router::new().route( "/{filename}", axum::routing::get(map_to_static).layer(Extension(resources)), ) }