Initial introduction of tower-service-util (#193)

This commit is contained in:
Carl Lerche 2019-03-14 11:27:36 -07:00 committed by GitHub
parent 50fc5e8e63
commit acda5a75b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 587 additions and 373 deletions

View File

@ -13,6 +13,7 @@ members = [
"tower-reconnect",
"tower-retry",
"tower-service",
"tower-service-util",
"tower-timeout",
"tower-util",
"tower-watch",

View File

@ -11,7 +11,7 @@ rand = "0.6"
tokio-timer = "0.2.4"
tower-service = "0.2.0"
tower-discover = { version = "0.1", path = "../tower-discover" }
tower-util = { version = "0.1", path = "../tower-util" }
tower-service-util = { version = "0.1", path = "../tower-service-util" }
indexmap = "1"
[dev-dependencies]

View File

@ -9,7 +9,7 @@ extern crate rand;
extern crate tokio_timer;
extern crate tower_discover;
extern crate tower_service;
extern crate tower_util;
extern crate tower_service_util;
use futures::{Async, Future, Poll};
use indexmap::IndexMap;

View File

@ -18,7 +18,7 @@ use super::{Balance, Choose};
use futures::{Async, Future, Poll};
use tower_discover::{Change, Discover};
use tower_service::Service;
use tower_util::MakeService;
use tower_service_util::MakeService;
enum Load {
/// Load is low -- remove a service instance.

View File

@ -11,4 +11,3 @@ tower-layer = { version = "0.1", path = "../tower-layer" }
[dev-dependencies]
tower-mock = { version = "0.1", path = "../tower-mock" }
tower-util = { version = "0.1", path = "../tower-util" }

View File

@ -2,7 +2,6 @@ extern crate futures;
extern crate tower_filter;
extern crate tower_mock;
extern crate tower_service;
extern crate tower_util;
use futures::*;
use tower_filter::error::Error;

View File

@ -8,4 +8,4 @@ publish = false
log = "0.4.1"
futures = "0.1"
tower-service = "0.2.0"
tower-util = { version = "0.1", path = "../tower-util" }
tower-service-util = { version = "0.1", path = "../tower-service-util" }

View File

@ -2,7 +2,7 @@ extern crate futures;
#[macro_use]
extern crate log;
extern crate tower_service;
extern crate tower_util;
extern crate tower_service_util;
pub mod future;
@ -10,7 +10,7 @@ use crate::future::ResponseFuture;
use futures::{Async, Future, Poll};
use tower_service::Service;
use tower_util::MakeService;
use tower_service_util::MakeService;
use std::fmt;

View File

@ -0,0 +1,28 @@
[package]
name = "tower-service-util"
# When releasing to crates.io:
# - Update html_root_url.
# - Update CHANGELOG.md.
# - Update documentation URL
# - Create "v0.x.y" git tag.
version = "0.1.0"
authors = ["Carl Lerche <me@carllerche.com>"]
license = "MIT"
readme = "README.md"
repository = "https://github.com/tower-rs/tower"
homepage = "https://github.com/tower-rs/tower"
documentation = "https://docs.rs/tokio-service-util/0.1.0"
description = """
Utilities for working with tower-service.
"""
categories = ["asynchronous", "network-programming"]
[features]
io = ["tokio-io"]
[dependencies]
either = { version = "1.5.1", optional = true }
futures = "0.1.23"
tower-service = "0.2.0"
tokio-io = { version = "0.1.12", optional = true }

View File

@ -0,0 +1,25 @@
Copyright (c) 2019 Tower Contributors
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,13 @@
# Tower Service Util
Utilities for working with `tower-service`.
## License
This project is licensed under the [MIT license](LICENSE).
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in Tower by you, shall be licensed as MIT, without any additional
terms or conditions.

View File

@ -0,0 +1,62 @@
//! Trait object `Service` instances
//!
//! Dynamically dispatched `Service` objects allow for erasing the underlying
//! `Service` type and using the `Service` instances as opaque handles. This can
//! be useful when the service instance cannot be explicitly named for whatever
//! reason.
//!
//! There are two variants of service objects. `BoxService` requires both the
//! service and the response future to be `Send`. These values can move freely
//! across threads. `UnsyncBoxService` requires both the service and the
//! response future to remain on the current thread. This is useful for
//! representing services that are backed by `Rc` or other non-`Send` types.
//!
//! # Examples
//!
//! ```
//! # extern crate futures;
//! # extern crate tower_service;
//! # extern crate tower_service_util;
//! # use futures::*;
//! # use futures::future::FutureResult;
//! # use tower_service::Service;
//! # use tower_service_util::BoxService;
//! // Respond to requests using a closure. Since closures cannot be named,
//! // `ServiceFn` cannot be named either
//! pub struct ServiceFn<F> {
//! f: F,
//! }
//!
//! impl<F> Service<String> for ServiceFn<F>
//! where F: Fn(String) -> String,
//! {
//! type Response = String;
//! type Error = ();
//! type Future = FutureResult<String, ()>;
//!
//! fn poll_ready(&mut self) -> Poll<(), ()> {
//! Ok(().into())
//! }
//!
//! fn call(&mut self, request: String) -> FutureResult<String, ()> {
//! future::ok((self.f)(request))
//! }
//! }
//!
//! pub fn main() {
//! let f = |mut request: String| {
//! request.push_str(" response");
//! request
//! };
//!
//! let service: BoxService<String, String, ()> =
//! BoxService::new(ServiceFn { f });
//! # drop(service);
//! }
//! ```
mod sync;
mod unsync;
pub use self::sync::BoxService;
pub use self::unsync::UnsyncBoxService;

View File

@ -0,0 +1,80 @@
use futures::{Future, Poll};
use tower_service::Service;
use std::fmt;
/// A boxed `Service + Send` trait object.
///
/// `BoxService` turns a service into a trait object, allowing the response
/// future type to be dynamic. This type requires both the service and the
/// response future to be `Send`.
///
/// See module level documentation for more details.
pub struct BoxService<T, U, E> {
inner: Box<Service<T, Response = U, Error = E, Future = BoxFuture<U, E>> + Send>,
}
/// A boxed `Future + Send` trait object.
///
/// This type alias represents a boxed future that is `Send` and can be moved
/// across threads.
type BoxFuture<T, E> = Box<Future<Item = T, Error = E> + Send>;
#[derive(Debug)]
struct Boxed<S> {
inner: S,
}
impl<T, U, E> BoxService<T, U, E> {
pub fn new<S>(inner: S) -> Self
where
S: Service<T, Response = U, Error = E> + Send + 'static,
S::Future: Send + 'static,
{
let inner = Box::new(Boxed { inner });
BoxService { inner }
}
}
impl<T, U, E> Service<T> for BoxService<T, U, E> {
type Response = U;
type Error = E;
type Future = BoxFuture<U, E>;
fn poll_ready(&mut self) -> Poll<(), E> {
self.inner.poll_ready()
}
fn call(&mut self, request: T) -> BoxFuture<U, E> {
self.inner.call(request)
}
}
impl<T, U, E> fmt::Debug for BoxService<T, U, E>
where
T: fmt::Debug,
U: fmt::Debug,
E: fmt::Debug,
{
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("BoxService").finish()
}
}
impl<S, Request> Service<Request> for Boxed<S>
where
S: Service<Request> + 'static,
S::Future: Send + 'static,
{
type Response = S::Response;
type Error = S::Error;
type Future = Box<Future<Item = S::Response, Error = S::Error> + Send>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
self.inner.poll_ready()
}
fn call(&mut self, request: Request) -> Self::Future {
Box::new(self.inner.call(request))
}
}

View File

@ -0,0 +1,74 @@
use futures::{Future, Poll};
use tower_service::Service;
use std::fmt;
/// A boxed `Service` trait object.
pub struct UnsyncBoxService<T, U, E> {
inner: Box<Service<T, Response = U, Error = E, Future = UnsyncBoxFuture<U, E>>>,
}
/// A boxed `Future` trait object.
///
/// This type alias represents a boxed future that is *not* `Send` and must
/// remain on the current thread.
type UnsyncBoxFuture<T, E> = Box<Future<Item = T, Error = E>>;
#[derive(Debug)]
struct UnsyncBoxed<S> {
inner: S,
}
impl<T, U, E> UnsyncBoxService<T, U, E> {
pub fn new<S>(inner: S) -> Self
where
S: Service<T, Response = U, Error = E> + 'static,
S::Future: 'static,
{
let inner = Box::new(UnsyncBoxed { inner });
UnsyncBoxService { inner }
}
}
impl<T, U, E> Service<T> for UnsyncBoxService<T, U, E> {
type Response = U;
type Error = E;
type Future = UnsyncBoxFuture<U, E>;
fn poll_ready(&mut self) -> Poll<(), E> {
self.inner.poll_ready()
}
fn call(&mut self, request: T) -> UnsyncBoxFuture<U, E> {
self.inner.call(request)
}
}
impl<T, U, E> fmt::Debug for UnsyncBoxService<T, U, E>
where
T: fmt::Debug,
U: fmt::Debug,
E: fmt::Debug,
{
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("UnsyncBoxService").finish()
}
}
impl<S, Request> Service<Request> for UnsyncBoxed<S>
where
S: Service<Request> + 'static,
S::Future: 'static,
{
type Response = S::Response;
type Error = S::Error;
type Future = Box<Future<Item = S::Response, Error = S::Error>>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
self.inner.poll_ready()
}
fn call(&mut self, request: Request) -> Self::Future {
Box::new(self.inner.call(request))
}
}

View File

@ -0,0 +1,42 @@
use super::Error;
use _either::Either;
use futures::{Future, Poll};
#[derive(Debug)]
pub struct ResponseFuture<A, B> {
inner: Either<A, B>,
}
impl<A, B> ResponseFuture<A, B> {
pub(crate) fn new_left(left: A) -> ResponseFuture<A, B> {
ResponseFuture {
inner: Either::Left(left),
}
}
pub(crate) fn new_right(right: B) -> ResponseFuture<A, B> {
ResponseFuture {
inner: Either::Right(right),
}
}
}
impl<A, B> Future for ResponseFuture<A, B>
where
A: Future,
A::Error: Into<Error>,
B: Future<Item = A::Item>,
B::Error: Into<Error>,
{
type Item = A::Item;
type Error = Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
use self::Either::*;
match &mut self.inner {
Left(fut) => fut.poll().map_err(Into::into),
Right(fut) => fut.poll().map_err(Into::into),
}
}
}

View File

@ -0,0 +1,63 @@
//! Contains `EitherService` and related types and functions.
//!
//! See `EitherService` documentation for more details.
pub mod future;
use self::future::ResponseFuture;
use _either::Either;
use futures::Poll;
use tower_service::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 struct EitherService<A, B> {
inner: Either<A, B>,
}
type Error = Box<::std::error::Error + Send + Sync>;
impl<A, B> EitherService<A, B> {
pub fn new<Request>(inner: Either<A, B>) -> EitherService<A, B>
where
A: Service<Request>,
A::Error: Into<Error>,
B: Service<Request, Response = A::Response>,
B::Error: Into<Error>,
{
EitherService { inner }
}
}
impl<A, B, Request> Service<Request> for EitherService<A, B>
where
A: Service<Request>,
A::Error: Into<Error>,
B: Service<Request, Response = A::Response>,
B::Error: Into<Error>,
{
type Response = A::Response;
type Error = Error;
type Future = ResponseFuture<A::Future, B::Future>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
use self::Either::*;
match &mut self.inner {
Left(service) => service.poll_ready().map_err(Into::into),
Right(service) => service.poll_ready().map_err(Into::into),
}
}
fn call(&mut self, request: Request) -> Self::Future {
use self::Either::*;
match &mut self.inner {
Left(service) => ResponseFuture::new_left(service.call(request)),
Right(service) => ResponseFuture::new_right(service.call(request)),
}
}
}

View File

@ -0,0 +1,41 @@
//! Various utility types and functions that are generally with Tower.
#[cfg(feature = "either")]
extern crate either as _either;
extern crate futures;
#[cfg(feature = "io")]
extern crate tokio_io;
extern crate tower_service;
mod boxed;
#[cfg(feature = "either")]
mod either;
#[cfg(feature = "io")]
mod make_connection;
mod make_service;
mod option;
mod sealed;
mod service_fn;
pub use crate::boxed::{BoxService, UnsyncBoxService};
#[cfg(feature = "either")]
pub use crate::either::EitherService;
#[cfg(feature = "io")]
pub use crate::make_connection::MakeConnection;
pub use crate::make_service::MakeService;
pub use crate::option::OptionService;
pub use crate::service_fn::ServiceFn;
pub mod error {
//! Error types
pub use crate::option::error as option;
}
pub mod future {
//! Future types
#[cfg(feature = "either")]
pub use crate::either::future as either;
pub use crate::option::future as option;
}

View File

@ -1,3 +1,4 @@
use crate::sealed::Sealed;
use futures::Future;
use tokio_io::{AsyncRead, AsyncWrite};
use tower_service::Service;
@ -7,7 +8,7 @@ use tower_service::Service;
/// The goal of this service is to allow composable methods for creating
/// `AsyncRead + AsyncWrite` transports. This could mean creating a TLS
/// based connection or using some other method to authenticate the connection.
pub trait MakeConnection<Request> {
pub trait MakeConnection<Request>: Sealed<(Request,)> {
/// The transport provided by this service
type Response: AsyncRead + AsyncWrite;
@ -21,7 +22,7 @@ pub trait MakeConnection<Request> {
fn make_connection(&mut self, target: Request) -> Self::Future;
}
impl<S, Request> self::sealed::Sealed<Request> for S where S: Service<Request> {}
impl<S, Request> Sealed<(Request,)> for S where S: Service<Request> {}
impl<C, Request> MakeConnection<Request> for C
where
@ -36,7 +37,3 @@ where
Service::call(self, target)
}
}
mod sealed {
pub trait Sealed<A> {}
}

View File

@ -1,3 +1,4 @@
use crate::sealed::Sealed;
use futures::{Future, Poll};
use tower_service::Service;
@ -10,7 +11,7 @@ use tower_service::Service;
/// requests on that new TCP stream.
///
/// This is essentially a trait alias for a `Service` of `Service`s.
pub trait MakeService<Target, Request>: self::sealed::Sealed<Target, Request> {
pub trait MakeService<Target, Request>: Sealed<(Target, Request)> {
/// Responses given by the service
type Response;
@ -41,7 +42,7 @@ pub trait MakeService<Target, Request>: self::sealed::Sealed<Target, Request> {
fn make_service(&mut self, target: Target) -> Self::Future;
}
impl<M, S, Target, Request> self::sealed::Sealed<Target, Request> for M
impl<M, S, Target, Request> Sealed<(Target, Request)> for M
where
M: Service<Target, Response = S>,
S: Service<Request>,
@ -67,7 +68,3 @@ where
Service::call(self, target)
}
}
mod sealed {
pub trait Sealed<A, B> {}
}

View File

@ -0,0 +1,21 @@
use std::error;
use std::fmt;
#[derive(Debug)]
pub struct None(());
pub(crate) type Error = Box<error::Error + Send + Sync>;
impl None {
pub(crate) fn new() -> None {
None(())
}
}
impl fmt::Display for None {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "None")
}
}
impl error::Error for None {}

View File

@ -0,0 +1,29 @@
use super::{error, Error};
use futures::{Future, Poll};
/// Response future returned by `OptionService`.
pub struct ResponseFuture<T> {
inner: Option<T>,
}
impl<T> ResponseFuture<T> {
pub(crate) fn new(inner: Option<T>) -> ResponseFuture<T> {
ResponseFuture { inner }
}
}
impl<T> Future for ResponseFuture<T>
where
T: Future,
T::Error: Into<Error>,
{
type Item = T::Item;
type Error = Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
match self.inner {
Some(ref mut inner) => inner.poll().map_err(Into::into),
None => Err(error::None::new().into()),
}
}
}

View File

@ -0,0 +1,55 @@
//! Contains `OptionService` and related types and functions.
//!
//! See `OptionService` documentation for more details.
//!
pub mod error;
pub mod future;
use self::error::Error;
use self::future::ResponseFuture;
use futures::Poll;
use tower_service::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>,
}
// ===== impl OptionService =====
impl<T> OptionService<T> {
/// Create a new `OptionService`
pub fn new<Request>(inner: Option<T>) -> OptionService<T>
where
T: Service<Request>,
T::Error: Into<Error>,
{
OptionService { inner }
}
}
impl<T, Request> Service<Request> for OptionService<T>
where
T: Service<Request>,
T::Error: Into<Error>,
{
type Response = T::Response;
type Error = 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(Into::into),
// None services are always ready
None => Ok(().into()),
}
}
fn call(&mut self, request: Request) -> Self::Future {
let inner = self.inner.as_mut().map(|i| i.call(request));
ResponseFuture::new(inner)
}
}

View File

@ -0,0 +1 @@
pub trait Sealed<T> {}

View File

@ -0,0 +1,32 @@
use futures::{IntoFuture, Poll};
use tower_service::Service;
/// A `Service` implemented by a closure.
pub struct ServiceFn<T> {
f: T,
}
impl<T> ServiceFn<T> {
/// Returns a new `NewServiceFn` with the given closure.
pub fn new(f: T) -> Self {
ServiceFn { f }
}
}
impl<T, F, Request> Service<Request> for ServiceFn<T>
where
T: Fn(Request) -> F,
F: IntoFuture,
{
type Response = F::Item;
type Error = F::Error;
type Future = F::Future;
fn poll_ready(&mut self) -> Poll<(), F::Error> {
Ok(().into())
}
fn call(&mut self, req: Request) -> Self::Future {
(self.f)(req).into_future()
}
}

View File

@ -7,7 +7,7 @@ publish = false
[dependencies]
futures = "0.1"
tower-service = "0.2.0"
tokio-io = "0.1"
tower-service-util = { version = "0.1.0", path = "../tower-service-util", features = ["io", "either"] }
[dev-dependencies]
tokio-mock-task = "0.1"

View File

@ -1,215 +0,0 @@
//! Trait object `Service` instances
//!
//! Dynamically dispatched `Service` objects allow for erasing the underlying
//! `Service` type and using the `Service` instances as opaque handles. This can
//! be useful when the service instance cannot be explicitly named for whatever
//! reason.
//!
//! There are two variants of service objects. `BoxService` requires both the
//! service and the response future to be `Send`. These values can move freely
//! across threads. `UnsyncBoxService` requires both the service and the
//! response future to remain on the current thread. This is useful for
//! representing services that are backed by `Rc` or other non-`Send` types.
//!
//! # Examples
//!
//! ```
//! # extern crate futures;
//! # extern crate tower_service;
//! # extern crate tower_util;
//! # use futures::*;
//! # use futures::future::FutureResult;
//! # use tower_service::*;
//! # use tower_util::*;
//! // Respond to requests using a closure. Since closures cannot be named,
//! // `ServiceFn` cannot be named either
//! pub struct ServiceFn<F> {
//! f: F,
//! }
//!
//! impl<F> Service<String> for ServiceFn<F>
//! where F: Fn(String) -> String,
//! {
//! type Response = String;
//! type Error = ();
//! type Future = FutureResult<String, ()>;
//!
//! fn poll_ready(&mut self) -> Poll<(), ()> {
//! Ok(().into())
//! }
//!
//! fn call(&mut self, request: String) -> FutureResult<String, ()> {
//! future::ok((self.f)(request))
//! }
//! }
//!
//! pub fn main() {
//! let f = |mut request: String| {
//! request.push_str(" response");
//! request
//! };
//!
//! let service: BoxService<String, String, ()> =
//! BoxService::new(ServiceFn { f });
//! # drop(service);
//! }
//! ```
use futures::{Future, Poll};
use tower_service::Service;
use std::fmt;
/// A boxed `Service + Send` trait object.
///
/// `BoxService` turns a service into a trait object, allowing the response
/// future type to be dynamic. This type requires both the service and the
/// response future to be `Send`.
///
/// See module level documentation for more details.
pub struct BoxService<T, U, E> {
inner: Box<Service<T, Response = U, Error = E, Future = BoxFuture<U, E>> + Send>,
}
/// A boxed `Future + Send` trait object.
///
/// This type alias represents a boxed future that is `Send` and can be moved
/// across threads.
pub type BoxFuture<T, E> = Box<Future<Item = T, Error = E> + Send>;
/// A boxed `Service` trait object.
pub struct UnsyncBoxService<T, U, E> {
inner: Box<Service<T, Response = U, Error = E, Future = UnsyncBoxFuture<U, E>>>,
}
/// A boxed `Future` trait object.
///
/// This type alias represents a boxed future that is *not* `Send` and must
/// remain on the current thread.
pub type UnsyncBoxFuture<T, E> = Box<Future<Item = T, Error = E>>;
#[derive(Debug)]
struct Boxed<S> {
inner: S,
}
#[derive(Debug)]
struct UnsyncBoxed<S> {
inner: S,
}
// ===== impl BoxService =====
impl<T, U, E> BoxService<T, U, E> {
pub fn new<S>(inner: S) -> Self
where
S: Service<T, Response = U, Error = E> + Send + 'static,
S::Future: Send + 'static,
{
let inner = Box::new(Boxed { inner });
BoxService { inner }
}
}
impl<T, U, E> Service<T> for BoxService<T, U, E> {
type Response = U;
type Error = E;
type Future = BoxFuture<U, E>;
fn poll_ready(&mut self) -> Poll<(), E> {
self.inner.poll_ready()
}
fn call(&mut self, request: T) -> BoxFuture<U, E> {
self.inner.call(request)
}
}
impl<T, U, E> fmt::Debug for BoxService<T, U, E>
where
T: fmt::Debug,
U: fmt::Debug,
E: fmt::Debug,
{
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("BoxService").finish()
}
}
// ===== impl UnsyncBoxService =====
impl<T, U, E> UnsyncBoxService<T, U, E> {
pub fn new<S>(inner: S) -> Self
where
S: Service<T, Response = U, Error = E> + 'static,
S::Future: 'static,
{
let inner = Box::new(UnsyncBoxed { inner });
UnsyncBoxService { inner }
}
}
impl<T, U, E> Service<T> for UnsyncBoxService<T, U, E> {
type Response = U;
type Error = E;
type Future = UnsyncBoxFuture<U, E>;
fn poll_ready(&mut self) -> Poll<(), E> {
self.inner.poll_ready()
}
fn call(&mut self, request: T) -> UnsyncBoxFuture<U, E> {
self.inner.call(request)
}
}
impl<T, U, E> fmt::Debug for UnsyncBoxService<T, U, E>
where
T: fmt::Debug,
U: fmt::Debug,
E: fmt::Debug,
{
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("UnsyncBoxService").finish()
}
}
// ===== impl Boxed =====
impl<S, Request> Service<Request> for Boxed<S>
where
S: Service<Request> + 'static,
S::Future: Send + 'static,
{
type Response = S::Response;
type Error = S::Error;
type Future = Box<Future<Item = S::Response, Error = S::Error> + Send>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
self.inner.poll_ready()
}
fn call(&mut self, request: Request) -> Self::Future {
Box::new(self.inner.call(request))
}
}
// ===== impl UnsyncBoxed =====
impl<S, Request> Service<Request> for UnsyncBoxed<S>
where
S: Service<Request> + 'static,
S::Future: 'static,
{
type Response = S::Response;
type Error = S::Error;
type Future = Box<Future<Item = S::Response, Error = S::Error>>;
fn poll_ready(&mut self) -> Poll<(), Self::Error> {
self.inner.poll_ready()
}
fn call(&mut self, request: Request) -> Self::Future {
Box::new(self.inner.call(request))
}
}

View File

@ -1,45 +0,0 @@
//! Contains `EitherService` and related types and functions.
//!
//! See `EitherService` documentation for more details.
use futures::future::Either;
use futures::Poll;
use tower_service::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, Request> Service<Request> for EitherService<A, B>
where
A: Service<Request>,
B: Service<Request, Response = A::Response, Error = A::Error>,
{
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: 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)),
}
}
}

View File

@ -2,23 +2,17 @@
#[macro_use]
extern crate futures;
extern crate tokio_io;
#[cfg(test)]
extern crate tokio_mock_task;
extern crate tower_service;
extern crate tower_service_util;
pub mod boxed;
pub mod either;
pub mod ext;
mod make_connection;
mod make_service;
pub mod option;
mod service_fn;
pub use boxed::BoxService;
pub use either::EitherService;
pub use ext::ServiceExt;
pub use make_connection::MakeConnection;
pub use make_service::MakeService;
pub use option::OptionService;
pub use service_fn::ServiceFn;
pub use tower_service_util::BoxService;
pub use tower_service_util::EitherService;
pub use tower_service_util::MakeConnection;
pub use tower_service_util::MakeService;
pub use tower_service_util::OptionService;
pub use tower_service_util::ServiceFn;

View File

@ -1,79 +0,0 @@
//! Contains `OptionService` and related types and functions.
//!
//! See `OptionService` documentation for more details.
//!
use futures::{Future, Poll};
use tower_service::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.
#[derive(Debug)]
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, Request> Service<Request> for OptionService<T>
where
T: Service<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: 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),
}
}
}