Add tower-util (#19)

* NewServiceFn
* OptionService
* EitherService
This commit is contained in:
Carl Lerche 2017-10-21 14:06:39 -07:00 committed by GitHub
parent f1a19a9b1e
commit 4cb50eef77
7 changed files with 222 additions and 0 deletions

View File

@ -27,6 +27,7 @@ members = [
"tower-reconnect",
"tower-route",
"tower-timeout",
"tower-util",
]
[dependencies]

8
tower-util/Cargo.toml Normal file
View File

@ -0,0 +1,8 @@
[package]
name = "tower-util"
version = "0.1.0"
authors = ["Carl Lerche <me@carllerche.com>"]
[dependencies]
futures = "0.1"
tower = { version = "0.1", path = ".." }

13
tower-util/README.md Normal file
View File

@ -0,0 +1,13 @@
# Tower Util
Additional, generally useful, utilities for working with Tower. Contents from
this crate may eventually be moved into `tower` proper. They are kept here until
they prove their utility.
# License
`tower-timeout` is primarily distributed under the terms of both the MIT license
and the Apache License (Version 2.0), with portions covered by various BSD-like
licenses.
See LICENSE-APACHE, and LICENSE-MIT for details.

47
tower-util/src/either.rs Normal file
View File

@ -0,0 +1,47 @@
//! Contains `EitherService` and related types and functions.
//!
//! See `EitherService` documentation for more details.
use futures::Poll;
use futures::future::Either;
use tower::Service;
/// Combine two different service types into a single type.
///
/// Both services must be of the same request, response, and error types.
/// `EitherService` is useful for handling conditional branching in service
/// middleware to different inner service types.
pub enum EitherService<A, B> {
A(A),
B(B),
}
impl<A, B> Service for EitherService<A, B>
where A: Service,
B: Service<Request = A::Request,
Response = A::Response,
Error = A::Error>,
{
type Request = A::Request;
type Response = A::Response;
type Error = A::Error;
type Future = Either<A::Future, B::Future>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
use self::EitherService::*;
match *self {
A(ref mut service) => service.poll_ready(),
B(ref mut service) => service.poll_ready(),
}
}
fn call(&mut self, request: Self::Request) -> Self::Future {
use self::EitherService::*;
match *self {
A(ref mut service) => Either::A(service.call(request)),
B(ref mut service) => Either::B(service.call(request)),
}
}
}

12
tower-util/src/lib.rs Normal file
View File

@ -0,0 +1,12 @@
//! Various utility types and functions that are generally with Tower.
extern crate futures;
extern crate tower;
pub mod either;
pub mod option;
mod service_fn;
pub use either::EitherService;
pub use service_fn::NewServiceFn;
pub use option::OptionService;

77
tower-util/src/option.rs Normal file
View File

@ -0,0 +1,77 @@
//! Contains `OptionService` and related types and functions.
//!
//! See `OptionService` documentation for more details.
//!
use futures::{Future, Poll};
use tower::Service;
/// Optionally forwards requests to an inner service.
///
/// If the inner service is `None`, `Error::None` is returned as the response.
pub struct OptionService<T> {
inner: Option<T>,
}
/// Response future returned by `OptionService`.
pub struct ResponseFuture<T> {
inner: Option<T>,
}
/// Error produced by `OptionService` responding to a request.
pub enum Error<T> {
Inner(T),
None,
}
// ===== impl OptionService =====
impl<T> OptionService<T> {
/// Returns an `OptionService` that forwards requests to `inner`.
pub fn some(inner: T) -> Self {
OptionService { inner: Some(inner) }
}
/// Returns an `OptionService` that responds to all requests with
/// `Error::None`.
pub fn none() -> Self {
OptionService { inner: None }
}
}
impl<T> Service for OptionService<T>
where T: Service,
{
type Request = T::Request;
type Response = T::Response;
type Error = Error<T::Error>;
type Future = ResponseFuture<T::Future>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
match self.inner {
Some(ref mut inner) => inner.poll_ready().map_err(Error::Inner),
// None services are always ready
None => Ok(().into()),
}
}
fn call(&mut self, request: Self::Request) -> Self::Future {
let inner = self.inner.as_mut().map(|i| i.call(request));
ResponseFuture { inner }
}
}
// ===== impl ResponseFuture =====
impl<T> Future for ResponseFuture<T>
where T: Future,
{
type Item = T::Item;
type Error = Error<T::Error>;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
match self.inner {
Some(ref mut inner) => inner.poll().map_err(Error::Inner),
None => Err(Error::None),
}
}
}

View File

@ -0,0 +1,64 @@
use futures::{IntoFuture, Poll};
use tower::{Service, NewService};
use std::marker::PhantomData;
/// A `NewService` implemented by a closure.
pub struct NewServiceFn<T> {
f: T,
}
// ===== impl NewServiceFn =====
impl<T, N> NewServiceFn<T>
where T: Fn() -> N,
N: Service,
{
/// Returns a new `NewServiceFn` with the given closure.
pub fn new(f: T) -> Self {
NewServiceFn { f }
}
}
impl<T, R, S> NewService for NewServiceFn<T>
where T: Fn() -> R,
R: IntoFuture<Item = S>,
S: Service,
{
type Request = S::Request;
type Response = S::Response;
type Error = S::Error;
type Service = R::Item;
type InitError = R::Error;
type Future = R::Future;
fn new_service(&self) -> Self::Future {
(self.f)().into_future()
}
}
#[cfg(test)]
mod test {
use super::*;
use futures::{future, Future};
use std::rc::Rc;
#[test]
fn smoke() {
fn f<T>(service: &mut T)
where T: Service<Request = u32,
Response = Rc<u32>,
Error = ()> + Sync
{
let resp = service.call(123);
assert_eq!(*resp.wait().unwrap(), 456);
}
let mut service = ServiceFn::new(|request| {
assert_eq!(request, 123);
future::ok(Rc::new(456))
});
f(&mut service);
}
}