autoagents_core/actor/
messaging.rs

1use std::sync::Arc;
2
3/// Generic trait for messages that can be sent between actors
4pub trait ActorMessage: Send + Sync + 'static {}
5
6// For messages that can be cloned
7pub trait CloneableMessage: ActorMessage + Clone {}
8
9pub struct SharedMessage<M> {
10    inner: Arc<M>,
11}
12
13// Manually implement Clone without requiring M: Clone
14impl<M> Clone for SharedMessage<M> {
15    fn clone(&self) -> Self {
16        Self {
17            inner: Arc::clone(&self.inner),
18        }
19    }
20}
21
22impl<M> SharedMessage<M> {
23    pub fn new(msg: M) -> Self {
24        Self {
25            inner: Arc::new(msg),
26        }
27    }
28
29    pub fn inner(&self) -> &M {
30        &self.inner
31    }
32
33    pub fn into_inner(self) -> Arc<M> {
34        self.inner
35    }
36}
37
38// SharedMessage<M> is always a PubSubMessage (but NOT CloneableMessage to avoid conflicts)
39impl<M: Send + Sync + 'static> ActorMessage for SharedMessage<M> {}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44    use std::sync::Arc;
45
46    // Test message types
47    #[derive(Debug, Clone, PartialEq)]
48    struct TestMessage {
49        content: String,
50    }
51
52    impl ActorMessage for TestMessage {}
53    impl CloneableMessage for TestMessage {}
54
55    #[derive(Debug, PartialEq)]
56    struct NonCloneableMessage {
57        data: String,
58    }
59
60    impl ActorMessage for NonCloneableMessage {}
61
62    #[test]
63    fn test_shared_message_creation() {
64        let msg = TestMessage {
65            content: "test".to_string(),
66        };
67        let shared = SharedMessage::new(msg);
68
69        assert_eq!(shared.inner().content, "test");
70    }
71
72    #[test]
73    fn test_shared_message_clone() {
74        let msg = TestMessage {
75            content: "original".to_string(),
76        };
77        let shared1 = SharedMessage::new(msg);
78        let shared2 = shared1.clone();
79
80        // Both should reference the same inner data
81        assert_eq!(shared1.inner().content, shared2.inner().content);
82        assert_eq!(shared1.inner().content, "original");
83
84        // Arc reference count should be 2
85        assert_eq!(Arc::strong_count(&shared1.inner), 2);
86        assert_eq!(Arc::strong_count(&shared2.inner), 2);
87    }
88
89    #[test]
90    fn test_shared_message_into_inner() {
91        let msg = TestMessage {
92            content: "into_inner_test".to_string(),
93        };
94        let shared = SharedMessage::new(msg);
95        let arc = shared.into_inner();
96
97        assert_eq!(arc.content, "into_inner_test");
98        assert_eq!(Arc::strong_count(&arc), 1);
99    }
100
101    #[test]
102    fn test_shared_message_inner_reference() {
103        let msg = TestMessage {
104            content: "reference_test".to_string(),
105        };
106        let shared = SharedMessage::new(msg);
107        let inner_ref = shared.inner();
108
109        assert_eq!(inner_ref.content, "reference_test");
110    }
111
112    #[test]
113    fn test_shared_message_with_non_cloneable() {
114        let msg = NonCloneableMessage {
115            data: "non_cloneable".to_string(),
116        };
117        let shared1 = SharedMessage::new(msg);
118        let shared2 = shared1.clone();
119
120        // Should work even with non-cloneable messages
121        assert_eq!(shared1.inner().data, "non_cloneable");
122        assert_eq!(shared2.inner().data, "non_cloneable");
123    }
124
125    #[test]
126    fn test_actor_message_trait_implemented() {
127        let msg = TestMessage {
128            content: "trait_test".to_string(),
129        };
130        let shared = SharedMessage::new(msg);
131
132        // Should be able to treat as ActorMessage
133        fn accepts_actor_message<T: ActorMessage>(_: T) {}
134        accepts_actor_message(shared);
135    }
136
137    #[test]
138    fn test_cloneable_message_trait() {
139        let msg = TestMessage {
140            content: "cloneable_test".to_string(),
141        };
142
143        // Should implement both ActorMessage and CloneableMessage
144        fn accepts_cloneable_message<T: CloneableMessage>(_: T) {}
145        accepts_cloneable_message(msg);
146    }
147
148    #[test]
149    fn test_shared_message_debug() {
150        let msg = TestMessage {
151            content: "debug_test".to_string(),
152        };
153        let shared = SharedMessage::new(msg);
154
155        // Debug should work through inner message's Debug impl
156        let debug_str = format!("{:?}", shared.inner());
157        assert!(debug_str.contains("debug_test"));
158    }
159
160    #[test]
161    fn test_multiple_shared_message_clones() {
162        let msg = TestMessage {
163            content: "multi_clone_test".to_string(),
164        };
165        let shared1 = SharedMessage::new(msg);
166        let shared2 = shared1.clone();
167        let shared3 = shared2.clone();
168        let shared4 = shared1.clone();
169
170        // All should reference the same data
171        assert_eq!(shared1.inner().content, "multi_clone_test");
172        assert_eq!(shared2.inner().content, "multi_clone_test");
173        assert_eq!(shared3.inner().content, "multi_clone_test");
174        assert_eq!(shared4.inner().content, "multi_clone_test");
175
176        // Arc reference count should be 4
177        assert_eq!(Arc::strong_count(&shared1.inner), 4);
178    }
179
180    #[test]
181    fn test_shared_message_arc_cleanup() {
182        let msg = TestMessage {
183            content: "cleanup_test".to_string(),
184        };
185        let shared1 = SharedMessage::new(msg);
186        let shared2 = shared1.clone();
187
188        assert_eq!(Arc::strong_count(&shared1.inner), 2);
189
190        drop(shared2);
191        assert_eq!(Arc::strong_count(&shared1.inner), 1);
192
193        drop(shared1);
194        // shared1 is dropped, so we can't check the count anymore
195    }
196}