use std::{any::Any, fmt::Debug, marker::PhantomData, rc::Rc};
use nautilus_core::UUID4;
use ustr::Ustr;
pub trait Handler<T: ?Sized>: 'static {
fn id(&self) -> Ustr;
fn handle(&self, message: &T);
}
impl<T: ?Sized, H: Handler<T>> Handler<T> for Rc<H> {
fn id(&self) -> Ustr {
(**self).id()
}
fn handle(&self, message: &T) {
(**self).handle(message);
}
}
pub struct TypedHandler<T: 'static + ?Sized>(pub Rc<dyn Handler<T>>);
impl<T: 'static + ?Sized> Clone for TypedHandler<T> {
fn clone(&self) -> Self {
Self(Rc::clone(&self.0))
}
}
impl<T: 'static + ?Sized> TypedHandler<T> {
pub fn id(&self) -> Ustr {
self.0.id()
}
pub fn handle(&self, message: &T) {
self.0.handle(message);
}
}
impl<T: 'static> TypedHandler<T> {
pub fn new<H: Handler<T>>(handler: H) -> Self {
Self(Rc::new(handler))
}
pub fn from<F>(callback: F) -> Self
where
F: Fn(&T) + 'static,
{
Self::new(CallbackHandler::new(None::<&str>, callback))
}
pub fn from_with_id<S: AsRef<str>, F>(id: S, callback: F) -> Self
where
F: Fn(&T) + 'static,
{
Self::new(CallbackHandler::new(Some(id), callback))
}
}
impl<T: 'static + ?Sized> Debug for TypedHandler<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct(stringify!(TypedHandler))
.field("id", &self.0.id())
.field("type", &std::any::type_name::<T>())
.finish()
}
}
impl<T: 'static + ?Sized> PartialEq for TypedHandler<T> {
fn eq(&self, other: &Self) -> bool {
self.0.id() == other.0.id()
}
}
impl<T: 'static + ?Sized> Eq for TypedHandler<T> {}
impl<T: 'static + ?Sized> std::hash::Hash for TypedHandler<T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.id().hash(state);
}
}
impl From<Rc<dyn Handler<dyn Any>>> for TypedHandler<dyn Any> {
fn from(handler: Rc<dyn Handler<dyn Any>>) -> Self {
Self(handler)
}
}
pub type ShareableMessageHandler = TypedHandler<dyn Any>;
#[must_use]
pub fn shareable_handler(handler: Rc<dyn Handler<dyn Any>>) -> ShareableMessageHandler {
TypedHandler(handler)
}
impl ShareableMessageHandler {
pub fn from_typed<T, F>(f: F) -> Self
where
T: 'static,
F: Fn(&T) + 'static,
{
TypedHandler(Rc::new(DowncastingHandler::new(None::<&str>, f)))
}
pub fn from_any<F>(f: F) -> Self
where
F: Fn(&dyn Any) + 'static,
{
TypedHandler(Rc::new(AnyCallbackHandler::new(None::<&str>, f)))
}
}
struct DowncastingHandler<T, F: Fn(&T)> {
id: Ustr,
callback: F,
_marker: PhantomData<T>,
}
impl<T: 'static, F: Fn(&T) + 'static> DowncastingHandler<T, F> {
fn new<S: AsRef<str>>(id: Option<S>, callback: F) -> Self {
let id_ustr = id.map_or_else(
|| generate_handler_id::<T, F>(&callback),
|s| Ustr::from(s.as_ref()),
);
Self {
id: id_ustr,
callback,
_marker: PhantomData,
}
}
}
impl<T: 'static, F: Fn(&T) + 'static> Handler<dyn Any> for DowncastingHandler<T, F> {
fn id(&self) -> Ustr {
self.id
}
fn handle(&self, message: &dyn Any) {
if let Some(typed_msg) = message.downcast_ref::<T>() {
(self.callback)(typed_msg);
} else {
log::error!(
"DowncastingHandler downcast failed: expected {} got {:?}",
std::any::type_name::<T>(),
message.type_id()
);
}
}
}
struct AnyCallbackHandler<F: Fn(&dyn Any)> {
id: Ustr,
callback: F,
}
impl<F: Fn(&dyn Any) + 'static> AnyCallbackHandler<F> {
fn new<S: AsRef<str>>(id: Option<S>, callback: F) -> Self {
let id_ustr = id.map_or_else(
|| {
let callback_ptr = std::ptr::from_ref(&callback);
let uuid = UUID4::new();
Ustr::from(&format!("<{callback_ptr:?}>-{uuid}"))
},
|s| Ustr::from(s.as_ref()),
);
Self {
id: id_ustr,
callback,
}
}
}
impl<F: Fn(&dyn Any) + 'static> Handler<dyn Any> for AnyCallbackHandler<F> {
fn id(&self) -> Ustr {
self.id
}
fn handle(&self, message: &dyn Any) {
(self.callback)(message);
}
}
pub struct CallbackHandler<T, F: Fn(&T)> {
id: Ustr,
callback: F,
_marker: PhantomData<T>,
}
impl<T: 'static, F: Fn(&T) + 'static> CallbackHandler<T, F> {
pub fn new<S: AsRef<str>>(id: Option<S>, callback: F) -> Self {
let id_ustr = id.map_or_else(
|| generate_handler_id(&callback),
|s| Ustr::from(s.as_ref()),
);
Self {
id: id_ustr,
callback,
_marker: PhantomData,
}
}
}
impl<T: 'static, F: Fn(&T) + 'static> Handler<T> for CallbackHandler<T, F> {
fn id(&self) -> Ustr {
self.id
}
fn handle(&self, message: &T) {
(self.callback)(message);
}
}
impl<T, F: Fn(&T)> Debug for CallbackHandler<T, F> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct(stringify!(CallbackHandler))
.field("id", &self.id)
.field("type", &std::any::type_name::<T>())
.finish()
}
}
fn generate_handler_id<T: 'static + ?Sized, F: 'static + Fn(&T)>(callback: &F) -> Ustr {
let callback_ptr = std::ptr::from_ref(callback);
let uuid = UUID4::new();
Ustr::from(&format!("<{callback_ptr:?}>-{uuid}"))
}
fn generate_into_handler_id<T: 'static, F: 'static + Fn(T)>(callback: &F) -> Ustr {
let callback_ptr = std::ptr::from_ref(callback);
let uuid = UUID4::new();
Ustr::from(&format!("<{callback_ptr:?}>-{uuid}"))
}
pub trait IntoHandler<T>: 'static {
fn id(&self) -> Ustr;
fn handle(&self, message: T);
}
impl<T, H: IntoHandler<T>> IntoHandler<T> for Rc<H> {
fn id(&self) -> Ustr {
(**self).id()
}
fn handle(&self, message: T) {
(**self).handle(message);
}
}
pub struct TypedIntoHandler<T: 'static>(pub Rc<dyn IntoHandler<T>>);
impl<T: 'static> Clone for TypedIntoHandler<T> {
fn clone(&self) -> Self {
Self(Rc::clone(&self.0))
}
}
impl<T: 'static> TypedIntoHandler<T> {
pub fn new<H: IntoHandler<T>>(handler: H) -> Self {
Self(Rc::new(handler))
}
pub fn from<F>(callback: F) -> Self
where
F: Fn(T) + 'static,
{
Self::new(IntoCallbackHandler::new(None::<&str>, callback))
}
pub fn from_with_id<S: AsRef<str>, F>(id: S, callback: F) -> Self
where
F: Fn(T) + 'static,
{
Self::new(IntoCallbackHandler::new(Some(id), callback))
}
pub fn id(&self) -> Ustr {
self.0.id()
}
pub fn handle(&self, message: T) {
self.0.handle(message);
}
}
impl<T: 'static> Debug for TypedIntoHandler<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct(stringify!(TypedIntoHandler))
.field("id", &self.0.id())
.field("type", &std::any::type_name::<T>())
.finish()
}
}
impl<T: 'static> PartialEq for TypedIntoHandler<T> {
fn eq(&self, other: &Self) -> bool {
self.0.id() == other.0.id()
}
}
impl<T: 'static> Eq for TypedIntoHandler<T> {}
impl<T: 'static> std::hash::Hash for TypedIntoHandler<T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.id().hash(state);
}
}
pub struct IntoCallbackHandler<T, F: Fn(T)> {
id: Ustr,
callback: F,
_marker: PhantomData<T>,
}
impl<T: 'static, F: Fn(T) + 'static> IntoCallbackHandler<T, F> {
pub fn new<S: AsRef<str>>(id: Option<S>, callback: F) -> Self {
let id_ustr = id.map_or_else(
|| generate_into_handler_id(&callback),
|s| Ustr::from(s.as_ref()),
);
Self {
id: id_ustr,
callback,
_marker: PhantomData,
}
}
}
impl<T: 'static, F: Fn(T) + 'static> IntoHandler<T> for IntoCallbackHandler<T, F> {
fn id(&self) -> Ustr {
self.id
}
fn handle(&self, message: T) {
(self.callback)(message);
}
}
impl<T, F: Fn(T)> Debug for IntoCallbackHandler<T, F> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct(stringify!(IntoCallbackHandler))
.field("id", &self.id)
.field("type", &std::any::type_name::<T>())
.finish()
}
}
#[cfg(test)]
mod tests {
use std::cell::RefCell;
use rstest::rstest;
use super::*;
#[rstest]
fn test_typed_handler_from_fn() {
let received = Rc::new(RefCell::new(Vec::new()));
let received_clone = received.clone();
let handler = TypedHandler::from(move |msg: &String| {
received_clone.borrow_mut().push(msg.clone());
});
handler.handle(&"test1".to_string());
handler.handle(&"test2".to_string());
assert_eq!(*received.borrow(), vec!["test1", "test2"]);
}
#[rstest]
fn test_typed_handler_with_custom_id() {
let handler = TypedHandler::from_with_id("custom-id", |_msg: &i32| {});
assert_eq!(handler.id().as_str(), "custom-id");
}
#[rstest]
fn test_typed_handler_equality() {
let handler1 = TypedHandler::from_with_id("same-id", |_: &u32| {});
let handler2 = TypedHandler::from_with_id("same-id", |_: &u32| {});
let handler3 = TypedHandler::from_with_id("different-id", |_: &u32| {});
assert_eq!(handler1, handler2);
assert_ne!(handler1, handler3);
}
#[rstest]
fn test_typed_handler_hash() {
use std::collections::HashSet;
let handler1 = TypedHandler::from_with_id("id-a", |_: &u32| {});
let handler2 = TypedHandler::from_with_id("id-a", |_: &u32| {});
let handler3 = TypedHandler::from_with_id("id-b", |_: &u32| {});
let mut set = HashSet::new();
set.insert(handler1);
assert!(!set.insert(handler2));
assert!(set.insert(handler3));
assert_eq!(set.len(), 2);
}
#[rstest]
fn test_typed_handler_debug() {
let handler = TypedHandler::from_with_id("debug-test", |_: &String| {});
let debug_str = format!("{handler:?}");
assert!(debug_str.contains("TypedHandler"));
assert!(debug_str.contains("debug-test"));
}
struct TestHandler {
id: Ustr,
call_count: RefCell<usize>,
}
impl TestHandler {
fn new(id: &str) -> Self {
Self {
id: Ustr::from(id),
call_count: RefCell::new(0),
}
}
}
impl Handler<i32> for TestHandler {
fn id(&self) -> Ustr {
self.id
}
fn handle(&self, _message: &i32) {
*self.call_count.borrow_mut() += 1;
}
}
#[rstest]
fn test_rc_handler_delegates_id() {
let handler = TestHandler::new("rc-test-id");
let rc_handler: Rc<TestHandler> = Rc::new(handler);
assert_eq!(rc_handler.id(), Ustr::from("rc-test-id"));
}
#[rstest]
fn test_rc_handler_delegates_handle() {
let handler = TestHandler::new("rc-handle-test");
let rc_handler: Rc<TestHandler> = Rc::new(handler);
rc_handler.handle(&42);
rc_handler.handle(&100);
assert_eq!(*rc_handler.call_count.borrow(), 2);
}
#[rstest]
fn test_rc_handler_shared_state() {
let handler = TestHandler::new("shared-state");
let rc1: Rc<TestHandler> = Rc::new(handler);
let rc2 = rc1.clone();
rc1.handle(&1);
rc2.handle(&2);
rc1.handle(&3);
assert_eq!(*rc1.call_count.borrow(), 3);
assert_eq!(*rc2.call_count.borrow(), 3);
}
#[rstest]
fn test_typed_handler_from_rc() {
let handler = Rc::new(TestHandler::new("from-rc-test"));
let typed: TypedHandler<i32> = TypedHandler::new(handler.clone());
typed.handle(&42);
assert_eq!(*handler.call_count.borrow(), 1);
assert_eq!(typed.id(), Ustr::from("from-rc-test"));
}
}