use axum::{http::StatusCode, response::IntoResponse, Json};
use serde_json::json;
use std::path::PathBuf;
use super::super::safe_path::VerifiedRoot;
#[allow(clippy::result_large_err)]
pub(crate) fn validate_resource_name(
name: &str,
label: &str,
reserved: Option<&str>,
) -> Result<(), axum::response::Response> {
use super::super::safe_path::SafePath;
let is_reserved = reserved.is_some_and(|r| name == r);
if is_reserved || SafePath::validate_name(name).is_err() {
return Err((
StatusCode::BAD_REQUEST,
Json(json!({"error": format!("Invalid {} name", label)})),
)
.into_response());
}
Ok(())
}
#[allow(clippy::result_large_err)]
pub(crate) fn require_configured_dir(
dir: &Option<PathBuf>,
label: &str,
status: StatusCode,
) -> Result<PathBuf, axum::response::Response> {
dir.clone().ok_or_else(|| {
(
status,
Json(json!({"error": format!("No {} directory configured", label)})),
)
.into_response()
})
}
#[allow(clippy::result_large_err)]
pub(crate) fn resolve_resource_path(
dir: &std::path::Path,
name: &str,
ext: &str,
) -> Result<PathBuf, axum::response::Response> {
let root = VerifiedRoot::new(dir).map_err(|e| e.into_response())?;
let file_path = root.as_path().join(format!("{}.{}", name, ext));
if file_path.exists() {
let safe = super::super::safe_path::SafePath::resolve(&file_path, &root)
.map_err(|e| e.into_response())?;
Ok(safe.as_path().to_path_buf())
} else {
Ok(file_path)
}
}
pub(crate) async fn spawn_blocking_io<F, T, E>(
label: &str,
f: F,
) -> Result<T, axum::response::Response>
where
F: FnOnce() -> Result<T, E> + Send + 'static,
T: Send + 'static,
E: std::fmt::Display + Send + 'static,
{
let label = label.to_string();
match tokio::task::spawn_blocking(f).await {
Ok(Ok(val)) => Ok(val),
Ok(Err(e)) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({"error": format!("Failed to {}: {}", label, e)})),
)
.into_response()),
Err(e) => Err((
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({"error": format!("{} task failed: {}", label, e)})),
)
.into_response()),
}
}