wastebin is a pastebin ๐ bin.bloerg.net
pastebin
Something went wrong. Try again.
3.8 kB ยท 139 lines
Rust
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140use std::time::Duration;
use axum::response::{IntoResponse, Response};use axum_extra::{TypedHeader, headers};use sha2::{Digest, Sha256};
use wastebin_highlight::Theme;
/// An asset associated with a MIME type.#[derive(Clone)]pub(crate) struct Asset { /// Route that this will be served under. pub route: String, /// MIME type of this asset determined for the `ContentType` response header. mime: mime::Mime, /// Actual asset content. content: Vec<u8>,}
/// Asset kind.#[derive(Copy, Clone)]pub(crate) enum Kind { Css, Js,}
impl IntoResponse for Asset { fn into_response(self) -> Response { let content_type_header = headers::ContentType::from(self.mime);
let headers = ( TypedHeader(content_type_header), TypedHeader( headers::CacheControl::new() .with_max_age(Duration::from_secs(60 * 60 * 24 * 30)) .with_immutable(), ), );
(headers, self.content).into_response() }}
impl Asset { /// Construct new asset under the given `name`, `mime` type and `content`. pub fn new(name: &str, mime: mime::Mime, content: Vec<u8>) -> Self { Self { route: format!("/{name}"), mime, content, } }
/// Construct new hashed asset under the given `name`, `kind` and `content`. pub fn new_hashed(name: &str, kind: Kind, content: Vec<u8>) -> Self { let (mime, ext) = match kind { Kind::Css => (mime::TEXT_CSS, "css"), Kind::Js => (mime::TEXT_JAVASCRIPT, "js"), };
let route = format!( "/{name}.{}.{ext}", hex::encode(Sha256::digest(&content)) .get(0..16) .expect("at least 16 characters") );
Self { route, mime, content, } }
pub fn route(&self) -> &str { &self.route }}
/// Collection of light and dark CSS and main UI style CSS derived from them.pub(crate) struct Css { /// Main UI CSS stylesheet. pub style: Asset, /// Light theme colors. pub light: Asset, /// Dark theme colors. pub dark: Asset, /// Overrides applied when JavaScript is disabled. pub no_js: Asset,}
impl Css { /// Create CSS assets for `theme`. pub fn new(theme: Theme) -> Self { let style = Asset::new_hashed("style", Kind::Css, include_str!("style.css").into()); let light = Asset::new_hashed("light", Kind::Css, theme.light_css()); let dark = Asset::new_hashed("dark", Kind::Css, theme.dark_css()); let no_js = Asset::new_hashed("no-js", Kind::Css, include_str!("no-js.css").into());
Self { style, light, dark, no_js, } }}
#[cfg(test)]mod tests { use super::*;
#[test] fn hashed_asset() { let asset = Asset::new_hashed("style", Kind::Css, String::from("body {}").into_bytes()); assert_eq!(asset.route, "/style.62368a1a29259b30.css");
let asset = Asset::new_hashed("main", Kind::Js, String::from("1 + 1").into_bytes()); assert_eq!(asset.route, "/main.72fce59447a01f48.js"); }
#[test] fn asset_response() { let asset = Asset::new( "foo.css", mime::TEXT_CSS, String::from("body {}").into_bytes(), );
let response = asset.into_response(); let headers = response.headers();
assert_eq!(headers.get(http::header::CONTENT_TYPE).unwrap(), "text/css"); assert_eq!( headers.get(http::header::CACHE_CONTROL).unwrap(), "immutable, max-age=2592000" ); }}