static-site-server-rs/src/utils.rs

161 lines
5.2 KiB
Rust

use std::{
ffi::OsStr,
fs,
path::{Path, PathBuf},
time::SystemTime,
};
use chrono::{DateTime, Utc};
pub static DOUBLE_NEW_LINES_UNIX: [u8; 2] = [b'\n', b'\n'];
pub static DOUBLE_NEW_LINES_WIN: [u8; 4] = [b'\r', b'\n', b'\r', b'\n'];
pub const STATIC_404_DEFAULT: &str = r#"<!DOCTYPE html>
<html lang="en">
<head>
<title>Not Found</title>
</head>
<body>
<main>
<h1>Not Found</h1>
<p>The requested resource could not be found.</p>
<p><a href="https://simple.wikipedia.org/wiki/HTTP_404"><small><em>
Read mode about this error.
</em></small></a></p>
</main>
</body>
</html>"#;
pub const STATIC_500_DEFAULT: &str = r#"<!DOCTYPE html>
<html lang="en">
<head>
<title>Internal Server Error</title>
</head>
<body>
<main>
<h1>Internal Server Error</h1>
<p>DEFAULT_ERROR_MESSAGE</p>
<p><a href="https://en.wikipedia.org/wiki/HTTP_500"><small><em>
Read mode about this error.
</em></small></a></p>
</main>
</body>
</html>"#;
pub fn render_http500(reason: Option<&str>) -> String {
STATIC_500_DEFAULT.replace(
"DEFAULT_ERROR_MESSAGE",
reason.unwrap_or("A generic and unspecified error happened server-side."),
)
}
pub const HEX_ASCII_VALID_STR: &str = "0123456789ABCDEFabcdef";
//pub static HEX_ASCII_VALID_STA: &str = HEX_ASCII_VALID_STR;
pub static HEX_ASCII_VALID_STA_U8: &[u8] = HEX_ASCII_VALID_STR.as_bytes();
pub static PCT_ASCII: u8 = "%".as_bytes()[0];
pub static QSM_ASCII: u8 = "?".as_bytes()[0];
pub static AMP_ASCII: u8 = "&".as_bytes()[0];
pub static EQL_ASCII: u8 = "=".as_bytes()[0];
// pub fn charhex2val(c: char) -> Option<u8> {
// HEX_ASCII_VALID_STA.chars().position(|x| x == c).map(|x| (if x < 16 {x} else {x-6}) as u8 )
// }
pub fn bytehex2val(c: u8) -> Option<u8> {
HEX_ASCII_VALID_STA_U8
.iter()
.position(|x| *x == c)
.map(|x| (if x < 16 { x } else { x - 6 }) as u8)
}
pub fn content_type_for_exension_osstr(ext: Option<&OsStr>) -> Option<String> {
content_type_for_exension(ext.and_then(|x| x.to_str()))
}
pub fn content_type_for_exension(ext: Option<&str>) -> Option<String> {
Some(
mime_guess::from_ext(ext?)
.first()?
.essence_str()
.to_string(),
)
}
pub fn parse_modified_since<T: AsRef<str>>(modified_since: Option<T>) -> Option<DateTime<Utc>> {
modified_since.and_then(|s| {
DateTime::parse_from_rfc2822(s.as_ref())
.ok()
.map(|x| x.into())
})
}
pub fn parse_modified_since_or_epoch<T: AsRef<str>>(modified_since: Option<T>) -> DateTime<Utc> {
parse_modified_since(modified_since)
.unwrap_or_else(|| DateTime::<Utc>::from(SystemTime::UNIX_EPOCH))
}
pub fn datetime_to_secs(datetime: DateTime<Utc>) -> i64 {
datetime
.signed_duration_since::<Utc>(DateTime::<Utc>::from(SystemTime::UNIX_EPOCH))
.num_seconds()
}
// pub fn parse_modified_since_or_epoch_as_secs<T: AsRef<str>>(modified_since: Option<T>) -> i64 {
// datetime_to_secs(parse_modified_since_or_epoch(modified_since))
// }
pub fn gen_true() -> bool {
true
}
pub fn escape_html(src: &str) -> String {
src.replace('&', "&amp;")
.replace('<', "&lt;")
.replace('>', "&gt;")
.replace('"', "&quot;")
.replace('\'', "&#039;")
}
#[inline(always)]
pub fn search_for_file<'a>(
route_dot_path: &'a Path,
requested_file: &'a str,
try_files: &'a [String],
subdomain: Option<&'a str>,
) -> impl Iterator<Item = PathBuf> + 'a {
route_dot_path
.canonicalize()
.into_iter()
.flat_map(move |route_path_canonical| {
Some(route_path_canonical.clone())
.into_iter()
.chain(subdomain.and_then(|df| route_dot_path.join(df).canonicalize().ok()))
.map(move |x| (x, route_path_canonical.clone()))
})
.flat_map(move |(route_path, route_path_canonical)| {
let requested_relative_path = route_path.join(requested_file);
Some(requested_relative_path.clone())
.into_iter()
.chain(try_files.iter().flat_map(move |try_file| {
Some(requested_relative_path.join(try_file))
.into_iter()
.chain(try_file.starts_with('.').then_some(()).and_then(|_| {
requested_relative_path
.file_name()
.and_then(OsStr::to_str)
.map(|fnm| {
requested_relative_path
.with_file_name(format!("{}{}", fnm, try_file))
})
}))
}))
.map(move |x| (x, route_path_canonical.clone()))
})
.filter_map(move |(possible_path, route_path_canonical)| {
fs::canonicalize(possible_path)
.into_iter()
.find(|canonical_path| {
canonical_path.is_file() && canonical_path.starts_with(&route_path_canonical)
})
})
}