Skip to main content

hyper_util/client/pool/
map.rs

1//! Map pool utilities
2//!
3//! The map isn't a typical `Service`, but rather stand-alone type that can map
4//! requests to a key and  service factory. This is because the service is more
5//! of a router, and cannot determine which inner service to check for
6//! backpressure since it's not know until the request is made.
7//!
8//! The map implementation allows customization of extracting a key, and how to
9//! construct a MakeService for that key.
10//!
11//! # Example
12//!
13//! ```rust,ignore
14//! # async fn run() {
15//! # use hyper_util::client::pool;
16//! # let req = http::Request::new(());
17//! # let some_http1_connector = || {
18//! #     tower::service::service_fn(|_req| async { Ok::<_, &'static str>(()) })
19//! # };
20//! let mut map = pool::map::Map::builder()
21//!     .keys(|uri| (uri.scheme().clone(), uri.authority().clone()))
22//!     .values(|_uri| {
23//!         some_http1_connector()
24//!     })
25//!     .build();
26//!
27//! let resp = map.service(req.uri()).call(req).await;
28//! # }
29//! ```
30
31use std::collections::HashMap;
32
33// expose the documentation
34#[cfg(docsrs)]
35pub use self::builder::Builder;
36
37/// A map caching `MakeService`s per key.
38///
39/// Create one with the [`Map::builder()`].
40pub struct Map<T, Req>
41where
42    T: target::Target<Req>,
43{
44    map: HashMap<T::Key, T::Service>,
45    targeter: T,
46}
47
48// impl Map
49
50impl Map<builder::StartHere, builder::StartHere> {
51    #[allow(
52        rustdoc::broken_intra_doc_links,
53        reason = "this link is intended for docs.rs builds"
54    )]
55    /// Create a [`Builder`] to configure a new `Map`.
56    pub fn builder<Dst>() -> builder::Builder<Dst, builder::WantsKeyer, builder::WantsServiceMaker>
57    {
58        builder::Builder::new()
59    }
60}
61
62impl<T, Req> Map<T, Req>
63where
64    T: target::Target<Req>,
65{
66    fn new(targeter: T) -> Self {
67        Map {
68            map: HashMap::new(),
69            targeter,
70        }
71    }
72}
73
74impl<T, Req> Map<T, Req>
75where
76    T: target::Target<Req>,
77    T::Key: Eq + std::hash::Hash,
78{
79    /// Get a service after extracting the key from `req`.
80    pub fn service(&mut self, req: &Req) -> &mut T::Service {
81        let key = self.targeter.key(req);
82        self.map
83            .entry(key)
84            .or_insert_with(|| self.targeter.service(req))
85    }
86
87    /// Retains only the services specified by the predicate.
88    pub fn retain<F>(&mut self, predicate: F)
89    where
90        F: FnMut(&T::Key, &mut T::Service) -> bool,
91    {
92        self.map.retain(predicate);
93    }
94
95    /// Clears the map, removing all key-value pairs.
96    pub fn clear(&mut self) {
97        self.map.clear();
98    }
99}
100
101// sealed and unnameable for now
102mod target {
103    pub trait Target<Dst> {
104        type Key;
105        type Service;
106
107        fn key(&self, dst: &Dst) -> Self::Key;
108        fn service(&self, dst: &Dst) -> Self::Service;
109    }
110}
111
112// sealed and unnameable for now
113mod builder {
114    use std::marker::PhantomData;
115
116    /// A builder to configure a `Map`.
117    ///
118    /// # Unnameable
119    ///
120    /// This type is normally unnameable, forbidding naming of the type within
121    /// code. The type is exposed in the documentation to show which methods
122    /// can be publicly called.
123    pub struct Builder<Dst, K, S> {
124        _dst: PhantomData<fn(Dst)>,
125        keys: K,
126        svcs: S,
127    }
128
129    pub struct WantsKeyer;
130    pub struct WantsServiceMaker;
131
132    pub enum StartHere {}
133
134    pub struct Built<K, S> {
135        keys: K,
136        svcs: S,
137    }
138
139    impl<Dst> Builder<Dst, WantsKeyer, WantsServiceMaker> {
140        pub(super) fn new() -> Self {
141            Builder {
142                _dst: PhantomData,
143                keys: WantsKeyer,
144                svcs: WantsServiceMaker,
145            }
146        }
147    }
148
149    impl<Dst, S> Builder<Dst, WantsKeyer, S> {
150        /// Provide a closure that extracts a pool key for the destination.
151        pub fn keys<K, KK>(self, keyer: K) -> Builder<Dst, K, S>
152        where
153            K: Fn(&Dst) -> KK,
154        {
155            Builder {
156                _dst: PhantomData,
157                keys: keyer,
158                svcs: self.svcs,
159            }
160        }
161    }
162
163    impl<Dst, K> Builder<Dst, K, WantsServiceMaker> {
164        /// Provide a closure to create a new `MakeService` for the destination.
165        pub fn values<S, SS>(self, svcs: S) -> Builder<Dst, K, S>
166        where
167            S: Fn(&Dst) -> SS,
168        {
169            Builder {
170                _dst: PhantomData,
171                keys: self.keys,
172                svcs,
173            }
174        }
175    }
176
177    impl<Dst, K, S> Builder<Dst, K, S>
178    where
179        Built<K, S>: super::target::Target<Dst>,
180        <Built<K, S> as super::target::Target<Dst>>::Key: Eq + std::hash::Hash,
181    {
182        /// Build the `Map` pool.
183        pub fn build(self) -> super::Map<Built<K, S>, Dst> {
184            super::Map::new(Built {
185                keys: self.keys,
186                svcs: self.svcs,
187            })
188        }
189    }
190
191    impl super::target::Target<StartHere> for StartHere {
192        type Key = StartHere;
193        type Service = StartHere;
194
195        fn key(&self, _: &StartHere) -> Self::Key {
196            match *self {}
197        }
198
199        fn service(&self, _: &StartHere) -> Self::Service {
200            match *self {}
201        }
202    }
203
204    impl<K, KK, S, SS, Dst> super::target::Target<Dst> for Built<K, S>
205    where
206        K: Fn(&Dst) -> KK,
207        S: Fn(&Dst) -> SS,
208        KK: Eq + std::hash::Hash,
209    {
210        type Key = KK;
211        type Service = SS;
212
213        fn key(&self, dst: &Dst) -> Self::Key {
214            (self.keys)(dst)
215        }
216
217        fn service(&self, dst: &Dst) -> Self::Service {
218            (self.svcs)(dst)
219        }
220    }
221}
222
223#[cfg(test)]
224mod tests {
225    #[test]
226    fn smoke() {
227        let mut pool = super::Map::builder().keys(|_| "a").values(|_| "b").build();
228        pool.service(&"hello");
229    }
230}