Source code
Revision control
Copy as Markdown
Other Tools
//! Map pool utilities
//!
//! The map isn't a typical `Service`, but rather stand-alone type that can map
//! requests to a key and service factory. This is because the service is more
//! of a router, and cannot determine which inner service to check for
//! backpressure since it's not know until the request is made.
//!
//! The map implementation allows customization of extracting a key, and how to
//! construct a MakeService for that key.
//!
//! # Example
//!
//! ```rust,ignore
//! # async fn run() {
//! # use hyper_util::client::pool;
//! # let req = http::Request::new(());
//! # let some_http1_connector = || {
//! # tower::service::service_fn(|_req| async { Ok::<_, &'static str>(()) })
//! # };
//! let mut map = pool::map::Map::builder()
//! .keys(|uri| (uri.scheme().clone(), uri.authority().clone()))
//! .values(|_uri| {
//! some_http1_connector()
//! })
//! .build();
//!
//! let resp = map.service(req.uri()).call(req).await;
//! # }
//! ```
use std::collections::HashMap;
// expose the documentation
#[cfg(docsrs)]
pub use self::builder::Builder;
/// A map caching `MakeService`s per key.
///
/// Create one with the [`Map::builder()`].
pub struct Map<T, Req>
where
T: target::Target<Req>,
{
map: HashMap<T::Key, T::Service>,
targeter: T,
}
// impl Map
impl Map<builder::StartHere, builder::StartHere> {
/// Create a [`Builder`] to configure a new `Map`.
pub fn builder<Dst>() -> builder::Builder<Dst, builder::WantsKeyer, builder::WantsServiceMaker>
{
builder::Builder::new()
}
}
impl<T, Req> Map<T, Req>
where
T: target::Target<Req>,
{
fn new(targeter: T) -> Self {
Map {
map: HashMap::new(),
targeter,
}
}
}
impl<T, Req> Map<T, Req>
where
T: target::Target<Req>,
T::Key: Eq + std::hash::Hash,
{
/// Get a service after extracting the key from `req`.
pub fn service(&mut self, req: &Req) -> &mut T::Service {
let key = self.targeter.key(req);
self.map
.entry(key)
.or_insert_with(|| self.targeter.service(req))
}
/// Retains only the services specified by the predicate.
pub fn retain<F>(&mut self, predicate: F)
where
F: FnMut(&T::Key, &mut T::Service) -> bool,
{
self.map.retain(predicate);
}
/// Clears the map, removing all key-value pairs.
pub fn clear(&mut self) {
self.map.clear();
}
}
// sealed and unnameable for now
mod target {
pub trait Target<Dst> {
type Key;
type Service;
fn key(&self, dst: &Dst) -> Self::Key;
fn service(&self, dst: &Dst) -> Self::Service;
}
}
// sealed and unnameable for now
mod builder {
use std::marker::PhantomData;
/// A builder to configure a `Map`.
///
/// # Unnameable
///
/// This type is normally unnameable, forbidding naming of the type within
/// code. The type is exposed in the documentation to show which methods
/// can be publicly called.
pub struct Builder<Dst, K, S> {
_dst: PhantomData<fn(Dst)>,
keys: K,
svcs: S,
}
pub struct WantsKeyer;
pub struct WantsServiceMaker;
pub enum StartHere {}
pub struct Built<K, S> {
keys: K,
svcs: S,
}
impl<Dst> Builder<Dst, WantsKeyer, WantsServiceMaker> {
pub(super) fn new() -> Self {
Builder {
_dst: PhantomData,
keys: WantsKeyer,
svcs: WantsServiceMaker,
}
}
}
impl<Dst, S> Builder<Dst, WantsKeyer, S> {
/// Provide a closure that extracts a pool key for the destination.
pub fn keys<K, KK>(self, keyer: K) -> Builder<Dst, K, S>
where
K: Fn(&Dst) -> KK,
{
Builder {
_dst: PhantomData,
keys: keyer,
svcs: self.svcs,
}
}
}
impl<Dst, K> Builder<Dst, K, WantsServiceMaker> {
/// Provide a closure to create a new `MakeService` for the destination.
pub fn values<S, SS>(self, svcs: S) -> Builder<Dst, K, S>
where
S: Fn(&Dst) -> SS,
{
Builder {
_dst: PhantomData,
keys: self.keys,
svcs,
}
}
}
impl<Dst, K, S> Builder<Dst, K, S>
where
Built<K, S>: super::target::Target<Dst>,
<Built<K, S> as super::target::Target<Dst>>::Key: Eq + std::hash::Hash,
{
/// Build the `Map` pool.
pub fn build(self) -> super::Map<Built<K, S>, Dst> {
super::Map::new(Built {
keys: self.keys,
svcs: self.svcs,
})
}
}
impl super::target::Target<StartHere> for StartHere {
type Key = StartHere;
type Service = StartHere;
fn key(&self, _: &StartHere) -> Self::Key {
match *self {}
}
fn service(&self, _: &StartHere) -> Self::Service {
match *self {}
}
}
impl<K, KK, S, SS, Dst> super::target::Target<Dst> for Built<K, S>
where
K: Fn(&Dst) -> KK,
S: Fn(&Dst) -> SS,
KK: Eq + std::hash::Hash,
{
type Key = KK;
type Service = SS;
fn key(&self, dst: &Dst) -> Self::Key {
(self.keys)(dst)
}
fn service(&self, dst: &Dst) -> Self::Service {
(self.svcs)(dst)
}
}
}
#[cfg(test)]
mod tests {
#[test]
fn smoke() {
let mut pool = super::Map::builder().keys(|_| "a").values(|_| "b").build();
pool.service(&"hello");
}
}