Skip to main content

hexeract_core/
registration.rs

1//! Handler registration metadata collected at link time by the `#[handler]`
2//! procedural macro from `hexeract-macros`.
3//!
4//! Each expansion of `#[handler]` emits an
5//! [`inventory::submit!`] producing a [`HandlerRegistration`] entry. The
6//! Hexeract mediator iterates the collected entries through
7//! `inventory::iter::<HandlerRegistration>` (see
8//! `MediatorBuilder::verify_handlers`) to detect handlers that were
9//! declared with the macro but never wired into the registry.
10//!
11//! The macro does not auto-populate the registry; handlers must still be
12//! registered explicitly via the fluent builder so that stateful handlers
13//! (database pools, configuration) can be constructed by the caller.
14
15use inventory;
16
17/// Kind of handler described by a [`HandlerRegistration`].
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19pub enum HandlerKind {
20    /// `CommandHandler<C>` registration.
21    Command,
22    /// `QueryHandler<Q>` registration.
23    Query,
24    /// `NotificationHandler<N>` registration.
25    Notification,
26}
27
28/// One handler discovered at link time by the `#[handler]` macro.
29///
30/// `message_type_name` and `handler_type_name` are function pointers to
31/// monomorphized [`core::any::type_name`] invocations. The pointer form
32/// is used because `type_name::<T>` is not yet a `const fn`, while
33/// [`inventory::submit!`] requires a const-initialized static. Callers
34/// resolve the name by calling the pointer:
35/// `(reg.message_type_name)()`.
36#[derive(Debug, Clone, Copy)]
37pub struct HandlerRegistration {
38    /// Kind of handler.
39    pub kind: HandlerKind,
40    /// Resolves to the fully-qualified type name of the message type.
41    pub message_type_name: fn() -> &'static str,
42    /// Resolves to the fully-qualified type name of the handler type.
43    pub handler_type_name: fn() -> &'static str,
44}
45
46inventory::collect!(HandlerRegistration);
47
48#[doc(hidden)]
49pub mod __private {
50    pub use inventory;
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56
57    fn test_cmd_name() -> &'static str {
58        "hexeract_core::registration::tests::TestCmd"
59    }
60
61    fn test_handler_name() -> &'static str {
62        "hexeract_core::registration::tests::TestHandler"
63    }
64
65    inventory::submit!(HandlerRegistration {
66        kind: HandlerKind::Command,
67        message_type_name: test_cmd_name,
68        handler_type_name: test_handler_name,
69    });
70
71    #[test]
72    fn inventory_collects_registrations_submitted_in_tests() {
73        let found = inventory::iter::<HandlerRegistration>
74            .into_iter()
75            .any(|r| (r.message_type_name)().ends_with("::TestCmd"));
76        assert!(
77            found,
78            "registration submitted from the tests module must be visible to inventory::iter",
79        );
80    }
81
82    #[test]
83    fn handler_kind_is_copyable_and_comparable() {
84        let a = HandlerKind::Command;
85        let b = a;
86        assert_eq!(a, b);
87        assert_ne!(HandlerKind::Command, HandlerKind::Query);
88    }
89}