pub mod web { /// Macro shenanigans to format a str at compile time pub macro make_static_cache_header($max_age:expr, $stale_revalidate:expr) {{ // type guards lmfao const _: ::std::time::Duration = $max_age; const _: ::std::time::Duration = $stale_revalidate; // hopefully ident encapsulation will save my ass here const MAX_AGE: u64 = $max_age.as_secs(); const STALE_REVALIDATE: u64 = $stale_revalidate.as_secs(); const_str::format!( "public, max-age={}, stale-while-revalidate={}", MAX_AGE, STALE_REVALIDATE ) }} #[cfg(test)] mod test { use std::time::Duration; #[test] fn const_concat_worked() { const MA: Duration = Duration::from_mins(15); const SR: Duration = Duration::from_hours(1); const NAME: &str = super::make_static_cache_header!(MA, SR); assert_eq!( NAME, format!( "public, max-age={}, stale-while-revalidate={}", MA.as_secs(), SR.as_secs() ) ); } } } pub mod shasum { #[must_use] pub fn sha256sum_to_hex_string(sha256sum: &[u8]) -> String { let mut out = String::with_capacity(sha256sum.len() * 2); for &b in sha256sum { /// Only correct as long as `n` is ranged within a nibble #[inline] const fn nibble_to_hex(n: u8) -> char { (match n { 0..=9 => b'0' + n, _ => b'a' + (n - 10), }) as char } let hi = (b >> 4) & 0x0f; let lo = b & 0x0f; out.push(nibble_to_hex(hi)); out.push(nibble_to_hex(lo)); } out } #[cfg(test)] mod test { use crate::utils::shasum::sha256sum_to_hex_string; #[test] fn sha256sum_string() { let null_sha = sha256sum_to_hex_string(&[0; 32]); let full_sha = sha256sum_to_hex_string(&[0xff; 32]); assert_eq!( null_sha, "0000000000000000000000000000000000000000000000000000000000000000" ); assert_eq!( full_sha, "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" ); } } } pub mod fs { use std::{io, os::unix::fs::MetadataExt as _, path::Path}; use tokio::{fs::File, io::AsyncReadExt}; /// # Errors /// /// On any underlaying file i/o error. /// /// # Panics /// /// If a file's [`u64`] doesn't fit in memory. pub async fn read_limited_path(path: &Path) -> io::Result> { let f = File::open(path).await?; let size = f.metadata().await?.size(); if size > MAXSIZE { return Err(io::Error::new( io::ErrorKind::FileTooLarge, "filesize is bigger than MAXSIZE", )); } let mut buf = Vec::with_capacity(size.try_into().expect("u64 to fit in usize")); // `.take()` just in case an open fd happens to grow, `.metadata()` SHOULD take // properties from the fd and lock the read fd until its closed but still f.take(MAXSIZE).read_to_end(&mut buf).await?; Ok(buf) } /// # Errors /// /// On any underlaying file i/o error. /// /// # Panics /// /// If a file's [`u64`] doesn't fit in memory. pub async fn read_limited_path_str(path: &Path) -> io::Result { let f = File::open(path).await?; let size = f.metadata().await?.size(); if size > MAXSIZE { return Err(io::Error::new( io::ErrorKind::FileTooLarge, "filesize is bigger than MAXSIZE", )); } let mut buf = String::with_capacity(size.try_into().expect("u64 to fit in usize")); // `.take()` just in case an open fd happens to grow, `.metadata()` SHOULD take // properties from the fd and lock the read fd until its closed but still f.take(MAXSIZE).read_to_string(&mut buf).await?; Ok(buf) } }