#![allow(dead_code)]
pub mod mime;
use std::fs::File;
use std::io::Read;
use std::path::{Component, Path, PathBuf};
use xxhash_rust::xxh64::xxh64;
pub fn hash_string(s: &str) -> String {
format!("{:016x}", xxh64(s.as_bytes(), 0))
}
pub fn hash_file(path: &Path) -> std::io::Result<String> {
let mut file = File::open(path)?;
let mut hasher = xxhash_rust::xxh64::Xxh64::new(0);
let mut buffer = [0u8; 65536];
loop {
let count = file.read(&mut buffer)?;
if count == 0 {
break;
}
hasher.update(&buffer[..count]);
}
Ok(format!("{:016x}", hasher.digest()))
}
pub fn hash_bytes(bytes: &[u8]) -> String {
format!("{:016x}", xxh64(bytes, 0))
}
pub fn sanitize_request_path(raw_path: &str) -> Option<PathBuf> {
let unescaped = urlencoding::decode(raw_path).ok()?;
let path = Path::new(unescaped.as_ref());
let mut clean_path = PathBuf::new();
for comp in path.components() {
match comp {
Component::Normal(c) => clean_path.push(c),
Component::RootDir => {}
Component::CurDir => {}
Component::ParentDir | Component::Prefix(_) => return None,
}
}
Some(clean_path)
}
pub fn has_traversal(p: &str) -> bool {
p.contains("..") || p.contains("./") && p.starts_with('.')
}
pub fn image_exists(path: &Path) -> bool {
if let Ok(metadata) = std::fs::metadata(path) {
metadata.is_file() && metadata.len() > 0
} else {
false
}
}
pub fn atomic_write(dst: &Path, content: &[u8]) -> std::io::Result<()> {
if let Some(parent) = dst.parent() {
std::fs::create_dir_all(parent)?;
}
let temp_name = format!("{}.tmp.{}", dst.to_string_lossy(), fastrand::u64(..));
let temp_path = PathBuf::from(temp_name);
std::fs::write(&temp_path, content)?;
std::fs::rename(&temp_path, dst)
}
pub fn get_compression_rate(raw_size: u64, opt_size: u64) -> String {
if raw_size == 0 {
return "1.00".to_string();
}
format!("{:.2}", opt_size as f64 / raw_size as f64)
}
pub fn get_extension(filename: &str) -> String {
Path::new(filename)
.extension()
.and_then(|ext| ext.to_str())
.map(|s| s.to_lowercase())
.unwrap_or_default()
}
pub fn get_sharded_path(base_dir: &Path, subdir: &str, id: &str, ext: &str) -> PathBuf {
if id.len() >= 4 {
base_dir
.join(subdir)
.join(&id[0..2])
.join(&id[2..4])
.join(format!("{}.{}", id, ext))
} else {
base_dir
.join(subdir)
.join(format!("{}.{}", id, ext))
}
}