use axum::http::StatusCode;
use axum::response::{IntoResponse, Response};
use serde::Serialize;
#[derive(Debug, Serialize)]
struct ErrorBody {
error: String,
status: u16,
}
pub(crate) fn json_error_response(status: StatusCode, message: impl Into<String>) -> Response {
let body = ErrorBody {
error: message.into(),
status: status.as_u16(),
};
let json = serde_json::to_string(&body).unwrap_or_else(|_| {
format!(
r#"{{"error":"internal server error","status":{}}}"#,
status.as_u16()
)
});
(status, [("content-type", "application/json")], json).into_response()
}
pub async fn fallback_404() -> Response {
json_error_response(StatusCode::NOT_FOUND, "not found")
}
pub async fn fallback_405() -> Response {
json_error_response(StatusCode::METHOD_NOT_ALLOWED, "method not allowed")
}
#[cfg(test)]
mod tests {
use super::*;
use http_body_util::BodyExt;
async fn response_json(resp: Response) -> serde_json::Value {
let bytes = resp.into_body().collect().await.unwrap().to_bytes();
serde_json::from_slice(&bytes).unwrap()
}
#[tokio::test]
async fn fallback_404_returns_json() {
let resp = fallback_404().await;
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
assert_eq!(
resp.headers().get("content-type").unwrap(),
"application/json"
);
let json = response_json(resp).await;
assert_eq!(json["error"], "not found");
assert_eq!(json["status"], 404);
}
#[tokio::test]
async fn fallback_405_returns_json() {
let resp = fallback_405().await;
assert_eq!(resp.status(), StatusCode::METHOD_NOT_ALLOWED);
assert_eq!(
resp.headers().get("content-type").unwrap(),
"application/json"
);
let json = response_json(resp).await;
assert_eq!(json["error"], "method not allowed");
assert_eq!(json["status"], 405);
}
}