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