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}