use actix_web::http::StatusCode; use actix_web::HttpResponse; use actix_web::HttpRequest; use actix_web::web::resource; use actix_web::Resource; /// Represents a single static asset /// /// Each asset is embeded into the binary, and can be served under the /// `/static/{filename}` route. All static assets should be embeded in this way, and can /// be referenced from this module. pub struct StaticAsset { /// The filename of this asset relative to `:/web/assets/` and the `/static/` route pub filename: &'static str, /// The content of the file pub bytes: &'static [u8], } impl StaticAsset { /// Generate a actix resource for serving this asset /// /// The resource will handle requests at `/static/{filename}`. Caching headers are /// applied to allow the content to be considered fresh up to one day, and are /// validated against the crate version after that. pub fn generate_resource(&self) -> Resource { let bytes = self.bytes; resource(format!("/static/{}", self.filename)) .to(move|req: HttpRequest| async move { let mut response = HttpResponse::Ok(); response .header("Etag", env!("CARGO_PKG_VERSION")) .header("Cache-Control", "max-age=86400"); let req_etag = req.headers().get("If-None-Match"); match req_etag { Some(etag) if etag == env!("CARGO_PKG_VERSION") => { response.status(StatusCode::from_u16(304).unwrap()).finish() } _ => { response.body(bytes) } } }) } } /// Generate a [`StaticAsset`] at compiletime from a filename. #[cfg(any(feature = "embed_static_assets", feature = "ogp_images"))] macro_rules! static_asset { ( $filename: expr ) => { StaticAsset { filename: $filename, bytes: include_bytes!(concat!("../assets/", $filename)), } } } /// The decorative font to be used for pronouns displayed at a very large size #[cfg(any(feature = "embed_static_assets", feature = "ogp_images"))] pub const FONT: StaticAsset = static_asset!("font.otf"); /// A list of static assets which should be served by the server pub const STATIC_ASSETS: &[StaticAsset] = &[ #[cfg(any(feature = "embed_static_assets"))] FONT, ];