use hyper::HeaderMap;
use tokio::task;
use std::{fs, path::Path};
use crate::{
response::{
error_response::{internal_server_error_response, not_found_response},
file_response::FileResponse,
},
types::BoxBody,
};
use apimock_routing::util::json::JSON_COMPATIBLE_EXTENSIONS;
pub async fn dyn_route_content(
url_path: &str,
fallback_respond_dir: &str,
request_headers: &HeaderMap,
) -> Result<hyper::Response<BoxBody>, hyper::http::Error> {
let request_path =
Path::new(fallback_respond_dir).join(url_path.strip_prefix("/").unwrap_or_default());
let request_file_name = request_path
.file_name()
.unwrap_or_default()
.to_str()
.unwrap_or_default();
let Some(parent) = request_path.parent() else {
return internal_server_error_response(
&format!("parent dir not found: url_path = {}", url_path),
request_headers,
);
};
if !parent.exists() {
return not_found_response(request_headers);
}
let dir = parent.to_owned();
let dir_for_blocking_task = dir.clone();
let read_dir_result = task::spawn_blocking(move || -> Result<Vec<_>, String> {
let entries = fs::read_dir(dir_for_blocking_task.as_path()).map_err(|err| {
format!(
"failed to get dir: {} ({})",
dir_for_blocking_task.to_string_lossy(),
err
)
})?;
entries
.map(|entry| {
entry.map_err(|err| {
format!(
"failed to get dir entry from dir: {} ({})",
dir_for_blocking_task.to_string_lossy(),
err
)
})
})
.collect()
})
.await;
let entries = match read_dir_result {
Ok(Ok(v)) => v,
Ok(Err(err)) => {
return internal_server_error_response(err.as_str(), request_headers);
}
Err(err) => {
return internal_server_error_response(
&format!("failed to get dir entries (async handling: {})", err),
request_headers,
);
}
};
let mut found = entries.into_iter().find_map(|entry| {
let path = entry.path();
let name = path
.file_name()
.unwrap_or_default()
.to_str()
.unwrap_or_default()
.to_owned();
if name.eq_ignore_ascii_case(request_file_name) {
Some(path)
} else {
None
}
});
if found.is_none() && request_path.extension().is_none() {
if let Some(stem) = request_path.file_stem().and_then(|s| s.to_str()) {
for ext in JSON_COMPATIBLE_EXTENSIONS {
let file_path = dir.join(format!("{}.{}", stem, ext));
if file_path.exists() {
found = Some(file_path);
break;
}
}
}
}
let Some(found) = found else {
return not_found_response(request_headers);
};
let file_path = found.to_str().unwrap_or_default();
FileResponse::new(file_path, None, request_headers)
.file_content_response()
.await
}