1use crate::errors::HttpResult;
4use crate::response::{ElifResponse, ElifStatusCode, IntoElifResponse};
5use axum::{
6 extract::{FromRequest, Request},
7 response::{IntoResponse, Response},
8 Json,
9};
10use serde::{de::DeserializeOwned, Deserialize, Serialize};
11use std::ops::{Deref, DerefMut};
12
13#[derive(Debug)]
15pub struct ElifJson<T>(pub T);
16
17impl<T> ElifJson<T> {
18 pub fn new(data: T) -> Self {
20 Self(data)
21 }
22
23 pub fn into_inner(self) -> T {
25 self.0
26 }
27}
28
29impl<T> Deref for ElifJson<T> {
30 type Target = T;
31
32 fn deref(&self) -> &Self::Target {
33 &self.0
34 }
35}
36
37impl<T> DerefMut for ElifJson<T> {
38 fn deref_mut(&mut self) -> &mut Self::Target {
39 &mut self.0
40 }
41}
42
43impl<T> From<T> for ElifJson<T> {
44 fn from(data: T) -> Self {
45 Self(data)
46 }
47}
48
49#[axum::async_trait]
51impl<T, S> FromRequest<S> for ElifJson<T>
52where
53 T: DeserializeOwned,
54 S: Send + Sync,
55{
56 type Rejection = JsonError;
57
58 async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
59 match Json::<T>::from_request(req, state).await {
60 Ok(Json(data)) => Ok(ElifJson(data)),
61 Err(rejection) => Err(JsonError::from_axum_json_rejection(rejection)),
62 }
63 }
64}
65
66impl<T> IntoElifResponse for ElifJson<T>
68where
69 T: Serialize,
70{
71 fn into_response(self) -> ElifResponse {
72 match ElifResponse::ok().json(&self.0) {
73 Ok(response) => response,
74 Err(_) => ElifResponse::internal_server_error().text("JSON serialization failed"),
75 }
76 }
77}
78
79impl<T> IntoResponse for ElifJson<T>
81where
82 T: Serialize,
83{
84 fn into_response(self) -> Response {
85 match serde_json::to_vec(&self.0) {
86 Ok(bytes) => {
87 let mut response = Response::new(bytes.into());
88 response.headers_mut().insert(
89 axum::http::header::CONTENT_TYPE,
90 axum::http::HeaderValue::from_static("application/json"),
91 );
92 response
93 }
94 Err(err) => {
95 tracing::error!("JSON serialization failed: {}", err);
96 let mut response =
97 Response::new("Internal server error: JSON serialization failed".into());
98 *response.status_mut() = axum::http::StatusCode::INTERNAL_SERVER_ERROR;
99 response.headers_mut().insert(
100 axum::http::header::CONTENT_TYPE,
101 axum::http::HeaderValue::from_static("text/plain"),
102 );
103 response
104 }
105 }
106 }
107}
108
109#[derive(Debug)]
111pub struct JsonError {
112 pub status: ElifStatusCode,
113 pub message: String,
114 pub details: Option<String>,
115}
116
117impl JsonError {
118 pub fn new(status: ElifStatusCode, message: String) -> Self {
120 Self {
121 status,
122 message,
123 details: None,
124 }
125 }
126
127 pub fn with_details(status: ElifStatusCode, message: String, details: String) -> Self {
129 Self {
130 status,
131 message,
132 details: Some(details),
133 }
134 }
135
136 pub(crate) fn from_axum_json_rejection(
138 rejection: axum::extract::rejection::JsonRejection,
139 ) -> Self {
140 use axum::extract::rejection::JsonRejection::*;
141
142 match rejection {
143 JsonDataError(err) => Self::with_details(
144 ElifStatusCode::BAD_REQUEST,
145 "Invalid JSON data".to_string(),
146 err.to_string(),
147 ),
148 JsonSyntaxError(err) => Self::with_details(
149 ElifStatusCode::BAD_REQUEST,
150 "JSON syntax error".to_string(),
151 err.to_string(),
152 ),
153 MissingJsonContentType(_) => Self::new(
154 ElifStatusCode::BAD_REQUEST,
155 "Missing 'Content-Type: application/json' header".to_string(),
156 ),
157 BytesRejection(err) => Self::with_details(
158 ElifStatusCode::BAD_REQUEST,
159 "Failed to read request body".to_string(),
160 err.to_string(),
161 ),
162 _ => Self::new(
163 ElifStatusCode::BAD_REQUEST,
164 "Invalid JSON request".to_string(),
165 ),
166 }
167 }
168}
169
170impl IntoResponse for JsonError {
171 fn into_response(self) -> Response {
172 let error_body = if let Some(details) = self.details {
173 serde_json::json!({
174 "error": {
175 "code": self.status.as_u16(),
176 "message": self.message,
177 "details": details
178 }
179 })
180 } else {
181 serde_json::json!({
182 "error": {
183 "code": self.status.as_u16(),
184 "message": self.message
185 }
186 })
187 };
188
189 match serde_json::to_vec(&error_body) {
190 Ok(bytes) => {
191 let mut response = Response::new(bytes.into());
192 *response.status_mut() = self.status.to_axum();
193 response.headers_mut().insert(
194 axum::http::header::CONTENT_TYPE,
195 axum::http::HeaderValue::from_static("application/json"),
196 );
197 response
198 }
199 Err(_) => {
200 let mut response = Response::new(self.message.into());
202 *response.status_mut() = self.status.to_axum();
203 response.headers_mut().insert(
204 axum::http::header::CONTENT_TYPE,
205 axum::http::HeaderValue::from_static("text/plain"),
206 );
207 response
208 }
209 }
210 }
211}
212
213pub struct JsonResponse;
215
216impl JsonResponse {
217 pub fn ok<T: Serialize>(data: &T) -> HttpResult<Response> {
219 ElifResponse::json_ok(data)
220 }
221
222 pub fn with_status<T: Serialize>(status: ElifStatusCode, data: &T) -> HttpResult<Response> {
224 ElifResponse::with_status(status).json(data)?.build()
225 }
226
227 pub fn paginated<T: Serialize>(
229 data: &[T],
230 page: u32,
231 per_page: u32,
232 total: u64,
233 ) -> HttpResult<Response> {
234 let total_pages = (total as f64 / per_page as f64).ceil() as u32;
235
236 let response_data = serde_json::json!({
237 "data": data,
238 "pagination": {
239 "page": page,
240 "per_page": per_page,
241 "total": total,
242 "total_pages": total_pages,
243 "has_next": page < total_pages,
244 "has_prev": page > 1
245 }
246 });
247
248 ElifResponse::ok().json_value(response_data).build()
249 }
250
251 pub fn error(status: ElifStatusCode, message: &str) -> HttpResult<Response> {
253 ElifResponse::json_error(status, message)
254 }
255
256 pub fn validation_error<T: Serialize>(errors: &T) -> HttpResult<Response> {
258 ElifResponse::validation_error(errors)
259 }
260
261 pub fn success_message(message: &str) -> HttpResult<Response> {
263 let response_data = serde_json::json!({
264 "success": true,
265 "message": message
266 });
267
268 ElifResponse::ok().json_value(response_data).build()
269 }
270
271 pub fn created<T: Serialize>(data: &T) -> HttpResult<Response> {
273 ElifResponse::created().json(data)?.build()
274 }
275
276 pub fn no_content() -> HttpResult<Response> {
278 ElifResponse::no_content().build()
279 }
280}
281
282#[derive(Debug, Serialize, Deserialize)]
284pub struct ValidationErrors {
285 pub errors: std::collections::HashMap<String, Vec<String>>,
286}
287
288impl ValidationErrors {
289 pub fn new() -> Self {
291 Self {
292 errors: std::collections::HashMap::new(),
293 }
294 }
295
296 pub fn add_error(&mut self, field: String, error: String) {
298 self.errors.entry(field).or_default().push(error);
299 }
300
301 pub fn add_errors(&mut self, field: String, errors: Vec<String>) {
303 self.errors.entry(field).or_default().extend(errors);
304 }
305
306 pub fn has_errors(&self) -> bool {
308 !self.errors.is_empty()
309 }
310
311 pub fn error_count(&self) -> usize {
313 self.errors.values().map(|v| v.len()).sum()
314 }
315
316 pub fn to_response(self) -> HttpResult<Response> {
318 JsonResponse::validation_error(&self)
319 }
320}
321
322impl Default for ValidationErrors {
323 fn default() -> Self {
324 Self::new()
325 }
326}
327
328#[derive(Debug, Serialize)]
330pub struct ApiResponse<T> {
331 pub success: bool,
332 pub data: Option<T>,
333 pub message: Option<String>,
334 pub errors: Option<serde_json::Value>,
335}
336
337impl<T: Serialize> ApiResponse<T> {
338 pub fn success(data: T) -> Self {
340 Self {
341 success: true,
342 data: Some(data),
343 message: None,
344 errors: None,
345 }
346 }
347
348 pub fn success_with_message(data: T, message: String) -> Self {
350 Self {
351 success: true,
352 data: Some(data),
353 message: Some(message),
354 errors: None,
355 }
356 }
357
358 pub fn error(message: String) -> ApiResponse<()> {
360 ApiResponse {
361 success: false,
362 data: None,
363 message: Some(message),
364 errors: None,
365 }
366 }
367
368 pub fn validation_error(message: String, errors: serde_json::Value) -> ApiResponse<()> {
370 ApiResponse {
371 success: false,
372 data: None,
373 message: Some(message),
374 errors: Some(errors),
375 }
376 }
377
378 pub fn to_response(self) -> HttpResult<Response> {
380 let status = if self.success {
381 ElifStatusCode::OK
382 } else {
383 ElifStatusCode::BAD_REQUEST
384 };
385
386 ElifResponse::with_status(status).json(&self)?.build()
387 }
388}
389
390impl<T: Serialize> IntoResponse for ApiResponse<T> {
391 fn into_response(self) -> Response {
392 match self.to_response() {
393 Ok(response) => response,
394 Err(e) => {
395 tracing::error!("Failed to create API response: {}", e);
396 (
397 ElifStatusCode::INTERNAL_SERVER_ERROR.to_axum(),
398 "Internal server error",
399 )
400 .into_response()
401 }
402 }
403 }
404}