use axum::{
body::Body,
http::{HeaderValue, StatusCode, header},
response::Response,
};
use tokio_util::io::ReaderStream;
use crate::error::ApiError;
pub async fn serve_local_file(
path: &std::path::Path,
download_name: &str,
mime: &str,
) -> Result<Response, ApiError> {
let file = tokio::fs::File::open(path)
.await
.map_err(|e| ApiError::NotFound(format!("raw file not found on disk: {e}")))?;
let metadata = file
.metadata()
.await
.map_err(|e| ApiError::Internal(anyhow::anyhow!("metadata read error: {e}")))?;
let content_length = metadata.len();
let stream = ReaderStream::new(file);
let body = Body::from_stream(stream);
let content_disposition = format!("attachment; filename=\"{download_name}\"");
let response = Response::builder()
.status(StatusCode::OK)
.header(
header::CONTENT_TYPE,
HeaderValue::from_str(mime)
.unwrap_or_else(|_| HeaderValue::from_static("application/octet-stream")),
)
.header(
header::CONTENT_DISPOSITION,
HeaderValue::from_str(&content_disposition)
.unwrap_or_else(|_| HeaderValue::from_static("attachment")),
)
.header(header::CONTENT_LENGTH, content_length.to_string())
.body(body)
.map_err(|e| ApiError::Internal(anyhow::anyhow!("response build error: {e}")))?;
Ok(response)
}
#[allow(clippy::result_large_err)] pub fn serve_bytes(data: Vec<u8>, download_name: &str, mime: &str) -> Result<Response, ApiError> {
let content_disposition = format!("attachment; filename=\"{download_name}\"");
let response = Response::builder()
.status(StatusCode::OK)
.header(
header::CONTENT_TYPE,
HeaderValue::from_str(mime)
.unwrap_or_else(|_| HeaderValue::from_static("application/octet-stream")),
)
.header(
header::CONTENT_DISPOSITION,
HeaderValue::from_str(&content_disposition)
.unwrap_or_else(|_| HeaderValue::from_static("attachment")),
)
.header(header::CONTENT_LENGTH, data.len().to_string())
.body(Body::from(data))
.map_err(|e| ApiError::Internal(anyhow::anyhow!("response build error: {e}")))?;
Ok(response)
}