use std::future::Future; use std::pin::Pin; use std::rc::Rc; use std::sync::Arc; use std::task::{Context, Poll}; use crate::transform_err::TransformMapInitErr; use crate::{IntoServiceFactory, Service, ServiceFactory}; use pin_project::pin_project; /// The `Transform` trait defines the interface of a Service factory. `Transform` /// is often implemented for middleware, defining how to construct a /// middleware Service. A Service that is constructed by the factory takes /// the Service that follows it during execution as a parameter, assuming /// ownership of the next Service. pub trait Transform { /// Requests handled by the service. type Request; /// Responses given by the service. type Response; /// Errors produced by the service. type Error; /// The `TransformService` value created by this factory type Transform: Service< Request = Self::Request, Response = Self::Response, Error = Self::Error, >; /// Errors produced while building a service. type InitError; /// The future response value. type Future: Future>; /// Creates and returns a new Service component, asynchronously fn new_transform(&self, service: S) -> Self::Future; /// Map this service's factory error to a different error, /// returning a new transform service factory. fn map_init_err(self, f: F) -> TransformMapInitErr where Self: Sized, F: Fn(Self::InitError) -> E, { TransformMapInitErr::new(self, f) } } impl Transform for Rc where T: Transform, { type Request = T::Request; type Response = T::Response; type Error = T::Error; type InitError = T::InitError; type Transform = T::Transform; type Future = T::Future; fn new_transform(&self, service: S) -> T::Future { self.as_ref().new_transform(service) } } impl Transform for Arc where T: Transform, { type Request = T::Request; type Response = T::Response; type Error = T::Error; type InitError = T::InitError; type Transform = T::Transform; type Future = T::Future; fn new_transform(&self, service: S) -> T::Future { self.as_ref().new_transform(service) } } /// Apply transform to a service. Function returns /// services factory that in initialization creates /// service and applies transform to this service. pub fn apply( t: T, service: U, ) -> impl ServiceFactory< Config = S::Config, Request = T::Request, Response = T::Response, Error = T::Error, Service = T::Transform, InitError = S::InitError, > + Clone where S: ServiceFactory, T: Transform, U: IntoServiceFactory, { ApplyTransform::new(t, service.into_factory()) } /// `Apply` transform to new service struct ApplyTransform { s: Rc, t: Rc, } impl ApplyTransform where S: ServiceFactory, T: Transform, { /// Create new `ApplyTransform` new service instance fn new(t: T, service: S) -> Self { Self { s: Rc::new(service), t: Rc::new(t), } } } impl Clone for ApplyTransform { fn clone(&self) -> Self { ApplyTransform { s: self.s.clone(), t: self.t.clone(), } } } impl ServiceFactory for ApplyTransform where S: ServiceFactory, T: Transform, { type Request = T::Request; type Response = T::Response; type Error = T::Error; type Config = S::Config; type Service = T::Transform; type InitError = T::InitError; type Future = ApplyTransformFuture; fn new_service(&self, cfg: &S::Config) -> Self::Future { ApplyTransformFuture { t_cell: self.t.clone(), fut_a: self.s.new_service(cfg), fut_t: None, } } } #[pin_project] struct ApplyTransformFuture where S: ServiceFactory, T: Transform, { #[pin] fut_a: S::Future, #[pin] fut_t: Option, t_cell: Rc, } impl Future for ApplyTransformFuture where S: ServiceFactory, T: Transform, { type Output = Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let mut this = self.project(); if this.fut_t.as_mut().as_pin_mut().is_none() { if let Poll::Ready(service) = this.fut_a.poll(cx)? { this.fut_t.set(Some(this.t_cell.new_transform(service))); } } if let Some(fut) = this.fut_t.as_mut().as_pin_mut() { fut.poll(cx) } else { Poll::Pending } } }