diff --git a/Cargo.toml b/Cargo.toml index 527e295..10cdf76 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ members = [ "tower-reconnect", "tower-retry", "tower-service", + "tower-service-util", "tower-timeout", "tower-util", "tower-watch", diff --git a/tower-balance/Cargo.toml b/tower-balance/Cargo.toml index 93d8bd5..63b7275 100644 --- a/tower-balance/Cargo.toml +++ b/tower-balance/Cargo.toml @@ -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] diff --git a/tower-balance/src/lib.rs b/tower-balance/src/lib.rs index 0c2771c..153bed7 100644 --- a/tower-balance/src/lib.rs +++ b/tower-balance/src/lib.rs @@ -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; diff --git a/tower-balance/src/pool.rs b/tower-balance/src/pool.rs index f85c46a..162ea76 100644 --- a/tower-balance/src/pool.rs +++ b/tower-balance/src/pool.rs @@ -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. diff --git a/tower-filter/Cargo.toml b/tower-filter/Cargo.toml index d637a91..de79f5f 100644 --- a/tower-filter/Cargo.toml +++ b/tower-filter/Cargo.toml @@ -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" } diff --git a/tower-filter/tests/filter.rs b/tower-filter/tests/filter.rs index 41cf786..e1ee25e 100644 --- a/tower-filter/tests/filter.rs +++ b/tower-filter/tests/filter.rs @@ -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; diff --git a/tower-reconnect/Cargo.toml b/tower-reconnect/Cargo.toml index 000a049..c124ca3 100644 --- a/tower-reconnect/Cargo.toml +++ b/tower-reconnect/Cargo.toml @@ -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" } diff --git a/tower-reconnect/src/lib.rs b/tower-reconnect/src/lib.rs index 47ef82f..e94648b 100644 --- a/tower-reconnect/src/lib.rs +++ b/tower-reconnect/src/lib.rs @@ -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; diff --git a/tower-service-util/Cargo.toml b/tower-service-util/Cargo.toml new file mode 100644 index 0000000..623c55c --- /dev/null +++ b/tower-service-util/Cargo.toml @@ -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 "] +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 } diff --git a/tower-service-util/LICENSE b/tower-service-util/LICENSE new file mode 100644 index 0000000..b980cac --- /dev/null +++ b/tower-service-util/LICENSE @@ -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. diff --git a/tower-service-util/README.md b/tower-service-util/README.md new file mode 100644 index 0000000..14f47f4 --- /dev/null +++ b/tower-service-util/README.md @@ -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. diff --git a/tower-service-util/src/boxed/mod.rs b/tower-service-util/src/boxed/mod.rs new file mode 100644 index 0000000..6f4e351 --- /dev/null +++ b/tower-service-util/src/boxed/mod.rs @@ -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, +//! } +//! +//! impl Service for ServiceFn +//! where F: Fn(String) -> String, +//! { +//! type Response = String; +//! type Error = (); +//! type Future = FutureResult; +//! +//! fn poll_ready(&mut self) -> Poll<(), ()> { +//! Ok(().into()) +//! } +//! +//! fn call(&mut self, request: String) -> FutureResult { +//! future::ok((self.f)(request)) +//! } +//! } +//! +//! pub fn main() { +//! let f = |mut request: String| { +//! request.push_str(" response"); +//! request +//! }; +//! +//! let service: BoxService = +//! BoxService::new(ServiceFn { f }); +//! # drop(service); +//! } +//! ``` + +mod sync; +mod unsync; + +pub use self::sync::BoxService; +pub use self::unsync::UnsyncBoxService; diff --git a/tower-service-util/src/boxed/sync.rs b/tower-service-util/src/boxed/sync.rs new file mode 100644 index 0000000..ae19182 --- /dev/null +++ b/tower-service-util/src/boxed/sync.rs @@ -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 { + inner: Box> + 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 = Box + Send>; + +#[derive(Debug)] +struct Boxed { + inner: S, +} + +impl BoxService { + pub fn new(inner: S) -> Self + where + S: Service + Send + 'static, + S::Future: Send + 'static, + { + let inner = Box::new(Boxed { inner }); + BoxService { inner } + } +} + +impl Service for BoxService { + type Response = U; + type Error = E; + type Future = BoxFuture; + + fn poll_ready(&mut self) -> Poll<(), E> { + self.inner.poll_ready() + } + + fn call(&mut self, request: T) -> BoxFuture { + self.inner.call(request) + } +} + +impl fmt::Debug for BoxService +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 Service for Boxed +where + S: Service + 'static, + S::Future: Send + 'static, +{ + type Response = S::Response; + type Error = S::Error; + type Future = Box + 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)) + } +} diff --git a/tower-service-util/src/boxed/unsync.rs b/tower-service-util/src/boxed/unsync.rs new file mode 100644 index 0000000..f1151d3 --- /dev/null +++ b/tower-service-util/src/boxed/unsync.rs @@ -0,0 +1,74 @@ +use futures::{Future, Poll}; +use tower_service::Service; + +use std::fmt; + +/// A boxed `Service` trait object. +pub struct UnsyncBoxService { + inner: Box>>, +} + +/// 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 = Box>; + +#[derive(Debug)] +struct UnsyncBoxed { + inner: S, +} + +impl UnsyncBoxService { + pub fn new(inner: S) -> Self + where + S: Service + 'static, + S::Future: 'static, + { + let inner = Box::new(UnsyncBoxed { inner }); + UnsyncBoxService { inner } + } +} + +impl Service for UnsyncBoxService { + type Response = U; + type Error = E; + type Future = UnsyncBoxFuture; + + fn poll_ready(&mut self) -> Poll<(), E> { + self.inner.poll_ready() + } + + fn call(&mut self, request: T) -> UnsyncBoxFuture { + self.inner.call(request) + } +} + +impl fmt::Debug for UnsyncBoxService +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 Service for UnsyncBoxed +where + S: Service + 'static, + S::Future: 'static, +{ + type Response = S::Response; + type Error = S::Error; + type Future = Box>; + + 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)) + } +} diff --git a/tower-service-util/src/either/future.rs b/tower-service-util/src/either/future.rs new file mode 100644 index 0000000..1b5457c --- /dev/null +++ b/tower-service-util/src/either/future.rs @@ -0,0 +1,42 @@ +use super::Error; +use _either::Either; +use futures::{Future, Poll}; + +#[derive(Debug)] +pub struct ResponseFuture { + inner: Either, +} + +impl ResponseFuture { + pub(crate) fn new_left(left: A) -> ResponseFuture { + ResponseFuture { + inner: Either::Left(left), + } + } + + pub(crate) fn new_right(right: B) -> ResponseFuture { + ResponseFuture { + inner: Either::Right(right), + } + } +} + +impl Future for ResponseFuture +where + A: Future, + A::Error: Into, + B: Future, + B::Error: Into, +{ + type Item = A::Item; + type Error = Error; + + fn poll(&mut self) -> Poll { + use self::Either::*; + + match &mut self.inner { + Left(fut) => fut.poll().map_err(Into::into), + Right(fut) => fut.poll().map_err(Into::into), + } + } +} diff --git a/tower-service-util/src/either/mod.rs b/tower-service-util/src/either/mod.rs new file mode 100644 index 0000000..32acea0 --- /dev/null +++ b/tower-service-util/src/either/mod.rs @@ -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 { + inner: Either, +} + +type Error = Box<::std::error::Error + Send + Sync>; + +impl EitherService { + pub fn new(inner: Either) -> EitherService + where + A: Service, + A::Error: Into, + B: Service, + B::Error: Into, + { + EitherService { inner } + } +} + +impl Service for EitherService +where + A: Service, + A::Error: Into, + B: Service, + B::Error: Into, +{ + type Response = A::Response; + type Error = Error; + type Future = ResponseFuture; + + 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)), + } + } +} diff --git a/tower-service-util/src/lib.rs b/tower-service-util/src/lib.rs new file mode 100644 index 0000000..e8afc53 --- /dev/null +++ b/tower-service-util/src/lib.rs @@ -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; +} diff --git a/tower-util/src/make_connection.rs b/tower-service-util/src/make_connection.rs similarity index 87% rename from tower-util/src/make_connection.rs rename to tower-service-util/src/make_connection.rs index 6cdcdc0..976c1cf 100644 --- a/tower-util/src/make_connection.rs +++ b/tower-service-util/src/make_connection.rs @@ -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 { +pub trait MakeConnection: Sealed<(Request,)> { /// The transport provided by this service type Response: AsyncRead + AsyncWrite; @@ -21,7 +22,7 @@ pub trait MakeConnection { fn make_connection(&mut self, target: Request) -> Self::Future; } -impl self::sealed::Sealed for S where S: Service {} +impl Sealed<(Request,)> for S where S: Service {} impl MakeConnection for C where @@ -36,7 +37,3 @@ where Service::call(self, target) } } - -mod sealed { - pub trait Sealed {} -} diff --git a/tower-util/src/make_service.rs b/tower-service-util/src/make_service.rs similarity index 91% rename from tower-util/src/make_service.rs rename to tower-service-util/src/make_service.rs index 31614d6..b1b1fe5 100644 --- a/tower-util/src/make_service.rs +++ b/tower-service-util/src/make_service.rs @@ -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: self::sealed::Sealed { +pub trait MakeService: Sealed<(Target, Request)> { /// Responses given by the service type Response; @@ -41,7 +42,7 @@ pub trait MakeService: self::sealed::Sealed { fn make_service(&mut self, target: Target) -> Self::Future; } -impl self::sealed::Sealed for M +impl Sealed<(Target, Request)> for M where M: Service, S: Service, @@ -67,7 +68,3 @@ where Service::call(self, target) } } - -mod sealed { - pub trait Sealed {} -} diff --git a/tower-service-util/src/option/error.rs b/tower-service-util/src/option/error.rs new file mode 100644 index 0000000..c90d015 --- /dev/null +++ b/tower-service-util/src/option/error.rs @@ -0,0 +1,21 @@ +use std::error; +use std::fmt; + +#[derive(Debug)] +pub struct None(()); + +pub(crate) type Error = Box; + +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 {} diff --git a/tower-service-util/src/option/future.rs b/tower-service-util/src/option/future.rs new file mode 100644 index 0000000..21a2503 --- /dev/null +++ b/tower-service-util/src/option/future.rs @@ -0,0 +1,29 @@ +use super::{error, Error}; +use futures::{Future, Poll}; + +/// Response future returned by `OptionService`. +pub struct ResponseFuture { + inner: Option, +} + +impl ResponseFuture { + pub(crate) fn new(inner: Option) -> ResponseFuture { + ResponseFuture { inner } + } +} + +impl Future for ResponseFuture +where + T: Future, + T::Error: Into, +{ + type Item = T::Item; + type Error = Error; + + fn poll(&mut self) -> Poll { + match self.inner { + Some(ref mut inner) => inner.poll().map_err(Into::into), + None => Err(error::None::new().into()), + } + } +} diff --git a/tower-service-util/src/option/mod.rs b/tower-service-util/src/option/mod.rs new file mode 100644 index 0000000..062e1b7 --- /dev/null +++ b/tower-service-util/src/option/mod.rs @@ -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 { + inner: Option, +} + +// ===== impl OptionService ===== + +impl OptionService { + /// Create a new `OptionService` + pub fn new(inner: Option) -> OptionService + where + T: Service, + T::Error: Into, + { + OptionService { inner } + } +} + +impl Service for OptionService +where + T: Service, + T::Error: Into, +{ + type Response = T::Response; + type Error = Error; + type Future = ResponseFuture; + + 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) + } +} diff --git a/tower-service-util/src/sealed.rs b/tower-service-util/src/sealed.rs new file mode 100644 index 0000000..cf4c71a --- /dev/null +++ b/tower-service-util/src/sealed.rs @@ -0,0 +1 @@ +pub trait Sealed {} diff --git a/tower-service-util/src/service_fn.rs b/tower-service-util/src/service_fn.rs new file mode 100644 index 0000000..95451ef --- /dev/null +++ b/tower-service-util/src/service_fn.rs @@ -0,0 +1,32 @@ +use futures::{IntoFuture, Poll}; +use tower_service::Service; + +/// A `Service` implemented by a closure. +pub struct ServiceFn { + f: T, +} + +impl ServiceFn { + /// Returns a new `NewServiceFn` with the given closure. + pub fn new(f: T) -> Self { + ServiceFn { f } + } +} + +impl Service for ServiceFn +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() + } +} diff --git a/tower-util/Cargo.toml b/tower-util/Cargo.toml index 863c8ab..66ab551 100644 --- a/tower-util/Cargo.toml +++ b/tower-util/Cargo.toml @@ -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" diff --git a/tower-util/src/boxed.rs b/tower-util/src/boxed.rs deleted file mode 100644 index 089942d..0000000 --- a/tower-util/src/boxed.rs +++ /dev/null @@ -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, -//! } -//! -//! impl Service for ServiceFn -//! where F: Fn(String) -> String, -//! { -//! type Response = String; -//! type Error = (); -//! type Future = FutureResult; -//! -//! fn poll_ready(&mut self) -> Poll<(), ()> { -//! Ok(().into()) -//! } -//! -//! fn call(&mut self, request: String) -> FutureResult { -//! future::ok((self.f)(request)) -//! } -//! } -//! -//! pub fn main() { -//! let f = |mut request: String| { -//! request.push_str(" response"); -//! request -//! }; -//! -//! let service: BoxService = -//! 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 { - inner: Box> + 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 = Box + Send>; - -/// A boxed `Service` trait object. -pub struct UnsyncBoxService { - inner: Box>>, -} - -/// 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 = Box>; - -#[derive(Debug)] -struct Boxed { - inner: S, -} - -#[derive(Debug)] -struct UnsyncBoxed { - inner: S, -} - -// ===== impl BoxService ===== - -impl BoxService { - pub fn new(inner: S) -> Self - where - S: Service + Send + 'static, - S::Future: Send + 'static, - { - let inner = Box::new(Boxed { inner }); - BoxService { inner } - } -} - -impl Service for BoxService { - type Response = U; - type Error = E; - type Future = BoxFuture; - - fn poll_ready(&mut self) -> Poll<(), E> { - self.inner.poll_ready() - } - - fn call(&mut self, request: T) -> BoxFuture { - self.inner.call(request) - } -} - -impl fmt::Debug for BoxService -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 UnsyncBoxService { - pub fn new(inner: S) -> Self - where - S: Service + 'static, - S::Future: 'static, - { - let inner = Box::new(UnsyncBoxed { inner }); - UnsyncBoxService { inner } - } -} - -impl Service for UnsyncBoxService { - type Response = U; - type Error = E; - type Future = UnsyncBoxFuture; - - fn poll_ready(&mut self) -> Poll<(), E> { - self.inner.poll_ready() - } - - fn call(&mut self, request: T) -> UnsyncBoxFuture { - self.inner.call(request) - } -} - -impl fmt::Debug for UnsyncBoxService -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 Service for Boxed -where - S: Service + 'static, - S::Future: Send + 'static, -{ - type Response = S::Response; - type Error = S::Error; - type Future = Box + 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 Service for UnsyncBoxed -where - S: Service + 'static, - S::Future: 'static, -{ - type Response = S::Response; - type Error = S::Error; - type Future = Box>; - - 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)) - } -} diff --git a/tower-util/src/either.rs b/tower-util/src/either.rs deleted file mode 100644 index 8dd4e39..0000000 --- a/tower-util/src/either.rs +++ /dev/null @@ -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(A), - B(B), -} - -impl Service for EitherService -where - A: Service, - B: Service, -{ - type Response = A::Response; - type Error = A::Error; - type Future = Either; - - 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)), - } - } -} diff --git a/tower-util/src/lib.rs b/tower-util/src/lib.rs index 6dd8d65..871c7fb 100644 --- a/tower-util/src/lib.rs +++ b/tower-util/src/lib.rs @@ -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; diff --git a/tower-util/src/option.rs b/tower-util/src/option.rs deleted file mode 100644 index 4af7249..0000000 --- a/tower-util/src/option.rs +++ /dev/null @@ -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 { - inner: Option, -} - -/// Response future returned by `OptionService`. -pub struct ResponseFuture { - inner: Option, -} - -/// Error produced by `OptionService` responding to a request. -#[derive(Debug)] -pub enum Error { - Inner(T), - None, -} - -// ===== impl OptionService ===== - -impl OptionService { - /// 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 Service for OptionService -where - T: Service, -{ - type Response = T::Response; - type Error = Error; - type Future = ResponseFuture; - - 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 Future for ResponseFuture -where - T: Future, -{ - type Item = T::Item; - type Error = Error; - - fn poll(&mut self) -> Poll { - match self.inner { - Some(ref mut inner) => inner.poll().map_err(Error::Inner), - None => Err(Error::None), - } - } -}