use core::{ marker::PhantomData, task::{Context, Poll}, }; use crate::and_then::{AndThenService, AndThenServiceFactory}; use crate::map::{Map, MapServiceFactory}; use crate::map_err::{MapErr, MapErrServiceFactory}; use crate::map_init_err::MapInitErr; use crate::then::{ThenService, ThenServiceFactory}; use crate::{IntoService, IntoServiceFactory, Service, ServiceFactory}; /// Construct new pipeline with one service in pipeline chain. pub fn pipeline(service: I) -> Pipeline where I: IntoService, S: Service, { Pipeline { service: service.into_service(), _phantom: PhantomData, } } /// Construct new pipeline factory with one service factory. pub fn pipeline_factory(factory: I) -> PipelineFactory where I: IntoServiceFactory, SF: ServiceFactory, { PipelineFactory { factory: factory.into_factory(), _phantom: PhantomData, } } /// Pipeline service - pipeline allows to compose multiple service into one service. pub struct Pipeline { service: S, _phantom: PhantomData, } impl Pipeline where S: Service, { /// Call another service after call to this one has resolved successfully. /// /// This function can be used to chain two services together and ensure that /// the second service isn't called until call to the fist service have /// finished. Result of the call to the first service is used as an /// input parameter for the second service's call. /// /// Note that this function consumes the receiving service and returns a /// wrapped version of it. pub fn and_then( self, service: I, ) -> Pipeline + Clone, Req> where Self: Sized, I: IntoService, S1: Service, { Pipeline { service: AndThenService::new(self.service, service.into_service()), _phantom: PhantomData, } } /// Chain on a computation for when a call to the service finished, /// passing the result of the call to the next service `U`. /// /// Note that this function consumes the receiving pipeline and returns a /// wrapped version of it. pub fn then( self, service: F, ) -> Pipeline + Clone, Req> where Self: Sized, F: IntoService>, S1: Service, Error = S::Error>, { Pipeline { service: ThenService::new(self.service, service.into_service()), _phantom: PhantomData, } } /// Map this service's output to a different type, returning a new service /// of the resulting type. /// /// This function is similar to the `Option::map` or `Iterator::map` where /// it will change the type of the underlying service. /// /// Note that this function consumes the receiving service and returns a /// wrapped version of it, similar to the existing `map` methods in the /// standard library. pub fn map(self, f: F) -> Pipeline, Req> where Self: Sized, F: FnMut(S::Response) -> R, { Pipeline { service: Map::new(self.service, f), _phantom: PhantomData, } } /// Map this service's error to a different error, returning a new service. /// /// This function is similar to the `Result::map_err` where it will change /// the error type of the underlying service. This is useful for example to /// ensure that services have the same error type. /// /// Note that this function consumes the receiving service and returns a /// wrapped version of it. pub fn map_err(self, f: F) -> Pipeline, Req> where Self: Sized, F: Fn(S::Error) -> E, { Pipeline { service: MapErr::new(self.service, f), _phantom: PhantomData, } } } impl Clone for Pipeline where T: Clone, { fn clone(&self) -> Self { Pipeline { service: self.service.clone(), _phantom: PhantomData, } } } impl, Req> Service for Pipeline { type Response = S::Response; type Error = S::Error; type Future = S::Future; #[inline] fn poll_ready(&self, ctx: &mut Context<'_>) -> Poll> { self.service.poll_ready(ctx) } #[inline] fn call(&self, req: Req) -> Self::Future { self.service.call(req) } } /// Pipeline factory pub struct PipelineFactory { factory: SF, _phantom: PhantomData, } impl PipelineFactory where SF: ServiceFactory, { /// Call another service after call to this one has resolved successfully. pub fn and_then( self, factory: I, ) -> PipelineFactory< impl ServiceFactory< Req, Response = SF1::Response, Error = SF::Error, Config = SF::Config, InitError = SF::InitError, Service = impl Service + Clone, > + Clone, Req, > where Self: Sized, SF::Config: Clone, I: IntoServiceFactory, SF1: ServiceFactory< SF::Response, Config = SF::Config, Error = SF::Error, InitError = SF::InitError, >, { PipelineFactory { factory: AndThenServiceFactory::new(self.factory, factory.into_factory()), _phantom: PhantomData, } } /// Create `NewService` to chain on a computation for when a call to the /// service finished, passing the result of the call to the next /// service `U`. /// /// Note that this function consumes the receiving pipeline and returns a /// wrapped version of it. pub fn then( self, factory: I, ) -> PipelineFactory< impl ServiceFactory< Req, Response = SF1::Response, Error = SF::Error, Config = SF::Config, InitError = SF::InitError, Service = impl Service + Clone, > + Clone, Req, > where Self: Sized, SF::Config: Clone, I: IntoServiceFactory>, SF1: ServiceFactory< Result, Config = SF::Config, Error = SF::Error, InitError = SF::InitError, >, { PipelineFactory { factory: ThenServiceFactory::new(self.factory, factory.into_factory()), _phantom: PhantomData, } } /// Map this service's output to a different type, returning a new service /// of the resulting type. pub fn map(self, f: F) -> PipelineFactory, Req> where Self: Sized, F: FnMut(SF::Response) -> R + Clone, { PipelineFactory { factory: MapServiceFactory::new(self.factory, f), _phantom: PhantomData, } } /// Map this service's error to a different error, returning a new service. pub fn map_err( self, f: F, ) -> PipelineFactory, Req> where Self: Sized, F: Fn(SF::Error) -> E + Clone, { PipelineFactory { factory: MapErrServiceFactory::new(self.factory, f), _phantom: PhantomData, } } /// Map this factory's init error to a different error, returning a new service. pub fn map_init_err(self, f: F) -> PipelineFactory, Req> where Self: Sized, F: Fn(SF::InitError) -> E + Clone, { PipelineFactory { factory: MapInitErr::new(self.factory, f), _phantom: PhantomData, } } } impl Clone for PipelineFactory where T: Clone, { fn clone(&self) -> Self { PipelineFactory { factory: self.factory.clone(), _phantom: PhantomData, } } } impl ServiceFactory for PipelineFactory where SF: ServiceFactory, { type Config = SF::Config; type Response = SF::Response; type Error = SF::Error; type Service = SF::Service; type InitError = SF::InitError; type Future = SF::Future; #[inline] fn new_service(&self, cfg: SF::Config) -> Self::Future { self.factory.new_service(cfg) } }