apalis_core/
service_fn.rs

1use crate::error::Error;
2use crate::layers::extensions::Data;
3use crate::request::Request;
4use crate::response::IntoResponse;
5use futures::future::Map;
6use futures::FutureExt;
7use std::fmt;
8use std::future::Future;
9use std::marker::PhantomData;
10use std::task::{Context, Poll};
11use tower::Service;
12
13/// A helper method to build functions
14pub fn service_fn<T, Req, Ctx, FnArgs>(f: T) -> ServiceFn<T, Req, Ctx, FnArgs> {
15    ServiceFn {
16        f,
17        req: PhantomData,
18        fn_args: PhantomData,
19    }
20}
21
22/// An executable service implemented by a closure.
23///
24/// See [`service_fn`] for more details.
25#[derive(Copy, Clone)]
26pub struct ServiceFn<T, Req, Ctx, FnArgs> {
27    f: T,
28    req: PhantomData<Request<Req, Ctx>>,
29    fn_args: PhantomData<FnArgs>,
30}
31
32impl<T, Req, Ctx, FnArgs> fmt::Debug for ServiceFn<T, Req, Ctx, FnArgs> {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        f.debug_struct("ServiceFn")
35            .field("f", &format_args!("{}", std::any::type_name::<T>()))
36            .finish()
37    }
38}
39
40/// The Future returned from [`ServiceFn`] service.
41pub type FnFuture<F, O, R, E> = Map<F, fn(O) -> std::result::Result<R, E>>;
42
43/// Handles extraction
44pub trait FromRequest<Req>: Sized {
45    /// Perform the extraction.
46    fn from_request(req: &Req) -> Result<Self, Error>;
47}
48
49impl<T: Clone + Send + Sync + 'static, Req, Ctx> FromRequest<Request<Req, Ctx>> for Data<T> {
50    fn from_request(req: &Request<Req, Ctx>) -> Result<Self, Error> {
51        req.parts.data.get_checked().cloned().map(Data::new)
52    }
53}
54
55macro_rules! impl_service_fn {
56    ($($K:ident),+) => {
57        #[allow(unused_parens)]
58        impl<T, F, Req, E, R, Ctx, $($K),+> Service<Request<Req, Ctx>> for ServiceFn<T, Req, Ctx, ($($K),+)>
59        where
60            T: FnMut(Req, $($K),+) -> F,
61            F: Future,
62            F::Output: IntoResponse<Result = std::result::Result<R, E>>,
63            $($K: FromRequest<Request<Req, Ctx>>),+,
64            E: From<Error>
65        {
66            type Response = R;
67            type Error = E;
68            type Future = futures::future::Either<futures::future::Ready<Result<R, E>>, FnFuture<F, F::Output, R, E>>;
69
70            fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
71                Poll::Ready(Ok(()))
72            }
73
74            fn call(&mut self, task: Request<Req, Ctx>) -> Self::Future {
75
76                #[allow(non_snake_case)]
77                let fut = {
78                    let results: Result<($($K),+), E> = (|| {
79                        Ok(($($K::from_request(&task)?),+))
80                    })();
81
82                    match results {
83                        Ok(($($K),+)) => {
84                            let req = task.args;
85                            (self.f)(req, $($K),+)
86                        }
87                        Err(e) => return futures::future::Either::Left(futures::future::err(e).into()),
88                    }
89                };
90
91
92                futures::future::Either::Right(fut.map(F::Output::into_response))
93            }
94        }
95    };
96}
97
98impl<T, F, Req, E, R, Ctx> Service<Request<Req, Ctx>> for ServiceFn<T, Req, Ctx, ()>
99where
100    T: FnMut(Req) -> F,
101    F: Future,
102    F::Output: IntoResponse<Result = std::result::Result<R, E>>,
103{
104    type Response = R;
105    type Error = E;
106    type Future = FnFuture<F, F::Output, R, E>;
107
108    fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
109        Poll::Ready(Ok(()))
110    }
111
112    fn call(&mut self, task: Request<Req, Ctx>) -> Self::Future {
113        let fut = (self.f)(task.args);
114
115        fut.map(F::Output::into_response)
116    }
117}
118
119impl_service_fn!(A);
120impl_service_fn!(A1, A2);
121impl_service_fn!(A1, A2, A3);
122impl_service_fn!(A1, A2, A3, A4);
123impl_service_fn!(A1, A2, A3, A4, A5);
124impl_service_fn!(A1, A2, A3, A4, A5, A6);
125impl_service_fn!(A1, A2, A3, A4, A5, A6, A7);
126impl_service_fn!(A1, A2, A3, A4, A5, A6, A7, A8);
127impl_service_fn!(A1, A2, A3, A4, A5, A6, A7, A8, A9);
128impl_service_fn!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10);
129impl_service_fn!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11);
130impl_service_fn!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12);
131impl_service_fn!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13);
132impl_service_fn!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14);
133impl_service_fn!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15);
134impl_service_fn!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16);