Skip to content

util: add Wrap #676

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
util: Add examples for ServiceExt::wrap and ServiceExt::decorate
  • Loading branch information
lilyball committed Jul 13, 2022
commit d141ec4f84f2524609789bf07235bd77c8149e1c
8 changes: 8 additions & 0 deletions tower/src/builder/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -826,9 +826,13 @@ impl<L> ServiceBuilder<L> {
/// returned by the inner service, `T` is the shared state provided by `pre`, and `Response`
/// and `Err` match the types used in `pre`. The returned [`Result`] is the overall result from
/// the wrapped service.
///
/// See the documentation for the [`decorate` combinator][] for examples.
///
/// See also [`wrap`](Self::wrap) for when you just need to share state across the inner
/// service and don't need asynchronous functions or short-circuiting.
///
/// [`decorate` combinator]: crate::util::ServiceExt::decorate
#[cfg(feature = "util")]
#[cfg_attr(docsrs, doc(cfg(feature = "util")))]
pub fn decorate<Pre, Post>(
Expand All @@ -849,6 +853,10 @@ impl<L> ServiceBuilder<L> {
/// request given to the `Wrap` service, `Response` is the response returned from the inner
/// service, and `T` is the response returned from the `Wrap` service. If the inner service
/// returns an error the error is output directly without being given to the post function.
///
/// See the documentation for the [`wrap` combinator][] for examples.
///
/// [`wrap` combinator]: crate::util::ServiceExt::wrap
#[cfg(feature = "util")]
#[cfg_attr(docsrs, doc(cfg(feature = "util")))]
pub fn wrap<F, F2, Request, Response, T, E>(
Expand Down
95 changes: 94 additions & 1 deletion tower/src/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ pub mod future {
pub use super::map_result::MapResultFuture;
pub use super::optional::future as optional;
pub use super::then::ThenFuture;
pub use super::wrap::{WrapFuture, WrapPreFuture, WrapPostFuture};
pub use super::wrap::{WrapFuture, WrapPostFuture, WrapPreFuture};
}

pub mod helper {
Expand Down Expand Up @@ -1066,6 +1066,55 @@ pub trait ServiceExt<Request>: tower_service::Service<Request> {
///
/// See also [`wrap`](Self::wrap) for when you just need to share state across the inner
/// service and don't need asynchronous functions or short-circuiting.
///
/// # Examples
///
/// Adding a cache in front of a service:
///
/// ```rust
/// use tower::{Service, ServiceExt};
/// use std::collections::HashMap;
/// use std::sync::{Arc, Mutex};
/// # #[derive(Clone, PartialEq, Eq, Hash)] struct Request;
/// # impl Request { fn new(_: &str) -> Self { Request }}
/// # #[derive(Clone)] struct Response;
///
/// # fn main() {
/// # async {
/// // A service returning Result<Response, Error>
/// let service = /* ... */
/// # tower::service_fn(|_: Request| async { Ok::<_, ()>(Response) });
///
/// // Wrap the service in a cache for successful responses
/// let cache = Arc::new(Mutex::new(HashMap::new()));
/// let mut new_service = service.decorate(
/// {
/// let cache = cache.clone();
/// move |req: Request| std::future::ready(
/// cache.lock().unwrap().get(&req).cloned().map_or_else(
/// || Ok((req.clone(), req)),
/// |response| Err(Ok(response))),
/// )
/// },
/// move |res: Result<Response, _>, req| async move {
/// if let Ok(ref val) = res {
/// cache.lock().unwrap().insert(req, val.clone());
/// }
/// res
/// },
/// );
///
/// // Call the new service
/// let request = Request::new("cats");
/// let response = new_service
/// .ready()
/// .await?
/// .call(request)
/// .await?;
/// # Ok::<(), ()>(())
/// # };
/// # }
/// ```
fn decorate<Pre, Post>(self, pre: Pre, post: Post) -> Wrap<Self, Pre, Post>
where
Self: Sized,
Expand All @@ -1080,6 +1129,50 @@ pub trait ServiceExt<Request>: tower_service::Service<Request> {
/// request given to the `Wrap` service, `Response` is the response returned from the inner
/// service, and `T` is the response returned from the `Wrap` service. If the inner service
/// returns an error the error is output directly without being given to the post function.
///
/// # Examples
///
/// Attaching a unique identifier to the request and response:
///
/// ```rust
/// use tower::{Service, ServiceExt};
/// # #[derive(Clone)] struct UUID;
/// # impl UUID { fn new() -> Self { UUID } }
/// # struct Request;
/// # impl Request {
/// # fn new(_: &str) -> Self { Request }
/// # fn set_identifier(&mut self, _: UUID) {}
/// # }
/// # struct Response;
/// # impl Response { fn set_identifier(&mut self, _: UUID) {} }
///
/// # fn main() {
/// # async {
/// // A service returning Result<Response, Error>
/// let service = /* ... */
/// # tower::service_fn(|_: Request| async { Ok::<_, ()>(Response) });
///
/// // Attach a unique identifier to each request and response
/// let mut new_service = service.wrap(|req| {
/// let uuid = UUID::new();
/// req.set_identifier(uuid.clone());
/// move |mut res| {
/// res.set_identifier(uuid);
/// res
/// }
/// });
///
/// // Call the new service
/// let request = Request::new("cats");
/// let response = new_service
/// .ready()
/// .await?
/// .call(request)
/// .await?;
/// # Ok::<(), ()>(())
/// # };
/// # }
/// ```
fn wrap<F, F2, T>(
self,
f: F,
Expand Down
16 changes: 16 additions & 0 deletions tower/src/util/wrap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,12 @@ impl<S, F, F2> Wrap<S, F, F2> {
/// and `Err` match the types used in `pre`. The returned [`Result`] is the overall result from
/// the wrapped service.
///
/// See the documentation for the [`decorate` combinator][] for examples.
///
/// See also [`new`](Self::new) for when you just need to share state across the inner service
/// and don't need asynchronous functions or short-circuiting.
///
/// [`decorate` combinator]: crate::util::ServiceExt::decorate
pub fn decorate(inner: S, pre: F, post: F2) -> Self {
Self { inner, pre, post }
}
Expand All @@ -52,6 +56,10 @@ impl<S, F, F2> Wrap<S, F, F2> {
/// request given to the `Wrap` service, `Response` is the response returned from the inner
/// service, and `T` is the response returned from the `Wrap` service. If the inner service
/// returns an error the error is output directly without being given to the post function.
///
/// See the documentation for the [`wrap` combinator][] for examples.
///
/// [`wrap` combinator]: crate::util::ServiceExt::wrap
pub fn new<Request, T>(
inner: S,
f: F,
Expand Down Expand Up @@ -113,8 +121,12 @@ impl<F, F2> WrapLayer<F, F2> {
/// and `Err` match the types used in `pre`. The returned [`Result`] is the overall result from
/// the wrapped service.
///
/// See the documentation for the [`decorate` combinator][] for examples.
///
/// See also [`new`](Self::new) for when you just need to share state across the inner service
/// and don't need asynchronous functions or short-circuiting.
///
/// [`decorate` combinator]: crate::util::ServiceExt::decorate
pub fn decorate(pre: F, post: F2) -> Self {
Self { pre, post }
}
Expand All @@ -127,6 +139,10 @@ impl<F, F2> WrapLayer<F, F2> {
/// request given to the `Wrap` service, `Response` is the response returned from the inner
/// service, and `T` is the response returned from the `Wrap` service. If the inner service
/// returns an error the error is output directly without being given to the post function.
///
/// See the documentation for the [`wrap` combinator][] for examples.
///
/// [`wrap` combinator]: crate::util::ServiceExt::wrap
pub fn new<Request, Response, T, E>(f: F) -> WrapLayer<Pre<Request, T, E, F>, Post<F2, E>>
where
F: FnMut(&mut Request) -> F2,
Expand Down