diff options
Diffstat (limited to 'src/ui/assets.rs')
| -rw-r--r-- | src/ui/assets.rs | 63 |
1 files changed, 63 insertions, 0 deletions
diff --git a/src/ui/assets.rs b/src/ui/assets.rs new file mode 100644 index 0000000..6a7563a --- /dev/null +++ b/src/ui/assets.rs @@ -0,0 +1,63 @@ +use ::mime::{FromStrError, Mime}; +use axum::{ + http::{header, StatusCode}, + response::{IntoResponse, Response}, +}; +use rust_embed::EmbeddedFile; + +use super::{error::NotFound, mime}; +use crate::error::Internal; + +#[derive(rust_embed::Embed)] +#[folder = "target/ui"] +pub struct Assets; + +impl Assets { + pub fn load(path: impl AsRef<str>) -> Result<Asset, Error> { + let path = path.as_ref(); + let mime = mime::from_path(path)?; + + Self::get(path) + .map(|file| Asset(mime, file)) + .ok_or(Error::NotFound(path.into())) + } + + pub fn index() -> Result<Asset, Internal> { + // "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)] + Mime(#[from] FromStrError), +} + +impl IntoResponse for Error { + fn into_response(self) -> Response { + #[allow(clippy::match_wildcard_for_single_variants)] + match self { + Self::NotFound(_) => NotFound(self.to_string()).into_response(), + other => Internal::from(other).into_response(), + } + } +} |
