use crate::backend::finalize::FinalizeBackend;
use crate::backend::{Backend, BackendConfig};
use crate::error::BoxDynError;
use crate::task::Task;
use crate::worker::service::{IntoWorkerService, WorkerService};
use futures_util::FutureExt;
use futures_util::future::Map;
use std::fmt;
use std::future::Future;
use std::marker::PhantomData;
use std::task::{Context, Poll};
use tower_service::Service;
use crate::task::{from_request::FromRequest, into_response::IntoResponse};
pub fn task_fn<F, Args, FnArgs>(f: F) -> TaskFn<F, Args, FnArgs> {
TaskFn {
f,
req: PhantomData,
fn_args: PhantomData,
}
}
pub struct TaskFn<F, Args, FnArgs> {
f: F,
req: PhantomData<Args>,
fn_args: PhantomData<FnArgs>,
}
impl<T: Copy, Args, FnArgs> Copy for TaskFn<T, Args, FnArgs> {}
impl<T: Clone, Args, FnArgs> Clone for TaskFn<T, Args, FnArgs> {
fn clone(&self) -> Self {
Self {
f: self.f.clone(),
req: PhantomData,
fn_args: PhantomData,
}
}
}
impl<T, Args, FnArgs> fmt::Debug for TaskFn<T, Args, FnArgs> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TaskFn")
.field("f", &std::any::type_name::<T>())
.field(
"req",
&format_args!("PhantomData<Task<{}>>", std::any::type_name::<Args>(),),
)
.field(
"fn_args",
&format_args!("PhantomData<{}>", std::any::type_name::<FnArgs>()),
)
.finish()
}
}
type FnFuture<F, O, R, E> = Map<F, fn(O) -> std::result::Result<R, E>>;
macro_rules! impl_service_fn {
($($K:ident),+) => {
#[allow(unused_parens)]
impl<T, F, Args: Send + 'static, R, $($K),+> Service<Task<Args>> for TaskFn<T, Args, ($($K),+)>
where
T: FnMut(Args, $($K),+) -> F + Send + Clone + 'static,
F: Future + Send,
F::Output: IntoResponse<Output = R>,
$(
$K: FromRequest<Task<Args>> + Send,
< $K as FromRequest<Task<Args>> >::Error: std::error::Error + 'static + Send + Sync,
)+
{
type Response = R;
type Error = BoxDynError;
type Future = futures_util::future::BoxFuture<'static, Result<R, BoxDynError>>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, task: Task<Args>) -> Self::Future {
let mut svc = self.f.clone();
#[allow(non_snake_case)]
let fut = async move {
#[allow(clippy::double_parens)]
let results: Result<($($K),+), BoxDynError> = { Ok(($($K::from_request(&task).await.map_err(|e| Box::new(e) as BoxDynError)?),+)) };
match results {
Ok(($($K),+)) => {
let req = task.args;
(svc)(req, $($K),+).map(F::Output::into_response).await
}
Err(e) => Err(e),
}
};
fut.boxed()
}
}
#[allow(unused_parens)]
impl<T, Args, F, R, B, O, $($K),+>
IntoWorkerService<B, TaskFn<T, Args, ($($K),+)>> for T
where
B: Backend + BackendConfig<Args = Args>,
B::Kind: FinalizeBackend<B, Args, Backend = O>,
O: Backend<Task = Task<Args>>,
T: FnMut(Args, $($K),+) -> F + Send + Clone + 'static,
F: Future + Send,
Args: Send + 'static,
F::Output: IntoResponse<Output = R>,
TaskFn<T, Args, ($($K),+)>: Service<O::Task>,
$(
$K: FromRequest<Task<Args>> + Send,
< $K as FromRequest<Task<Args>> >::Error: std::error::Error + 'static + Send + Sync,
)+
{
type Backend = O;
type Task = Task<Args>;
fn into_service(self, backend: B) -> WorkerService<O, TaskFn<T, Args, ($($K),+)>> {
let backend = B::Kind::finalize(backend);
WorkerService {
backend,
service: task_fn(self)
}
}
}
};
}
impl<T, F, Args, R> Service<Task<Args>> for TaskFn<T, Args, ()>
where
T: FnMut(Args) -> F,
F: Future,
F::Output: IntoResponse<Output = R>,
{
type Response = R;
type Error = BoxDynError;
type Future = FnFuture<F, F::Output, R, BoxDynError>;
fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, task: Task<Args>) -> Self::Future {
let fut = (self.f)(task.args);
fut.map(F::Output::into_response)
}
}
impl<T, Args, F, R, B, O> IntoWorkerService<B, TaskFn<T, Args, ()>> for T
where
T: FnMut(Args) -> F,
F: Future,
F::Output: IntoResponse<Output = R>,
B: Backend + BackendConfig<Args = Args>,
B::Kind: FinalizeBackend<B, Args, Backend = O>,
O: Backend<Task = Task<Args>>,
Args: Send,
TaskFn<T, Args, ()>: Service<O::Task>,
{
type Backend = O;
type Task = Task<Args>;
fn into_service(self, backend: B) -> WorkerService<O, TaskFn<T, Args, ()>> {
let backend = B::Kind::finalize(backend);
WorkerService {
backend,
service: task_fn(self),
}
}
}
impl<Args, S, B, O> IntoWorkerService<B, S> for S
where
B: Backend + BackendConfig<Args = Args>,
B::Kind: FinalizeBackend<B, Args, Backend = O>,
O: Backend<Task = Task<Args>>,
S: Service<O::Task>,
{
type Backend = O;
type Task = Task<Args>;
fn into_service(self, backend: B) -> WorkerService<O, S> {
let backend = B::Kind::finalize(backend);
WorkerService {
backend,
service: self,
}
}
}
impl_service_fn!(A);
impl_service_fn!(A1, A2);
impl_service_fn!(A1, A2, A3);
impl_service_fn!(A1, A2, A3, A4);
impl_service_fn!(A1, A2, A3, A4, A5);
impl_service_fn!(A1, A2, A3, A4, A5, A6);
impl_service_fn!(A1, A2, A3, A4, A5, A6, A7);
impl_service_fn!(A1, A2, A3, A4, A5, A6, A7, A8);
impl_service_fn!(A1, A2, A3, A4, A5, A6, A7, A8, A9);
impl_service_fn!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10);
impl_service_fn!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11);
impl_service_fn!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12);
impl_service_fn!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13);
impl_service_fn!(A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14);
impl_service_fn!(
A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15
);
impl_service_fn!(
A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16
);