1use axum::{
7 body::Body,
8 http::{HeaderValue, Request, Response, StatusCode, header::HeaderName},
9};
10use bytes::Bytes;
11use serde::{Deserialize, Serialize};
12use serde_json::Value;
13use std::collections::HashMap;
14use std::future::Future;
15use std::pin::Pin;
16
17#[derive(Clone, Debug)]
24pub struct StaticResponse {
25 pub status: u16,
27 pub headers: Vec<(HeaderName, HeaderValue)>,
29 pub body: Bytes,
31 pub content_type: HeaderValue,
33}
34
35impl Default for StaticResponse {
36 fn default() -> Self {
37 Self {
38 status: 200,
39 headers: Vec::new(),
40 body: Bytes::new(),
41 content_type: HeaderValue::from_static("text/plain"),
43 }
44 }
45}
46
47impl StaticResponse {
48 pub fn to_response(&self) -> Response<Body> {
54 let status = StatusCode::from_u16(self.status).unwrap_or(StatusCode::OK);
55 let mut builder = Response::builder()
56 .status(status)
57 .header(axum::http::header::CONTENT_TYPE, self.content_type.clone());
58 for (name, value) in &self.headers {
59 builder = builder.header(name.clone(), value.clone());
60 }
61 builder.body(Body::from(self.body.clone())).unwrap_or_else(|_| {
62 Response::builder()
63 .status(StatusCode::INTERNAL_SERVER_ERROR)
64 .body(Body::from("Failed to build static response"))
65 .expect("fallback response must build")
66 })
67 }
68}
69
70#[derive(Debug, Clone)]
80pub struct RequestData {
81 pub path_params: std::sync::Arc<HashMap<String, String>>,
83 pub query_params: std::sync::Arc<Value>,
85 pub validated_params: Option<std::sync::Arc<Value>>,
87 pub raw_query_params: std::sync::Arc<HashMap<String, Vec<String>>>,
89 pub body: std::sync::Arc<Value>,
91 pub raw_body: Option<bytes::Bytes>,
93 pub headers: std::sync::Arc<HashMap<String, String>>,
95 pub cookies: std::sync::Arc<HashMap<String, String>>,
97 pub method: String,
99 pub path: String,
101 #[cfg(feature = "di")]
103 pub dependencies: Option<std::sync::Arc<spikard_core::di::ResolvedDependencies>>,
104}
105
106impl Serialize for RequestData {
107 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
108 where
109 S: serde::Serializer,
110 {
111 use serde::ser::SerializeStruct;
112 #[cfg(feature = "di")]
113 let field_count = 11;
114 #[cfg(not(feature = "di"))]
115 let field_count = 10;
116
117 let mut state = serializer.serialize_struct("RequestData", field_count)?;
118 state.serialize_field("path_params", &*self.path_params)?;
119 state.serialize_field("query_params", &*self.query_params)?;
120 state.serialize_field("validated_params", &self.validated_params.as_deref())?;
121 state.serialize_field("raw_query_params", &*self.raw_query_params)?;
122 state.serialize_field("body", &*self.body)?;
123 state.serialize_field("raw_body", &self.raw_body.as_ref().map(|b| b.as_ref()))?;
124 state.serialize_field("headers", &*self.headers)?;
125 state.serialize_field("cookies", &*self.cookies)?;
126 state.serialize_field("method", &self.method)?;
127 state.serialize_field("path", &self.path)?;
128
129 #[cfg(feature = "di")]
130 {
131 state.serialize_field("has_dependencies", &self.dependencies.is_some())?;
132 }
133
134 state.end()
135 }
136}
137
138impl<'de> Deserialize<'de> for RequestData {
139 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
140 where
141 D: serde::Deserializer<'de>,
142 {
143 #[derive(Deserialize)]
144 #[serde(field_identifier, rename_all = "snake_case")]
145 enum Field {
146 PathParams,
147 QueryParams,
148 RawQueryParams,
149 ValidatedParams,
150 Body,
151 RawBody,
152 Headers,
153 Cookies,
154 Method,
155 Path,
156 #[cfg(feature = "di")]
157 HasDependencies,
158 }
159
160 struct RequestDataVisitor;
161
162 impl<'de> serde::de::Visitor<'de> for RequestDataVisitor {
163 type Value = RequestData;
164
165 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
166 formatter.write_str("struct RequestData")
167 }
168
169 fn visit_map<V>(self, mut map: V) -> Result<RequestData, V::Error>
170 where
171 V: serde::de::MapAccess<'de>,
172 {
173 let mut path_params = None;
174 let mut query_params = None;
175 let mut raw_query_params = None;
176 let mut validated_params = None;
177 let mut body = None;
178 let mut raw_body = None;
179 let mut headers = None;
180 let mut cookies = None;
181 let mut method = None;
182 let mut path = None;
183
184 while let Some(key) = map.next_key()? {
185 match key {
186 Field::PathParams => {
187 path_params = Some(std::sync::Arc::new(map.next_value()?));
188 }
189 Field::QueryParams => {
190 query_params = Some(std::sync::Arc::new(map.next_value()?));
191 }
192 Field::RawQueryParams => {
193 raw_query_params = Some(std::sync::Arc::new(map.next_value()?));
194 }
195 Field::ValidatedParams => {
196 validated_params = Some(std::sync::Arc::new(map.next_value()?));
197 }
198 Field::Body => {
199 body = Some(std::sync::Arc::new(map.next_value()?));
200 }
201 Field::RawBody => {
202 let bytes_vec: Option<Vec<u8>> = map.next_value()?;
203 raw_body = bytes_vec.map(bytes::Bytes::from);
204 }
205 Field::Headers => {
206 headers = Some(std::sync::Arc::new(map.next_value()?));
207 }
208 Field::Cookies => {
209 cookies = Some(std::sync::Arc::new(map.next_value()?));
210 }
211 Field::Method => {
212 method = Some(map.next_value()?);
213 }
214 Field::Path => {
215 path = Some(map.next_value()?);
216 }
217 #[cfg(feature = "di")]
218 Field::HasDependencies => {
219 let _: bool = map.next_value()?;
220 }
221 }
222 }
223
224 Ok(RequestData {
225 path_params: path_params.ok_or_else(|| serde::de::Error::missing_field("path_params"))?,
226 query_params: query_params.ok_or_else(|| serde::de::Error::missing_field("query_params"))?,
227 raw_query_params: raw_query_params
228 .ok_or_else(|| serde::de::Error::missing_field("raw_query_params"))?,
229 validated_params,
230 body: body.ok_or_else(|| serde::de::Error::missing_field("body"))?,
231 raw_body,
232 headers: headers.ok_or_else(|| serde::de::Error::missing_field("headers"))?,
233 cookies: cookies.ok_or_else(|| serde::de::Error::missing_field("cookies"))?,
234 method: method.ok_or_else(|| serde::de::Error::missing_field("method"))?,
235 path: path.ok_or_else(|| serde::de::Error::missing_field("path"))?,
236 #[cfg(feature = "di")]
237 dependencies: None,
238 })
239 }
240 }
241
242 #[cfg(feature = "di")]
243 const FIELDS: &[&str] = &[
244 "path_params",
245 "query_params",
246 "validated_params",
247 "raw_query_params",
248 "body",
249 "raw_body",
250 "headers",
251 "cookies",
252 "method",
253 "path",
254 "has_dependencies",
255 ];
256
257 #[cfg(not(feature = "di"))]
258 const FIELDS: &[&str] = &[
259 "path_params",
260 "query_params",
261 "validated_params",
262 "raw_query_params",
263 "body",
264 "raw_body",
265 "headers",
266 "cookies",
267 "method",
268 "path",
269 ];
270
271 deserializer.deserialize_struct("RequestData", FIELDS, RequestDataVisitor)
272 }
273}
274
275pub type HandlerResult = Result<Response<Body>, (StatusCode, String)>;
277
278pub trait Handler: Send + Sync {
283 fn call(
289 &self,
290 request: Request<Body>,
291 request_data: RequestData,
292 ) -> Pin<Box<dyn Future<Output = HandlerResult> + Send + '_>>;
293
294 fn prefers_raw_json_body(&self) -> bool {
300 false
301 }
302
303 fn prefers_parameter_extraction(&self) -> bool {
310 false
311 }
312
313 fn wants_headers(&self) -> bool {
318 true
319 }
320
321 fn wants_cookies(&self) -> bool {
325 true
326 }
327
328 fn wants_request_extensions(&self) -> bool {
333 false
334 }
335
336 fn static_response(&self) -> Option<StaticResponse> {
340 None
341 }
342}
343
344pub struct StaticResponseHandler {
350 response: StaticResponse,
351}
352
353impl StaticResponseHandler {
354 pub fn new(response: StaticResponse) -> Self {
356 Self { response }
357 }
358
359 pub fn from_parts(
364 status: u16,
365 body: impl Into<Bytes>,
366 content_type: Option<&str>,
367 extra_headers: Vec<(HeaderName, HeaderValue)>,
368 ) -> Self {
369 let ct = content_type
370 .and_then(|s| HeaderValue::from_str(s).ok())
371 .unwrap_or_else(|| HeaderValue::from_static("text/plain; charset=utf-8"));
372 Self {
373 response: StaticResponse {
374 status,
375 headers: extra_headers,
376 body: body.into(),
377 content_type: ct,
378 },
379 }
380 }
381}
382
383impl Handler for StaticResponseHandler {
384 fn call(
385 &self,
386 _request: Request<Body>,
387 _request_data: RequestData,
388 ) -> Pin<Box<dyn Future<Output = HandlerResult> + Send + '_>> {
389 let resp = self.response.to_response();
392 Box::pin(async move { Ok(resp) })
393 }
394
395 fn static_response(&self) -> Option<StaticResponse> {
396 Some(self.response.clone())
397 }
398}
399
400#[derive(Debug, Clone)]
402pub struct ValidatedParams {
403 pub params: HashMap<String, Value>,
404}
405
406#[cfg(test)]
407mod tests {
408 use super::*;
409 use std::collections::HashMap;
410
411 fn minimal_request_data() -> RequestData {
412 RequestData {
413 path_params: std::sync::Arc::new(HashMap::new()),
414 query_params: std::sync::Arc::new(Value::Object(serde_json::Map::new())),
415 validated_params: None,
416 raw_query_params: std::sync::Arc::new(HashMap::new()),
417 body: std::sync::Arc::new(Value::Null),
418 raw_body: None,
419 headers: std::sync::Arc::new(HashMap::new()),
420 cookies: std::sync::Arc::new(HashMap::new()),
421 method: "GET".to_string(),
422 path: "/".to_string(),
423 #[cfg(feature = "di")]
424 dependencies: None,
425 }
426 }
427
428 #[test]
429 fn test_request_data_serialization_minimal() {
430 let data = minimal_request_data();
431
432 let json = serde_json::to_value(&data).expect("serialization failed");
433
434 assert!(json["path_params"].is_object());
435 assert!(json["query_params"].is_object());
436 assert!(json["raw_query_params"].is_object());
437 assert!(json["body"].is_null());
438 assert!(json["headers"].is_object());
439 assert!(json["cookies"].is_object());
440 assert_eq!(json["method"], "GET");
441 assert_eq!(json["path"], "/");
442 }
443
444 #[test]
445 fn test_request_data_serialization_with_path_params() {
446 let mut path_params = HashMap::new();
447 path_params.insert("id".to_string(), "123".to_string());
448 path_params.insert("username".to_string(), "alice".to_string());
449
450 let data = RequestData {
451 path_params: std::sync::Arc::new(path_params),
452 ..minimal_request_data()
453 };
454
455 let json = serde_json::to_value(&data).expect("serialization failed");
456
457 assert_eq!(json["path_params"]["id"], "123");
458 assert_eq!(json["path_params"]["username"], "alice");
459 }
460
461 #[test]
462 fn test_request_data_serialization_with_query_params() {
463 let query_params = serde_json::json!({
464 "filter": "active",
465 "limit": 10,
466 "sort": "name"
467 });
468
469 let data = RequestData {
470 query_params: std::sync::Arc::new(query_params),
471 ..minimal_request_data()
472 };
473
474 let json = serde_json::to_value(&data).expect("serialization failed");
475
476 assert_eq!(json["query_params"]["filter"], "active");
477 assert_eq!(json["query_params"]["limit"], 10);
478 assert_eq!(json["query_params"]["sort"], "name");
479 }
480
481 #[test]
482 fn test_request_data_serialization_with_raw_query_params() {
483 let mut raw_query_params = HashMap::new();
484 raw_query_params.insert("tags".to_string(), vec!["rust".to_string(), "web".to_string()]);
485 raw_query_params.insert("category".to_string(), vec!["backend".to_string()]);
486
487 let data = RequestData {
488 raw_query_params: std::sync::Arc::new(raw_query_params),
489 ..minimal_request_data()
490 };
491
492 let json = serde_json::to_value(&data).expect("serialization failed");
493
494 assert!(json["raw_query_params"]["tags"].is_array());
495 assert_eq!(json["raw_query_params"]["tags"][0], "rust");
496 assert_eq!(json["raw_query_params"]["tags"][1], "web");
497 }
498
499 #[test]
500 fn test_request_data_serialization_with_headers() {
501 let mut headers = HashMap::new();
502 headers.insert("content-type".to_string(), "application/json".to_string());
503 headers.insert("authorization".to_string(), "Bearer token123".to_string());
504 headers.insert("user-agent".to_string(), "test-client/1.0".to_string());
505
506 let data = RequestData {
507 headers: std::sync::Arc::new(headers),
508 ..minimal_request_data()
509 };
510
511 let json = serde_json::to_value(&data).expect("serialization failed");
512
513 assert_eq!(json["headers"]["content-type"], "application/json");
514 assert_eq!(json["headers"]["authorization"], "Bearer token123");
515 assert_eq!(json["headers"]["user-agent"], "test-client/1.0");
516 }
517
518 #[test]
519 fn test_request_data_serialization_with_cookies() {
520 let mut cookies = HashMap::new();
521 cookies.insert("session_id".to_string(), "abc123def456".to_string());
522 cookies.insert("preferences".to_string(), "dark_mode=true".to_string());
523
524 let data = RequestData {
525 cookies: std::sync::Arc::new(cookies),
526 ..minimal_request_data()
527 };
528
529 let json = serde_json::to_value(&data).expect("serialization failed");
530
531 assert_eq!(json["cookies"]["session_id"], "abc123def456");
532 assert_eq!(json["cookies"]["preferences"], "dark_mode=true");
533 }
534
535 #[test]
536 fn test_request_data_serialization_with_json_body() {
537 let body = serde_json::json!({
538 "name": "test",
539 "age": 30,
540 "active": true,
541 "tags": ["a", "b"]
542 });
543
544 let data = RequestData {
545 body: std::sync::Arc::new(body),
546 ..minimal_request_data()
547 };
548
549 let json = serde_json::to_value(&data).expect("serialization failed");
550
551 assert_eq!(json["body"]["name"], "test");
552 assert_eq!(json["body"]["age"], 30);
553 assert_eq!(json["body"]["active"], true);
554 assert!(json["body"]["tags"].is_array());
555 }
556
557 #[test]
558 fn test_request_data_serialization_with_raw_body() {
559 let raw_body = bytes::Bytes::from("raw body content");
560 let data = RequestData {
561 raw_body: Some(raw_body),
562 ..minimal_request_data()
563 };
564
565 let json = serde_json::to_value(&data).expect("serialization failed");
566
567 assert!(json["raw_body"].is_array());
568 let serialized_bytes: Vec<u8> =
569 serde_json::from_value(json["raw_body"].clone()).expect("failed to deserialize bytes");
570 assert_eq!(serialized_bytes, b"raw body content".to_vec());
571 }
572
573 #[test]
574 fn test_request_data_serialization_with_empty_strings() {
575 let mut headers = HashMap::new();
576 headers.insert("x-empty".to_string(), "".to_string());
577
578 let data = RequestData {
579 method: "".to_string(),
580 path: "".to_string(),
581 headers: std::sync::Arc::new(headers),
582 ..minimal_request_data()
583 };
584
585 let json = serde_json::to_value(&data).expect("serialization failed");
586
587 assert_eq!(json["method"], "");
588 assert_eq!(json["path"], "");
589 assert_eq!(json["headers"]["x-empty"], "");
590 }
591
592 #[test]
593 fn test_request_data_serialization_with_nested_json_body() {
594 let body = serde_json::json!({
595 "user": {
596 "profile": {
597 "name": "Alice",
598 "contact": {
599 "email": "alice@example.com",
600 "phone": null
601 }
602 }
603 },
604 "settings": {
605 "notifications": [true, false, true]
606 }
607 });
608
609 let data = RequestData {
610 body: std::sync::Arc::new(body),
611 ..minimal_request_data()
612 };
613
614 let json = serde_json::to_value(&data).expect("serialization failed");
615
616 assert_eq!(json["body"]["user"]["profile"]["name"], "Alice");
617 assert_eq!(json["body"]["user"]["profile"]["contact"]["email"], "alice@example.com");
618 assert!(json["body"]["user"]["profile"]["contact"]["phone"].is_null());
619 assert_eq!(json["body"]["settings"]["notifications"][0], true);
620 }
621
622 #[test]
623 fn test_request_data_serialization_all_fields_complete() {
624 let mut path_params = HashMap::new();
625 path_params.insert("id".to_string(), "42".to_string());
626
627 let mut raw_query_params = HashMap::new();
628 raw_query_params.insert("filter".to_string(), vec!["active".to_string()]);
629
630 let mut headers = HashMap::new();
631 headers.insert("content-type".to_string(), "application/json".to_string());
632
633 let mut cookies = HashMap::new();
634 cookies.insert("session".to_string(), "xyz789".to_string());
635
636 let body = serde_json::json!({"action": "create"});
637 let raw_body = bytes::Bytes::from("body bytes");
638
639 let data = RequestData {
640 path_params: std::sync::Arc::new(path_params),
641 query_params: std::sync::Arc::new(serde_json::json!({"page": 1})),
642 validated_params: None,
643 raw_query_params: std::sync::Arc::new(raw_query_params),
644 body: std::sync::Arc::new(body),
645 raw_body: Some(raw_body),
646 headers: std::sync::Arc::new(headers),
647 cookies: std::sync::Arc::new(cookies),
648 method: "POST".to_string(),
649 path: "/api/users".to_string(),
650 #[cfg(feature = "di")]
651 dependencies: None,
652 };
653
654 let json = serde_json::to_value(&data).expect("serialization failed");
655
656 assert_eq!(json["path_params"]["id"], "42");
657 assert_eq!(json["query_params"]["page"], 1);
658 assert_eq!(json["raw_query_params"]["filter"][0], "active");
659 assert_eq!(json["body"]["action"], "create");
660 assert!(json["raw_body"].is_array());
661 assert_eq!(json["headers"]["content-type"], "application/json");
662 assert_eq!(json["cookies"]["session"], "xyz789");
663 assert_eq!(json["method"], "POST");
664 assert_eq!(json["path"], "/api/users");
665 }
666
667 #[test]
668 fn test_request_data_clone_shares_arc_data() {
669 let mut path_params = HashMap::new();
670 path_params.insert("id".to_string(), "original".to_string());
671
672 let data1 = RequestData {
673 path_params: std::sync::Arc::new(path_params),
674 ..minimal_request_data()
675 };
676
677 let data2 = data1.clone();
678
679 assert!(std::sync::Arc::ptr_eq(&data1.path_params, &data2.path_params));
680 }
681
682 #[test]
683 fn test_request_data_deserialization_complete() {
684 let json = serde_json::json!({
685 "path_params": {"id": "123"},
686 "query_params": {"filter": "active"},
687 "raw_query_params": {"tags": ["rust", "web"]},
688 "body": {"name": "test"},
689 "raw_body": null,
690 "headers": {"content-type": "application/json"},
691 "cookies": {"session": "abc"},
692 "method": "POST",
693 "path": "/api/test"
694 });
695
696 let data: RequestData = serde_json::from_value(json).expect("deserialization failed");
697
698 assert_eq!(data.path_params.get("id").unwrap(), "123");
699 assert_eq!(data.query_params["filter"], "active");
700 assert_eq!(data.raw_query_params.get("tags").unwrap()[0], "rust");
701 assert_eq!(data.body["name"], "test");
702 assert!(data.raw_body.is_none());
703 assert_eq!(data.headers.get("content-type").unwrap(), "application/json");
704 assert_eq!(data.cookies.get("session").unwrap(), "abc");
705 assert_eq!(data.method, "POST");
706 assert_eq!(data.path, "/api/test");
707 }
708
709 #[test]
710 fn test_request_data_deserialization_with_raw_body_bytes() {
711 let json = serde_json::json!({
712 "path_params": {},
713 "query_params": {},
714 "raw_query_params": {},
715 "body": null,
716 "raw_body": [72, 101, 108, 108, 111],
717 "headers": {},
718 "cookies": {},
719 "method": "GET",
720 "path": "/"
721 });
722
723 let data: RequestData = serde_json::from_value(json).expect("deserialization failed");
724
725 assert!(data.raw_body.is_some());
726 assert_eq!(data.raw_body.unwrap().as_ref(), b"Hello");
727 }
728
729 #[test]
730 fn test_request_data_deserialization_missing_required_field_path_params() {
731 let json = serde_json::json!({
732 "query_params": {},
733 "raw_query_params": {},
734 "body": null,
735 "headers": {},
736 "cookies": {},
737 "method": "GET",
738 "path": "/"
739 });
740
741 let result: Result<RequestData, _> = serde_json::from_value(json);
742 assert!(result.is_err());
743 assert!(result.unwrap_err().to_string().contains("path_params"));
744 }
745
746 #[test]
747 fn test_request_data_deserialization_missing_required_field_method() {
748 let json = serde_json::json!({
749 "path_params": {},
750 "query_params": {},
751 "raw_query_params": {},
752 "body": null,
753 "headers": {},
754 "cookies": {},
755 "path": "/"
756 });
757
758 let result: Result<RequestData, _> = serde_json::from_value(json);
759 assert!(result.is_err());
760 assert!(result.unwrap_err().to_string().contains("method"));
761 }
762
763 #[test]
764 fn test_request_data_serialization_roundtrip() {
765 let original = RequestData {
766 path_params: std::sync::Arc::new({
767 let mut map = HashMap::new();
768 map.insert("id".to_string(), "999".to_string());
769 map
770 }),
771 query_params: std::sync::Arc::new(serde_json::json!({"limit": 50, "offset": 10})),
772 validated_params: None,
773 raw_query_params: std::sync::Arc::new({
774 let mut map = HashMap::new();
775 map.insert("sort".to_string(), vec!["name".to_string(), "date".to_string()]);
776 map
777 }),
778 body: std::sync::Arc::new(serde_json::json!({"title": "New Post", "content": "Hello World"})),
779 raw_body: None,
780 headers: std::sync::Arc::new({
781 let mut map = HashMap::new();
782 map.insert("accept".to_string(), "application/json".to_string());
783 map
784 }),
785 cookies: std::sync::Arc::new({
786 let mut map = HashMap::new();
787 map.insert("user_id".to_string(), "user42".to_string());
788 map
789 }),
790 method: "PUT".to_string(),
791 path: "/blog/posts/999".to_string(),
792 #[cfg(feature = "di")]
793 dependencies: None,
794 };
795
796 let json = serde_json::to_value(&original).expect("serialization failed");
797 let restored: RequestData = serde_json::from_value(json).expect("deserialization failed");
798
799 assert_eq!(*original.path_params, *restored.path_params);
800 assert_eq!(original.query_params, restored.query_params);
801 assert_eq!(*original.raw_query_params, *restored.raw_query_params);
802 assert_eq!(original.body, restored.body);
803 assert_eq!(original.raw_body, restored.raw_body);
804 assert_eq!(*original.headers, *restored.headers);
805 assert_eq!(*original.cookies, *restored.cookies);
806 assert_eq!(original.method, restored.method);
807 assert_eq!(original.path, restored.path);
808 }
809
810 #[test]
811 fn test_request_data_serialization_large_body() {
812 let mut large_object = serde_json::Map::new();
813 for i in 0..100 {
814 large_object.insert(format!("key_{}", i), serde_json::Value::String(format!("value_{}", i)));
815 }
816
817 let data = RequestData {
818 body: std::sync::Arc::new(Value::Object(large_object)),
819 ..minimal_request_data()
820 };
821
822 let json = serde_json::to_value(&data).expect("serialization failed");
823
824 assert!(json["body"].is_object());
825 assert_eq!(json["body"].get("key_0").unwrap(), "value_0");
826 assert_eq!(json["body"].get("key_99").unwrap(), "value_99");
827 }
828
829 #[test]
830 fn test_request_data_empty_collections() {
831 let data = RequestData {
832 path_params: std::sync::Arc::new(HashMap::new()),
833 query_params: std::sync::Arc::new(Value::Object(serde_json::Map::new())),
834 validated_params: None,
835 raw_query_params: std::sync::Arc::new(HashMap::new()),
836 body: std::sync::Arc::new(Value::Object(serde_json::Map::new())),
837 raw_body: None,
838 headers: std::sync::Arc::new(HashMap::new()),
839 cookies: std::sync::Arc::new(HashMap::new()),
840 method: "GET".to_string(),
841 path: "/".to_string(),
842 #[cfg(feature = "di")]
843 dependencies: None,
844 };
845
846 let json = serde_json::to_value(&data).expect("serialization failed");
847
848 assert_eq!(json["path_params"].as_object().unwrap().len(), 0);
849 assert_eq!(json["query_params"].as_object().unwrap().len(), 0);
850 assert_eq!(json["raw_query_params"].as_object().unwrap().len(), 0);
851 assert_eq!(json["body"].as_object().unwrap().len(), 0);
852 assert!(json["raw_body"].is_null());
853 assert_eq!(json["headers"].as_object().unwrap().len(), 0);
854 assert_eq!(json["cookies"].as_object().unwrap().len(), 0);
855 }
856
857 #[test]
858 fn test_request_data_special_characters_in_strings() {
859 let mut headers = HashMap::new();
860 headers.insert("x-custom".to_string(), "value with \"quotes\"".to_string());
861 headers.insert("x-unicode".to_string(), "Café ☕ 🚀".to_string());
862
863 let data = RequestData {
864 method: "POST".to_string(),
865 path: "/api/v1/users\\test".to_string(),
866 headers: std::sync::Arc::new(headers),
867 body: std::sync::Arc::new(serde_json::json!({"note": "Contains\nnewline"})),
868 ..minimal_request_data()
869 };
870
871 let json = serde_json::to_value(&data).expect("serialization failed");
872
873 assert_eq!(json["headers"]["x-custom"], "value with \"quotes\"");
874 assert_eq!(json["headers"]["x-unicode"], "Café ☕ 🚀");
875 assert_eq!(json["path"], "/api/v1/users\\test");
876 assert_eq!(json["body"]["note"], "Contains\nnewline");
877 }
878
879 #[test]
880 #[cfg(feature = "di")]
881 fn test_request_data_serialization_with_di_feature_no_dependencies() {
882 let data = minimal_request_data();
883
884 let json = serde_json::to_value(&data).expect("serialization failed");
885
886 assert_eq!(json["has_dependencies"], false);
887 }
888
889 #[test]
890 fn test_request_data_method_variants() {
891 let methods = vec!["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"];
892
893 for method in methods {
894 let data = RequestData {
895 method: method.to_string(),
896 ..minimal_request_data()
897 };
898
899 let json = serde_json::to_value(&data).expect("serialization failed");
900
901 assert_eq!(json["method"], method);
902 }
903 }
904
905 #[test]
906 fn test_request_data_serialization_null_body() {
907 let data = RequestData {
908 body: std::sync::Arc::new(Value::Null),
909 ..minimal_request_data()
910 };
911
912 let json = serde_json::to_value(&data).expect("serialization failed");
913
914 assert!(json["body"].is_null());
915 }
916
917 #[test]
918 fn test_request_data_serialization_array_body() {
919 let data = RequestData {
920 body: std::sync::Arc::new(serde_json::json!([1, 2, 3, "four", {"five": 5}])),
921 ..minimal_request_data()
922 };
923
924 let json = serde_json::to_value(&data).expect("serialization failed");
925
926 assert!(json["body"].is_array());
927 assert_eq!(json["body"][0], 1);
928 assert_eq!(json["body"][1], 2);
929 assert_eq!(json["body"][3], "four");
930 assert_eq!(json["body"][4]["five"], 5);
931 }
932
933 #[test]
934 fn test_request_data_serialization_numeric_edge_cases() {
935 let data = RequestData {
936 body: std::sync::Arc::new(serde_json::json!({
937 "zero": 0,
938 "negative": -42,
939 "large": 9223372036854775807i64,
940 "float": 3.14159
941 })),
942 ..minimal_request_data()
943 };
944
945 let json = serde_json::to_value(&data).expect("serialization failed");
946
947 assert_eq!(json["body"]["zero"], 0);
948 assert_eq!(json["body"]["negative"], -42);
949 assert_eq!(json["body"]["large"], 9223372036854775807i64);
950 assert_eq!(json["body"]["float"], 3.14159);
951 }
952
953 #[test]
954 fn test_validated_params_basic_creation() {
955 let mut params = HashMap::new();
956 params.insert("id".to_string(), Value::String("123".to_string()));
957 params.insert("active".to_string(), Value::Bool(true));
958
959 let validated = ValidatedParams { params };
960
961 assert_eq!(validated.params.get("id").unwrap(), &Value::String("123".to_string()));
962 assert_eq!(validated.params.get("active").unwrap(), &Value::Bool(true));
963 }
964
965 #[test]
966 fn test_static_response_handler_new() {
967 let sr = StaticResponse {
968 status: 200,
969 headers: vec![],
970 body: Bytes::from("OK"),
971 content_type: HeaderValue::from_static("text/plain"),
972 };
973 let handler = StaticResponseHandler::new(sr);
974 let resp = handler.static_response();
975 assert!(resp.is_some());
976 let resp = resp.unwrap();
977 assert_eq!(resp.status, 200);
978 assert_eq!(resp.body.as_ref(), b"OK");
979 }
980
981 #[test]
982 fn test_static_response_handler_from_parts_defaults() {
983 let handler = StaticResponseHandler::from_parts(204, "No Content", None, vec![]);
984 let resp = handler.static_response().unwrap();
985 assert_eq!(resp.status, 204);
986 assert_eq!(resp.body.as_ref(), b"No Content");
987 assert_eq!(resp.content_type, "text/plain; charset=utf-8");
988 }
989
990 #[test]
991 fn test_static_response_handler_from_parts_custom_content_type() {
992 let handler = StaticResponseHandler::from_parts(200, r#"{"ok":true}"#, Some("application/json"), vec![]);
993 let resp = handler.static_response().unwrap();
994 assert_eq!(resp.content_type, "application/json");
995 }
996
997 #[test]
998 fn test_static_response_handler_from_parts_extra_headers() {
999 let handler = StaticResponseHandler::from_parts(
1000 200,
1001 "OK",
1002 None,
1003 vec![(HeaderName::from_static("x-custom"), HeaderValue::from_static("value"))],
1004 );
1005 let resp = handler.static_response().unwrap();
1006 assert_eq!(resp.headers.len(), 1);
1007 assert_eq!(resp.headers[0].0, "x-custom");
1008 assert_eq!(resp.headers[0].1, "value");
1009 }
1010
1011 #[tokio::test]
1012 async fn test_static_response_handler_call_fallback() {
1013 use http_body_util::BodyExt;
1014
1015 let handler = StaticResponseHandler::from_parts(201, "created", Some("text/plain"), vec![]);
1016 let request = Request::builder().body(Body::empty()).unwrap();
1017 let result = handler.call(request, minimal_request_data()).await;
1018 assert!(result.is_ok());
1019 let response = result.unwrap();
1020 assert_eq!(response.status(), StatusCode::CREATED);
1021 assert_eq!(response.headers().get("content-type").unwrap(), "text/plain");
1022 let body = response.into_body().collect().await.unwrap().to_bytes();
1023 assert_eq!(body.as_ref(), b"created");
1024 }
1025
1026 #[test]
1027 fn test_default_handler_static_response_is_none() {
1028 struct DummyHandler;
1029 impl Handler for DummyHandler {
1030 fn call(
1031 &self,
1032 _: Request<Body>,
1033 _: RequestData,
1034 ) -> Pin<Box<dyn Future<Output = HandlerResult> + Send + '_>> {
1035 Box::pin(async { Err((StatusCode::OK, String::new())) })
1036 }
1037 }
1038 assert!(DummyHandler.static_response().is_none());
1039 }
1040}