1use crate::{Error, HttpRequest, HttpResponse};
20use std::future::Future;
21use std::marker::PhantomData;
22use std::pin::Pin;
23use std::sync::atomic::{AtomicU64, Ordering};
24
25pub trait Extract: Sized {
31 fn extract(req: &HttpRequest) -> Result<Self, Error>;
33}
34
35#[derive(Debug)]
37pub struct One<E: Extract>(pub E);
38
39impl<E: Extract> Extract for One<E> {
40 #[inline]
41 fn extract(req: &HttpRequest) -> Result<Self, Error> {
42 E::extract(req).map(One)
43 }
44}
45
46#[derive(Debug)]
48pub struct Two<E1: Extract, E2: Extract>(pub E1, pub E2);
49
50impl<E1: Extract, E2: Extract> Extract for Two<E1, E2> {
51 #[inline]
52 fn extract(req: &HttpRequest) -> Result<Self, Error> {
53 let e1 = E1::extract(req)?;
54 let e2 = E2::extract(req)?;
55 Ok(Two(e1, e2))
56 }
57}
58
59#[derive(Debug)]
61pub struct Three<E1: Extract, E2: Extract, E3: Extract>(pub E1, pub E2, pub E3);
62
63impl<E1: Extract, E2: Extract, E3: Extract> Extract for Three<E1, E2, E3> {
64 #[inline]
65 fn extract(req: &HttpRequest) -> Result<Self, Error> {
66 let e1 = E1::extract(req)?;
67 let e2 = E2::extract(req)?;
68 let e3 = E3::extract(req)?;
69 Ok(Three(e1, e2, e3))
70 }
71}
72
73#[derive(Debug)]
75pub struct Four<E1: Extract, E2: Extract, E3: Extract, E4: Extract>(pub E1, pub E2, pub E3, pub E4);
76
77impl<E1: Extract, E2: Extract, E3: Extract, E4: Extract> Extract for Four<E1, E2, E3, E4> {
78 #[inline]
79 fn extract(req: &HttpRequest) -> Result<Self, Error> {
80 let e1 = E1::extract(req)?;
81 let e2 = E2::extract(req)?;
82 let e3 = E3::extract(req)?;
83 let e4 = E4::extract(req)?;
84 Ok(Four(e1, e2, e3, e4))
85 }
86}
87
88#[derive(Debug)]
90pub struct Five<E1: Extract, E2: Extract, E3: Extract, E4: Extract, E5: Extract>(
91 pub E1,
92 pub E2,
93 pub E3,
94 pub E4,
95 pub E5,
96);
97
98impl<E1: Extract, E2: Extract, E3: Extract, E4: Extract, E5: Extract> Extract
99 for Five<E1, E2, E3, E4, E5>
100{
101 #[inline]
102 fn extract(req: &HttpRequest) -> Result<Self, Error> {
103 let e1 = E1::extract(req)?;
104 let e2 = E2::extract(req)?;
105 let e3 = E3::extract(req)?;
106 let e4 = E4::extract(req)?;
107 let e5 = E5::extract(req)?;
108 Ok(Five(e1, e2, e3, e4, e5))
109 }
110}
111
112impl<E1: Extract, E2: Extract> Extract for (E1, E2) {
114 #[inline]
115 fn extract(req: &HttpRequest) -> Result<Self, Error> {
116 let e1 = E1::extract(req)?;
117 let e2 = E2::extract(req)?;
118 Ok((e1, e2))
119 }
120}
121
122impl<E1: Extract, E2: Extract, E3: Extract> Extract for (E1, E2, E3) {
123 #[inline]
124 fn extract(req: &HttpRequest) -> Result<Self, Error> {
125 let e1 = E1::extract(req)?;
126 let e2 = E2::extract(req)?;
127 let e3 = E3::extract(req)?;
128 Ok((e1, e2, e3))
129 }
130}
131
132impl<E1: Extract, E2: Extract, E3: Extract, E4: Extract> Extract for (E1, E2, E3, E4) {
133 #[inline]
134 fn extract(req: &HttpRequest) -> Result<Self, Error> {
135 let e1 = E1::extract(req)?;
136 let e2 = E2::extract(req)?;
137 let e3 = E3::extract(req)?;
138 let e4 = E4::extract(req)?;
139 Ok((e1, e2, e3, e4))
140 }
141}
142
143impl<E1: Extract, E2: Extract, E3: Extract, E4: Extract, E5: Extract> Extract
144 for (E1, E2, E3, E4, E5)
145{
146 #[inline]
147 fn extract(req: &HttpRequest) -> Result<Self, Error> {
148 let e1 = E1::extract(req)?;
149 let e2 = E2::extract(req)?;
150 let e3 = E3::extract(req)?;
151 let e4 = E4::extract(req)?;
152 let e5 = E5::extract(req)?;
153 Ok((e1, e2, e3, e4, e5))
154 }
155}
156
157#[derive(Debug, Clone)]
163pub struct RawBody(pub bytes::Bytes);
164
165impl Extract for RawBody {
166 #[inline]
167 fn extract(req: &HttpRequest) -> Result<Self, Error> {
168 EXTRACTOR_STATS.record_extraction("RawBody");
169 Ok(RawBody(req.body_bytes()))
170 }
171}
172
173impl std::ops::Deref for RawBody {
174 type Target = bytes::Bytes;
175
176 fn deref(&self) -> &Self::Target {
177 &self.0
178 }
179}
180
181#[derive(Debug, Clone)]
183pub struct JsonBody<T>(pub T);
184
185impl<T: serde::de::DeserializeOwned> Extract for JsonBody<T> {
186 #[inline]
187 fn extract(req: &HttpRequest) -> Result<Self, Error> {
188 EXTRACTOR_STATS.record_extraction("JsonBody");
189 req.json().map(JsonBody)
190 }
191}
192
193impl<T> std::ops::Deref for JsonBody<T> {
194 type Target = T;
195
196 fn deref(&self) -> &Self::Target {
197 &self.0
198 }
199}
200
201#[derive(Debug, Clone)]
203pub struct QueryParams<T>(pub T);
204
205impl<T: serde::de::DeserializeOwned> Extract for QueryParams<T> {
206 #[inline]
207 fn extract(req: &HttpRequest) -> Result<Self, Error> {
208 EXTRACTOR_STATS.record_extraction("QueryParams");
209 let query_string: String = req
210 .query_params
211 .iter()
212 .map(|(k, v)| format!("{}={}", k, v))
213 .collect::<Vec<_>>()
214 .join("&");
215
216 serde_urlencoded::from_str(&query_string)
217 .map(QueryParams)
218 .map_err(|e| Error::Deserialization(format!("Query parsing error: {}", e)))
219 }
220}
221
222impl<T> std::ops::Deref for QueryParams<T> {
223 type Target = T;
224
225 fn deref(&self) -> &Self::Target {
226 &self.0
227 }
228}
229
230#[derive(Debug, Clone)]
232pub struct PathParam<const INDEX: usize>(pub String);
233
234impl<const INDEX: usize> Extract for PathParam<INDEX> {
235 #[inline]
236 fn extract(req: &HttpRequest) -> Result<Self, Error> {
237 EXTRACTOR_STATS.record_extraction("PathParam");
238 req.path_params
240 .values()
241 .nth(INDEX)
242 .cloned()
243 .map(PathParam)
244 .ok_or_else(|| {
245 Error::RouteNotFound(format!("Path parameter at index {} not found", INDEX))
246 })
247 }
248}
249
250impl<const INDEX: usize> std::ops::Deref for PathParam<INDEX> {
251 type Target = String;
252
253 fn deref(&self) -> &Self::Target {
254 &self.0
255 }
256}
257
258#[derive(Debug, Clone)]
260pub struct Header {
261 pub name: String,
263 pub value: Option<String>,
265}
266
267impl Header {
268 pub fn named(name: impl Into<String>, req: &HttpRequest) -> Self {
270 let name = name.into();
271 let value = req.headers.get(&name).cloned();
272 EXTRACTOR_STATS.record_extraction("Header");
273 Self { name, value }
274 }
275
276 pub fn required(self) -> Result<String, Error> {
278 self.value
279 .ok_or_else(|| Error::Validation(format!("Required header '{}' not found", self.name)))
280 }
281
282 pub fn unwrap_or(self, default: impl Into<String>) -> String {
284 self.value.unwrap_or_else(|| default.into())
285 }
286
287 pub fn is_present(&self) -> bool {
289 self.value.is_some()
290 }
291}
292
293#[derive(Debug, Clone)]
295pub struct ContentType(pub Option<String>);
296
297impl Extract for ContentType {
298 #[inline]
299 fn extract(req: &HttpRequest) -> Result<Self, Error> {
300 EXTRACTOR_STATS.record_extraction("ContentType");
301 Ok(ContentType(req.headers.get("content-type").cloned()))
302 }
303}
304
305impl ContentType {
306 pub fn is_json(&self) -> bool {
308 self.0
309 .as_ref()
310 .is_some_and(|v| v.contains("application/json"))
311 }
312
313 pub fn value(&self) -> Option<&str> {
315 self.0.as_deref()
316 }
317}
318
319#[derive(Debug, Clone)]
321pub struct Authorization(pub Option<String>);
322
323impl Extract for Authorization {
324 #[inline]
325 fn extract(req: &HttpRequest) -> Result<Self, Error> {
326 EXTRACTOR_STATS.record_extraction("Authorization");
327 Ok(Authorization(req.headers.get("authorization").cloned()))
328 }
329}
330
331impl Authorization {
332 pub fn bearer(&self) -> Option<&str> {
334 self.0.as_ref().and_then(|v| v.strip_prefix("Bearer "))
335 }
336
337 pub fn basic(&self) -> Option<&str> {
339 self.0.as_ref().and_then(|v| v.strip_prefix("Basic "))
340 }
341
342 pub fn value(&self) -> Option<&str> {
344 self.0.as_deref()
345 }
346}
347
348#[derive(Debug, Clone)]
350pub struct Method(pub String);
351
352impl Extract for Method {
353 #[inline]
354 fn extract(req: &HttpRequest) -> Result<Self, Error> {
355 EXTRACTOR_STATS.record_extraction("Method");
356 Ok(Method(req.method.clone()))
357 }
358}
359
360impl std::ops::Deref for Method {
361 type Target = str;
362
363 fn deref(&self) -> &Self::Target {
364 &self.0
365 }
366}
367
368#[derive(Debug, Clone)]
370pub struct RequestPath(pub String);
371
372impl Extract for RequestPath {
373 #[inline]
374 fn extract(req: &HttpRequest) -> Result<Self, Error> {
375 EXTRACTOR_STATS.record_extraction("RequestPath");
376 Ok(RequestPath(req.path.clone()))
377 }
378}
379
380impl std::ops::Deref for RequestPath {
381 type Target = str;
382
383 fn deref(&self) -> &Self::Target {
384 &self.0
385 }
386}
387
388#[derive(Debug, Clone)]
390pub struct Optional<E>(pub Option<E>);
391
392impl<E: Extract> Extract for Optional<E> {
393 #[inline]
394 fn extract(req: &HttpRequest) -> Result<Self, Error> {
395 Ok(Optional(E::extract(req).ok()))
396 }
397}
398
399impl<E> std::ops::Deref for Optional<E> {
400 type Target = Option<E>;
401
402 fn deref(&self) -> &Self::Target {
403 &self.0
404 }
405}
406
407pub trait Layer<S> {
413 type Service;
415
416 fn layer(&self, inner: S) -> Self::Service;
418}
419
420pub trait Service<Request> {
422 type Response;
424 type Error;
426 type Future: Future<Output = Result<Self::Response, Self::Error>> + Send;
428
429 fn call(&self, req: Request) -> Self::Future;
431}
432
433#[derive(Debug, Clone, Copy, Default)]
435pub struct Identity;
436
437impl<S> Layer<S> for Identity {
438 type Service = S;
439
440 #[inline]
441 fn layer(&self, inner: S) -> Self::Service {
442 inner
443 }
444}
445
446#[derive(Debug, Clone)]
448pub struct Stack<Inner, Outer> {
449 inner: Inner,
450 outer: Outer,
451}
452
453impl<Inner, Outer> Stack<Inner, Outer> {
454 pub fn new(inner: Inner, outer: Outer) -> Self {
456 Self { inner, outer }
457 }
458}
459
460impl<S, Inner, Outer> Layer<S> for Stack<Inner, Outer>
461where
462 Inner: Layer<S>,
463 Outer: Layer<Inner::Service>,
464{
465 type Service = Outer::Service;
466
467 #[inline]
468 fn layer(&self, service: S) -> Self::Service {
469 let inner = self.inner.layer(service);
470 self.outer.layer(inner)
471 }
472}
473
474#[derive(Debug, Clone)]
476pub struct LayerBuilder<L> {
477 layer: L,
478}
479
480impl LayerBuilder<Identity> {
481 pub fn new() -> Self {
483 Self { layer: Identity }
484 }
485}
486
487impl Default for LayerBuilder<Identity> {
488 fn default() -> Self {
489 Self::new()
490 }
491}
492
493impl<L> LayerBuilder<L> {
494 pub fn layer<NewLayer>(self, new_layer: NewLayer) -> LayerBuilder<Stack<L, NewLayer>> {
496 LayerBuilder {
497 layer: Stack::new(self.layer, new_layer),
498 }
499 }
500
501 pub fn service<S>(self, service: S) -> L::Service
503 where
504 L: Layer<S>,
505 {
506 self.layer.layer(service)
507 }
508
509 pub fn into_layer(self) -> L {
511 self.layer
512 }
513}
514
515#[derive(Debug, Clone)]
521pub struct LoggingLayer {
522 level: LogLevel,
523}
524
525#[derive(Debug, Clone, Copy, PartialEq, Eq)]
526pub enum LogLevel {
527 Debug,
528 Info,
529 Warn,
530 Error,
531}
532
533impl LoggingLayer {
534 pub fn new(level: LogLevel) -> Self {
536 Self { level }
537 }
538
539 pub fn info() -> Self {
541 Self::new(LogLevel::Info)
542 }
543
544 pub fn debug() -> Self {
546 Self::new(LogLevel::Debug)
547 }
548}
549
550impl Default for LoggingLayer {
551 fn default() -> Self {
552 Self::info()
553 }
554}
555
556impl<S> Layer<S> for LoggingLayer {
557 type Service = LoggingService<S>;
558
559 fn layer(&self, inner: S) -> Self::Service {
560 LoggingService {
561 inner,
562 level: self.level,
563 }
564 }
565}
566
567#[derive(Debug, Clone)]
569pub struct LoggingService<S> {
570 inner: S,
571 #[allow(dead_code)]
572 level: LogLevel,
573}
574
575impl<S> Service<HttpRequest> for LoggingService<S>
576where
577 S: Service<HttpRequest, Response = HttpResponse, Error = Error> + Clone + Send + Sync + 'static,
578 S::Future: Send,
579{
580 type Response = HttpResponse;
581 type Error = Error;
582 type Future = Pin<Box<dyn Future<Output = Result<HttpResponse, Error>> + Send>>;
583
584 fn call(&self, req: HttpRequest) -> Self::Future {
585 let inner = self.inner.clone();
586 let method = req.method.clone();
587 let path = req.path.clone();
588
589 MIDDLEWARE_STATS.record_call("Logging");
590
591 Box::pin(async move {
592 let start = std::time::Instant::now();
593 let result = inner.call(req).await;
594 let duration = start.elapsed();
595
596 match &result {
597 Ok(resp) => {
598 tracing::info!(
599 method = %method,
600 path = %path,
601 status = resp.status,
602 duration_ms = duration.as_millis() as u64,
603 "Request completed"
604 );
605 }
606 Err(e) => {
607 tracing::error!(
608 method = %method,
609 path = %path,
610 error = %e,
611 duration_ms = duration.as_millis() as u64,
612 "Request failed"
613 );
614 }
615 }
616
617 result
618 })
619 }
620}
621
622#[derive(Debug, Clone)]
624pub struct TimeoutLayer {
625 duration: std::time::Duration,
626}
627
628impl TimeoutLayer {
629 pub fn new(duration: std::time::Duration) -> Self {
631 Self { duration }
632 }
633
634 pub fn from_secs(secs: u64) -> Self {
636 Self::new(std::time::Duration::from_secs(secs))
637 }
638
639 pub fn from_millis(millis: u64) -> Self {
641 Self::new(std::time::Duration::from_millis(millis))
642 }
643}
644
645impl<S> Layer<S> for TimeoutLayer {
646 type Service = TimeoutService<S>;
647
648 fn layer(&self, inner: S) -> Self::Service {
649 TimeoutService {
650 inner,
651 duration: self.duration,
652 }
653 }
654}
655
656#[derive(Debug, Clone)]
658pub struct TimeoutService<S> {
659 inner: S,
660 duration: std::time::Duration,
661}
662
663impl<S> Service<HttpRequest> for TimeoutService<S>
664where
665 S: Service<HttpRequest, Response = HttpResponse, Error = Error> + Clone + Send + Sync + 'static,
666 S::Future: Send,
667{
668 type Response = HttpResponse;
669 type Error = Error;
670 type Future = Pin<Box<dyn Future<Output = Result<HttpResponse, Error>> + Send>>;
671
672 fn call(&self, req: HttpRequest) -> Self::Future {
673 let inner = self.inner.clone();
674 let duration = self.duration;
675
676 MIDDLEWARE_STATS.record_call("Timeout");
677
678 Box::pin(async move {
679 match tokio::time::timeout(duration, inner.call(req)).await {
680 Ok(result) => result,
681 Err(_) => Err(Error::timeout("Request timed out")),
682 }
683 })
684 }
685}
686
687#[derive(Debug, Clone, Default)]
689pub struct RequestIdLayer;
690
691impl<S> Layer<S> for RequestIdLayer {
692 type Service = RequestIdService<S>;
693
694 fn layer(&self, inner: S) -> Self::Service {
695 RequestIdService { inner }
696 }
697}
698
699#[derive(Debug, Clone)]
701pub struct RequestIdService<S> {
702 inner: S,
703}
704
705impl<S> Service<HttpRequest> for RequestIdService<S>
706where
707 S: Service<HttpRequest, Response = HttpResponse, Error = Error> + Clone + Send + Sync + 'static,
708 S::Future: Send,
709{
710 type Response = HttpResponse;
711 type Error = Error;
712 type Future = Pin<Box<dyn Future<Output = Result<HttpResponse, Error>> + Send>>;
713
714 fn call(&self, mut req: HttpRequest) -> Self::Future {
715 let inner = self.inner.clone();
716
717 MIDDLEWARE_STATS.record_call("RequestId");
718
719 let request_id = req
721 .headers
722 .get("x-request-id")
723 .cloned()
724 .unwrap_or_else(|| uuid::Uuid::new_v4().to_string());
725
726 req.headers
727 .insert("x-request-id".to_string(), request_id.clone());
728
729 Box::pin(async move {
730 let mut resp = inner.call(req).await?;
731 resp.headers.insert("x-request-id".to_string(), request_id);
732 Ok(resp)
733 })
734 }
735}
736
737#[derive(Clone)]
743pub struct HandlerService<H, Args> {
744 handler: H,
745 _args: PhantomData<Args>,
746}
747
748impl<H, Args> HandlerService<H, Args> {
749 pub fn new(handler: H) -> Self {
751 Self {
752 handler,
753 _args: PhantomData,
754 }
755 }
756}
757
758pub trait IntoService<Args> {
760 type Service;
762
763 fn into_service(self) -> Self::Service;
765}
766
767impl<H, Fut> IntoService<()> for H
769where
770 H: Fn() -> Fut + Clone + Send + Sync + 'static,
771 Fut: Future<Output = Result<HttpResponse, Error>> + Send + 'static,
772{
773 type Service = HandlerService<H, ()>;
774
775 fn into_service(self) -> Self::Service {
776 HandlerService::new(self)
777 }
778}
779
780impl<H, Fut> Service<HttpRequest> for HandlerService<H, ()>
781where
782 H: Fn() -> Fut + Clone + Send + Sync + 'static,
783 Fut: Future<Output = Result<HttpResponse, Error>> + Send + 'static,
784{
785 type Response = HttpResponse;
786 type Error = Error;
787 type Future = Pin<Box<dyn Future<Output = Result<HttpResponse, Error>> + Send>>;
788
789 fn call(&self, _req: HttpRequest) -> Self::Future {
790 let handler = self.handler.clone();
791 Box::pin(async move { handler().await })
792 }
793}
794
795impl<H, Fut> IntoService<(HttpRequest,)> for H
797where
798 H: Fn(HttpRequest) -> Fut + Clone + Send + Sync + 'static,
799 Fut: Future<Output = Result<HttpResponse, Error>> + Send + 'static,
800{
801 type Service = HandlerService<H, (HttpRequest,)>;
802
803 fn into_service(self) -> Self::Service {
804 HandlerService::new(self)
805 }
806}
807
808impl<H, Fut> Service<HttpRequest> for HandlerService<H, (HttpRequest,)>
809where
810 H: Fn(HttpRequest) -> Fut + Clone + Send + Sync + 'static,
811 Fut: Future<Output = Result<HttpResponse, Error>> + Send + 'static,
812{
813 type Response = HttpResponse;
814 type Error = Error;
815 type Future = Pin<Box<dyn Future<Output = Result<HttpResponse, Error>> + Send>>;
816
817 fn call(&self, req: HttpRequest) -> Self::Future {
818 let handler = self.handler.clone();
819 Box::pin(async move { handler(req).await })
820 }
821}
822
823impl<H, Fut, E1> IntoService<(E1,)> for H
825where
826 H: Fn(E1) -> Fut + Clone + Send + Sync + 'static,
827 Fut: Future<Output = Result<HttpResponse, Error>> + Send + 'static,
828 E1: Extract + Send + 'static,
829{
830 type Service = ExtractorHandlerService<H, (E1,)>;
831
832 fn into_service(self) -> Self::Service {
833 ExtractorHandlerService::new(self)
834 }
835}
836
837#[derive(Clone)]
839pub struct ExtractorHandlerService<H, Args> {
840 handler: H,
841 _args: PhantomData<Args>,
842}
843
844impl<H, Args> ExtractorHandlerService<H, Args> {
845 pub fn new(handler: H) -> Self {
847 Self {
848 handler,
849 _args: PhantomData,
850 }
851 }
852}
853
854impl<H, Fut, E1> Service<HttpRequest> for ExtractorHandlerService<H, (E1,)>
855where
856 H: Fn(E1) -> Fut + Clone + Send + Sync + 'static,
857 Fut: Future<Output = Result<HttpResponse, Error>> + Send + 'static,
858 E1: Extract + Send + 'static,
859{
860 type Response = HttpResponse;
861 type Error = Error;
862 type Future = Pin<Box<dyn Future<Output = Result<HttpResponse, Error>> + Send>>;
863
864 fn call(&self, req: HttpRequest) -> Self::Future {
865 let handler = self.handler.clone();
866 Box::pin(async move {
867 let e1 = E1::extract(&req)?;
868 handler(e1).await
869 })
870 }
871}
872
873#[derive(Debug, Default)]
879pub struct ZeroCostStats {
880 extractions: AtomicU64,
881 middleware_calls: AtomicU64,
882}
883
884impl ZeroCostStats {
885 fn record_extraction(&self, _name: &str) {
886 self.extractions.fetch_add(1, Ordering::Relaxed);
887 }
888
889 fn record_call(&self, _name: &str) {
890 self.middleware_calls.fetch_add(1, Ordering::Relaxed);
891 }
892
893 pub fn extractions(&self) -> u64 {
895 self.extractions.load(Ordering::Relaxed)
896 }
897
898 pub fn middleware_calls(&self) -> u64 {
900 self.middleware_calls.load(Ordering::Relaxed)
901 }
902}
903
904static EXTRACTOR_STATS: ZeroCostStats = ZeroCostStats {
905 extractions: AtomicU64::new(0),
906 middleware_calls: AtomicU64::new(0),
907};
908
909static MIDDLEWARE_STATS: ZeroCostStats = ZeroCostStats {
910 extractions: AtomicU64::new(0),
911 middleware_calls: AtomicU64::new(0),
912};
913
914pub fn extractor_stats() -> &'static ZeroCostStats {
916 &EXTRACTOR_STATS
917}
918
919pub fn middleware_stats() -> &'static ZeroCostStats {
921 &MIDDLEWARE_STATS
922}
923
924#[cfg(test)]
929mod tests {
930 use super::*;
931
932 fn create_request() -> HttpRequest {
933 let mut req = HttpRequest::new("GET".to_string(), "/api/users/123".to_string());
934 req.headers
935 .insert("content-type".to_string(), "application/json".to_string());
936 req.headers
937 .insert("authorization".to_string(), "Bearer token123".to_string());
938 req.body = br#"{"name":"test"}"#.to_vec();
939 req.path_params.insert("id".to_string(), "123".to_string());
940 req.query_params.insert("page".to_string(), "1".to_string());
941 req.query_params
942 .insert("limit".to_string(), "10".to_string());
943 req
944 }
945
946 #[test]
947 fn test_raw_body_extract() {
948 let req = create_request();
949 let body = RawBody::extract(&req).unwrap();
950 assert_eq!(body.as_ref(), br#"{"name":"test"}"#);
951 }
952
953 #[test]
954 fn test_method_extract() {
955 let req = create_request();
956 let method = Method::extract(&req).unwrap();
957 assert_eq!(&*method, "GET");
958 }
959
960 #[test]
961 fn test_path_extract() {
962 let req = create_request();
963 let path = RequestPath::extract(&req).unwrap();
964 assert_eq!(&*path, "/api/users/123");
965 }
966
967 #[test]
968 fn test_content_type_extract() {
969 let req = create_request();
970 let content_type = ContentType::extract(&req).unwrap();
971 assert!(content_type.is_json());
972 }
973
974 #[test]
975 fn test_authorization_extract() {
976 let req = create_request();
977 let auth = Authorization::extract(&req).unwrap();
978 assert_eq!(auth.bearer(), Some("token123"));
979 }
980
981 #[test]
982 fn test_path_param_extract() {
983 let req = create_request();
984 let id = PathParam::<0>::extract(&req).unwrap();
985 assert_eq!(&*id, "123");
986 }
987
988 #[test]
989 fn test_optional_extract() {
990 let req = create_request();
991
992 let auth = Optional::<Authorization>::extract(&req).unwrap();
994 assert!(auth.0.is_some());
995
996 let ct = Optional::<ContentType>::extract(&req).unwrap();
998 assert!(ct.0.is_some());
999 }
1000
1001 #[test]
1002 fn test_tuple_extract() {
1003 let req = create_request();
1004
1005 let (method, path) = <(Method, RequestPath)>::extract(&req).unwrap();
1006 assert_eq!(&*method, "GET");
1007 assert_eq!(&*path, "/api/users/123");
1008 }
1009
1010 #[test]
1011 fn test_two_extract() {
1012 let req = create_request();
1013
1014 let Two(method, path) = Two::<Method, RequestPath>::extract(&req).unwrap();
1015 assert_eq!(&*method, "GET");
1016 assert_eq!(&*path, "/api/users/123");
1017 }
1018
1019 #[test]
1020 fn test_layer_builder() {
1021 let builder = LayerBuilder::new()
1022 .layer(LoggingLayer::info())
1023 .layer(TimeoutLayer::from_secs(30))
1024 .layer(RequestIdLayer);
1025
1026 let _layer = builder.into_layer();
1027 }
1028
1029 #[test]
1030 fn test_identity_layer() {
1031 struct DummyService;
1032 let identity = Identity;
1033 let _service = identity.layer(DummyService);
1034 }
1035
1036 #[test]
1037 fn test_logging_layer() {
1038 let layer = LoggingLayer::new(LogLevel::Info);
1039 assert_eq!(layer.level, LogLevel::Info);
1040 }
1041
1042 #[test]
1043 fn test_timeout_layer() {
1044 let layer = TimeoutLayer::from_secs(30);
1045 assert_eq!(layer.duration, std::time::Duration::from_secs(30));
1046 }
1047
1048 #[test]
1049 fn test_stats() {
1050 let extractor = extractor_stats();
1051 let _ = extractor.extractions();
1052
1053 let middleware = middleware_stats();
1054 let _ = middleware.middleware_calls();
1055 }
1056}