a3s_boot/routing/
handler.rs1use super::route::RouteDefinition;
2use crate::{BootError, BootRequest, BootResponse, BoxFuture, ModuleRef, Result};
3use std::future::Future;
4use std::marker::PhantomData;
5use std::sync::Arc;
6
7pub 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}