Use a hashmap for asset files and clean up index code.

technically, the file structs are stored in a vector but the hashmaps contain the index so I can ask the hashmap for the index via the filename or hashed filename string.
This commit is contained in:
Fries 2024-05-27 00:22:04 -07:00
parent b8682f3960
commit 132dcdf412
6 changed files with 130 additions and 106 deletions

View file

@ -3,7 +3,7 @@ use askama::Template;
use rocket::http::Status;
use rust_embed::RustEmbed;
use sha2::{Digest, Sha256};
use std::{borrow::Cow, sync::OnceLock};
use std::{collections::HashMap, sync::OnceLock};
#[derive(RustEmbed)]
#[folder = "public/"]
@ -11,11 +11,32 @@ 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,
file_vector: Vec<File>,
hash_filename_dictionary: HashMap<String, usize>,
filename_dictionary: HashMap<String, usize>,
}
impl Files {
pub fn hash_filename_get(&self, name: &str) -> Option<&File> {
match self.hash_filename_dictionary.get(name) {
Some(index) => Some(&self.file_vector[*index]),
None => None,
}
}
pub fn filename_get(&self, name: &str) -> Option<&File> {
match self.filename_dictionary.get(name) {
Some(index) => Some(&self.file_vector[*index]),
None => None,
}
}
pub fn insert_file_into_hashmap(&mut self, file: File) {
let index = self.file_vector.len();
self.hash_filename_dictionary
.insert(file.metadata.get_hash_filename(), index);
self.filename_dictionary
.insert(file.metadata.filename.clone(), index);
self.file_vector.push(file);
}
}
#[derive(Debug, Clone)]
@ -34,25 +55,16 @@ impl FileMetadata {
}
#[derive(Debug)]
pub struct BinaryFile {
pub data: Cow<'static, [u8]>,
pub struct File {
pub metadata: FileMetadata,
data: Box<[u8]>,
}
#[derive(Debug, Clone)]
pub struct TextFile {
pub text: String,
pub metadata: FileMetadata,
}
trait GetFile {
impl File {
fn get(filename: &str, extension: &str) -> Option<Self>
where
Self: Sized;
}
impl GetFile for BinaryFile {
fn get(filename: &str, extension: &str) -> Option<Self> {
Self: Sized,
{
match Assets::get(&format!("{}.{}", filename, extension)) {
Some(file) => {
let metadata = FileMetadata {
@ -60,24 +72,22 @@ impl GetFile for BinaryFile {
extension: extension.into(),
hash: hex::encode(file.metadata.sha256_hash()),
};
Some(BinaryFile {
data: file.data,
Some(File {
data: file.data.into(),
metadata,
})
}
None => None,
}
}
}
impl GetFile for TextFile {
fn get(filename: &str, extension: &str) -> Option<Self> {
let file = BinaryFile::get(filename, extension)?;
match std::str::from_utf8(&file.data) {
Ok(string) => Some(TextFile {
text: string.into(),
metadata: file.metadata,
}),
pub fn get_data(&self) -> &[u8] {
&self.data
}
pub fn get_text(&self) -> Option<String> {
match std::str::from_utf8(&self.data) {
Ok(str) => Some(str.into()),
Err(_) => None,
}
}
@ -103,7 +113,7 @@ fn get_hyperlegible(
latin_woff2_filename: String,
latin_ext_woff2_filename: String,
all_woff_filename: String,
) -> TextFile {
) -> File {
let hyperlegible_template = HyperlegibleTemplate {
atkinson_latin_woff2_filename: latin_woff2_filename,
atkinson_latin_ext_woff2_filename: latin_ext_woff2_filename,
@ -119,30 +129,32 @@ fn get_hyperlegible(
hash,
};
TextFile {
text: rendered_template,
File {
data: rendered_template.as_bytes().into(),
metadata,
}
}
pub fn initialize_files() {
pub fn initialize_files() -> Result<(), Files> {
let atkinson_latin_woff2 =
BinaryFile::get("atkinson-hyperlegible-latin-400-normal", "woff2").unwrap();
File::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();
File::get("atkinson-hyperlegible-latin-ext-400-normal", "woff2").unwrap();
let atkinson_all_woff = File::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,
let mut files = Files {
file_vector: Vec::new(),
hash_filename_dictionary: HashMap::new(),
filename_dictionary: HashMap::new(),
};
FILES.set(files).unwrap();
files.insert_file_into_hashmap(File::get("style", "css").unwrap());
files.insert_file_into_hashmap(get_hyperlegible(
atkinson_latin_woff2.metadata.get_hash_filename(),
atkinson_latin_ext_woff2.metadata.get_hash_filename(),
atkinson_all_woff.metadata.get_hash_filename(),
));
files.insert_file_into_hashmap(atkinson_latin_woff2);
files.insert_file_into_hashmap(atkinson_latin_ext_woff2);
files.insert_file_into_hashmap(atkinson_all_woff);
return FILES.set(files);
}

View file

@ -4,15 +4,14 @@ use rocket::{
response::{self, Responder},
Response,
};
use std::borrow::Cow;
#[derive(Responder)]
#[response(status = 200, content_type = "font/woff2")]
pub struct RawWoff2Font(pub Cow<'static, [u8]>);
pub struct RawWoff2Font(pub &'static [u8]);
#[derive(Responder)]
#[response(status = 200, content_type = "font/woff")]
pub struct RawWoffFont(pub Cow<'static, [u8]>);
pub struct RawWoffFont(pub &'static [u8]);
#[derive(Responder)]
pub struct ErrorTemplateResponder<'a> {

View file

@ -6,46 +6,45 @@ use rocket::{http::Status, response::content::RawCss};
#[get("/css/<style>")]
pub fn style(style: &str) -> Result<CachedResponse<RawCss<String>>, Status> {
let style_file = &get_file_wrapper()?.style;
let hyperlegible_file = &get_file_wrapper()?.hyperlegible;
let style_name = style_file.metadata.get_hash_filename();
let hyperlegible_name = hyperlegible_file.metadata.get_hash_filename();
if style == style_name {
Ok(RawCss::<String>(style_file.text.clone()).into())
} else if style == hyperlegible_name {
Ok(RawCss::<String>(hyperlegible_file.text.clone()).into())
} else {
Err(Status::NotFound)
let file_wrapper = get_file_wrapper()?;
match file_wrapper.hash_filename_get(style) {
Some(style) => {
if style.metadata.extension != "css" {
return Err(Status::NotFound);
}
match style.get_text() {
Some(text) => Ok(RawCss::<String>(text).into()),
None => Err(Status::NotFound),
}
}
None => Err(Status::NotFound),
}
}
#[get("/woff2/<font>")]
pub fn woff2_font(font: &str) -> Result<CachedResponse<RawWoff2Font>, Status> {
let latin_file = &get_file_wrapper()?.atkinson_latin_woff2;
let latin_ext_file = &get_file_wrapper()?.atkinson_latin_ext_woff2;
let latin = latin_file.metadata.get_hash_filename();
let latin_ext = latin_file.metadata.get_hash_filename();
if font == latin {
Ok(RawWoff2Font(latin_file.data.clone()).into())
} else if font == latin_ext {
Ok(RawWoff2Font(latin_ext_file.data.clone()).into())
} else {
Err(Status::NotFound)
let file_wrapper = get_file_wrapper()?;
match file_wrapper.hash_filename_get(font) {
Some(font) => {
if font.metadata.extension != "woff2" {
return Err(Status::NotFound);
}
Ok(RawWoff2Font(font.get_data()).into())
}
None => Err(Status::NotFound),
}
}
#[get("/woff/<font>")]
pub fn woff_font(font: &str) -> Result<CachedResponse<RawWoffFont>, Status> {
let all_file = &get_file_wrapper()?.atkinson_all_woff;
let all = all_file.metadata.get_hash_filename();
if font == all {
Ok(RawWoffFont(all_file.data.clone()).into())
} else {
Err(Status::NotFound)
let file_wrapper = get_file_wrapper()?;
match file_wrapper.hash_filename_get(font) {
Some(font) => {
if font.metadata.extension != "woff" {
return Err(Status::NotFound);
}
Ok(RawWoffFont(font.get_data()).into())
}
None => Err(Status::NotFound),
}
}

View file

@ -1,9 +1,29 @@
use shared::names::Site;
trait IndexArithmetic {
fn index_add(self, length: usize, num: usize) -> usize;
fn index_subtract(self, length: usize, num: usize) -> usize;
}
impl IndexArithmetic for usize {
fn index_add(self, length: usize, num: usize) -> usize {
if self > (length - 1) {
return 0;
}
return self + num;
}
fn index_subtract(self, length: usize, num: usize) -> usize {
match self.checked_sub(num) {
Some(num) => num,
None => length - 1,
}
}
}
pub fn previous_url(source_url: &String, names: &Vec<Site>) -> Option<String> {
match names.iter().position(|r| &r.url == source_url) {
Some(index) if index == 0 => Some(names[names.len() - 1].url.to_string()),
Some(index) => Some(names[index - 1].url.to_string()),
Some(index) => Some(names[index.index_subtract(names.len(), 1)].url.clone()),
None => None,
}
}
@ -11,19 +31,14 @@ pub fn previous_url(source_url: &String, names: &Vec<Site>) -> Option<String> {
pub fn next_url(source_url: &String, names: &Vec<Site>) -> Option<String> {
// this is gay
match names.iter().position(|r| &r.url == source_url) {
Some(index) if index == names.len() - 1 => Some(names[0].url.to_string()),
Some(index) => Some(names[index + 1].url.to_string()),
Some(index) => Some(names[index.index_add(names.len(), 1)].url.clone()),
None => None,
}
}
pub fn previous_name(source_url: &String, names: &Vec<Site>) -> Option<String> {
match names.iter().position(|r| &r.url == source_url) {
Some(index) if index == 0 => match &names[names.len() - 1].name {
Some(name) => Some(name.clone()),
None => previous_url(source_url, names),
},
Some(index) => match &names[index - 1].name {
Some(index) => match &names[index.index_subtract(names.len(), 1)].name {
Some(name) => Some(name.clone()),
None => previous_url(source_url, names),
},
@ -33,11 +48,7 @@ pub fn previous_name(source_url: &String, names: &Vec<Site>) -> Option<String> {
pub fn next_name(source_url: &String, names: &Vec<Site>) -> Option<String> {
match names.iter().position(|r| &r.url == source_url) {
Some(index) if index == names.len() - 1 => match &names[0].name {
Some(name) => Some(name.clone()),
None => next_url(source_url, names),
},
Some(index) => match &names[index + 1].name {
Some(index) => match &names[index.index_add(names.len(), 1)].name {
Some(name) => Some(name.clone()),
None => next_url(source_url, names),
},

View file

@ -15,7 +15,7 @@ mod watcher;
#[launch]
async fn rocket() -> _ {
init_names().unwrap();
initialize_files();
initialize_files().unwrap();
tokio::task::spawn_blocking(hot_reloading);
rocket::build()

View file

@ -4,7 +4,7 @@ use crate::{
sites::get_global_names,
};
use meowy_assets::{
files::{get_file_wrapper, FileMetadata},
files::{get_file_wrapper, File},
templates::{BaseTemplate, ErrorTemplate, IndexTemplate},
};
use rocket::{
@ -13,15 +13,18 @@ use rocket::{
serde::{json::Json, Serialize},
};
fn get_hash_filename(metadata: &FileMetadata) -> Result<String, Status> {
Ok(metadata.get_hash_filename())
fn get_file(filename: &str) -> Result<&File, Status> {
let files = get_file_wrapper()?;
match files.filename_get(filename) {
Some(file) => Ok(file),
None => Err(Status::NotFound),
}
}
fn get_base_template() -> Result<BaseTemplate, Status> {
let files = get_file_wrapper()?;
let hyperlegible_filename = get_hash_filename(&files.hyperlegible.metadata)?;
let style_filename = get_hash_filename(&files.style.metadata)?;
let hyperlegible_filename = get_file("hyperlegible")?.metadata.get_hash_filename();
let style_filename = get_file("style")?.metadata.get_hash_filename();
let template = BaseTemplate {
hyperlegible_filename,