1use crate::handler_trait::RequestData;
4use crate::query_parser::parse_query_string_to_json;
5use axum::body::Body;
6use http_body_util::BodyExt;
7use serde_json::Value;
8use std::collections::HashMap;
9use std::sync::Arc;
10
11pub fn extract_query_params(uri: &axum::http::Uri) -> Value {
13 let query_string = uri.query().unwrap_or("");
14 if query_string.is_empty() {
15 Value::Object(serde_json::Map::new())
16 } else {
17 parse_query_string_to_json(query_string.as_bytes(), true)
18 }
19}
20
21pub fn extract_raw_query_params(uri: &axum::http::Uri) -> HashMap<String, Vec<String>> {
24 let query_string = uri.query().unwrap_or("");
25 if query_string.is_empty() {
26 HashMap::new()
27 } else {
28 crate::query_parser::parse_query_string(query_string.as_bytes(), '&')
29 .into_iter()
30 .fold(HashMap::new(), |mut acc, (k, v)| {
31 acc.entry(k).or_insert_with(Vec::new).push(v);
32 acc
33 })
34 }
35}
36
37pub fn extract_headers(headers: &axum::http::HeaderMap) -> HashMap<String, String> {
39 let mut map = HashMap::new();
40 for (name, value) in headers.iter() {
41 if let Ok(val_str) = value.to_str() {
42 map.insert(name.as_str().to_lowercase(), val_str.to_string());
43 }
44 }
45 map
46}
47
48pub fn extract_cookies(headers: &axum::http::HeaderMap) -> HashMap<String, String> {
50 let mut cookies = HashMap::new();
51
52 if let Some(cookie_str) = headers.get(axum::http::header::COOKIE).and_then(|h| h.to_str().ok()) {
53 for cookie in cookie::Cookie::split_parse(cookie_str).flatten() {
54 cookies.insert(cookie.name().to_string(), cookie.value().to_string());
55 }
56 }
57
58 cookies
59}
60
61pub fn create_request_data_without_body(
65 uri: &axum::http::Uri,
66 method: &axum::http::Method,
67 headers: &axum::http::HeaderMap,
68 path_params: HashMap<String, String>,
69) -> RequestData {
70 RequestData {
71 path_params: Arc::new(path_params),
72 query_params: extract_query_params(uri),
73 raw_query_params: Arc::new(extract_raw_query_params(uri)),
74 headers: Arc::new(extract_headers(headers)),
75 cookies: Arc::new(extract_cookies(headers)),
76 body: Value::Null,
77 raw_body: None,
78 method: method.as_str().to_string(),
79 path: uri.path().to_string(),
80 #[cfg(feature = "di")]
81 dependencies: None,
82 }
83}
84
85pub async fn create_request_data_with_body(
91 parts: &axum::http::request::Parts,
92 path_params: HashMap<String, String>,
93 body: Body,
94) -> Result<RequestData, (axum::http::StatusCode, String)> {
95 let body_bytes = body
96 .collect()
97 .await
98 .map_err(|e| {
99 (
100 axum::http::StatusCode::BAD_REQUEST,
101 format!("Failed to read body: {}", e),
102 )
103 })?
104 .to_bytes();
105
106 Ok(RequestData {
107 path_params: Arc::new(path_params),
108 query_params: extract_query_params(&parts.uri),
109 raw_query_params: Arc::new(extract_raw_query_params(&parts.uri)),
110 headers: Arc::new(extract_headers(&parts.headers)),
111 cookies: Arc::new(extract_cookies(&parts.headers)),
112 body: Value::Null,
113 raw_body: if body_bytes.is_empty() { None } else { Some(body_bytes) },
114 method: parts.method.as_str().to_string(),
115 path: parts.uri.path().to_string(),
116 #[cfg(feature = "di")]
117 dependencies: None,
118 })
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124 use axum::http::{HeaderMap, HeaderValue, Method, Uri};
125 use serde_json::json;
126
127 #[test]
128 fn test_extract_query_params_empty() {
129 let uri: Uri = "/path".parse().unwrap();
130 let result = extract_query_params(&uri);
131 assert_eq!(result, json!({}));
132 }
133
134 #[test]
135 fn test_extract_query_params_single_param() {
136 let uri: Uri = "/path?name=value".parse().unwrap();
137 let result = extract_query_params(&uri);
138 assert_eq!(result, json!({"name": "value"}));
139 }
140
141 #[test]
142 fn test_extract_query_params_multiple_params() {
143 let uri: Uri = "/path?foo=1&bar=2".parse().unwrap();
144 let result = extract_query_params(&uri);
145 assert_eq!(result, json!({"foo": 1, "bar": 2}));
146 }
147
148 #[test]
149 fn test_extract_query_params_array_params() {
150 let uri: Uri = "/path?tags=rust&tags=http".parse().unwrap();
151 let result = extract_query_params(&uri);
152 assert_eq!(result, json!({"tags": ["rust", "http"]}));
153 }
154
155 #[test]
156 fn test_extract_query_params_mixed_array_and_single() {
157 let uri: Uri = "/path?tags=rust&tags=web&id=123".parse().unwrap();
158 let result = extract_query_params(&uri);
159 assert_eq!(result, json!({"tags": ["rust", "web"], "id": 123}));
160 }
161
162 #[test]
163 fn test_extract_query_params_url_encoded() {
164 let uri: Uri = "/path?email=test%40example.com&name=john+doe".parse().unwrap();
165 let result = extract_query_params(&uri);
166 assert_eq!(result, json!({"email": "test@example.com", "name": "john doe"}));
167 }
168
169 #[test]
170 fn test_extract_query_params_boolean_values() {
171 let uri: Uri = "/path?active=true&enabled=false".parse().unwrap();
172 let result = extract_query_params(&uri);
173 assert_eq!(result, json!({"active": true, "enabled": false}));
174 }
175
176 #[test]
177 fn test_extract_query_params_null_value() {
178 let uri: Uri = "/path?value=null".parse().unwrap();
179 let result = extract_query_params(&uri);
180 assert_eq!(result, json!({"value": null}));
181 }
182
183 #[test]
184 fn test_extract_query_params_empty_string_value() {
185 let uri: Uri = "/path?key=".parse().unwrap();
186 let result = extract_query_params(&uri);
187 assert_eq!(result, json!({"key": ""}));
188 }
189
190 #[test]
191 fn test_extract_raw_query_params_empty() {
192 let uri: Uri = "/path".parse().unwrap();
193 let result = extract_raw_query_params(&uri);
194 assert!(result.is_empty());
195 }
196
197 #[test]
198 fn test_extract_raw_query_params_single() {
199 let uri: Uri = "/path?name=value".parse().unwrap();
200 let result = extract_raw_query_params(&uri);
201 assert_eq!(result.get("name"), Some(&vec!["value".to_string()]));
202 }
203
204 #[test]
205 fn test_extract_raw_query_params_multiple_values() {
206 let uri: Uri = "/path?tag=rust&tag=http".parse().unwrap();
207 let result = extract_raw_query_params(&uri);
208 assert_eq!(result.get("tag"), Some(&vec!["rust".to_string(), "http".to_string()]));
209 }
210
211 #[test]
212 fn test_extract_raw_query_params_mixed() {
213 let uri: Uri = "/path?id=123&tags=rust&tags=web&active=true".parse().unwrap();
214 let result = extract_raw_query_params(&uri);
215 assert_eq!(result.get("id"), Some(&vec!["123".to_string()]));
216 assert_eq!(result.get("tags"), Some(&vec!["rust".to_string(), "web".to_string()]));
217 assert_eq!(result.get("active"), Some(&vec!["true".to_string()]));
218 }
219
220 #[test]
221 fn test_extract_raw_query_params_url_encoded() {
222 let uri: Uri = "/path?email=test%40example.com".parse().unwrap();
223 let result = extract_raw_query_params(&uri);
224 assert_eq!(result.get("email"), Some(&vec!["test@example.com".to_string()]));
225 }
226
227 #[test]
228 fn test_extract_headers_empty() {
229 let headers = HeaderMap::new();
230 let result = extract_headers(&headers);
231 assert!(result.is_empty());
232 }
233
234 #[test]
235 fn test_extract_headers_single() {
236 let mut headers = HeaderMap::new();
237 headers.insert("content-type", HeaderValue::from_static("application/json"));
238 let result = extract_headers(&headers);
239 assert_eq!(result.get("content-type"), Some(&"application/json".to_string()));
240 }
241
242 #[test]
243 fn test_extract_headers_multiple() {
244 let mut headers = HeaderMap::new();
245 headers.insert("content-type", HeaderValue::from_static("application/json"));
246 headers.insert("user-agent", HeaderValue::from_static("test-agent"));
247 headers.insert("authorization", HeaderValue::from_static("Bearer token123"));
248
249 let result = extract_headers(&headers);
250 assert_eq!(result.len(), 3);
251 assert_eq!(result.get("content-type"), Some(&"application/json".to_string()));
252 assert_eq!(result.get("user-agent"), Some(&"test-agent".to_string()));
253 assert_eq!(result.get("authorization"), Some(&"Bearer token123".to_string()));
254 }
255
256 #[test]
257 fn test_extract_headers_case_insensitive() {
258 let mut headers = HeaderMap::new();
259 headers.insert("Content-Type", HeaderValue::from_static("text/html"));
260 headers.insert("USER-Agent", HeaderValue::from_static("chrome"));
261
262 let result = extract_headers(&headers);
263 assert_eq!(result.get("content-type"), Some(&"text/html".to_string()));
264 assert_eq!(result.get("user-agent"), Some(&"chrome".to_string()));
265 }
266
267 #[test]
268 fn test_extract_headers_with_dashes() {
269 let mut headers = HeaderMap::new();
270 headers.insert("x-custom-header", HeaderValue::from_static("custom-value"));
271 headers.insert("x-request-id", HeaderValue::from_static("req-12345"));
272
273 let result = extract_headers(&headers);
274 assert_eq!(result.get("x-custom-header"), Some(&"custom-value".to_string()));
275 assert_eq!(result.get("x-request-id"), Some(&"req-12345".to_string()));
276 }
277
278 #[test]
279 fn test_extract_cookies_no_cookie_header() {
280 let headers = HeaderMap::new();
281 let result = extract_cookies(&headers);
282 assert!(result.is_empty());
283 }
284
285 #[test]
286 fn test_extract_cookies_single() {
287 let mut headers = HeaderMap::new();
288 headers.insert(axum::http::header::COOKIE, HeaderValue::from_static("session=abc123"));
289
290 let result = extract_cookies(&headers);
291 assert_eq!(result.get("session"), Some(&"abc123".to_string()));
292 }
293
294 #[test]
295 fn test_extract_cookies_multiple() {
296 let mut headers = HeaderMap::new();
297 headers.insert(
298 axum::http::header::COOKIE,
299 HeaderValue::from_static("session=abc123; user_id=42; theme=dark"),
300 );
301
302 let result = extract_cookies(&headers);
303 assert_eq!(result.len(), 3);
304 assert_eq!(result.get("session"), Some(&"abc123".to_string()));
305 assert_eq!(result.get("user_id"), Some(&"42".to_string()));
306 assert_eq!(result.get("theme"), Some(&"dark".to_string()));
307 }
308
309 #[test]
310 fn test_extract_cookies_with_spaces() {
311 let mut headers = HeaderMap::new();
312 headers.insert(
313 axum::http::header::COOKIE,
314 HeaderValue::from_static("session = abc123 ; theme = light"),
315 );
316
317 let result = extract_cookies(&headers);
318 assert!(result.len() >= 1);
319 }
320
321 #[test]
322 fn test_extract_cookies_empty_value() {
323 let mut headers = HeaderMap::new();
324 headers.insert(axum::http::header::COOKIE, HeaderValue::from_static("empty="));
325
326 let result = extract_cookies(&headers);
327 assert_eq!(result.get("empty"), Some(&String::new()));
328 }
329
330 #[test]
331 fn test_create_request_data_without_body_minimal() {
332 let uri: Uri = "/test".parse().unwrap();
333 let method = Method::GET;
334 let headers = HeaderMap::new();
335 let path_params = HashMap::new();
336
337 let result = create_request_data_without_body(&uri, &method, &headers, path_params);
338
339 assert_eq!(result.method, "GET");
340 assert_eq!(result.path, "/test");
341 assert!(result.path_params.is_empty());
342 assert_eq!(result.query_params, json!({}));
343 assert!(result.raw_query_params.is_empty());
344 assert!(result.headers.is_empty());
345 assert!(result.cookies.is_empty());
346 assert_eq!(result.body, Value::Null);
347 assert!(result.raw_body.is_none());
348 }
349
350 #[test]
351 fn test_create_request_data_without_body_with_path_params() {
352 let uri: Uri = "/users/42".parse().unwrap();
353 let method = Method::GET;
354 let headers = HeaderMap::new();
355 let mut path_params = HashMap::new();
356 path_params.insert("user_id".to_string(), "42".to_string());
357
358 let result = create_request_data_without_body(&uri, &method, &headers, path_params);
359
360 assert_eq!(result.path_params.get("user_id"), Some(&"42".to_string()));
361 }
362
363 #[test]
364 fn test_create_request_data_without_body_with_query_params() {
365 let uri: Uri = "/search?q=rust&limit=10".parse().unwrap();
366 let method = Method::GET;
367 let headers = HeaderMap::new();
368 let path_params = HashMap::new();
369
370 let result = create_request_data_without_body(&uri, &method, &headers, path_params);
371
372 assert_eq!(result.query_params, json!({"q": "rust", "limit": 10}));
373 assert_eq!(result.raw_query_params.get("q"), Some(&vec!["rust".to_string()]));
374 assert_eq!(result.raw_query_params.get("limit"), Some(&vec!["10".to_string()]));
375 }
376
377 #[test]
378 fn test_create_request_data_without_body_with_headers() {
379 let uri: Uri = "/test".parse().unwrap();
380 let method = Method::POST;
381 let mut headers = HeaderMap::new();
382 headers.insert("content-type", HeaderValue::from_static("application/json"));
383 headers.insert("authorization", HeaderValue::from_static("Bearer token"));
384 let path_params = HashMap::new();
385
386 let result = create_request_data_without_body(&uri, &method, &headers, path_params);
387
388 assert_eq!(
389 result.headers.get("content-type"),
390 Some(&"application/json".to_string())
391 );
392 assert_eq!(result.headers.get("authorization"), Some(&"Bearer token".to_string()));
393 }
394
395 #[test]
396 fn test_create_request_data_without_body_with_cookies() {
397 let uri: Uri = "/test".parse().unwrap();
398 let method = Method::GET;
399 let mut headers = HeaderMap::new();
400 headers.insert(
401 axum::http::header::COOKIE,
402 HeaderValue::from_static("session=xyz; theme=dark"),
403 );
404 let path_params = HashMap::new();
405
406 let result = create_request_data_without_body(&uri, &method, &headers, path_params);
407
408 assert_eq!(result.cookies.get("session"), Some(&"xyz".to_string()));
409 assert_eq!(result.cookies.get("theme"), Some(&"dark".to_string()));
410 }
411
412 #[test]
413 fn test_create_request_data_without_body_different_methods() {
414 let uri: Uri = "/resource".parse().unwrap();
415 let headers = HeaderMap::new();
416 let path_params = HashMap::new();
417
418 for method in &[Method::GET, Method::POST, Method::PUT, Method::DELETE, Method::PATCH] {
419 let result = create_request_data_without_body(&uri, method, &headers, path_params.clone());
420 assert_eq!(result.method, method.as_str());
421 }
422 }
423
424 #[tokio::test]
425 async fn test_create_request_data_with_body_empty() {
426 let parts = axum::http::request::Request::builder()
427 .method(Method::POST)
428 .uri("/test")
429 .body(Body::empty())
430 .unwrap()
431 .into_parts();
432
433 let body = Body::empty();
434 let path_params = HashMap::new();
435
436 let result = create_request_data_with_body(&parts.0, path_params, body)
437 .await
438 .unwrap();
439
440 assert_eq!(result.method, "POST");
441 assert_eq!(result.path, "/test");
442 assert_eq!(result.body, Value::Null);
443 assert!(result.raw_body.is_none());
444 }
445
446 #[tokio::test]
447 async fn test_create_request_data_with_body_json() {
448 let request_body = Body::from(r#"{"key":"value"}"#);
449 let request = axum::http::request::Request::builder()
450 .method(Method::POST)
451 .uri("/test")
452 .body(Body::empty())
453 .unwrap();
454
455 let (parts, _) = request.into_parts();
456 let path_params = HashMap::new();
457
458 let result = create_request_data_with_body(&parts, path_params, request_body)
459 .await
460 .unwrap();
461
462 assert_eq!(result.method, "POST");
463 assert_eq!(result.body, Value::Null);
464 assert!(result.raw_body.is_some());
465 assert_eq!(result.raw_body.as_ref().unwrap().as_ref(), br#"{"key":"value"}"#);
466 }
467
468 #[tokio::test]
469 async fn test_create_request_data_with_body_with_query_params() {
470 let request_body = Body::from("test");
471 let request = axum::http::request::Request::builder()
472 .method(Method::POST)
473 .uri("/test?foo=bar&baz=qux")
474 .body(Body::empty())
475 .unwrap();
476
477 let (parts, _) = request.into_parts();
478 let path_params = HashMap::new();
479
480 let result = create_request_data_with_body(&parts, path_params, request_body)
481 .await
482 .unwrap();
483
484 assert_eq!(result.query_params, json!({"foo": "bar", "baz": "qux"}));
485 }
486
487 #[tokio::test]
488 async fn test_create_request_data_with_body_with_headers() {
489 let request_body = Body::from("test");
490 let request = axum::http::request::Request::builder()
491 .method(Method::POST)
492 .uri("/test")
493 .header("content-type", "application/json")
494 .header("x-request-id", "req123")
495 .body(Body::empty())
496 .unwrap();
497
498 let (parts, _) = request.into_parts();
499 let path_params = HashMap::new();
500
501 let result = create_request_data_with_body(&parts, path_params, request_body)
502 .await
503 .unwrap();
504
505 assert_eq!(
506 result.headers.get("content-type"),
507 Some(&"application/json".to_string())
508 );
509 assert_eq!(result.headers.get("x-request-id"), Some(&"req123".to_string()));
510 }
511
512 #[tokio::test]
513 async fn test_create_request_data_with_body_with_cookies() {
514 let request_body = Body::from("test");
515 let request = axum::http::request::Request::builder()
516 .method(Method::POST)
517 .uri("/test")
518 .header("cookie", "session=xyz; user=123")
519 .body(Body::empty())
520 .unwrap();
521
522 let (parts, _) = request.into_parts();
523 let path_params = HashMap::new();
524
525 let result = create_request_data_with_body(&parts, path_params, request_body)
526 .await
527 .unwrap();
528
529 assert_eq!(result.cookies.get("session"), Some(&"xyz".to_string()));
530 assert_eq!(result.cookies.get("user"), Some(&"123".to_string()));
531 }
532
533 #[tokio::test]
534 async fn test_create_request_data_with_body_large_payload() {
535 let large_json = json!({
536 "data": (0..100).map(|i| json!({"id": i, "value": format!("item-{}", i)})).collect::<Vec<_>>()
537 });
538 let json_str = serde_json::to_string(&large_json).unwrap();
539 let request_body = Body::from(json_str.clone());
540
541 let request = axum::http::request::Request::builder()
542 .method(Method::POST)
543 .uri("/test")
544 .body(Body::empty())
545 .unwrap();
546
547 let (parts, _) = request.into_parts();
548 let path_params = HashMap::new();
549
550 let result = create_request_data_with_body(&parts, path_params, request_body)
551 .await
552 .unwrap();
553
554 assert!(result.raw_body.is_some());
555 assert_eq!(result.raw_body.as_ref().unwrap().as_ref(), json_str.as_bytes());
556 }
557
558 #[tokio::test]
559 async fn test_create_request_data_with_body_preserves_all_fields() {
560 let request_body = Body::from("request data");
561 let request = axum::http::request::Request::builder()
562 .method(Method::PUT)
563 .uri("/users/42?action=update")
564 .header("authorization", "Bearer token")
565 .header("cookie", "session=abc")
566 .body(Body::empty())
567 .unwrap();
568
569 let (parts, _) = request.into_parts();
570 let mut path_params = HashMap::new();
571 path_params.insert("user_id".to_string(), "42".to_string());
572
573 let result = create_request_data_with_body(&parts, path_params, request_body)
574 .await
575 .unwrap();
576
577 assert_eq!(result.method, "PUT");
578 assert_eq!(result.path, "/users/42");
579 assert_eq!(result.path_params.get("user_id"), Some(&"42".to_string()));
580 assert_eq!(result.query_params, json!({"action": "update"}));
581 assert!(result.headers.contains_key("authorization"));
582 assert!(result.cookies.contains_key("session"));
583 assert!(result.raw_body.is_some());
584 }
585
586 #[test]
587 fn test_arc_wrapping_for_cheap_cloning() {
588 let uri: Uri = "/test".parse().unwrap();
589 let method = Method::GET;
590 let mut headers = HeaderMap::new();
591 headers.insert(axum::http::header::COOKIE, HeaderValue::from_static("session=abc"));
592 let mut path_params = HashMap::new();
593 path_params.insert("id".to_string(), "1".to_string());
594
595 let request_data = create_request_data_without_body(&uri, &method, &headers, path_params.clone());
596
597 let cloned = request_data.clone();
598
599 assert!(Arc::ptr_eq(&request_data.path_params, &cloned.path_params));
600 assert!(Arc::ptr_eq(&request_data.headers, &cloned.headers));
601 assert!(Arc::ptr_eq(&request_data.cookies, &cloned.cookies));
602 assert!(Arc::ptr_eq(&request_data.raw_query_params, &cloned.raw_query_params));
603 }
604}