From 629ef23371d056e628a152141c65d7f648c2b7a6 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Sat, 9 Mar 2019 09:01:02 -0800 Subject: [PATCH] add .apply_cfg new service combinator --- actix-service/CHANGES.md | 5 +- actix-service/src/apply_cfg.rs | 163 +++++++++++++++++++++++++++++++++ actix-service/src/lib.rs | 21 ++++- 3 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 actix-service/src/apply_cfg.rs diff --git a/actix-service/CHANGES.md b/actix-service/CHANGES.md index 6df36058..881667b2 100644 --- a/actix-service/CHANGES.md +++ b/actix-service/CHANGES.md @@ -1,11 +1,14 @@ # Changes -## [0.3.2] - 2019-03-xx +## [0.3.3] - 2019-03-xx ### Added * Add `ApplyTransform` new service for transform and new service. +* Add `NewService::apply_cfg()` combinator, it allows to use + nested `NewService` with different config parameter. + ### Changed * Revert IntoFuture change diff --git a/actix-service/src/apply_cfg.rs b/actix-service/src/apply_cfg.rs new file mode 100644 index 00000000..a96663ea --- /dev/null +++ b/actix-service/src/apply_cfg.rs @@ -0,0 +1,163 @@ +use std::marker::PhantomData; + +use futures::{Async, Future, Poll}; + +use crate::and_then::AndThen; +use crate::{IntoNewService, NewService}; + +/// `ApplyNewService` new service combinator +pub struct ApplyConfig { + a: A, + b: B, + f: F, + r: PhantomData<(C1, C2)>, +} + +impl ApplyConfig +where + A: NewService, + B: NewService, + F: Fn(&C1) -> C2, +{ + /// Create new `ApplyNewService` new service instance + pub fn new, B1: IntoNewService>( + a: A1, + b: B1, + f: F, + ) -> Self { + Self { + f, + a: a.into_new_service(), + b: b.into_new_service(), + r: PhantomData, + } + } +} + +impl Clone for ApplyConfig +where + A: Clone, + B: Clone, + F: Clone, +{ + fn clone(&self) -> Self { + Self { + a: self.a.clone(), + b: self.b.clone(), + f: self.f.clone(), + r: PhantomData, + } + } +} + +impl NewService for ApplyConfig +where + A: NewService, + B: NewService, + F: Fn(&C1) -> C2, +{ + type Request = A::Request; + type Response = B::Response; + type Error = A::Error; + type Service = AndThen; + + type InitError = A::InitError; + type Future = ApplyConfigResponse; + + fn new_service(&self, cfg: &C1) -> Self::Future { + let cfg2 = (self.f)(cfg); + + ApplyConfigResponse { + a: None, + b: None, + fut_a: self.a.new_service(cfg), + fut_b: self.b.new_service(&cfg2), + } + } +} + +pub struct ApplyConfigResponse +where + A: NewService, + B: NewService, +{ + fut_b: B::Future, + fut_a: A::Future, + a: Option, + b: Option, +} + +impl Future for ApplyConfigResponse +where + A: NewService, + B: NewService, +{ + type Item = AndThen; + type Error = A::InitError; + + fn poll(&mut self) -> Poll { + if self.a.is_none() { + if let Async::Ready(service) = self.fut_a.poll()? { + self.a = Some(service); + } + } + + if self.b.is_none() { + if let Async::Ready(service) = self.fut_b.poll()? { + self.b = Some(service); + } + } + + if self.a.is_some() && self.b.is_some() { + Ok(Async::Ready(AndThen::new( + self.a.take().unwrap(), + self.b.take().unwrap(), + ))) + } else { + Ok(Async::NotReady) + } + } +} + +#[cfg(test)] +mod tests { + use futures::future::{ok, FutureResult}; + use futures::{Async, Future, Poll}; + + use crate::{fn_cfg_factory, NewService, Service}; + + #[derive(Clone)] + struct Srv; + impl Service for Srv { + type Request = (); + type Response = (); + type Error = (); + type Future = FutureResult<(), ()>; + + fn poll_ready(&mut self) -> Poll<(), Self::Error> { + Ok(Async::Ready(())) + } + + fn call(&mut self, _: ()) -> Self::Future { + ok(()) + } + } + + #[test] + fn test_new_service() { + let new_srv = fn_cfg_factory(|_: &usize| Ok::<_, ()>(Srv)).apply_cfg( + fn_cfg_factory(|s: &String| { + assert_eq!(s, "test"); + Ok::<_, ()>(Srv) + }), + |cfg: &usize| { + assert_eq!(*cfg, 1); + "test".to_string() + }, + ); + + if let Async::Ready(mut srv) = new_srv.new_service(&1).poll().unwrap() { + assert!(srv.poll_ready().is_ok()); + } + } +} diff --git a/actix-service/src/lib.rs b/actix-service/src/lib.rs index 8dfc6b22..c0d61908 100644 --- a/actix-service/src/lib.rs +++ b/actix-service/src/lib.rs @@ -9,6 +9,7 @@ mod and_then; mod and_then_apply; mod and_then_apply_fn; mod apply; +mod apply_cfg; pub mod blank; pub mod boxed; mod cell; @@ -26,6 +27,7 @@ pub use self::and_then::{AndThen, AndThenNewService}; use self::and_then_apply::AndThenTransform; use self::and_then_apply_fn::{AndThenApply, AndThenApplyNewService}; pub use self::apply::{Apply, ApplyNewService}; +use self::apply_cfg::ApplyConfig; pub use self::fn_service::{fn_cfg_factory, fn_factory, fn_service, FnService}; pub use self::fn_transform::FnTransform; pub use self::from_err::{FromErr, FromErrNewService}; @@ -204,7 +206,7 @@ pub trait NewService { /// Create and return a new service value asynchronously. fn new_service(&self, cfg: &Config) -> Self::Future; - /// Apply function to specified service and use it as a next service in + /// Apply transform service to specified service and use it as a next service in /// chain. fn apply( self, @@ -240,6 +242,23 @@ pub trait NewService { AndThenApplyNewService::new(self, service, f) } + /// Map this service's config type to a different config, + /// and use for nested service + fn apply_cfg(self, service: B1, f: F) -> ApplyConfig + where + Self: Sized, + F: Fn(&Config) -> C, + B1: IntoNewService, + B: NewService< + C, + Request = Self::Response, + Error = Self::Error, + InitError = Self::InitError, + >, + { + ApplyConfig::new(self, service, f) + } + /// Call another service after call to this one has resolved successfully. fn and_then(self, new_service: F) -> AndThenNewService where