1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#![allow(non_snake_case)]

use core::{
    convert::Infallible,
    future::{ready, Ready},
    marker::PhantomData,
};

use super::{FromRequest, Responder};

use xitca_service::{fn_build, FnService, Service};

/// synchronous version of [handler_service]
///
/// `handler_sync_service` run given function on a separate thread pool from the event loop and async
/// logic of xitca-web itself. Therefore it comes with different requirement of function argument
/// compared to [handler_service] where the arguments must be types that impl [FromRequest] trait,
/// being thread safe with `Send` trait bound and with `'static` lifetime.
///
/// # Examples:
/// ```rust
/// # use xitca_web::{
/// #     handler::{
/// #         {handler_service, handler_sync_service},
/// #         uri::{UriOwn, UriRef},
/// #     },
/// #     App,
/// #     WebContext
/// # };
///
/// App::new()
///     .at("/valid", handler_sync_service(|_: UriOwn| "uri is thread safe and owned value"))
///     // uncomment the line below would result in compile error.
///     // .at("/invalid1", handler_sync_service(|_: UriRef<'_>| { "uri ref is borrowed value" }))
///     // uncomment the line below would result in compile error.
///     // .at("/invalid2", handler_sync_service(|_: &WebContext<'_>| { "web request is borrowed value and not thread safe" }))
///     # .at("/nah", handler_service(|_: &WebContext<'_>| async { "" }));
/// ```
///
/// [handler_service]: super::handler_service
pub fn handler_sync_service<Arg, F, T, O>(
    func: F,
) -> FnService<impl Fn(Arg) -> Ready<Result<HandlerServiceSync<F, T, O>, Infallible>>>
where
    F: Closure<T> + Send + Clone,
{
    fn_build(move |_| {
        ready(Ok(HandlerServiceSync {
            func: func.clone(),
            _p: PhantomData,
        }))
    })
}

pub struct HandlerServiceSync<F, T, O> {
    func: F,
    _p: PhantomData<(T, O)>,
}

impl<F, Req, T, O> Service<Req> for HandlerServiceSync<F, T, O>
where
    // for borrowed extractors, `T` is the `'static` version of the extractors
    T: FromRequest<'static, Req>,
    // just to assist type inference to pinpoint `T`
    F: Closure<T> + Send + Clone + 'static,
    F: for<'a> Closure<T::Type<'a>, Output = O>,
    O: Responder<Req> + Send + 'static,
    for<'a> T::Type<'a>: Send + 'static,
    T::Error: From<O::Error>,
{
    type Response = O::Response;
    type Error = T::Error;

    async fn call(&self, req: Req) -> Result<Self::Response, Self::Error> {
        let extract = T::Type::<'_>::from_request(&req).await?;
        let func = self.func.clone();
        let res = tokio::task::spawn_blocking(move || func.call(extract)).await.unwrap();
        res.respond(req).await.map_err(Into::into)
    }
}

#[doc(hidden)]
/// sync version of xitca_service::AsyncClosure trait.
pub trait Closure<Arg> {
    type Output;

    fn call(&self, arg: Arg) -> Self::Output;
}

macro_rules! closure_impl {
    ($($arg: ident),*) => {
        impl<Func, O, $($arg,)*> Closure<($($arg,)*)> for Func
        where
            Func: Fn($($arg),*) -> O,
        {
            type Output = O;

            #[inline]
            fn call(&self, ($($arg,)*): ($($arg,)*)) -> Self::Output {
                self($($arg,)*)
            }
        }
    }
}

closure_impl! {}
closure_impl! { A }
closure_impl! { A, B }
closure_impl! { A, B, C }
closure_impl! { A, B, C, D }
closure_impl! { A, B, C, D, E }
closure_impl! { A, B, C, D, E, F }
closure_impl! { A, B, C, D, E, F, G }
closure_impl! { A, B, C, D, E, F, G, H }
closure_impl! { A, B, C, D, E, F, G, H, I }