140 lines
4.2 KiB
Rust
140 lines
4.2 KiB
Rust
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<const MAXSIZE: u64>(path: &Path) -> io::Result<Vec<u8>> {
|
|
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<const MAXSIZE: u64>(path: &Path) -> io::Result<String> {
|
|
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)
|
|
}
|
|
}
|