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    /// Create a [`Builder`] to configure a new `Map`.
52    pub fn builder<Dst>() -> builder::Builder<Dst, builder::WantsKeyer, builder::WantsServiceMaker>
53    {
54        builder::Builder::new()
55    }
56}
57
58impl<T, Req> Map<T, Req>
59where
60    T: target::Target<Req>,
61{
62    fn new(targeter: T) -> Self {
63        Map {
64            map: HashMap::new(),
65            targeter,
66        }
67    }
68}
69
70impl<T, Req> Map<T, Req>
71where
72    T: target::Target<Req>,
73    T::Key: Eq + std::hash::Hash,
74{
75    /// Get a service after extracting the key from `req`.
76    pub fn service(&mut self, req: &Req) -> &mut T::Service {
77        let key = self.targeter.key(req);
78        self.map
79            .entry(key)
80            .or_insert_with(|| self.targeter.service(req))
81    }
82
83    /// Retains only the services specified by the predicate.
84    pub fn retain<F>(&mut self, predicate: F)
85    where
86        F: FnMut(&T::Key, &mut T::Service) -> bool,
87    {
88        self.map.retain(predicate);
89    }
90
91    /// Clears the map, removing all key-value pairs.
92    pub fn clear(&mut self) {
93        self.map.clear();
94    }
95}
96
97// sealed and unnameable for now
98mod target {
99    pub trait Target<Dst> {
100        type Key;
101        type Service;
102
103        fn key(&self, dst: &Dst) -> Self::Key;
104        fn service(&self, dst: &Dst) -> Self::Service;
105    }
106}
107
108// sealed and unnameable for now
109mod builder {
110    use std::marker::PhantomData;
111
112    /// A builder to configure a `Map`.
113    ///
114    /// # Unnameable
115    ///
116    /// This type is normally unnameable, forbidding naming of the type within
117    /// code. The type is exposed in the documentation to show which methods
118    /// can be publicly called.
119    pub struct Builder<Dst, K, S> {
120        _dst: PhantomData<fn(Dst)>,
121        keys: K,
122        svcs: S,
123    }
124
125    pub struct WantsKeyer;
126    pub struct WantsServiceMaker;
127
128    pub enum StartHere {}
129
130    pub struct Built<K, S> {
131        keys: K,
132        svcs: S,
133    }
134
135    impl<Dst> Builder<Dst, WantsKeyer, WantsServiceMaker> {
136        pub(super) fn new() -> Self {
137            Builder {
138                _dst: PhantomData,
139                keys: WantsKeyer,
140                svcs: WantsServiceMaker,
141            }
142        }
143    }
144
145    impl<Dst, S> Builder<Dst, WantsKeyer, S> {
146        /// Provide a closure that extracts a pool key for the destination.
147        pub fn keys<K, KK>(self, keyer: K) -> Builder<Dst, K, S>
148        where
149            K: Fn(&Dst) -> KK,
150        {
151            Builder {
152                _dst: PhantomData,
153                keys: keyer,
154                svcs: self.svcs,
155            }
156        }
157    }
158
159    impl<Dst, K> Builder<Dst, K, WantsServiceMaker> {
160        /// Provide a closure to create a new `MakeService` for the destination.
161        pub fn values<S, SS>(self, svcs: S) -> Builder<Dst, K, S>
162        where
163            S: Fn(&Dst) -> SS,
164        {
165            Builder {
166                _dst: PhantomData,
167                keys: self.keys,
168                svcs,
169            }
170        }
171    }
172
173    impl<Dst, K, S> Builder<Dst, K, S>
174    where
175        Built<K, S>: super::target::Target<Dst>,
176        <Built<K, S> as super::target::Target<Dst>>::Key: Eq + std::hash::Hash,
177    {
178        /// Build the `Map` pool.
179        pub fn build(self) -> super::Map<Built<K, S>, Dst> {
180            super::Map::new(Built {
181                keys: self.keys,
182                svcs: self.svcs,
183            })
184        }
185    }
186
187    impl super::target::Target<StartHere> for StartHere {
188        type Key = StartHere;
189        type Service = StartHere;
190
191        fn key(&self, _: &StartHere) -> Self::Key {
192            match *self {}
193        }
194
195        fn service(&self, _: &StartHere) -> Self::Service {
196            match *self {}
197        }
198    }
199
200    impl<K, KK, S, SS, Dst> super::target::Target<Dst> for Built<K, S>
201    where
202        K: Fn(&Dst) -> KK,
203        S: Fn(&Dst) -> SS,
204        KK: Eq + std::hash::Hash,
205    {
206        type Key = KK;
207        type Service = SS;
208
209        fn key(&self, dst: &Dst) -> Self::Key {
210            (self.keys)(dst)
211        }
212
213        fn service(&self, dst: &Dst) -> Self::Service {
214            (self.svcs)(dst)
215        }
216    }
217}
218
219#[cfg(test)]
220mod tests {
221    #[test]
222    fn smoke() {
223        let mut pool = super::Map::builder().keys(|_| "a").values(|_| "b").build();
224        pool.service(&"hello");
225    }
226}