Skip to main content

a3s_boot/routing/
handler.rs

1use super::route::RouteDefinition;
2use crate::{BootError, BootRequest, BootResponse, BoxFuture, ModuleRef, Result};
3use std::future::Future;
4use std::marker::PhantomData;
5use std::sync::Arc;
6
7/// Type-erased route handler used by adapters.
8pub trait RouteHandler: Send + Sync + 'static {
9    fn call(&self, request: BootRequest) -> BoxFuture<'static, Result<BootResponse>>;
10}
11
12impl<F, Fut> RouteHandler for F
13where
14    F: Fn(BootRequest) -> Fut + Send + Sync + 'static,
15    Fut: Future<Output = Result<BootResponse>> + Send + 'static,
16{
17    fn call(&self, request: BootRequest) -> BoxFuture<'static, Result<BootResponse>> {
18        Box::pin(self(request))
19    }
20}
21
22pub(crate) struct RequestScopedRouteHandler<F> {
23    factory: F,
24}
25
26impl<F> RequestScopedRouteHandler<F> {
27    pub(crate) fn new(factory: F) -> Self {
28        Self { factory }
29    }
30}
31
32impl<F, H> RouteHandler for RequestScopedRouteHandler<F>
33where
34    F: Fn(&ModuleRef) -> Result<H> + Send + Sync + 'static,
35    H: RouteHandler,
36{
37    fn call(&self, request: BootRequest) -> BoxFuture<'static, Result<BootResponse>> {
38        let Some(module_ref) = request.module_ref().cloned() else {
39            return Box::pin(async {
40                Err(BootError::Internal(
41                    "request-scoped route requires a module context".to_string(),
42                ))
43            });
44        };
45
46        match (self.factory)(&module_ref) {
47            Ok(handler) => handler.call(request),
48            Err(error) => Box::pin(async move { Err(error) }),
49        }
50    }
51}
52
53pub(crate) struct ProviderRouteHandler<T, F> {
54    factory: F,
55    marker: PhantomData<fn() -> T>,
56}
57
58impl<T, F> ProviderRouteHandler<T, F> {
59    pub(crate) fn new(factory: F) -> Self {
60        Self {
61            factory,
62            marker: PhantomData,
63        }
64    }
65}
66
67impl<T, F> RouteHandler for ProviderRouteHandler<T, F>
68where
69    T: Send + Sync + 'static,
70    F: Fn(Arc<T>) -> Result<RouteDefinition> + Send + Sync + 'static,
71{
72    fn call(&self, request: BootRequest) -> BoxFuture<'static, Result<BootResponse>> {
73        let Some(module_ref) = request.module_ref().cloned() else {
74            return Box::pin(async {
75                Err(BootError::Internal(
76                    "provider-backed route requires a module context".to_string(),
77                ))
78            });
79        };
80
81        let route = module_ref
82            .get::<T>()
83            .and_then(|controller| (self.factory)(controller));
84        match route {
85            Ok(route) => route.handler.call(request),
86            Err(error) => Box::pin(async move { Err(error) }),
87        }
88    }
89}