hyperlane_core/hook/impl.rs
1use super::*;
2
3/// A blanket implementation for any function that takes a `Context` and returns a value.
4///
5/// This implementation makes it easy to use any compatible function as a `FnContext`,
6/// promoting a flexible and functional programming style.
7impl<F, R> FnContext<R> for F where F: Fn(&mut Context) -> R + Send + Sync {}
8
9/// A blanket implementation for functions that return a pinned, boxed, sendable future.
10///
11/// This trait is a common pattern for asynchronous handlers in Rust, enabling type
12/// erasure and dynamic dispatch for futures. It is essential for storing different
13/// async functions in a collection.
14impl<F, T> FnContextPinBox<T> for F where F: FnContext<FutureBox<T>> {}
15
16/// A blanket implementation for static, sendable, synchronous functions that return a future.
17///
18/// This trait is used for handlers that are known at compile time, ensuring they
19/// are safe to be sent across threads and have a static lifetime. This is crucial
20/// for handlers that are part of the application's long-lived state.
21impl<F, Fut, T> FnContextStatic<Fut, T> for F
22where
23 F: FnContext<Fut> + 'static,
24 Fut: Future<Output = T> + Send,
25{
26}
27
28/// A blanket implementation for any future that is sendable and has a static lifetime.
29///
30/// This is a convenient trait for working with futures in an asynchronous context,
31/// ensuring that they can be safely managed by the async runtime across different
32/// threads.
33impl<T, R> FutureSendStatic<R> for T where T: Future<Output = R> + Send + 'static {}
34
35/// Blanket implementation of `FutureSend` for any type that satisfies the bounds.
36impl<T, O> FutureSend<O> for T where T: Future<Output = O> + Send {}
37
38/// Blanket implementation of `FutureFn` for any type that satisfies the bounds.
39impl<T, O> FutureFn<O> for T where T: Fn() -> FutureBox<O> + Send + Sync {}
40
41/// Provides a default implementation for `ServerControlHook`.
42impl Default for ServerControlHook {
43 /// Creates a new `ServerControlHook` instance with default no-op hooks.
44 ///
45 /// The default `wait_hook` and `shutdown_hook` do nothing, allowing the server
46 /// to run without specific shutdown or wait logic unless configured otherwise.
47 ///
48 /// # Returns
49 ///
50 /// - `Self` - A new instance with default hooks.
51 #[inline(always)]
52 fn default() -> Self {
53 Self {
54 wait_hook: Hook::default_control_handler(),
55 shutdown_hook: Hook::default_control_handler(),
56 }
57 }
58}
59
60/// Manages server lifecycle hooks, including waiting and shutdown procedures.
61///
62/// This struct holds closures that are executed during specific server lifecycle events.
63impl ServerControlHook {
64 /// Waits for the server's shutdown signal or completion.
65 ///
66 /// This method asynchronously waits until the server's `wait_hook` is triggered,
67 /// typically indicating that the server has finished its operations or is ready to shut down.
68 pub async fn wait(&self) {
69 self.get_wait_hook()().await;
70 }
71
72 /// Initiates the server shutdown process.
73 ///
74 /// This method asynchronously calls the `shutdown_hook`, which is responsible for
75 /// performing any necessary cleanup or graceful shutdown procedures.
76 pub async fn shutdown(&self) {
77 self.get_shutdown_hook()().await;
78 }
79}
80
81/// Factory and utility functions for creating hook handlers.
82///
83/// This impl block groups semantically related factory methods that create
84/// various hook handler types used throughout the server lifecycle.
85impl Hook {
86 /// Creates a default `ServerControlHookHandler` instance with default no-op hooks.
87 ///
88 /// The default `wait_hook` and `shutdown_hook` do nothing, allowing the server
89 /// to run without specific shutdown or wait logic unless configured otherwise.
90 ///
91 /// # Returns
92 ///
93 /// - `ServerControlHookHandler<()>` - A default `ServerControlHookHandler<()>` instance.
94 #[inline(always)]
95 pub fn default_control_handler() -> ServerControlHookHandler<()> {
96 Arc::new(|| Box::pin(async {}))
97 }
98
99 /// Creates a default `ServerHookHandler` from a trait object.
100 ///
101 /// # Returns
102 ///
103 /// - `ServerHookHandler` - A default `ServerHookHandler` instance.
104 #[inline(always)]
105 pub fn default_handler() -> ServerHookHandler {
106 Arc::new(|_: &mut Stream, _: &mut Context| -> FutureBox<Status> {
107 Box::pin(async move { Status::default() })
108 })
109 }
110
111 /// Creates a new `ServerHookHandler` from a trait object.
112 ///
113 /// The hook implementation is chosen by the caller through the type
114 /// parameter `R`, so this function takes no runtime argument.
115 ///
116 /// # Returns
117 ///
118 /// - `ServerHookHandler` - A new `ServerHookHandler` instance.
119 #[inline(always)]
120 pub fn factory<R>() -> ServerHookHandler
121 where
122 R: ServerHook,
123 {
124 Arc::new(
125 move |stream: &mut Stream, ctx: &mut Context| -> FutureBox<Status> {
126 let ctx_address: usize = ctx.into();
127 let stream_address: usize = stream.into();
128 Box::pin(async move {
129 let ctx: &mut Context = ctx_address.into();
130 let stream: &mut Stream = stream_address.into();
131 R::new(stream, ctx).await.handle(stream, ctx).await
132 })
133 },
134 )
135 }
136}
137
138/// Implements the `PartialEq` trait for `HookType`.
139///
140/// This allows for comparing two `HookType` instances for equality.
141/// Function pointers are compared using `std::ptr::fn_addr_eq` for reliable comparison.
142impl PartialEq for HookType {
143 /// Checks if two `HookType` instances are equal.
144 ///
145 /// # Arguments
146 ///
147 /// - `&Self` - The other `HookType` instance to compare against.
148 ///
149 /// # Returns
150 ///
151 /// - `bool` - `true` if the instances are equal, `false` otherwise.
152 #[inline(always)]
153 fn eq(&self, other: &Self) -> bool {
154 match (self, other) {
155 (HookType::TaskPanic(order1, factory1), HookType::TaskPanic(order2, factory2)) => {
156 order1 == order2 && ptr::fn_addr_eq(*factory1, *factory2)
157 }
158 (
159 HookType::RequestError(order1, factory1),
160 HookType::RequestError(order2, factory2),
161 ) => order1 == order2 && ptr::fn_addr_eq(*factory1, *factory2),
162 (
163 HookType::RequestMiddleware(order1, factory1),
164 HookType::RequestMiddleware(order2, factory2),
165 ) => order1 == order2 && ptr::fn_addr_eq(*factory1, *factory2),
166 (HookType::Route(path1, factory1), HookType::Route(path2, factory2)) => {
167 path1 == path2 && ptr::fn_addr_eq(*factory1, *factory2)
168 }
169 (
170 HookType::ResponseMiddleware(order1, factory1),
171 HookType::ResponseMiddleware(order2, factory2),
172 ) => order1 == order2 && ptr::fn_addr_eq(*factory1, *factory2),
173 _ => false,
174 }
175 }
176}
177
178/// Implements the `Eq` trait for `HookType`.
179///
180/// This indicates that `HookType` has a total equality relation.
181impl Eq for HookType {}
182
183/// Implements the `Hash` trait for `HookType`.
184///
185/// This allows `HookType` to be used as a key in hash-based collections.
186/// Function pointers are hashed using their addresses.
187impl Hash for HookType {
188 /// Hashes the `HookType` instance.
189 ///
190 /// # Arguments
191 ///
192 /// - `&mut H` - The hasher to use.
193 #[inline]
194 fn hash<H: Hasher>(&self, state: &mut H) {
195 match self {
196 HookType::TaskPanic(order, factory) => {
197 0u8.hash(state);
198 order.hash(state);
199 (factory as *const fn() -> ServerHookHandler).hash(state);
200 }
201 HookType::RequestError(order, factory) => {
202 1u8.hash(state);
203 order.hash(state);
204 (factory as *const fn() -> ServerHookHandler).hash(state);
205 }
206 HookType::RequestMiddleware(order, factory) => {
207 2u8.hash(state);
208 order.hash(state);
209 (factory as *const fn() -> ServerHookHandler).hash(state);
210 }
211 HookType::Route(path, factory) => {
212 3u8.hash(state);
213 path.hash(state);
214 (factory as *const fn() -> ServerHookHandler).hash(state);
215 }
216 HookType::ResponseMiddleware(order, factory) => {
217 4u8.hash(state);
218 order.hash(state);
219 (factory as *const fn() -> ServerHookHandler).hash(state);
220 }
221 }
222 }
223}
224
225/// Implementation block for `HookType`.
226///
227/// This block defines utility methods associated with the `HookType` enum.
228/// These methods provide additional functionality for working with hooks,
229/// such as extracting the execution order (priority) used in duplicate checks.
230impl HookType {
231 /// Returns the optional execution priority (`order`) of a hook.
232 ///
233 /// Hooks that carry an `order` indicate their execution priority.
234 /// Hooks without an `order` are considered unordered and are ignored in duplicate checks.
235 ///
236 /// # Returns
237 ///
238 /// - `Option<isize>` - `Some(order)` if the hook defines a priority, otherwise `None`.
239 #[inline(always)]
240 pub fn try_get_order(&self) -> Option<isize> {
241 match *self {
242 HookType::RequestMiddleware(order, _)
243 | HookType::ResponseMiddleware(order, _)
244 | HookType::TaskPanic(order, _)
245 | HookType::RequestError(order, _) => order,
246 _ => None,
247 }
248 }
249
250 /// Returns the hook factory carried by this `HookType`, when it has one.
251 ///
252 /// Route hooks store a pattern instead of a factory, so `None` is
253 /// returned for them.
254 ///
255 /// # Returns
256 ///
257 /// - `Option<ServerHookHandlerFactory>` - `Some(factory)` for the hook
258 /// variants that carry one, otherwise `None`.
259 #[inline(always)]
260 pub fn try_get_hook(&self) -> Option<ServerHookHandlerFactory> {
261 match *self {
262 HookType::RequestMiddleware(_, hook)
263 | HookType::ResponseMiddleware(_, hook)
264 | HookType::TaskPanic(_, hook)
265 | HookType::RequestError(_, hook) => Some(hook),
266 _ => None,
267 }
268 }
269
270 /// Verifies that hooks with the same type and execution priority are unique.
271 ///
272 /// This function validates that no two hooks of the same type have identical
273 /// execution priorities (orders). Only hooks that define an explicit priority
274 /// (non-None order) are checked for uniqueness. Hooks without a priority are
275 /// ignored in duplicate detection.
276 ///
277 /// # Arguments
278 ///
279 /// - `Vec<HookType>` - A vector of `HookType` instances to validate for uniqueness.
280 ///
281 /// # Panics
282 ///
283 /// - Panics if duplicate hooks are detected with the same type and priority,
284 /// displaying the hook type and order in the error message.
285 #[inline(always)]
286 pub fn assert_unique_order(list: Vec<HookType>) {
287 let mut seen: HashSet<(HookType, isize)> = HashSet::new();
288 list.iter().for_each(|hook: &HookType| {
289 if let Some(order) = hook.try_get_order()
290 && !seen.insert((*hook, order))
291 {
292 panic!("Duplicate hook detected: {} with order {}", hook, order);
293 }
294 });
295 }
296}
297
298/// Implements `ServerHook` for `DefaultServerHook`
299///
300/// This implementation provides default no-op handlers for server hook operations.
301impl ServerHook for DefaultServerHook {
302 /// Creates a new `DefaultServerHook` instance.
303 ///
304 /// # Arguments
305 ///
306 /// - `&mut Stream` - The stream object providing server configuration and state
307 /// - `&mut Context` - The context object providing server configuration and state
308 ///
309 /// # Returns
310 ///
311 /// - `Self` - A new instance of `DefaultServerHook`
312 async fn new(_: &mut Stream, _: &mut Context) -> Self {
313 Self
314 }
315
316 /// Handles server hook operations with a no-op implementation.
317 ///
318 /// # Arguments
319 ///
320 /// - `&mut Stream` - The stream object providing server configuration and state
321 /// - `&mut Context` - The context object providing server configuration and state
322 ///
323 /// # Returns
324 ///
325 /// - `Status` - `Status::Reject` by default, indicating the pipeline should be aborted.
326 async fn handle(self, _: &mut Stream, _: &mut Context) -> Status {
327 Status::default()
328 }
329}