use axum::http::{HeaderMap, StatusCode}; use chrono::TimeDelta; use pixel_transform::TransformerChain; use std::{pin::Pin, str::FromStr}; use toolbox::mime::MimeType; use tracing::{error, trace}; use crate::{Rendered, RenderedBody, RequestContext, Servable}; pub struct StaticAsset { pub bytes: &'static [u8], pub mime: MimeType, } impl Servable for StaticAsset { fn render<'a>( &'a self, ctx: &'a RequestContext, ) -> Pin + 'a + Send + Sync>> { Box::pin(async { let ttl = Some(TimeDelta::days(30)); // Automatically provide transformation if this is an image let is_image = TransformerChain::mime_is_image(&self.mime); let transform = match (is_image, ctx.query.get("t")) { (false, _) | (_, None) => None, (true, Some(x)) => match TransformerChain::from_str(x) { Ok(x) => Some(x), Err(err) => { return Rendered { code: StatusCode::BAD_REQUEST, body: RenderedBody::String(err), ttl, immutable: true, headers: HeaderMap::new(), mime: None, }; } }, }; match transform { Some(transform) => { trace!(message = "Transforming image", ?transform); let task = { let mime = Some(self.mime.clone()); let bytes = self.bytes; tokio::task::spawn_blocking(move || { transform.transform_bytes(bytes, mime.as_ref()) }) }; let res = match task.await { Ok(x) => x, Err(error) => { error!(message = "Error while transforming image", ?error); return Rendered { code: StatusCode::INTERNAL_SERVER_ERROR, body: RenderedBody::String(format!( "Error while transforming image: {error:?}" )), ttl: None, immutable: true, headers: HeaderMap::new(), mime: None, }; } }; match res { Ok((mime, bytes)) => { return Rendered { code: StatusCode::OK, body: RenderedBody::Bytes(bytes), ttl, immutable: true, headers: HeaderMap::new(), mime: Some(mime), }; } Err(err) => { return Rendered { code: StatusCode::INTERNAL_SERVER_ERROR, body: RenderedBody::String(format!("{err}")), ttl, immutable: true, headers: HeaderMap::new(), mime: None, }; } } } None => { return Rendered { code: StatusCode::OK, body: RenderedBody::Static(self.bytes), ttl, immutable: true, headers: HeaderMap::new(), mime: Some(self.mime.clone()), }; } } }) } }