1use std::{any::Any, fmt::Debug, marker::PhantomData, rc::Rc};
22
23use nautilus_core::UUID4;
24use ustr::Ustr;
25
26pub trait Handler<T: ?Sized>: 'static {
31 fn id(&self) -> Ustr;
33
34 fn handle(&self, message: &T);
36}
37
38impl<T: ?Sized, H: Handler<T>> Handler<T> for Rc<H> {
39 fn id(&self) -> Ustr {
40 (**self).id()
41 }
42
43 fn handle(&self, message: &T) {
44 (**self).handle(message);
45 }
46}
47
48pub struct TypedHandler<T: 'static + ?Sized>(pub Rc<dyn Handler<T>>);
59
60impl<T: 'static + ?Sized> Clone for TypedHandler<T> {
61 fn clone(&self) -> Self {
62 Self(Rc::clone(&self.0))
63 }
64}
65
66impl<T: 'static + ?Sized> TypedHandler<T> {
67 pub fn id(&self) -> Ustr {
69 self.0.id()
70 }
71
72 pub fn handle(&self, message: &T) {
74 self.0.handle(message);
75 }
76}
77
78impl<T: 'static> TypedHandler<T> {
79 pub fn new<H: Handler<T>>(handler: H) -> Self {
81 Self(Rc::new(handler))
82 }
83
84 pub fn from<F>(callback: F) -> Self
86 where
87 F: Fn(&T) + 'static,
88 {
89 Self::new(CallbackHandler::new(None::<&str>, callback))
90 }
91
92 pub fn from_with_id<S: AsRef<str>, F>(id: S, callback: F) -> Self
94 where
95 F: Fn(&T) + 'static,
96 {
97 Self::new(CallbackHandler::new(Some(id), callback))
98 }
99}
100
101impl<T: 'static + ?Sized> Debug for TypedHandler<T> {
102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 f.debug_struct(stringify!(TypedHandler))
104 .field("id", &self.0.id())
105 .field("type", &std::any::type_name::<T>())
106 .finish()
107 }
108}
109
110impl<T: 'static + ?Sized> PartialEq for TypedHandler<T> {
111 fn eq(&self, other: &Self) -> bool {
112 self.0.id() == other.0.id()
113 }
114}
115
116impl<T: 'static + ?Sized> Eq for TypedHandler<T> {}
117
118impl<T: 'static + ?Sized> std::hash::Hash for TypedHandler<T> {
119 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
120 self.0.id().hash(state);
121 }
122}
123
124impl From<Rc<dyn Handler<dyn Any>>> for TypedHandler<dyn Any> {
125 fn from(handler: Rc<dyn Handler<dyn Any>>) -> Self {
126 Self(handler)
127 }
128}
129
130pub type ShareableMessageHandler = TypedHandler<dyn Any>;
135
136#[must_use]
138pub fn shareable_handler(handler: Rc<dyn Handler<dyn Any>>) -> ShareableMessageHandler {
139 TypedHandler(handler)
140}
141
142impl ShareableMessageHandler {
143 pub fn from_typed<T, F>(f: F) -> Self
148 where
149 T: 'static,
150 F: Fn(&T) + 'static,
151 {
152 TypedHandler(Rc::new(DowncastingHandler::new(f)))
153 }
154
155 pub fn from_any<F>(f: F) -> Self
157 where
158 F: Fn(&dyn Any) + 'static,
159 {
160 TypedHandler(Rc::new(AnyCallbackHandler::new(f)))
161 }
162}
163
164struct DowncastingHandler<T, F: Fn(&T)> {
166 id: Ustr,
167 callback: F,
168 _marker: PhantomData<T>,
169}
170
171impl<T: 'static, F: Fn(&T) + 'static> DowncastingHandler<T, F> {
172 fn new(callback: F) -> Self {
173 Self {
174 id: generate_handler_id(&callback),
175 callback,
176 _marker: PhantomData,
177 }
178 }
179}
180
181impl<T: 'static, F: Fn(&T) + 'static> Handler<dyn Any> for DowncastingHandler<T, F> {
182 fn id(&self) -> Ustr {
183 self.id
184 }
185
186 fn handle(&self, message: &dyn Any) {
187 if let Some(typed_msg) = message.downcast_ref::<T>() {
188 (self.callback)(typed_msg);
189 } else {
190 log::error!(
191 "DowncastingHandler downcast failed: expected {} got {:?}",
192 std::any::type_name::<T>(),
193 message.type_id()
194 );
195 }
196 }
197}
198
199struct AnyCallbackHandler<F: Fn(&dyn Any)> {
201 id: Ustr,
202 callback: F,
203}
204
205impl<F: Fn(&dyn Any) + 'static> AnyCallbackHandler<F> {
206 fn new(callback: F) -> Self {
207 Self {
208 id: generate_handler_id(&callback),
209 callback,
210 }
211 }
212}
213
214impl<F: Fn(&dyn Any) + 'static> Handler<dyn Any> for AnyCallbackHandler<F> {
215 fn id(&self) -> Ustr {
216 self.id
217 }
218
219 fn handle(&self, message: &dyn Any) {
220 (self.callback)(message);
221 }
222}
223
224pub struct CallbackHandler<T, F: Fn(&T)> {
229 id: Ustr,
230 callback: F,
231 _marker: PhantomData<T>,
232}
233
234impl<T: 'static, F: Fn(&T) + 'static> CallbackHandler<T, F> {
235 pub fn new<S: AsRef<str>>(id: Option<S>, callback: F) -> Self {
237 let id_ustr = id.map_or_else(
238 || generate_handler_id(&callback),
239 |s| Ustr::from(s.as_ref()),
240 );
241
242 Self {
243 id: id_ustr,
244 callback,
245 _marker: PhantomData,
246 }
247 }
248}
249
250impl<T: 'static, F: Fn(&T) + 'static> Handler<T> for CallbackHandler<T, F> {
251 fn id(&self) -> Ustr {
252 self.id
253 }
254
255 fn handle(&self, message: &T) {
256 (self.callback)(message);
257 }
258}
259
260impl<T, F: Fn(&T)> Debug for CallbackHandler<T, F> {
261 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
262 f.debug_struct(stringify!(CallbackHandler))
263 .field("id", &self.id)
264 .field("type", &std::any::type_name::<T>())
265 .finish()
266 }
267}
268
269fn generate_handler_id<F: ?Sized>(callback: &F) -> Ustr {
270 let callback_ptr = std::ptr::from_ref(callback);
271 let uuid = UUID4::new();
272 Ustr::from(&format!("<{callback_ptr:?}>-{uuid}"))
273}
274
275pub trait IntoHandler<T>: 'static {
281 fn id(&self) -> Ustr;
283
284 fn handle(&self, message: T);
286}
287
288impl<T, H: IntoHandler<T>> IntoHandler<T> for Rc<H> {
289 fn id(&self) -> Ustr {
290 (**self).id()
291 }
292
293 fn handle(&self, message: T) {
294 (**self).handle(message);
295 }
296}
297
298pub struct TypedIntoHandler<T: 'static>(pub Rc<dyn IntoHandler<T>>);
309
310impl<T: 'static> Clone for TypedIntoHandler<T> {
311 fn clone(&self) -> Self {
312 Self(Rc::clone(&self.0))
313 }
314}
315
316impl<T: 'static> TypedIntoHandler<T> {
317 pub fn new<H: IntoHandler<T>>(handler: H) -> Self {
319 Self(Rc::new(handler))
320 }
321
322 pub fn from<F>(callback: F) -> Self
324 where
325 F: Fn(T) + 'static,
326 {
327 Self::new(IntoCallbackHandler::new(None::<&str>, callback))
328 }
329
330 pub fn from_with_id<S: AsRef<str>, F>(id: S, callback: F) -> Self
332 where
333 F: Fn(T) + 'static,
334 {
335 Self::new(IntoCallbackHandler::new(Some(id), callback))
336 }
337
338 pub fn id(&self) -> Ustr {
340 self.0.id()
341 }
342
343 pub fn handle(&self, message: T) {
345 self.0.handle(message);
346 }
347}
348
349impl<T: 'static> Debug for TypedIntoHandler<T> {
350 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
351 f.debug_struct(stringify!(TypedIntoHandler))
352 .field("id", &self.0.id())
353 .field("type", &std::any::type_name::<T>())
354 .finish()
355 }
356}
357
358impl<T: 'static> PartialEq for TypedIntoHandler<T> {
359 fn eq(&self, other: &Self) -> bool {
360 self.0.id() == other.0.id()
361 }
362}
363
364impl<T: 'static> Eq for TypedIntoHandler<T> {}
365
366impl<T: 'static> std::hash::Hash for TypedIntoHandler<T> {
367 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
368 self.0.id().hash(state);
369 }
370}
371
372pub struct IntoCallbackHandler<T, F: Fn(T)> {
376 id: Ustr,
377 callback: F,
378 _marker: PhantomData<T>,
379}
380
381impl<T: 'static, F: Fn(T) + 'static> IntoCallbackHandler<T, F> {
382 pub fn new<S: AsRef<str>>(id: Option<S>, callback: F) -> Self {
384 let id_ustr = id.map_or_else(
385 || generate_handler_id(&callback),
386 |s| Ustr::from(s.as_ref()),
387 );
388
389 Self {
390 id: id_ustr,
391 callback,
392 _marker: PhantomData,
393 }
394 }
395}
396
397impl<T: 'static, F: Fn(T) + 'static> IntoHandler<T> for IntoCallbackHandler<T, F> {
398 fn id(&self) -> Ustr {
399 self.id
400 }
401
402 fn handle(&self, message: T) {
403 (self.callback)(message);
404 }
405}
406
407impl<T, F: Fn(T)> Debug for IntoCallbackHandler<T, F> {
408 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
409 f.debug_struct(stringify!(IntoCallbackHandler))
410 .field("id", &self.id)
411 .field("type", &std::any::type_name::<T>())
412 .finish()
413 }
414}
415
416#[cfg(test)]
417mod tests {
418 use std::{cell::RefCell, collections::HashSet};
419
420 use rstest::rstest;
421
422 use super::*;
423
424 #[rstest]
425 fn test_typed_handler_from_fn() {
426 let received = Rc::new(RefCell::new(Vec::new()));
427 let received_clone = received.clone();
428
429 let handler = TypedHandler::from(move |msg: &String| {
430 received_clone.borrow_mut().push(msg.clone());
431 });
432
433 handler.handle(&"test1".to_string());
434 handler.handle(&"test2".to_string());
435
436 assert_eq!(*received.borrow(), vec!["test1", "test2"]);
437 }
438
439 #[rstest]
440 fn test_generated_handler_ids_are_unique() {
441 let ids = (0..2)
442 .flat_map(|index| {
443 [
444 TypedHandler::<u32>::from(move |_: &u32| {
445 std::hint::black_box(index);
446 })
447 .id(),
448 ShareableMessageHandler::from_typed::<u32, _>(move |_: &u32| {
449 std::hint::black_box(index);
450 })
451 .id(),
452 ShareableMessageHandler::from_any(move |_: &dyn Any| {
453 std::hint::black_box(index);
454 })
455 .id(),
456 TypedIntoHandler::<u32>::from(move |_: u32| {
457 std::hint::black_box(index);
458 })
459 .id(),
460 ]
461 })
462 .collect::<Vec<_>>();
463
464 assert_eq!(ids.iter().copied().collect::<HashSet<_>>().len(), ids.len());
465 }
466
467 #[rstest]
468 fn test_typed_handler_with_custom_id() {
469 let handler = TypedHandler::from_with_id("custom-id", |_msg: &i32| {});
470
471 assert_eq!(handler.id(), "custom-id");
472 }
473
474 #[rstest]
475 fn test_typed_handler_equality() {
476 let handler1 = TypedHandler::from_with_id("same-id", |_: &u32| {});
477 let handler2 = TypedHandler::from_with_id("same-id", |_: &u32| {});
478 let handler3 = TypedHandler::from_with_id("different-id", |_: &u32| {});
479
480 assert_eq!(handler1, handler2);
481 assert_ne!(handler1, handler3);
482 }
483
484 #[rstest]
485 fn test_typed_handler_hash() {
486 use std::collections::HashSet;
487
488 let handler1 = TypedHandler::from_with_id("id-a", |_: &u32| {});
489 let handler2 = TypedHandler::from_with_id("id-a", |_: &u32| {});
490 let handler3 = TypedHandler::from_with_id("id-b", |_: &u32| {});
491
492 let mut set = HashSet::new();
493 set.insert(handler1);
494
495 assert!(!set.insert(handler2));
497 assert!(set.insert(handler3));
499 assert_eq!(set.len(), 2);
500 }
501
502 #[rstest]
503 fn test_typed_handler_debug() {
504 let handler = TypedHandler::from_with_id("debug-test", |_: &String| {});
505 let debug_str = format!("{handler:?}");
506
507 assert!(debug_str.contains("TypedHandler"));
508 assert!(debug_str.contains("debug-test"));
509 }
510
511 struct TestHandler {
513 id: Ustr,
514 call_count: RefCell<usize>,
515 }
516
517 impl TestHandler {
518 fn new(id: &str) -> Self {
519 Self {
520 id: Ustr::from(id),
521 call_count: RefCell::new(0),
522 }
523 }
524 }
525
526 impl Handler<i32> for TestHandler {
527 fn id(&self) -> Ustr {
528 self.id
529 }
530
531 fn handle(&self, _message: &i32) {
532 *self.call_count.borrow_mut() += 1;
533 }
534 }
535
536 #[rstest]
537 fn test_rc_handler_delegates_id() {
538 let handler = TestHandler::new("rc-test-id");
539 let rc_handler: Rc<TestHandler> = Rc::new(handler);
540
541 assert_eq!(rc_handler.id(), Ustr::from("rc-test-id"));
542 }
543
544 #[rstest]
545 fn test_rc_handler_delegates_handle() {
546 let handler = TestHandler::new("rc-handle-test");
547 let rc_handler: Rc<TestHandler> = Rc::new(handler);
548
549 rc_handler.handle(&42);
550 rc_handler.handle(&100);
551
552 assert_eq!(*rc_handler.call_count.borrow(), 2);
553 }
554
555 #[rstest]
556 fn test_rc_handler_shared_state() {
557 let handler = TestHandler::new("shared-state");
558 let rc1: Rc<TestHandler> = Rc::new(handler);
559 let rc2 = rc1.clone();
560
561 rc1.handle(&1);
563 rc2.handle(&2);
564 rc1.handle(&3);
565
566 assert_eq!(*rc1.call_count.borrow(), 3);
568 assert_eq!(*rc2.call_count.borrow(), 3);
569 }
570
571 #[rstest]
572 fn test_typed_handler_from_rc() {
573 let handler = Rc::new(TestHandler::new("from-rc-test"));
574 let typed: TypedHandler<i32> = TypedHandler::new(handler.clone());
575
576 typed.handle(&42);
577
578 assert_eq!(*handler.call_count.borrow(), 1);
579 assert_eq!(typed.id(), Ustr::from("from-rc-test"));
580 }
581}