1use axum::{
4 extract::{FromRequest, Request},
5 response::{IntoResponse, Response},
6 Json,
7};
8use serde::{Deserialize, Serialize, de::DeserializeOwned};
9use std::ops::{Deref, DerefMut};
10use crate::errors::HttpResult;
11use crate::response::{ElifResponse, IntoElifResponse, ElifStatusCode};
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 = Response::new("Internal server error: JSON serialization failed".into());
97 *response.status_mut() = axum::http::StatusCode::INTERNAL_SERVER_ERROR;
98 response.headers_mut().insert(
99 axum::http::header::CONTENT_TYPE,
100 axum::http::HeaderValue::from_static("text/plain"),
101 );
102 response
103 }
104 }
105 }
106}
107
108#[derive(Debug)]
110pub struct JsonError {
111 pub status: ElifStatusCode,
112 pub message: String,
113 pub details: Option<String>,
114}
115
116impl JsonError {
117 pub fn new(status: ElifStatusCode, message: String) -> Self {
119 Self {
120 status,
121 message,
122 details: None,
123 }
124 }
125
126 pub fn with_details(status: ElifStatusCode, message: String, details: String) -> Self {
128 Self {
129 status,
130 message,
131 details: Some(details),
132 }
133 }
134
135 pub(crate) fn from_axum_json_rejection(rejection: axum::extract::rejection::JsonRejection) -> Self {
137 use axum::extract::rejection::JsonRejection::*;
138
139 match rejection {
140 JsonDataError(err) => {
141 Self::with_details(
142 ElifStatusCode::BAD_REQUEST,
143 "Invalid JSON data".to_string(),
144 err.to_string(),
145 )
146 }
147 JsonSyntaxError(err) => {
148 Self::with_details(
149 ElifStatusCode::BAD_REQUEST,
150 "JSON syntax error".to_string(),
151 err.to_string(),
152 )
153 }
154 MissingJsonContentType(_) => {
155 Self::new(
156 ElifStatusCode::BAD_REQUEST,
157 "Missing 'Content-Type: application/json' header".to_string(),
158 )
159 }
160 BytesRejection(err) => {
161 Self::with_details(
162 ElifStatusCode::BAD_REQUEST,
163 "Failed to read request body".to_string(),
164 err.to_string(),
165 )
166 }
167 _ => {
168 Self::new(
169 ElifStatusCode::BAD_REQUEST,
170 "Invalid JSON request".to_string(),
171 )
172 }
173 }
174 }
175}
176
177impl IntoResponse for JsonError {
178 fn into_response(self) -> Response {
179 let error_body = if let Some(details) = self.details {
180 serde_json::json!({
181 "error": {
182 "code": self.status.as_u16(),
183 "message": self.message,
184 "details": details
185 }
186 })
187 } else {
188 serde_json::json!({
189 "error": {
190 "code": self.status.as_u16(),
191 "message": self.message
192 }
193 })
194 };
195
196 match serde_json::to_vec(&error_body) {
197 Ok(bytes) => {
198 let mut response = Response::new(bytes.into());
199 *response.status_mut() = self.status.to_axum();
200 response.headers_mut().insert(
201 axum::http::header::CONTENT_TYPE,
202 axum::http::HeaderValue::from_static("application/json"),
203 );
204 response
205 }
206 Err(_) => {
207 let mut response = Response::new(self.message.into());
209 *response.status_mut() = self.status.to_axum();
210 response.headers_mut().insert(
211 axum::http::header::CONTENT_TYPE,
212 axum::http::HeaderValue::from_static("text/plain"),
213 );
214 response
215 }
216 }
217 }
218}
219
220pub struct JsonResponse;
222
223impl JsonResponse {
224 pub fn ok<T: Serialize>(data: &T) -> HttpResult<Response> {
226 ElifResponse::json_ok(data)
227 }
228
229 pub fn with_status<T: Serialize>(status: ElifStatusCode, data: &T) -> HttpResult<Response> {
231 ElifResponse::with_status(status).json(data)?.build()
232 }
233
234 pub fn paginated<T: Serialize>(
236 data: &[T],
237 page: u32,
238 per_page: u32,
239 total: u64,
240 ) -> HttpResult<Response> {
241 let total_pages = (total as f64 / per_page as f64).ceil() as u32;
242
243 let response_data = serde_json::json!({
244 "data": data,
245 "pagination": {
246 "page": page,
247 "per_page": per_page,
248 "total": total,
249 "total_pages": total_pages,
250 "has_next": page < total_pages,
251 "has_prev": page > 1
252 }
253 });
254
255 ElifResponse::ok().json_value(response_data).build()
256 }
257
258 pub fn error(status: ElifStatusCode, message: &str) -> HttpResult<Response> {
260 ElifResponse::json_error(status, message)
261 }
262
263 pub fn validation_error<T: Serialize>(errors: &T) -> HttpResult<Response> {
265 ElifResponse::validation_error(errors)
266 }
267
268 pub fn success_message(message: &str) -> HttpResult<Response> {
270 let response_data = serde_json::json!({
271 "success": true,
272 "message": message
273 });
274
275 ElifResponse::ok().json_value(response_data).build()
276 }
277
278 pub fn created<T: Serialize>(data: &T) -> HttpResult<Response> {
280 ElifResponse::created().json(data)?.build()
281 }
282
283 pub fn no_content() -> HttpResult<Response> {
285 ElifResponse::no_content().build()
286 }
287}
288
289#[derive(Debug, Serialize, Deserialize)]
291pub struct ValidationErrors {
292 pub errors: std::collections::HashMap<String, Vec<String>>,
293}
294
295impl ValidationErrors {
296 pub fn new() -> Self {
298 Self {
299 errors: std::collections::HashMap::new(),
300 }
301 }
302
303 pub fn add_error(&mut self, field: String, error: String) {
305 self.errors.entry(field).or_insert_with(Vec::new).push(error);
306 }
307
308 pub fn add_errors(&mut self, field: String, errors: Vec<String>) {
310 self.errors.entry(field).or_insert_with(Vec::new).extend(errors);
311 }
312
313 pub fn has_errors(&self) -> bool {
315 !self.errors.is_empty()
316 }
317
318 pub fn error_count(&self) -> usize {
320 self.errors.values().map(|v| v.len()).sum()
321 }
322
323 pub fn to_response(self) -> HttpResult<Response> {
325 JsonResponse::validation_error(&self)
326 }
327}
328
329impl Default for ValidationErrors {
330 fn default() -> Self {
331 Self::new()
332 }
333}
334
335#[derive(Debug, Serialize)]
337pub struct ApiResponse<T> {
338 pub success: bool,
339 pub data: Option<T>,
340 pub message: Option<String>,
341 pub errors: Option<serde_json::Value>,
342}
343
344impl<T: Serialize> ApiResponse<T> {
345 pub fn success(data: T) -> Self {
347 Self {
348 success: true,
349 data: Some(data),
350 message: None,
351 errors: None,
352 }
353 }
354
355 pub fn success_with_message(data: T, message: String) -> Self {
357 Self {
358 success: true,
359 data: Some(data),
360 message: Some(message),
361 errors: None,
362 }
363 }
364
365 pub fn error(message: String) -> ApiResponse<()> {
367 ApiResponse {
368 success: false,
369 data: None,
370 message: Some(message),
371 errors: None,
372 }
373 }
374
375 pub fn validation_error(message: String, errors: serde_json::Value) -> ApiResponse<()> {
377 ApiResponse {
378 success: false,
379 data: None,
380 message: Some(message),
381 errors: Some(errors),
382 }
383 }
384
385 pub fn to_response(self) -> HttpResult<Response> {
387 let status = if self.success {
388 ElifStatusCode::OK
389 } else {
390 ElifStatusCode::BAD_REQUEST
391 };
392
393 ElifResponse::with_status(status).json(&self)?.build()
394 }
395}
396
397impl<T: Serialize> IntoResponse for ApiResponse<T> {
398 fn into_response(self) -> Response {
399 match self.to_response() {
400 Ok(response) => response,
401 Err(e) => {
402 tracing::error!("Failed to create API response: {}", e);
403 (ElifStatusCode::INTERNAL_SERVER_ERROR.to_axum(), "Internal server error").into_response()
404 }
405 }
406 }
407}