use ::mime::Mime; use axum::{ http::{StatusCode, header}, response::{IntoResponse, Response}, }; use rust_embed::EmbeddedFile; use super::{error::NotFound, mime}; use crate::error::{ Internal, failed::{Failed, ResultExt}, }; #[derive(rust_embed::Embed)] #[folder = "$OUT_DIR/ui"] pub struct Assets; impl Assets { pub fn load(path: impl AsRef) -> Result { let path = path.as_ref(); let mime = mime::from_path(path).fail("Failed to determine MIME type from asset path")?; Self::get(path) .map(|file| Asset(mime, file)) .ok_or(Error::NotFound(path.into())) } pub fn index() -> Result { // "not found" in this case really is an internal error, as it should // never happen. `index.html` is a known-valid path with a known-valid // file extension. Ok(Self::load("index.html")?) } } pub struct Asset(Mime, EmbeddedFile); impl IntoResponse for Asset { fn into_response(self) -> Response { let Self(mime, file) = self; ( StatusCode::OK, [(header::CONTENT_TYPE, mime.as_ref())], file.data, ) .into_response() } } #[derive(Debug, thiserror::Error)] pub enum Error { #[error("not found: {0}")] NotFound(String), #[error(transparent)] Failed(#[from] Failed), } impl IntoResponse for Error { fn into_response(self) -> Response { match self { Self::NotFound(_) => NotFound(self.to_string()).into_response(), Self::Failed(_) => Internal::from(self).into_response(), } } }