From 62dbe1b001c6e768e20c3e1b8f2fdfab2b93d490 Mon Sep 17 00:00:00 2001 From: Nikolay Kim Date: Wed, 12 Sep 2018 13:34:53 -0700 Subject: [PATCH] add some doc apis and tests --- src/service/and_then.rs | 71 ++++++++++++++++++++++++++++++++++++++++- src/service/apply.rs | 34 ++++++++++++++++++++ src/service/from_err.rs | 52 ++++++++++++++++++++++++++++++ src/service/map.rs | 68 +++++++++++++++++++++++++++++++-------- src/service/map_err.rs | 70 ++++++++++++++++++++++++++++++++-------- src/service/mod.rs | 33 +++++++++++++++++++ 6 files changed, 299 insertions(+), 29 deletions(-) diff --git a/src/service/and_then.rs b/src/service/and_then.rs index 8315e52d1..db69df19f 100644 --- a/src/service/and_then.rs +++ b/src/service/and_then.rs @@ -5,7 +5,10 @@ use futures::{Async, Future, Poll}; use super::{IntoNewService, NewService, Service}; -/// `AndThen` service combinator +/// Service for the `and_then` combinator, chaining a computation onto the end +/// of another service which completes successfully. +/// +/// This is created by the `ServiceExt::and_then` method. pub struct AndThen { a: A, b: Rc>, @@ -218,3 +221,69 @@ where } } } + +#[cfg(test)] +mod tests { + use futures::future::{ok, FutureResult}; + use futures::{Async, Poll}; + use std::cell::Cell; + use std::rc::Rc; + + use super::*; + use service::{Service, ServiceExt}; + + struct Srv1(Rc>); + impl Service for Srv1 { + type Request = &'static str; + type Response = &'static str; + type Error = (); + type Future = FutureResult; + + fn poll_ready(&mut self) -> Poll<(), Self::Error> { + self.0.set(self.0.get() + 1); + Ok(Async::Ready(())) + } + + fn call(&mut self, req: Self::Request) -> Self::Future { + ok(req) + } + } + + #[derive(Clone)] + struct Srv2(Rc>); + + impl Service for Srv2 { + type Request = &'static str; + type Response = (&'static str, &'static str); + type Error = (); + type Future = FutureResult; + + fn poll_ready(&mut self) -> Poll<(), Self::Error> { + self.0.set(self.0.get() + 1); + Ok(Async::Ready(())) + } + + fn call(&mut self, req: Self::Request) -> Self::Future { + ok((req, "srv2")) + } + } + + #[test] + fn test_poll_ready() { + let cnt = Rc::new(Cell::new(0)); + let mut srv = Srv1(cnt.clone()).and_then(Srv2(cnt.clone())); + let res = srv.poll_ready(); + assert!(res.is_ok()); + assert_eq!(res.unwrap(), Async::Ready(())); + assert_eq!(cnt.get(), 2); + } + + #[test] + fn test_call() { + let cnt = Rc::new(Cell::new(0)); + let mut srv = Srv1(cnt.clone()).and_then(Srv2(cnt)); + let res = srv.call("srv1").poll(); + assert!(res.is_ok()); + assert_eq!(res.unwrap(), Async::Ready(("srv1", "srv2"))); + } +} diff --git a/src/service/apply.rs b/src/service/apply.rs index e0adf0a02..11fffce0c 100644 --- a/src/service/apply.rs +++ b/src/service/apply.rs @@ -167,3 +167,37 @@ where } } } + +#[cfg(test)] +mod tests { + use futures::future::{ok, FutureResult}; + use futures::{Async, Future, Poll}; + + use service::{Service, ServiceExt}; + + #[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_call() { + let mut srv = + Srv.apply(|req: &'static str, srv| srv.call(()).map(move |res| (req, res))); + let res = srv.call("srv").poll(); + assert!(res.is_ok()); + assert_eq!(res.unwrap(), Async::Ready(("srv", ()))); + } +} diff --git a/src/service/from_err.rs b/src/service/from_err.rs index 3015cd005..671046d7f 100644 --- a/src/service/from_err.rs +++ b/src/service/from_err.rs @@ -4,6 +4,9 @@ use futures::{Future, Poll}; use super::Service; +/// Service for the `from_err` combinator, changing the error type of a service. +/// +/// This is created by the `ServiceExt::from_err` method. pub struct FromErr where A: Service, @@ -73,3 +76,52 @@ where self.fut.poll().map_err(E::from) } } + +#[cfg(test)] +mod tests { + use futures::future::{err, FutureResult}; + + use super::*; + use service::{Service, ServiceExt}; + + struct Srv; + impl Service for Srv { + type Request = (); + type Response = (); + type Error = (); + type Future = FutureResult<(), ()>; + + fn poll_ready(&mut self) -> Poll<(), Self::Error> { + Err(()) + } + + fn call(&mut self, _: ()) -> Self::Future { + err(()) + } + } + + #[derive(Debug, PartialEq)] + struct Error; + + impl From<()> for Error { + fn from(_: ()) -> Self { + Error + } + } + + #[test] + fn test_poll_ready() { + let mut srv = Srv.from_err::(); + let res = srv.poll_ready(); + assert!(res.is_err()); + assert_eq!(res.err().unwrap(), Error); + } + + #[test] + fn test_call() { + let mut srv = Srv.from_err::(); + let res = srv.call(()).poll(); + assert!(res.is_err()); + assert_eq!(res.err().unwrap(), Error); + } +} diff --git a/src/service/map.rs b/src/service/map.rs index 373205a95..7c1199534 100644 --- a/src/service/map.rs +++ b/src/service/map.rs @@ -4,11 +4,16 @@ use futures::{Async, Future, Poll}; use super::{NewService, Service}; -/// `Map` service combinator -pub struct Map { - a: A, +/// Service for the `map` combinator, changing the type of a service's response. +/// +/// This is created by the `ServiceExt::map` method. +pub struct Map +where + A: Service, + F: Fn(A::Response) -> R, +{ + service: A, f: F, - r: marker::PhantomData, } impl Map @@ -17,12 +22,8 @@ where F: Fn(A::Response) -> R, { /// Create new `Map` combinator - pub fn new(a: A, f: F) -> Self { - Self { - a, - f, - r: marker::PhantomData, - } + pub fn new(service: A, f: F) -> Self { + Self { service, f } } } @@ -33,9 +34,8 @@ where { fn clone(&self) -> Self { Map { - a: self.a.clone(), + service: self.service.clone(), f: self.f.clone(), - r: marker::PhantomData, } } } @@ -51,11 +51,11 @@ where type Future = MapFuture; fn poll_ready(&mut self) -> Poll<(), Self::Error> { - self.a.poll_ready() + self.service.poll_ready() } fn call(&mut self, req: Self::Request) -> Self::Future { - MapFuture::new(self.a.call(req), self.f.clone()) + MapFuture::new(self.service.call(req), self.f.clone()) } } @@ -183,3 +183,43 @@ where } } } + +#[cfg(test)] +mod tests { + use futures::future::{ok, FutureResult}; + + use super::*; + use service::{Service, ServiceExt}; + + 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_poll_ready() { + let mut srv = Srv.map(|_| "ok"); + let res = srv.poll_ready(); + assert!(res.is_ok()); + assert_eq!(res.unwrap(), Async::Ready(())); + } + + #[test] + fn test_call() { + let mut srv = Srv.map(|_| "ok"); + let res = srv.call(()).poll(); + assert!(res.is_ok()); + assert_eq!(res.unwrap(), Async::Ready("ok")); + } +} diff --git a/src/service/map_err.rs b/src/service/map_err.rs index db0ed5887..ad7e20a50 100644 --- a/src/service/map_err.rs +++ b/src/service/map_err.rs @@ -4,11 +4,17 @@ use futures::{Async, Future, Poll}; use super::{NewService, Service}; -/// `MapErr` service combinator -pub struct MapErr { - a: A, +/// Service for the `map_err` combinator, changing the type of a service's +/// error. +/// +/// This is created by the `ServiceExt::map_err` method. +pub struct MapErr +where + A: Service, + F: Fn(A::Error) -> E, +{ + service: A, f: F, - e: marker::PhantomData, } impl MapErr @@ -17,12 +23,8 @@ where F: Fn(A::Error) -> E, { /// Create new `MapErr` combinator - pub fn new(a: A, f: F) -> Self { - Self { - a, - f, - e: marker::PhantomData, - } + pub fn new(service: A, f: F) -> Self { + Self { service, f } } } @@ -33,9 +35,8 @@ where { fn clone(&self) -> Self { MapErr { - a: self.a.clone(), + service: self.service.clone(), f: self.f.clone(), - e: marker::PhantomData, } } } @@ -52,11 +53,11 @@ where type Future = MapErrFuture; fn poll_ready(&mut self) -> Poll<(), Self::Error> { - self.a.poll_ready().map_err(&self.f) + self.service.poll_ready().map_err(&self.f) } fn call(&mut self, req: Self::Request) -> Self::Future { - MapErrFuture::new(self.a.call(req), self.f.clone()) + MapErrFuture::new(self.service.call(req), self.f.clone()) } } @@ -181,3 +182,44 @@ where } } } + +#[cfg(test)] +mod tests { + use futures::future::{err, FutureResult}; + + use super::*; + use service::{Service, ServiceExt}; + + struct Srv; + + impl Service for Srv { + type Request = (); + type Response = (); + type Error = (); + type Future = FutureResult<(), ()>; + + fn poll_ready(&mut self) -> Poll<(), Self::Error> { + Err(()) + } + + fn call(&mut self, _: ()) -> Self::Future { + err(()) + } + } + + #[test] + fn test_poll_ready() { + let mut srv = Srv.map_err(|_| "error"); + let res = srv.poll_ready(); + assert!(res.is_err()); + assert_eq!(res.err().unwrap(), "error"); + } + + #[test] + fn test_call() { + let mut srv = Srv.map_err(|_| "error"); + let res = srv.call(()).poll(); + assert!(res.is_err()); + assert_eq!(res.err().unwrap(), "error"); + } +} diff --git a/src/service/mod.rs b/src/service/mod.rs index 5ab16180e..2a3c7f69b 100644 --- a/src/service/mod.rs +++ b/src/service/mod.rs @@ -21,6 +21,8 @@ pub use self::map::{Map, MapNewService}; pub use self::map_err::{MapErr, MapErrNewService}; pub use self::map_init_err::MapInitErr; +/// An extension trait for `Service`s that provides a variety of convenient +/// adapters pub trait ServiceExt: Service { fn apply(self, f: F) -> Apply where @@ -32,6 +34,15 @@ pub trait ServiceExt: Service { Apply::new(f, self) } + /// 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. fn and_then(self, service: F) -> AndThen where Self: Sized, @@ -41,6 +52,11 @@ pub trait ServiceExt: Service { AndThen::new(self, service.into_service()) } + /// Map this service's error to any error implementing `From` for + /// this service`s `Error`. + /// + /// Note that this function consumes the receiving service and returns a + /// wrapped version of it. fn from_err(self) -> FromErr where Self: Sized, @@ -49,6 +65,15 @@ pub trait ServiceExt: Service { FromErr::new(self) } + /// 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. fn map(self, f: F) -> Map where Self: Sized, @@ -57,6 +82,14 @@ pub trait ServiceExt: Service { Map::new(self, 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. fn map_err(self, f: F) -> MapErr where Self: Sized,