use super::templates::HyperlegibleTemplate; use askama::Template; use rocket::http::Status; use rust_embed::RustEmbed; use sha2::{Digest, Sha256}; use std::{borrow::Cow, sync::OnceLock}; #[derive(RustEmbed)] #[folder = "public/"] struct Assets; #[derive(Debug)] pub struct Files { pub style: TextFile, pub hyperlegible: TextFile, pub atkinson_latin_woff2: BinaryFile, pub atkinson_latin_ext_woff2: BinaryFile, pub atkinson_all_woff: BinaryFile, } #[derive(Debug, Clone)] pub struct FileMetadata { pub filename: String, pub extension: String, pub hash: String, } impl FileMetadata { pub fn get_hash_filename(&self) -> String { let mut hash = self.hash.clone(); hash.truncate(8); format!("{}.{}.{}", self.filename, hash, self.extension) } } #[derive(Debug)] pub struct BinaryFile { pub data: Cow<'static, [u8]>, pub metadata: FileMetadata, } #[derive(Debug, Clone)] pub struct TextFile { pub text: String, pub metadata: FileMetadata, } trait GetFile { fn get(filename: &str, extension: &str) -> Option where Self: Sized; } impl GetFile for BinaryFile { fn get(filename: &str, extension: &str) -> Option { match Assets::get(&format!("{}.{}", filename, extension)) { Some(file) => { let metadata = FileMetadata { filename: filename.into(), extension: extension.into(), hash: hex::encode(file.metadata.sha256_hash()), }; Some(BinaryFile { data: file.data, metadata, }) } None => None, } } } impl GetFile for TextFile { fn get(filename: &str, extension: &str) -> Option { let file = BinaryFile::get(filename, extension)?; match std::str::from_utf8(&file.data) { Ok(string) => Some(TextFile { text: string.into(), metadata: file.metadata, }), Err(_) => None, } } } pub static FILES: OnceLock = OnceLock::new(); pub fn get_file_wrapper() -> Result<&'static Files, Status> { match FILES.get() { Some(files) => Ok(files), None => Err(Status::InternalServerError), } } fn get_sha256_hash(string: &String) -> String { let mut hasher = Sha256::new(); hasher.update(string); let result = hasher.finalize(); hex::encode(result) } fn get_hyperlegible( latin_woff2_filename: String, latin_ext_woff2_filename: String, all_woff_filename: String, ) -> TextFile { let hyperlegible_template = HyperlegibleTemplate { atkinson_latin_woff2_filename: latin_woff2_filename, atkinson_latin_ext_woff2_filename: latin_ext_woff2_filename, atkinson_all_woff_filename: all_woff_filename, }; let rendered_template = hyperlegible_template.render().unwrap(); let hash = get_sha256_hash(&rendered_template); let metadata = FileMetadata { filename: "hyperlegible".into(), extension: "css".into(), hash, }; TextFile { text: rendered_template, metadata, } } pub fn initialize_files() { let atkinson_latin_woff2 = BinaryFile::get("atkinson-hyperlegible-latin-400-normal", "woff2").unwrap(); let atkinson_latin_ext_woff2 = BinaryFile::get("atkinson-hyperlegible-latin-ext-400-normal", "woff2").unwrap(); let atkinson_all_woff = BinaryFile::get("atkinson-hyperlegible-all-400-normal", "woff").unwrap(); let files = Files { style: TextFile::get("style", "css").unwrap(), hyperlegible: get_hyperlegible( atkinson_latin_woff2.metadata.get_hash_filename(), atkinson_latin_ext_woff2.metadata.get_hash_filename(), atkinson_all_woff.metadata.get_hash_filename(), ), atkinson_latin_woff2, atkinson_latin_ext_woff2, atkinson_all_woff, }; FILES.set(files).unwrap(); }