Skip to main content

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}