Skip to main content

rustapi_core/app/
dispatcher.rs

1use crate::interceptor::InterceptorChain;
2use crate::middleware::{BoxedNext, LayerStack};
3use crate::router::Router;
4use crate::{Request, Response};
5use http::Extensions;
6use std::sync::Arc;
7
8/// A dispatcher that can drive requests through the RustAPI pipeline
9/// (interceptors + layers + router) without any network or serialization overhead.
10///
11/// Obtained via [`crate::RustApi::request_dispatcher`].
12#[derive(Clone)]
13pub struct RequestDispatcher {
14    pub(super) router: Arc<Router>,
15    pub(super) layers: LayerStack,
16    pub(super) interceptors: InterceptorChain,
17}
18
19impl RequestDispatcher {
20    /// Returns the shared state Extensions from the underlying router.
21    /// Useful for in-process request construction to preserve `State<T>` etc.
22    pub fn state_ref(&self) -> Arc<Extensions> {
23        self.router.state_ref()
24    }
25
26    /// Dispatch a request through the full stack (interceptors, middleware layers,
27    /// route handler, and response interceptors).
28    ///
29    /// This replicates the logic used by the normal HTTP server.
30    pub async fn dispatch(&self, request: Request) -> Response {
31        let req = self.interceptors.intercept_request(request);
32
33        let path = req.path().to_owned();
34        let method = req.method().clone();
35
36        let response = if self.layers.is_empty() {
37            crate::server::route_request_direct(&self.router, req, &path, &method).await
38        } else {
39            let router = self.router.clone();
40            let p = path.clone();
41            let m = method.clone();
42
43            let routing_handler: BoxedNext = Arc::new(move |r: Request| {
44                let router = router.clone();
45                let pp = p.clone();
46                let mm = m.clone();
47                Box::pin(async move { crate::server::route_request(&router, r, &pp, &mm).await })
48            });
49
50            self.layers.execute(req, routing_handler).await
51        };
52
53        self.interceptors.intercept_response(response)
54    }
55}