use std::future::Future; use std::task::{Context, Poll}; use crate::and_then::{AndThenService, AndThenServiceFactory}; use crate::and_then_apply_fn::{AndThenApplyFn, AndThenApplyFnFactory}; 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}; /// Contruct new pipeline with one service in pipeline chain. pub fn pipeline(service: F) -> Pipeline where F: IntoService, T: Service, { Pipeline { service: service.into_service(), } } /// Contruct new pipeline factory with one service factory. pub fn pipeline_factory(factory: F) -> PipelineFactory where T: ServiceFactory, F: IntoServiceFactory, { PipelineFactory { factory: factory.into_factory(), } } /// Pipeline service - pipeline allows to compose multiple service into one service. pub struct Pipeline { service: T, } impl Pipeline { /// 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: F) -> Pipeline> where Self: Sized, F: IntoService, U: Service, { Pipeline { service: AndThenService::new(self.service, service.into_service()), } } /// Apply function to specified service and use it as a next service in /// chain. /// /// Short version of `pipeline_factory(...).and_then(apply_fn_factory(...))` pub fn and_then_apply_fn( self, service: I, f: F, ) -> Pipeline> where Self: Sized, I: IntoService, U: Service, F: FnMut(T::Response, &mut U) -> Fut, Fut: Future>, Err: From + From, { Pipeline { service: AndThenApplyFn::new(self.service, service.into_service(), f), } } /// 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> where Self: Sized, F: IntoService, U: Service, Error = T::Error>, { Pipeline { service: ThenService::new(self.service, service.into_service()), } } /// 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> where Self: Sized, F: FnMut(T::Response) -> R, { Pipeline { service: Map::new(self.service, f), } } /// 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> where Self: Sized, F: Fn(T::Error) -> E, { Pipeline { service: MapErr::new(self.service, f), } } } impl Clone for Pipeline where T: Clone, { fn clone(&self) -> Self { Pipeline { service: self.service.clone(), } } } impl Service for Pipeline { type Request = T::Request; type Response = T::Response; type Error = T::Error; type Future = T::Future; #[inline] fn poll_ready(&mut self, ctx: &mut Context<'_>) -> Poll> { self.service.poll_ready(ctx) } #[inline] fn call(&mut self, req: T::Request) -> Self::Future { self.service.call(req) } } /// Pipeline factory pub struct PipelineFactory { factory: T, } impl PipelineFactory { /// Call another service after call to this one has resolved successfully. pub fn and_then(self, factory: F) -> PipelineFactory> where Self: Sized, T::Config: Clone, F: IntoServiceFactory, U: ServiceFactory< Config = T::Config, Request = T::Response, Error = T::Error, InitError = T::InitError, >, { PipelineFactory { factory: AndThenServiceFactory::new(self.factory, factory.into_factory()), } } /// Apply function to specified service and use it as a next service in /// chain. /// /// Short version of `pipeline_factory(...).and_then(apply_fn_factory(...))` pub fn and_then_apply_fn( self, factory: I, f: F, ) -> PipelineFactory> where Self: Sized, T::Config: Clone, I: IntoServiceFactory, U: ServiceFactory, F: FnMut(T::Response, &mut U::Service) -> Fut + Clone, Fut: Future>, Err: From + From, { PipelineFactory { factory: AndThenApplyFnFactory::new(self.factory, factory.into_factory(), f), } } /// 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: F) -> PipelineFactory> where Self: Sized, T::Config: Clone, F: IntoServiceFactory, U: ServiceFactory< Config = T::Config, Request = Result, Error = T::Error, InitError = T::InitError, >, { PipelineFactory { factory: ThenServiceFactory::new(self.factory, factory.into_factory()), } } /// Map this service's output to a different type, returning a new service /// of the resulting type. pub fn map(self, f: F) -> PipelineFactory> where Self: Sized, F: FnMut(T::Response) -> R + Clone, { PipelineFactory { factory: MapServiceFactory::new(self.factory, f), } } /// Map this service's error to a different error, returning a new service. pub fn map_err(self, f: F) -> PipelineFactory> where Self: Sized, F: Fn(T::Error) -> E + Clone, { PipelineFactory { factory: MapErrServiceFactory::new(self.factory, f), } } /// Map this factory's init error to a different error, returning a new service. pub fn map_init_err(self, f: F) -> PipelineFactory> where Self: Sized, F: Fn(T::InitError) -> E + Clone, { PipelineFactory { factory: MapInitErr::new(self.factory, f), } } } impl Clone for PipelineFactory where T: Clone, { fn clone(&self) -> Self { PipelineFactory { factory: self.factory.clone(), } } } impl ServiceFactory for PipelineFactory { type Config = T::Config; type Request = T::Request; type Response = T::Response; type Error = T::Error; type Service = T::Service; type InitError = T::InitError; type Future = T::Future; #[inline] fn new_service(&self, cfg: T::Config) -> Self::Future { self.factory.new_service(cfg) } }