armature_core/
zero_cost.rs

1//! Zero-Cost Abstractions for High-Performance HTTP Handling
2//!
3//! This module provides compile-time optimizations that eliminate runtime overhead:
4//!
5//! - **Const Generic Extractors**: Combine multiple extractors at compile-time
6//! - **Static Dispatch Middleware**: Middleware without `Box<dyn>` overhead
7//!
8//! # Philosophy
9//!
10//! These abstractions follow the Rust principle of "zero-cost abstractions" -
11//! you don't pay for what you don't use, and what you do use is as efficient
12//! as hand-written code.
13//!
14//! # Performance Impact
15//!
16//! - Extractor chains: No heap allocation, inline extraction
17//! - Static middleware: Compile-time dispatch, no vtable lookups
18
19use crate::{Error, HttpRequest, HttpResponse};
20use std::future::Future;
21use std::marker::PhantomData;
22use std::pin::Pin;
23use std::sync::atomic::{AtomicU64, Ordering};
24
25// ============================================================================
26// Const Generic Extractor Chains
27// ============================================================================
28
29/// Trait for extractors that work with const generics.
30pub trait Extract: Sized {
31    /// Extract from request.
32    fn extract(req: &HttpRequest) -> Result<Self, Error>;
33}
34
35/// A single extractor wrapper.
36#[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/// Two extractors combined.
47#[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/// Three extractors combined.
60#[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/// Four extractors combined.
74#[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/// Five extractors combined.
89#[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
112// Implement Extract for tuples (ergonomic alternative)
113impl<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// ============================================================================
158// Common Extractor Implementations
159// ============================================================================
160
161/// Extract the raw request body as bytes.
162#[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/// Extract JSON body with compile-time type.
182#[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/// Extract query parameters with compile-time type.
202#[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/// Extract a single path parameter by index.
231#[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        // Get the Nth path parameter
239        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/// Extract a single header by name.
259#[derive(Debug, Clone)]
260pub struct Header {
261    /// Header name.
262    pub name: String,
263    /// Header value (None if not found).
264    pub value: Option<String>,
265}
266
267impl Header {
268    /// Create extractor for a specific header.
269    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    /// Get the header value or error if missing.
277    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    /// Get the header value or default.
283    pub fn unwrap_or(self, default: impl Into<String>) -> String {
284        self.value.unwrap_or_else(|| default.into())
285    }
286
287    /// Check if header is present.
288    pub fn is_present(&self) -> bool {
289        self.value.is_some()
290    }
291}
292
293/// Extract Content-Type header.
294#[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    /// Check if content type is JSON.
307    pub fn is_json(&self) -> bool {
308        self.0
309            .as_ref()
310            .is_some_and(|v| v.contains("application/json"))
311    }
312
313    /// Get raw value.
314    pub fn value(&self) -> Option<&str> {
315        self.0.as_deref()
316    }
317}
318
319/// Extract Authorization header.
320#[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    /// Get bearer token if present.
333    pub fn bearer(&self) -> Option<&str> {
334        self.0.as_ref().and_then(|v| v.strip_prefix("Bearer "))
335    }
336
337    /// Get basic auth credentials if present.
338    pub fn basic(&self) -> Option<&str> {
339        self.0.as_ref().and_then(|v| v.strip_prefix("Basic "))
340    }
341
342    /// Get raw value.
343    pub fn value(&self) -> Option<&str> {
344        self.0.as_deref()
345    }
346}
347
348/// Extract the HTTP method.
349#[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/// Extract the request path.
369#[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/// Optional extractor - never fails, returns None if extraction fails.
389#[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
407// ============================================================================
408// Static Dispatch Middleware
409// ============================================================================
410
411/// A middleware layer with static dispatch (no boxing).
412pub trait Layer<S> {
413    /// The wrapped service type.
414    type Service;
415
416    /// Wrap a service with this layer.
417    fn layer(&self, inner: S) -> Self::Service;
418}
419
420/// A service that can process requests.
421pub trait Service<Request> {
422    /// Response type.
423    type Response;
424    /// Error type.
425    type Error;
426    /// Future type.
427    type Future: Future<Output = Result<Self::Response, Self::Error>> + Send;
428
429    /// Process a request.
430    fn call(&self, req: Request) -> Self::Future;
431}
432
433/// Identity layer - does nothing, passes through.
434#[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/// Stack two layers.
447#[derive(Debug, Clone)]
448pub struct Stack<Inner, Outer> {
449    inner: Inner,
450    outer: Outer,
451}
452
453impl<Inner, Outer> Stack<Inner, Outer> {
454    /// Create a new stack.
455    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/// Builder for composing layers.
475#[derive(Debug, Clone)]
476pub struct LayerBuilder<L> {
477    layer: L,
478}
479
480impl LayerBuilder<Identity> {
481    /// Create a new layer builder.
482    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    /// Add a layer to the stack.
495    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    /// Build and wrap a service.
502    pub fn service<S>(self, service: S) -> L::Service
503    where
504        L: Layer<S>,
505    {
506        self.layer.layer(service)
507    }
508
509    /// Get the composed layer.
510    pub fn into_layer(self) -> L {
511        self.layer
512    }
513}
514
515// ============================================================================
516// Static Middleware Implementations
517// ============================================================================
518
519/// Logging middleware with static dispatch.
520#[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    /// Create new logging layer.
535    pub fn new(level: LogLevel) -> Self {
536        Self { level }
537    }
538
539    /// Create info-level logger.
540    pub fn info() -> Self {
541        Self::new(LogLevel::Info)
542    }
543
544    /// Create debug-level logger.
545    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/// Logging service wrapping inner service.
568#[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/// Timeout middleware with static dispatch.
623#[derive(Debug, Clone)]
624pub struct TimeoutLayer {
625    duration: std::time::Duration,
626}
627
628impl TimeoutLayer {
629    /// Create new timeout layer.
630    pub fn new(duration: std::time::Duration) -> Self {
631        Self { duration }
632    }
633
634    /// Create from seconds.
635    pub fn from_secs(secs: u64) -> Self {
636        Self::new(std::time::Duration::from_secs(secs))
637    }
638
639    /// Create from milliseconds.
640    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/// Timeout service.
657#[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/// Request ID middleware.
688#[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/// Request ID service.
700#[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        // Generate request ID if not present
720        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// ============================================================================
738// Handler Service Adapter
739// ============================================================================
740
741/// Wraps a handler function as a Service.
742#[derive(Clone)]
743pub struct HandlerService<H, Args> {
744    handler: H,
745    _args: PhantomData<Args>,
746}
747
748impl<H, Args> HandlerService<H, Args> {
749    /// Create new handler service.
750    pub fn new(handler: H) -> Self {
751        Self {
752            handler,
753            _args: PhantomData,
754        }
755    }
756}
757
758/// Trait for handlers that can be converted to services.
759pub trait IntoService<Args> {
760    /// The service type.
761    type Service;
762
763    /// Convert into a service.
764    fn into_service(self) -> Self::Service;
765}
766
767// Implement for async fn() -> HttpResponse
768impl<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
795// Implement for async fn(HttpRequest) -> HttpResponse
796impl<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
823// Implement for async fn(E1) -> HttpResponse where E1: Extract
824impl<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/// Handler service that extracts arguments.
838#[derive(Clone)]
839pub struct ExtractorHandlerService<H, Args> {
840    handler: H,
841    _args: PhantomData<Args>,
842}
843
844impl<H, Args> ExtractorHandlerService<H, Args> {
845    /// Create new extractor handler service.
846    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// ============================================================================
874// Statistics
875// ============================================================================
876
877/// Global statistics for zero-cost abstractions.
878#[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    /// Get extraction count.
894    pub fn extractions(&self) -> u64 {
895        self.extractions.load(Ordering::Relaxed)
896    }
897
898    /// Get middleware call count.
899    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
914/// Get extractor statistics.
915pub fn extractor_stats() -> &'static ZeroCostStats {
916    &EXTRACTOR_STATS
917}
918
919/// Get middleware statistics.
920pub fn middleware_stats() -> &'static ZeroCostStats {
921    &MIDDLEWARE_STATS
922}
923
924// ============================================================================
925// Tests
926// ============================================================================
927
928#[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        // Existing authorization
993        let auth = Optional::<Authorization>::extract(&req).unwrap();
994        assert!(auth.0.is_some());
995
996        // Optional content type
997        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}