autoagents_core/actor/
topic.rs

1use crate::actor::messaging::ActorMessage;
2use std::any::TypeId;
3use std::fmt::Debug;
4use std::marker::PhantomData;
5use uuid::Uuid;
6
7// Generic topic that is type-safe at compile time
8#[derive(Clone)]
9pub struct Topic<M: ActorMessage> {
10    name: String,
11    id: Uuid,
12    _phantom: PhantomData<M>,
13}
14impl<M: ActorMessage> Topic<M> {
15    pub fn new(name: impl Into<String>) -> Self {
16        Topic {
17            name: name.into(),
18            id: Uuid::new_v4(),
19            _phantom: PhantomData,
20        }
21    }
22
23    pub fn name(&self) -> &str {
24        &self.name
25    }
26
27    pub fn id(&self) -> Uuid {
28        self.id
29    }
30
31    pub fn type_id(&self) -> TypeId {
32        TypeId::of::<M>()
33    }
34}
35
36impl<M: ActorMessage> Debug for Topic<M> {
37    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38        f.debug_struct("Topic")
39            .field("name", &self.name)
40            .field("id", &self.id)
41            .finish()
42    }
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48    use std::any::TypeId;
49
50    // Test message types
51    #[derive(Debug, Clone)]
52    struct TestMessage {
53        _content: String,
54    }
55    impl ActorMessage for TestMessage {}
56
57    #[derive(Debug)]
58    struct AnotherTestMessage {
59        _data: i32,
60    }
61    impl ActorMessage for AnotherTestMessage {}
62
63    #[test]
64    fn test_topic_creation_with_string() {
65        let topic = Topic::<TestMessage>::new("test_topic");
66
67        assert_eq!(topic.name(), "test_topic");
68        assert!(!topic.id().is_nil());
69    }
70
71    #[test]
72    fn test_topic_creation_with_str() {
73        let topic = Topic::<TestMessage>::new("str_topic");
74
75        assert_eq!(topic.name(), "str_topic");
76        assert!(!topic.id().is_nil());
77    }
78
79    #[test]
80    fn test_topic_unique_ids() {
81        let topic1 = Topic::<TestMessage>::new("topic1");
82        let topic2 = Topic::<TestMessage>::new("topic2");
83
84        assert_ne!(topic1.id(), topic2.id());
85    }
86
87    #[test]
88    fn test_topic_same_name_different_ids() {
89        let topic1 = Topic::<TestMessage>::new("same_name");
90        let topic2 = Topic::<TestMessage>::new("same_name");
91
92        assert_eq!(topic1.name(), topic2.name());
93        assert_ne!(topic1.id(), topic2.id());
94    }
95
96    #[test]
97    fn test_topic_type_id_same_type() {
98        let topic1 = Topic::<TestMessage>::new("topic1");
99        let topic2 = Topic::<TestMessage>::new("topic2");
100
101        assert_eq!(topic1.type_id(), topic2.type_id());
102        assert_eq!(topic1.type_id(), TypeId::of::<TestMessage>());
103    }
104
105    #[test]
106    fn test_topic_type_id_different_types() {
107        let topic1 = Topic::<TestMessage>::new("topic1");
108        let topic2 = Topic::<AnotherTestMessage>::new("topic2");
109
110        assert_ne!(topic1.type_id(), topic2.type_id());
111        assert_eq!(topic1.type_id(), TypeId::of::<TestMessage>());
112        assert_eq!(topic2.type_id(), TypeId::of::<AnotherTestMessage>());
113    }
114
115    #[test]
116    fn test_topic_clone() {
117        let original = Topic::<TestMessage>::new("original_topic");
118        let cloned = original.clone();
119
120        assert_eq!(original.name(), cloned.name());
121        assert_eq!(original.id(), cloned.id());
122        assert_eq!(original.type_id(), cloned.type_id());
123    }
124
125    #[test]
126    fn test_topic_debug() {
127        let topic = Topic::<TestMessage>::new("debug_topic");
128        let debug_str = format!("{topic:?}");
129
130        assert!(debug_str.contains("Topic"));
131        assert!(debug_str.contains("debug_topic"));
132        assert!(debug_str.contains("name"));
133        assert!(debug_str.contains("id"));
134    }
135
136    #[test]
137    fn test_topic_name_accessor() {
138        let topic = Topic::<TestMessage>::new("accessor_test");
139
140        assert_eq!(topic.name(), "accessor_test");
141    }
142
143    #[test]
144    fn test_topic_id_accessor() {
145        let topic = Topic::<TestMessage>::new("id_test");
146        let id = topic.id();
147
148        assert!(!id.is_nil());
149        assert_eq!(topic.id(), id); // Should return the same ID
150    }
151
152    #[test]
153    fn test_topic_type_id_accessor() {
154        let topic = Topic::<TestMessage>::new("type_id_test");
155        let type_id = topic.type_id();
156
157        assert_eq!(type_id, TypeId::of::<TestMessage>());
158        assert_eq!(topic.type_id(), type_id); // Should return the same TypeId
159    }
160
161    #[test]
162    fn test_topic_with_empty_name() {
163        let topic = Topic::<TestMessage>::new("");
164
165        assert_eq!(topic.name(), "");
166        assert!(!topic.id().is_nil());
167    }
168
169    #[test]
170    fn test_topic_with_unicode_name() {
171        let topic = Topic::<TestMessage>::new("テスト_トピック_🚀");
172
173        assert_eq!(topic.name(), "テスト_トピック_🚀");
174        assert!(!topic.id().is_nil());
175    }
176
177    #[test]
178    fn test_topic_with_long_name() {
179        let long_name = "a".repeat(1000);
180        let topic = Topic::<TestMessage>::new(long_name.clone());
181
182        assert_eq!(topic.name(), long_name);
183        assert!(!topic.id().is_nil());
184    }
185
186    #[test]
187    fn test_topic_type_safety() {
188        // This test ensures compile-time type safety
189        let _topic1: Topic<TestMessage> = Topic::new("test");
190        let _topic2: Topic<AnotherTestMessage> = Topic::new("test");
191
192        // Different types should not be assignable
193        // This won't compile: let _topic3: Topic<TestMessage> = topic2;
194    }
195
196    #[test]
197    fn test_topic_phantom_data() {
198        // Ensure PhantomData doesn't affect size significantly
199        let topic = Topic::<TestMessage>::new("phantom_test");
200
201        // PhantomData should be zero-sized
202        assert_eq!(std::mem::size_of_val(&topic._phantom), 0);
203    }
204}