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    /// # 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}