use std::path::{Path, PathBuf}; const DEFAULT_UPLOAD_DIR: &str = "uploads"; const UPLOAD_DIR_ENV: &str = "WEB_PETTING_UPLOAD_DIR"; pub fn media_dir(client_id: i64, visit_id: i64) -> String { format!("{DEFAULT_UPLOAD_DIR}/{client_id}/{visit_id}") } pub fn testimonials_dir() -> String { format!("{DEFAULT_UPLOAD_DIR}/testimonials") } pub fn join_db_path(dir: &str, filename: &str) -> String { format!("{}/{}", dir.trim_end_matches('/'), filename) } pub fn resolve_db_path(db_path: &str) -> PathBuf { let path = PathBuf::from(db_path); if path.is_absolute() { return path; } let Some(upload_root) = std::env::var_os(UPLOAD_DIR_ENV) else { return path; }; let upload_root = PathBuf::from(upload_root); let logical_path = Path::new(db_path); match logical_path.strip_prefix(DEFAULT_UPLOAD_DIR) { Ok(stripped) => upload_root.join(stripped), Err(_) => upload_root.join(logical_path), } } pub fn resolved_display_path(db_path: &str) -> String { resolve_db_path(db_path).display().to_string() } pub async fn create_logical_dir(db_dir: &str) -> std::io::Result<()> { tokio::fs::create_dir_all(resolve_db_path(db_dir)).await } pub async fn write_db_file(db_path: &str, data: &[u8]) -> std::io::Result<()> { let physical_path = resolve_db_path(db_path); if let Some(parent) = physical_path.parent() { tokio::fs::create_dir_all(parent).await?; } tokio::fs::write(physical_path, data).await } pub async fn read_db_file(db_path: &str) -> std::io::Result> { tokio::fs::read(resolve_db_path(db_path)).await } pub async fn remove_db_file(db_path: &str) -> std::io::Result<()> { tokio::fs::remove_file(resolve_db_path(db_path)).await }