1use axum::{
6 extract::{FromRequest, Request},
7 response::{IntoResponse, Response},
8 http::{StatusCode, HeaderMap},
9 Json,
10};
11use serde::{Deserialize, Serialize, de::DeserializeOwned};
12use std::ops::{Deref, DerefMut};
13use crate::error::{HttpError, HttpResult};
14use crate::response::ElifResponse;
15
16#[derive(Debug)]
18pub struct ElifJson<T>(pub T);
19
20impl<T> ElifJson<T> {
21 pub fn new(data: T) -> Self {
23 Self(data)
24 }
25
26 pub fn into_inner(self) -> T {
28 self.0
29 }
30}
31
32impl<T> Deref for ElifJson<T> {
33 type Target = T;
34
35 fn deref(&self) -> &Self::Target {
36 &self.0
37 }
38}
39
40impl<T> DerefMut for ElifJson<T> {
41 fn deref_mut(&mut self) -> &mut Self::Target {
42 &mut self.0
43 }
44}
45
46impl<T> From<T> for ElifJson<T> {
47 fn from(data: T) -> Self {
48 Self(data)
49 }
50}
51
52#[axum::async_trait]
54impl<T, S> FromRequest<S> for ElifJson<T>
55where
56 T: DeserializeOwned,
57 S: Send + Sync,
58{
59 type Rejection = JsonError;
60
61 async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
62 match Json::<T>::from_request(req, state).await {
63 Ok(Json(data)) => Ok(ElifJson(data)),
64 Err(rejection) => Err(JsonError::from_axum_json_rejection(rejection)),
65 }
66 }
67}
68
69impl<T> IntoResponse for ElifJson<T>
71where
72 T: Serialize,
73{
74 fn into_response(self) -> Response {
75 match serde_json::to_vec(&self.0) {
76 Ok(bytes) => {
77 let mut response = Response::new(bytes.into());
78 response.headers_mut().insert(
79 axum::http::header::CONTENT_TYPE,
80 axum::http::HeaderValue::from_static("application/json"),
81 );
82 response
83 }
84 Err(err) => {
85 tracing::error!("JSON serialization failed: {}", err);
86 (
87 StatusCode::INTERNAL_SERVER_ERROR,
88 "Internal server error: JSON serialization failed"
89 ).into_response()
90 }
91 }
92 }
93}
94
95#[derive(Debug)]
97pub struct JsonError {
98 pub status: StatusCode,
99 pub message: String,
100 pub details: Option<String>,
101}
102
103impl JsonError {
104 pub fn new(status: StatusCode, message: String) -> Self {
106 Self {
107 status,
108 message,
109 details: None,
110 }
111 }
112
113 pub fn with_details(status: StatusCode, message: String, details: String) -> Self {
115 Self {
116 status,
117 message,
118 details: Some(details),
119 }
120 }
121
122 pub fn from_axum_json_rejection(rejection: axum::extract::rejection::JsonRejection) -> Self {
124 use axum::extract::rejection::JsonRejection::*;
125
126 match rejection {
127 JsonDataError(err) => {
128 Self::with_details(
129 StatusCode::BAD_REQUEST,
130 "Invalid JSON data".to_string(),
131 err.to_string(),
132 )
133 }
134 JsonSyntaxError(err) => {
135 Self::with_details(
136 StatusCode::BAD_REQUEST,
137 "JSON syntax error".to_string(),
138 err.to_string(),
139 )
140 }
141 MissingJsonContentType(_) => {
142 Self::new(
143 StatusCode::BAD_REQUEST,
144 "Missing 'Content-Type: application/json' header".to_string(),
145 )
146 }
147 BytesRejection(err) => {
148 Self::with_details(
149 StatusCode::BAD_REQUEST,
150 "Failed to read request body".to_string(),
151 err.to_string(),
152 )
153 }
154 _ => {
155 Self::new(
156 StatusCode::BAD_REQUEST,
157 "Invalid JSON request".to_string(),
158 )
159 }
160 }
161 }
162}
163
164impl IntoResponse for JsonError {
165 fn into_response(self) -> Response {
166 let error_body = if let Some(details) = self.details {
167 serde_json::json!({
168 "error": {
169 "code": self.status.as_u16(),
170 "message": self.message,
171 "details": details
172 }
173 })
174 } else {
175 serde_json::json!({
176 "error": {
177 "code": self.status.as_u16(),
178 "message": self.message
179 }
180 })
181 };
182
183 match ElifResponse::with_status(self.status)
184 .json_value(error_body)
185 .build()
186 {
187 Ok(response) => response,
188 Err(_) => {
189 (self.status, self.message).into_response()
191 }
192 }
193 }
194}
195
196pub struct JsonResponse;
198
199impl JsonResponse {
200 pub fn ok<T: Serialize>(data: &T) -> HttpResult<Response> {
202 ElifResponse::json_ok(data)
203 }
204
205 pub fn with_status<T: Serialize>(status: StatusCode, data: &T) -> HttpResult<Response> {
207 ElifResponse::with_status(status).json(data)?.build()
208 }
209
210 pub fn paginated<T: Serialize>(
212 data: &[T],
213 page: u32,
214 per_page: u32,
215 total: u64,
216 ) -> HttpResult<Response> {
217 let total_pages = (total as f64 / per_page as f64).ceil() as u32;
218
219 let response_data = serde_json::json!({
220 "data": data,
221 "pagination": {
222 "page": page,
223 "per_page": per_page,
224 "total": total,
225 "total_pages": total_pages,
226 "has_next": page < total_pages,
227 "has_prev": page > 1
228 }
229 });
230
231 ElifResponse::ok().json_value(response_data).build()
232 }
233
234 pub fn error(status: StatusCode, message: &str) -> HttpResult<Response> {
236 ElifResponse::json_error(status, message)
237 }
238
239 pub fn validation_error<T: Serialize>(errors: &T) -> HttpResult<Response> {
241 ElifResponse::validation_error(errors)
242 }
243
244 pub fn success_message(message: &str) -> HttpResult<Response> {
246 let response_data = serde_json::json!({
247 "success": true,
248 "message": message
249 });
250
251 ElifResponse::ok().json_value(response_data).build()
252 }
253
254 pub fn created<T: Serialize>(data: &T) -> HttpResult<Response> {
256 ElifResponse::created().json(data)?.build()
257 }
258
259 pub fn no_content() -> HttpResult<Response> {
261 ElifResponse::no_content().build()
262 }
263}
264
265#[derive(Debug, Serialize, Deserialize)]
267pub struct ValidationErrors {
268 pub errors: std::collections::HashMap<String, Vec<String>>,
269}
270
271impl ValidationErrors {
272 pub fn new() -> Self {
274 Self {
275 errors: std::collections::HashMap::new(),
276 }
277 }
278
279 pub fn add_error(&mut self, field: String, error: String) {
281 self.errors.entry(field).or_insert_with(Vec::new).push(error);
282 }
283
284 pub fn add_errors(&mut self, field: String, errors: Vec<String>) {
286 self.errors.entry(field).or_insert_with(Vec::new).extend(errors);
287 }
288
289 pub fn has_errors(&self) -> bool {
291 !self.errors.is_empty()
292 }
293
294 pub fn error_count(&self) -> usize {
296 self.errors.values().map(|v| v.len()).sum()
297 }
298
299 pub fn to_response(self) -> HttpResult<Response> {
301 JsonResponse::validation_error(&self)
302 }
303}
304
305impl Default for ValidationErrors {
306 fn default() -> Self {
307 Self::new()
308 }
309}
310
311#[derive(Debug, Serialize)]
313pub struct ApiResponse<T> {
314 pub success: bool,
315 pub data: Option<T>,
316 pub message: Option<String>,
317 pub errors: Option<serde_json::Value>,
318}
319
320impl<T: Serialize> ApiResponse<T> {
321 pub fn success(data: T) -> Self {
323 Self {
324 success: true,
325 data: Some(data),
326 message: None,
327 errors: None,
328 }
329 }
330
331 pub fn success_with_message(data: T, message: String) -> Self {
333 Self {
334 success: true,
335 data: Some(data),
336 message: Some(message),
337 errors: None,
338 }
339 }
340
341 pub fn error(message: String) -> ApiResponse<()> {
343 ApiResponse {
344 success: false,
345 data: None,
346 message: Some(message),
347 errors: None,
348 }
349 }
350
351 pub fn validation_error(message: String, errors: serde_json::Value) -> ApiResponse<()> {
353 ApiResponse {
354 success: false,
355 data: None,
356 message: Some(message),
357 errors: Some(errors),
358 }
359 }
360
361 pub fn to_response(self) -> HttpResult<Response> {
363 let status = if self.success {
364 StatusCode::OK
365 } else {
366 StatusCode::BAD_REQUEST
367 };
368
369 ElifResponse::with_status(status).json(&self)?.build()
370 }
371}
372
373impl<T: Serialize> IntoResponse for ApiResponse<T> {
374 fn into_response(self) -> Response {
375 match self.to_response() {
376 Ok(response) => response,
377 Err(e) => {
378 tracing::error!("Failed to create API response: {}", e);
379 (StatusCode::INTERNAL_SERVER_ERROR, "Internal server error").into_response()
380 }
381 }
382 }
383}
384
385#[cfg(test)]
386mod tests {
387 use super::*;
388 use serde_json::json;
389
390 #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
391 struct TestData {
392 name: String,
393 age: u32,
394 }
395
396 #[test]
397 fn test_elif_json_wrapper() {
398 let data = TestData {
399 name: "John".to_string(),
400 age: 30,
401 };
402
403 let json_data = ElifJson::new(data.clone());
404 assert_eq!(json_data.name, data.name);
405 assert_eq!(json_data.age, data.age);
406
407 let extracted = json_data.into_inner();
408 assert_eq!(extracted, data);
409 }
410
411 #[test]
412 fn test_validation_errors() {
413 let mut errors = ValidationErrors::new();
414 errors.add_error("name".to_string(), "Name is required".to_string());
415 errors.add_error("age".to_string(), "Age must be positive".to_string());
416
417 assert!(errors.has_errors());
418 assert_eq!(errors.error_count(), 2);
419 }
420
421 #[test]
422 fn test_api_response() {
423 let data = TestData {
424 name: "Jane".to_string(),
425 age: 25,
426 };
427
428 let success_response = ApiResponse::success(data);
429 assert!(success_response.success);
430 assert!(success_response.data.is_some());
431
432 let error_response = ApiResponse::<()>::error("Something went wrong".to_string());
433 assert!(!error_response.success);
434 assert!(error_response.data.is_none());
435 assert_eq!(error_response.message, Some("Something went wrong".to_string()));
436 }
437
438 #[test]
439 fn test_json_response_helpers() {
440 let data = vec![
441 TestData { name: "User1".to_string(), age: 20 },
442 TestData { name: "User2".to_string(), age: 25 },
443 ];
444
445 let response = JsonResponse::paginated(&data, 1, 10, 25);
447 assert!(response.is_ok());
448 }
449}