Trait tower::util::ServiceExt

source ·
pub trait ServiceExt<Request>: Service<Request> {
Show 16 methods // Provided methods fn ready(&mut self) -> Ready<'_, Self, Request> where Self: Sized { ... } fn ready_and(&mut self) -> ReadyAnd<'_, Self, Request> where Self: Sized { ... } fn ready_oneshot(self) -> ReadyOneshot<Self, Request> where Self: Sized { ... } fn oneshot(self, req: Request) -> Oneshot<Self, Request> where Self: Sized { ... } fn call_all<S>(self, reqs: S) -> CallAll<Self, S> where Self: Sized, Self::Error: Into<BoxError>, S: Stream<Item = Request> { ... } fn and_then<F>(self, f: F) -> AndThen<Self, F> where Self: Sized, F: Clone { ... } fn map_response<F, Response>(self, f: F) -> MapResponse<Self, F> where Self: Sized, F: FnOnce(Self::Response) -> Response + Clone { ... } fn map_err<F, Error>(self, f: F) -> MapErr<Self, F> where Self: Sized, F: FnOnce(Self::Error) -> Error + Clone { ... } fn map_result<F, Response, Error>(self, f: F) -> MapResult<Self, F> where Self: Sized, Error: From<Self::Error>, F: FnOnce(Result<Self::Response, Self::Error>) -> Result<Response, Error> + Clone { ... } fn map_request<F, NewRequest>(self, f: F) -> MapRequest<Self, F> where Self: Sized, F: FnMut(NewRequest) -> Request { ... } fn filter<F, NewRequest>(self, filter: F) -> Filter<Self, F> where Self: Sized, F: Predicate<NewRequest> { ... } fn filter_async<F, NewRequest>(self, filter: F) -> AsyncFilter<Self, F> where Self: Sized, F: AsyncPredicate<NewRequest> { ... } fn then<F, Response, Error, Fut>(self, f: F) -> Then<Self, F> where Self: Sized, Error: From<Self::Error>, F: FnOnce(Result<Self::Response, Self::Error>) -> Fut + Clone, Fut: Future<Output = Result<Response, Error>> { ... } fn map_future<F, Fut, Response, Error>(self, f: F) -> MapFuture<Self, F> where Self: Sized, F: FnMut(Self::Future) -> Fut, Error: From<Self::Error>, Fut: Future<Output = Result<Response, Error>> { ... } fn boxed(self) -> BoxService<Request, Self::Response, Self::Error> where Self: Sized + Send + 'static, Self::Future: Send + 'static { ... } fn boxed_clone( self ) -> BoxCloneService<Request, Self::Response, Self::Error> where Self: Clone + Sized + Send + 'static, Self::Future: Send + 'static { ... }
}
Expand description

An extension trait for Services that provides a variety of convenient adapters

Provided Methods§

source

fn ready(&mut self) -> Ready<'_, Self, Request>
where Self: Sized,

Yields a mutable reference to the service when it is ready to accept a request.

source

fn ready_and(&mut self) -> ReadyAnd<'_, Self, Request>
where Self: Sized,

👎Deprecated since 0.4.6: please use the ServiceExt::ready method instead

Yields a mutable reference to the service when it is ready to accept a request.

source

fn ready_oneshot(self) -> ReadyOneshot<Self, Request>
where Self: Sized,

Yields the service when it is ready to accept a request.

source

fn oneshot(self, req: Request) -> Oneshot<Self, Request>
where Self: Sized,

Consume this Service, calling with the providing request once it is ready.

source

fn call_all<S>(self, reqs: S) -> CallAll<Self, S>
where Self: Sized, Self::Error: Into<BoxError>, S: Stream<Item = Request>,

Process all requests from the given Stream, and produce a Stream of their responses.

This is essentially Stream<Item = Request> + Self => Stream<Item = Response>. See the documentation for CallAll for details.

source

fn and_then<F>(self, f: F) -> AndThen<Self, F>
where Self: Sized, F: Clone,

Executes a new future after this service’s future resolves. This does not alter the behaviour of the poll_ready method.

This method can be used to change the Response type of the service into a different type. You can use this method to chain along a computation once the service’s response has been resolved.

§Example
// A service returning Result<Record, _>
let service = DatabaseService::new("127.0.0.1:8080");

// Map the response into a new response
let mut new_service = service.and_then(|record: Record| async move {
    let name = record.name;
    avatar_lookup(name).await
});

// Call the new service
let id = 13;
let avatar = new_service.call(id).await.unwrap();
source

fn map_response<F, Response>(self, f: F) -> MapResponse<Self, F>
where Self: Sized, F: FnOnce(Self::Response) -> Response + Clone,

Maps this service’s response value to a different value. This does not alter the behaviour of the poll_ready method.

This method can be used to change the Response type of the service into a different type. It is similar to the Result::map method. You can use this method to chain along a computation once the service’s response has been resolved.

§Example
// A service returning Result<Record, _>
let service = DatabaseService::new("127.0.0.1:8080");

// Map the response into a new response
let mut new_service = service.map_response(|record| record.name);

// Call the new service
let id = 13;
let name = new_service
    .ready()
    .await?
    .call(id)
    .await?;
source

fn map_err<F, Error>(self, f: F) -> MapErr<Self, F>
where Self: Sized, F: FnOnce(Self::Error) -> Error + Clone,

Maps this service’s error value to a different value. This does not alter the behaviour of the poll_ready method.

This method can be used to change the Error type of the service into a different type. It is similar to the Result::map_err method.

§Example
// A service returning Result<_, Error>
let service = DatabaseService::new("127.0.0.1:8080");

// Map the error to a new error
let mut new_service = service.map_err(|err| err.code);

// Call the new service
let id = 13;
let code = new_service
    .ready()
    .await?
    .call(id)
    .await
    .unwrap_err();
source

fn map_result<F, Response, Error>(self, f: F) -> MapResult<Self, F>
where Self: Sized, Error: From<Self::Error>, F: FnOnce(Result<Self::Response, Self::Error>) -> Result<Response, Error> + Clone,

Maps this service’s result type (Result<Self::Response, Self::Error>) to a different value, regardless of whether the future succeeds or fails.

This is similar to the map_response and map_err combinators, except that the same function is invoked when the service’s future completes, whether it completes successfully or fails. This function takes the Result returned by the service’s future, and returns a Result.

Like the standard library’s Result::and_then, this method can be used to implement control flow based on Result values. For example, it may be used to implement error recovery, by turning some Err responses from the service into Ok responses. Similarly, some successful responses from the service could be rejected, by returning an Err conditionally, depending on the value inside the [Ok.] Finally, this method can also be used to implement behaviors that must run when a service’s future completes, regardless of whether it succeeded or failed.

This method can be used to change the Response type of the service into a different type. It can also be used to change the Error type of the service. However, because the map_result function is not applied to the errors returned by the service’s poll_ready method, it must be possible to convert the service’s Error type into the error type returned by the map_result function. This is trivial when the function returns the same error type as the service, but in other cases, it can be useful to use BoxError to erase differing error types.

§Examples

Recovering from certain errors:

// A service returning Result<Vec<Record>, DbError>
let service = DatabaseService::new("127.0.0.1:8080");

// If the database returns no records for the query, we just want an empty `Vec`.
let mut new_service = service.map_result(|result| match result {
    // If the error indicates that no records matched the query, return an empty
    // `Vec` instead.
    Err(DbError::NoRecordsFound) => Ok(Vec::new()),
    // Propagate all other responses (`Ok` and `Err`) unchanged
    x => x,
});

// Call the new service
let id = 13;
let name = new_service
    .ready()
    .await?
    .call(id)
    .await?;

Rejecting some Ok responses:

use tower::BoxError;

// A service returning Result<Record, DbError>
let service = DatabaseService::new("127.0.0.1:8080");

// If the user is zero years old, return an error.
let mut new_service = service.map_result(|result| {
   let record = result?;

   if record.age == 0 {
        // Users must have been born to use our app!
        let app_error = AppError::from("users cannot be 0 years old!");

        // Box the error to erase its type (as it can be an `AppError`
        // *or* the inner service's `DbError`).
        return Err(BoxError::from(app_error));
    }

    // Otherwise, return the record.
    Ok(record)
});

// Call the new service
let id = 13;
let record = new_service
    .ready()
    .await?
    .call(id)
    .await?;

Performing an action that must be run for both successes and failures:

// A service returning Result<Record, DbError>
let service = DatabaseService::new("127.0.0.1:8080");

// Print a message whenever a query completes.
let mut new_service = service.map_result(|result| {
    println!("query completed; success={}", result.is_ok());
    result
});

// Call the new service
let id = 13;
let response = new_service
    .ready()
    .await?
    .call(id)
    .await;
source

fn map_request<F, NewRequest>(self, f: F) -> MapRequest<Self, F>
where Self: Sized, F: FnMut(NewRequest) -> Request,

Composes a function in front of the service.

This adapter produces a new service that passes each value through the given function f before sending it to self.

§Example
// A service taking a String as a request
let service = DatabaseService::new("127.0.0.1:8080");

// Map the request to a new request
let mut new_service = service.map_request(|id: u32| id.to_string());

// Call the new service
let id = 13;
let response = new_service
    .ready()
    .await?
    .call(id)
    .await;
source

fn filter<F, NewRequest>(self, filter: F) -> Filter<Self, F>
where Self: Sized, F: Predicate<NewRequest>,

Composes this service with a Filter that conditionally accepts or rejects requests based on a predicate.

This adapter produces a new service that passes each value through the given function predicate before sending it to self.

§Example
// A service taking a u32 as a request and returning Result<_, DbError>
let service = DatabaseService::new("127.0.0.1:8080");

// Fallibly map the request to a new request
let mut new_service = service
    .filter(|id_str: &str| id_str.parse().map_err(DbError::Parse));

// Call the new service
let id = "13";
let response = new_service
    .ready()
    .await?
    .call(id)
    .await;
source

fn filter_async<F, NewRequest>(self, filter: F) -> AsyncFilter<Self, F>
where Self: Sized, F: AsyncPredicate<NewRequest>,

Composes this service with an AsyncFilter that conditionally accepts or rejects requests based on an [async predicate].

This adapter produces a new service that passes each value through the given function predicate before sending it to self.

§Example
// A service taking a u32 as a request and returning Result<_, DbError>
let service = DatabaseService::new("127.0.0.1:8080");

/// Returns `true` if we should query the database for an ID.
async fn should_query(id: u32) -> bool {
    // ...
}

// Filter requests based on `should_query`.
let mut new_service = service
    .filter_async(|id: u32| async move {
        if should_query(id).await {
            return Ok(id);
        }

        Err(DbError::Rejected)
    });

// Call the new service
let id = 13;
let response = new_service
    .ready()
    .await?
    .call(id)
    .await;
source

fn then<F, Response, Error, Fut>(self, f: F) -> Then<Self, F>
where Self: Sized, Error: From<Self::Error>, F: FnOnce(Result<Self::Response, Self::Error>) -> Fut + Clone, Fut: Future<Output = Result<Response, Error>>,

Composes an asynchronous function after this service.

This takes a function or closure returning a future, and returns a new Service that chains that function after this service’s Future. The new Service’s future will consist of this service’s future, followed by the future returned by calling the chained function with the future’s Output type. The chained function is called regardless of whether this service’s future completes with a successful response or with an error.

This method can be thought of as an equivalent to the futures crate’s FutureExt::then combinator, but acting on Services that return futures, rather than on an individual future. Similarly to that combinator, ServiceExt::then can be used to implement asynchronous error recovery, by calling some asynchronous function with errors returned by this service. Alternatively, it may also be used to call a fallible async function with the successful response of this service.

This method can be used to change the Response type of the service into a different type. It can also be used to change the Error type of the service. However, because the then function is not applied to the errors returned by the service’s poll_ready method, it must be possible to convert the service’s Error type into the error type returned by the then future. This is trivial when the function returns the same error type as the service, but in other cases, it can be useful to use BoxError to erase differing error types.

§Examples
// A service returning Result<Record, DbError>
let service = DatabaseService::new("127.0.0.1:8080");

// An async function that attempts to recover from errors returned by the
// database.
async fn recover_from_error(error: DbError) -> Result<Record, DbError> {
    // ...
}

// If the database service returns an error, attempt to recover by
// calling `recover_from_error`. Otherwise, return the successful response.
let mut new_service = service.then(|result| async move {
    match result {
        Ok(record) => Ok(record),
        Err(e) => recover_from_error(e).await,
    }
});

// Call the new service
let id = 13;
let record = new_service
    .ready()
    .await?
    .call(id)
    .await?;
source

fn map_future<F, Fut, Response, Error>(self, f: F) -> MapFuture<Self, F>
where Self: Sized, F: FnMut(Self::Future) -> Fut, Error: From<Self::Error>, Fut: Future<Output = Result<Response, Error>>,

Composes a function that transforms futures produced by the service.

This takes a function or closure returning a future computed from the future returned by the service’s call method, as opposed to the responses produced by the future.

§Examples
use std::time::Duration;
use tokio::time::timeout;

// A service returning Result<Record, DbError>
let service = DatabaseService::new("127.0.0.1:8080");

let mut new_service = service.map_future(|future| async move {
    let res = timeout(Duration::from_secs(1), future).await?;
    Ok::<_, BoxError>(res)
});

// Call the new service
let id = 13;
let record = new_service
    .ready()
    .await?
    .call(id)
    .await?;

Note that normally you wouldn’t implement timeouts like this and instead use Timeout.

source

fn boxed(self) -> BoxService<Request, Self::Response, Self::Error>
where Self: Sized + Send + 'static, Self::Future: Send + 'static,

Convert the service into a Service + Send trait object.

See BoxService for more details.

If Self implements the Clone trait, the boxed_clone method can be used instead, to produce a boxed service which will also implement Clone.

§Example
use tower::{Service, ServiceExt, BoxError, service_fn, util::BoxService};

let service = service_fn(|req: Request| async {
    Ok::<_, BoxError>(Response::new())
});

let service: BoxService<Request, Response, BoxError> = service
    .map_request(|req| {
        println!("received request");
        req
    })
    .map_response(|res| {
        println!("response produced");
        res
    })
    .boxed();
source

fn boxed_clone(self) -> BoxCloneService<Request, Self::Response, Self::Error>
where Self: Clone + Sized + Send + 'static, Self::Future: Send + 'static,

Convert the service into a Service + Clone + Send trait object.

This is similar to the boxed method, but it requires that Self implement Clone, and the returned boxed service implements Clone. See BoxCloneService for more details.

§Example
use tower::{Service, ServiceExt, BoxError, service_fn, util::BoxCloneService};

let service = service_fn(|req: Request| async {
    Ok::<_, BoxError>(Response::new())
});

let service: BoxCloneService<Request, Response, BoxError> = service
    .map_request(|req| {
        println!("received request");
        req
    })
    .map_response(|res| {
        println!("response produced");
        res
    })
    .boxed_clone();

// The boxed service can still be cloned.
service.clone();

Implementors§

source§

impl<T, Request> ServiceExt<Request> for T
where T: Service<Request> + ?Sized,