use crate::errors::HttpResult;
use crate::response::{ElifResponse, ElifStatusCode, IntoElifResponse};
use axum::{
extract::{FromRequest, Request},
response::{IntoResponse, Response},
Json,
};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::ops::{Deref, DerefMut};
#[derive(Debug)]
pub struct ElifJson<T>(pub T);
impl<T> ElifJson<T> {
pub fn new(data: T) -> Self {
Self(data)
}
pub fn into_inner(self) -> T {
self.0
}
}
impl<T> Deref for ElifJson<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for ElifJson<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<T> From<T> for ElifJson<T> {
fn from(data: T) -> Self {
Self(data)
}
}
#[axum::async_trait]
impl<T, S> FromRequest<S> for ElifJson<T>
where
T: DeserializeOwned,
S: Send + Sync,
{
type Rejection = JsonError;
async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
match Json::<T>::from_request(req, state).await {
Ok(Json(data)) => Ok(ElifJson(data)),
Err(rejection) => Err(JsonError::from_axum_json_rejection(rejection)),
}
}
}
impl<T> IntoElifResponse for ElifJson<T>
where
T: Serialize,
{
fn into_response(self) -> ElifResponse {
match ElifResponse::ok().json(&self.0) {
Ok(response) => response,
Err(_) => ElifResponse::internal_server_error().text("JSON serialization failed"),
}
}
}
impl<T> IntoResponse for ElifJson<T>
where
T: Serialize,
{
fn into_response(self) -> Response {
match serde_json::to_vec(&self.0) {
Ok(bytes) => {
let mut response = Response::new(bytes.into());
response.headers_mut().insert(
axum::http::header::CONTENT_TYPE,
axum::http::HeaderValue::from_static("application/json"),
);
response
}
Err(err) => {
tracing::error!("JSON serialization failed: {}", err);
let mut response =
Response::new("Internal server error: JSON serialization failed".into());
*response.status_mut() = axum::http::StatusCode::INTERNAL_SERVER_ERROR;
response.headers_mut().insert(
axum::http::header::CONTENT_TYPE,
axum::http::HeaderValue::from_static("text/plain"),
);
response
}
}
}
}
#[derive(Debug)]
pub struct JsonError {
pub status: ElifStatusCode,
pub message: String,
pub details: Option<String>,
}
impl JsonError {
pub fn new(status: ElifStatusCode, message: String) -> Self {
Self {
status,
message,
details: None,
}
}
pub fn with_details(status: ElifStatusCode, message: String, details: String) -> Self {
Self {
status,
message,
details: Some(details),
}
}
pub(crate) fn from_axum_json_rejection(
rejection: axum::extract::rejection::JsonRejection,
) -> Self {
use axum::extract::rejection::JsonRejection::*;
match rejection {
JsonDataError(err) => Self::with_details(
ElifStatusCode::BAD_REQUEST,
"Invalid JSON data".to_string(),
err.to_string(),
),
JsonSyntaxError(err) => Self::with_details(
ElifStatusCode::BAD_REQUEST,
"JSON syntax error".to_string(),
err.to_string(),
),
MissingJsonContentType(_) => Self::new(
ElifStatusCode::BAD_REQUEST,
"Missing 'Content-Type: application/json' header".to_string(),
),
BytesRejection(err) => Self::with_details(
ElifStatusCode::BAD_REQUEST,
"Failed to read request body".to_string(),
err.to_string(),
),
_ => Self::new(
ElifStatusCode::BAD_REQUEST,
"Invalid JSON request".to_string(),
),
}
}
}
impl IntoResponse for JsonError {
fn into_response(self) -> Response {
let error_body = if let Some(details) = self.details {
serde_json::json!({
"error": {
"code": self.status.as_u16(),
"message": self.message,
"details": details
}
})
} else {
serde_json::json!({
"error": {
"code": self.status.as_u16(),
"message": self.message
}
})
};
match serde_json::to_vec(&error_body) {
Ok(bytes) => {
let mut response = Response::new(bytes.into());
*response.status_mut() = self.status.to_axum();
response.headers_mut().insert(
axum::http::header::CONTENT_TYPE,
axum::http::HeaderValue::from_static("application/json"),
);
response
}
Err(_) => {
let mut response = Response::new(self.message.into());
*response.status_mut() = self.status.to_axum();
response.headers_mut().insert(
axum::http::header::CONTENT_TYPE,
axum::http::HeaderValue::from_static("text/plain"),
);
response
}
}
}
}
pub struct JsonResponse;
impl JsonResponse {
pub fn ok<T: Serialize>(data: &T) -> HttpResult<Response> {
ElifResponse::json_ok(data)
}
pub fn with_status<T: Serialize>(status: ElifStatusCode, data: &T) -> HttpResult<Response> {
ElifResponse::with_status(status).json(data)?.build()
}
pub fn paginated<T: Serialize>(
data: &[T],
page: u32,
per_page: u32,
total: u64,
) -> HttpResult<Response> {
let total_pages = (total as f64 / per_page as f64).ceil() as u32;
let response_data = serde_json::json!({
"data": data,
"pagination": {
"page": page,
"per_page": per_page,
"total": total,
"total_pages": total_pages,
"has_next": page < total_pages,
"has_prev": page > 1
}
});
ElifResponse::ok().json_value(response_data).build()
}
pub fn error(status: ElifStatusCode, message: &str) -> HttpResult<Response> {
ElifResponse::json_error(status, message)
}
pub fn validation_error<T: Serialize>(errors: &T) -> HttpResult<Response> {
ElifResponse::validation_error(errors)
}
pub fn success_message(message: &str) -> HttpResult<Response> {
let response_data = serde_json::json!({
"success": true,
"message": message
});
ElifResponse::ok().json_value(response_data).build()
}
pub fn created<T: Serialize>(data: &T) -> HttpResult<Response> {
ElifResponse::created().json(data)?.build()
}
pub fn no_content() -> HttpResult<Response> {
ElifResponse::no_content().build()
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ValidationErrors {
pub errors: std::collections::HashMap<String, Vec<String>>,
}
impl ValidationErrors {
pub fn new() -> Self {
Self {
errors: std::collections::HashMap::new(),
}
}
pub fn add_error(&mut self, field: String, error: String) {
self.errors.entry(field).or_default().push(error);
}
pub fn add_errors(&mut self, field: String, errors: Vec<String>) {
self.errors.entry(field).or_default().extend(errors);
}
pub fn has_errors(&self) -> bool {
!self.errors.is_empty()
}
pub fn error_count(&self) -> usize {
self.errors.values().map(|v| v.len()).sum()
}
pub fn to_response(self) -> HttpResult<Response> {
JsonResponse::validation_error(&self)
}
}
impl Default for ValidationErrors {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Serialize)]
pub struct ApiResponse<T> {
pub success: bool,
pub data: Option<T>,
pub message: Option<String>,
pub errors: Option<serde_json::Value>,
}
impl<T: Serialize> ApiResponse<T> {
pub fn success(data: T) -> Self {
Self {
success: true,
data: Some(data),
message: None,
errors: None,
}
}
pub fn success_with_message(data: T, message: String) -> Self {
Self {
success: true,
data: Some(data),
message: Some(message),
errors: None,
}
}
pub fn error(message: String) -> ApiResponse<()> {
ApiResponse {
success: false,
data: None,
message: Some(message),
errors: None,
}
}
pub fn validation_error(message: String, errors: serde_json::Value) -> ApiResponse<()> {
ApiResponse {
success: false,
data: None,
message: Some(message),
errors: Some(errors),
}
}
pub fn to_response(self) -> HttpResult<Response> {
let status = if self.success {
ElifStatusCode::OK
} else {
ElifStatusCode::BAD_REQUEST
};
ElifResponse::with_status(status).json(&self)?.build()
}
}
impl<T: Serialize> IntoResponse for ApiResponse<T> {
fn into_response(self) -> Response {
match self.to_response() {
Ok(response) => response,
Err(e) => {
tracing::error!("Failed to create API response: {}", e);
(
ElifStatusCode::INTERNAL_SERVER_ERROR.to_axum(),
"Internal server error",
)
.into_response()
}
}
}
}