use core::{ future::Future, marker::PhantomData, pin::Pin, task::{Context, Poll}, }; use futures_core::ready; use pin_project_lite::pin_project; use super::{IntoService, IntoServiceFactory, Service, ServiceFactory}; /// Apply transform function to a service. /// /// The In and Out type params refer to the request and response types for the wrapped service. pub fn apply_fn( service: I, wrap_fn: F, ) -> Apply where I: IntoService, S: Service, F: Fn(Req, &S) -> Fut, Fut: Future>, { Apply::new(service.into_service(), wrap_fn) } /// Service factory that produces `apply_fn` service. /// /// The In and Out type params refer to the request and response types for the wrapped service. pub fn apply_fn_factory( service: I, f: F, ) -> ApplyFactory where I: IntoServiceFactory, SF: ServiceFactory, F: Fn(Req, &SF::Service) -> Fut + Clone, Fut: Future>, { ApplyFactory::new(service.into_factory(), f) } /// `Apply` service combinator. /// /// The In and Out type params refer to the request and response types for the wrapped service. pub struct Apply where S: Service, { service: S, wrap_fn: F, _phantom: PhantomData<(Req, In, Res, Err)>, } impl Apply where S: Service, F: Fn(Req, &S) -> Fut, Fut: Future>, { /// Create new `Apply` combinator fn new(service: S, wrap_fn: F) -> Self { Self { service, wrap_fn, _phantom: PhantomData, } } } impl Clone for Apply where S: Service + Clone, F: Fn(Req, &S) -> Fut + Clone, Fut: Future>, { fn clone(&self) -> Self { Apply { service: self.service.clone(), wrap_fn: self.wrap_fn.clone(), _phantom: PhantomData, } } } impl Service for Apply where S: Service, F: Fn(Req, &S) -> Fut, Fut: Future>, { type Response = Res; type Error = Err; type Future = Fut; crate::forward_ready!(service); fn call(&self, req: Req) -> Self::Future { (self.wrap_fn)(req, &self.service) } } /// `ApplyFactory` service factory combinator. pub struct ApplyFactory { factory: SF, wrap_fn: F, _phantom: PhantomData<(Req, In, Res, Err)>, } impl ApplyFactory where SF: ServiceFactory, F: Fn(Req, &SF::Service) -> Fut + Clone, Fut: Future>, { /// Create new `ApplyFactory` new service instance fn new(factory: SF, wrap_fn: F) -> Self { Self { factory, wrap_fn, _phantom: PhantomData, } } } impl Clone for ApplyFactory where SF: ServiceFactory + Clone, F: Fn(Req, &SF::Service) -> Fut + Clone, Fut: Future>, { fn clone(&self) -> Self { Self { factory: self.factory.clone(), wrap_fn: self.wrap_fn.clone(), _phantom: PhantomData, } } } impl ServiceFactory for ApplyFactory where SF: ServiceFactory, F: Fn(Req, &SF::Service) -> Fut + Clone, Fut: Future>, { type Response = Res; type Error = Err; type Config = SF::Config; type Service = Apply; type InitError = SF::InitError; type Future = ApplyServiceFactoryResponse; fn new_service(&self, cfg: SF::Config) -> Self::Future { let svc = self.factory.new_service(cfg); ApplyServiceFactoryResponse::new(svc, self.wrap_fn.clone()) } } pin_project! { pub struct ApplyServiceFactoryResponse where SF: ServiceFactory, F: Fn(Req, &SF::Service) -> Fut, Fut: Future>, { #[pin] fut: SF::Future, wrap_fn: Option, _phantom: PhantomData<(Req, Res)>, } } impl ApplyServiceFactoryResponse where SF: ServiceFactory, F: Fn(Req, &SF::Service) -> Fut, Fut: Future>, { fn new(fut: SF::Future, wrap_fn: F) -> Self { Self { fut, wrap_fn: Some(wrap_fn), _phantom: PhantomData, } } } impl Future for ApplyServiceFactoryResponse where SF: ServiceFactory, F: Fn(Req, &SF::Service) -> Fut, Fut: Future>, { type Output = Result, SF::InitError>; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let this = self.project(); let svc = ready!(this.fut.poll(cx))?; Poll::Ready(Ok(Apply::new(svc, this.wrap_fn.take().unwrap()))) } } #[cfg(test)] mod tests { use core::task::Poll; use futures_util::future::lazy; use super::*; use crate::{ok, pipeline, pipeline_factory, Ready, Service, ServiceFactory}; #[derive(Clone)] struct Srv; impl Service<()> for Srv { type Response = (); type Error = (); type Future = Ready>; crate::always_ready!(); fn call(&self, _: ()) -> Self::Future { ok(()) } } #[actix_rt::test] async fn test_call() { let srv = pipeline(apply_fn(Srv, |req: &'static str, srv| { let fut = srv.call(()); async move { fut.await.unwrap(); Ok((req, ())) } })); assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(()))); let res = srv.call("srv").await; assert!(res.is_ok()); assert_eq!(res.unwrap(), ("srv", ())); } #[actix_rt::test] async fn test_new_service() { let new_srv = pipeline_factory(apply_fn_factory( || ok::<_, ()>(Srv), |req: &'static str, srv| { let fut = srv.call(()); async move { fut.await.unwrap(); Ok((req, ())) } }, )); let srv = new_srv.new_service(()).await.unwrap(); assert_eq!(lazy(|cx| srv.poll_ready(cx)).await, Poll::Ready(Ok(()))); let res = srv.call("srv").await; assert!(res.is_ok()); assert_eq!(res.unwrap(), ("srv", ())); } }