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 /// # Arguments
114 ///
115 /// - `ServerHook` - The trait object implementing `ServerHook`.
116 ///
117 /// # Returns
118 ///
119 /// - `ServerHookHandler` - A new `ServerHookHandler` instance.
120 #[inline(always)]
121 pub fn factory<R>() -> ServerHookHandler
122 where
123 R: ServerHook,
124 {
125 Arc::new(
126 move |stream: &mut Stream, ctx: &mut Context| -> FutureBox<Status> {
127 let ctx_address: usize = ctx.into();
128 let stream_address: usize = stream.into();
129 Box::pin(async move {
130 let ctx: &mut Context = ctx_address.into();
131 let stream: &mut Stream = stream_address.into();
132 R::new(stream, ctx).await.handle(stream, ctx).await
133 })
134 },
135 )
136 }
137}
138
139/// Implements the `PartialEq` trait for `HookType`.
140///
141/// This allows for comparing two `HookType` instances for equality.
142/// Function pointers are compared using `std::ptr::fn_addr_eq` for reliable comparison.
143impl PartialEq for HookType {
144 /// Checks if two `HookType` instances are equal.
145 ///
146 /// # Arguments
147 ///
148 /// - `&Self` - The other `HookType` instance to compare against.
149 ///
150 /// # Returns
151 ///
152 /// - `bool` - `true` if the instances are equal, `false` otherwise.
153 #[inline(always)]
154 fn eq(&self, other: &Self) -> bool {
155 match (self, other) {
156 (HookType::TaskPanic(order1, factory1), HookType::TaskPanic(order2, factory2)) => {
157 order1 == order2 && std::ptr::fn_addr_eq(*factory1, *factory2)
158 }
159 (
160 HookType::RequestError(order1, factory1),
161 HookType::RequestError(order2, factory2),
162 ) => order1 == order2 && std::ptr::fn_addr_eq(*factory1, *factory2),
163 (
164 HookType::RequestMiddleware(order1, factory1),
165 HookType::RequestMiddleware(order2, factory2),
166 ) => order1 == order2 && std::ptr::fn_addr_eq(*factory1, *factory2),
167 (HookType::Route(path1, factory1), HookType::Route(path2, factory2)) => {
168 path1 == path2 && std::ptr::fn_addr_eq(*factory1, *factory2)
169 }
170 (
171 HookType::ResponseMiddleware(order1, factory1),
172 HookType::ResponseMiddleware(order2, factory2),
173 ) => order1 == order2 && std::ptr::fn_addr_eq(*factory1, *factory2),
174 _ => false,
175 }
176 }
177}
178
179/// Implements the `Eq` trait for `HookType`.
180///
181/// This indicates that `HookType` has a total equality relation.
182impl Eq for HookType {}
183
184/// Implements the `Hash` trait for `HookType`.
185///
186/// This allows `HookType` to be used as a key in hash-based collections.
187/// Function pointers are hashed using their addresses.
188impl Hash for HookType {
189 /// Hashes the `HookType` instance.
190 ///
191 /// # Arguments
192 ///
193 /// - `&mut Hasher` - The hasher to use.
194 #[inline]
195 fn hash<H: Hasher>(&self, state: &mut H) {
196 match self {
197 HookType::TaskPanic(order, factory) => {
198 0u8.hash(state);
199 order.hash(state);
200 (factory as *const fn() -> ServerHookHandler).hash(state);
201 }
202 HookType::RequestError(order, factory) => {
203 1u8.hash(state);
204 order.hash(state);
205 (factory as *const fn() -> ServerHookHandler).hash(state);
206 }
207 HookType::RequestMiddleware(order, factory) => {
208 2u8.hash(state);
209 order.hash(state);
210 (factory as *const fn() -> ServerHookHandler).hash(state);
211 }
212 HookType::Route(path, factory) => {
213 3u8.hash(state);
214 path.hash(state);
215 (factory as *const fn() -> ServerHookHandler).hash(state);
216 }
217 HookType::ResponseMiddleware(order, factory) => {
218 4u8.hash(state);
219 order.hash(state);
220 (factory as *const fn() -> ServerHookHandler).hash(state);
221 }
222 }
223 }
224}
225
226/// Implementation block for `HookType`.
227///
228/// This block defines utility methods associated with the `HookType` enum.
229/// These methods provide additional functionality for working with hooks,
230/// such as extracting the execution order (priority) used in duplicate checks.
231impl HookType {
232 /// Returns the optional execution priority (`order`) of a hook.
233 ///
234 /// Hooks that carry an `order` indicate their execution priority.
235 /// Hooks without an `order` are considered unordered and are ignored in duplicate checks.
236 ///
237 /// # Returns
238 ///
239 /// - `Option<isize>` - `Some(order)` if the hook defines a priority, otherwise `None`.
240 #[inline(always)]
241 pub fn try_get_order(&self) -> Option<isize> {
242 match *self {
243 HookType::RequestMiddleware(order, _)
244 | HookType::ResponseMiddleware(order, _)
245 | HookType::TaskPanic(order, _)
246 | HookType::RequestError(order, _) => order,
247 _ => None,
248 }
249 }
250
251 #[inline(always)]
252 pub fn try_get_hook(&self) -> Option<ServerHookHandlerFactory> {
253 match *self {
254 HookType::RequestMiddleware(_, hook)
255 | HookType::ResponseMiddleware(_, hook)
256 | HookType::TaskPanic(_, hook)
257 | HookType::RequestError(_, hook) => Some(hook),
258 _ => None,
259 }
260 }
261
262 /// Verifies that hooks with the same type and execution priority are unique.
263 ///
264 /// This function validates that no two hooks of the same type have identical
265 /// execution priorities (orders). Only hooks that define an explicit priority
266 /// (non-None order) are checked for uniqueness. Hooks without a priority are
267 /// ignored in duplicate detection.
268 ///
269 /// # Arguments
270 ///
271 /// - `Vec<HookType>` - A vector of `HookType` instances to validate for uniqueness.
272 ///
273 /// # Panics
274 ///
275 /// - Panics if duplicate hooks are detected with the same type and priority,
276 /// displaying the hook type and order in the error message.
277 #[inline(always)]
278 pub fn assert_unique_order(list: Vec<HookType>) {
279 let mut seen: HashSet<(HookType, isize)> = HashSet::new();
280 list.iter().for_each(|hook: &HookType| {
281 if let Some(order) = hook.try_get_order()
282 && !seen.insert((*hook, order))
283 {
284 panic!("Duplicate hook detected: {} with order {}", hook, order);
285 }
286 });
287 }
288}
289
290/// Implements `ServerHook` for `DefaultServerHook`
291///
292/// This implementation provides default no-op handlers for server hook operations.
293impl ServerHook for DefaultServerHook {
294 /// Creates a new `DefaultServerHook` instance.
295 ///
296 /// # Arguments
297 ///
298 /// - `&mut Stream` - The stream object providing server configuration and state
299 /// - `&mut Context` - The context object providing server configuration and state
300 ///
301 /// # Returns
302 ///
303 /// - `Self` - A new instance of `DefaultServerHook`
304 async fn new(_: &mut Stream, _: &mut Context) -> Self {
305 Self
306 }
307
308 /// Handles server hook operations with a no-op implementation.
309 ///
310 /// # Arguments
311 ///
312 /// - `&mut Stream` - The stream object providing server configuration and state
313 /// - `&mut Context` - The context object providing server configuration and state
314 ///
315 /// # Returns
316 ///
317 /// - `Status` - `Status::Reject` by default, indicating the pipeline should be aborted.
318 async fn handle(self, _: &mut Stream, _: &mut Context) -> Status {
319 Status::default()
320 }
321}