nautilus_common/actor/
registry.rs1use std::{
40 any::TypeId,
41 cell::{RefCell, UnsafeCell},
42 fmt::Debug,
43 marker::PhantomData,
44 ops::{Deref, DerefMut},
45 rc::Rc,
46};
47
48use ahash::AHashMap;
49use ustr::Ustr;
50
51use super::Actor;
52
53pub struct ActorRef<T: Actor> {
66 actor_rc: Rc<UnsafeCell<dyn Actor>>,
67 _marker: PhantomData<T>,
68}
69
70impl<T: Actor> Debug for ActorRef<T> {
71 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72 f.debug_struct("ActorRef")
73 .field("actor_id", &self.deref().id())
74 .finish()
75 }
76}
77
78impl<T: Actor> Deref for ActorRef<T> {
79 type Target = T;
80
81 fn deref(&self) -> &Self::Target {
82 unsafe { &*(self.actor_rc.get() as *const T) }
84 }
85}
86
87impl<T: Actor> DerefMut for ActorRef<T> {
88 fn deref_mut(&mut self) -> &mut Self::Target {
89 unsafe { &mut *(self.actor_rc.get() as *mut T) }
91 }
92}
93
94thread_local! {
95 static ACTOR_REGISTRY: ActorRegistry = ActorRegistry::new();
96}
97
98pub struct ActorRegistry {
100 actors: RefCell<AHashMap<Ustr, Rc<UnsafeCell<dyn Actor>>>>,
101}
102
103impl Debug for ActorRegistry {
104 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105 let actors_ref = self.actors.borrow();
106 let keys: Vec<&Ustr> = actors_ref.keys().collect();
107 f.debug_struct(stringify!(ActorRegistry))
108 .field("actors", &keys)
109 .finish()
110 }
111}
112
113impl Default for ActorRegistry {
114 fn default() -> Self {
115 Self::new()
116 }
117}
118
119impl ActorRegistry {
120 pub fn new() -> Self {
121 Self {
122 actors: RefCell::new(AHashMap::new()),
123 }
124 }
125
126 pub fn insert(&self, id: Ustr, actor: Rc<UnsafeCell<dyn Actor>>) {
127 let mut actors = self.actors.borrow_mut();
128 if actors.contains_key(&id) {
129 log::warn!("Replacing existing actor with id: {id}");
130 }
131 actors.insert(id, actor);
132 }
133
134 pub fn get(&self, id: &Ustr) -> Option<Rc<UnsafeCell<dyn Actor>>> {
135 self.actors.borrow().get(id).cloned()
136 }
137
138 pub fn len(&self) -> usize {
140 self.actors.borrow().len()
141 }
142
143 pub fn is_empty(&self) -> bool {
145 self.actors.borrow().is_empty()
146 }
147
148 pub fn remove(&self, id: &Ustr) -> Option<Rc<UnsafeCell<dyn Actor>>> {
150 self.actors.borrow_mut().remove(id)
151 }
152
153 pub fn contains(&self, id: &Ustr) -> bool {
155 self.actors.borrow().contains_key(id)
156 }
157}
158
159pub fn get_actor_registry() -> &'static ActorRegistry {
160 ACTOR_REGISTRY.with(|registry| unsafe {
161 std::mem::transmute::<&ActorRegistry, &'static ActorRegistry>(registry)
166 })
167}
168
169pub fn register_actor<T>(actor: T) -> Rc<UnsafeCell<T>>
171where
172 T: Actor + 'static,
173{
174 let actor_id = actor.id();
175 let actor_ref = Rc::new(UnsafeCell::new(actor));
176
177 let actor_trait_ref: Rc<UnsafeCell<dyn Actor>> = actor_ref.clone();
179 get_actor_registry().insert(actor_id, actor_trait_ref);
180
181 actor_ref
182}
183
184pub fn get_actor(id: &Ustr) -> Option<Rc<UnsafeCell<dyn Actor>>> {
185 get_actor_registry().get(id)
186}
187
188#[must_use]
208pub fn get_actor_unchecked<T: Actor>(id: &Ustr) -> ActorRef<T> {
209 let registry = get_actor_registry();
210 let actor_rc = registry
211 .get(id)
212 .unwrap_or_else(|| panic!("Actor for {id} not found"));
213
214 let actor_ref = unsafe { &*actor_rc.get() };
216 let actual_type = actor_ref.as_any().type_id();
217 let expected_type = TypeId::of::<T>();
218
219 if actual_type != expected_type {
220 panic!("Actor type mismatch for '{id}': expected {expected_type:?}, found {actual_type:?}");
221 }
222
223 ActorRef {
224 actor_rc,
225 _marker: PhantomData,
226 }
227}
228
229#[must_use]
238pub fn try_get_actor_unchecked<T: Actor>(id: &Ustr) -> Option<ActorRef<T>> {
239 let registry = get_actor_registry();
240 let actor_rc = registry.get(id)?;
241
242 let actor_ref = unsafe { &*actor_rc.get() };
244 let actual_type = actor_ref.as_any().type_id();
245 let expected_type = TypeId::of::<T>();
246
247 if actual_type != expected_type {
248 return None;
249 }
250
251 Some(ActorRef {
252 actor_rc,
253 _marker: PhantomData,
254 })
255}
256
257pub fn actor_exists(id: &Ustr) -> bool {
259 get_actor_registry().contains(id)
260}
261
262pub fn actor_count() -> usize {
264 get_actor_registry().len()
265}
266
267#[cfg(test)]
268pub fn clear_actor_registry() {
270 let registry = get_actor_registry();
271 registry.actors.borrow_mut().clear();
272}
273
274#[cfg(test)]
275mod tests {
276 use std::any::Any;
277
278 use rstest::rstest;
279
280 use super::*;
281
282 #[derive(Debug)]
283 struct TestActor {
284 id: Ustr,
285 value: i32,
286 }
287
288 impl Actor for TestActor {
289 fn id(&self) -> Ustr {
290 self.id
291 }
292 fn handle(&mut self, _msg: &dyn Any) {}
293 fn as_any(&self) -> &dyn Any {
294 self
295 }
296 }
297
298 #[rstest]
299 fn test_register_and_get_actor() {
300 clear_actor_registry();
301
302 let id = Ustr::from("test-actor");
303 let actor = TestActor { id, value: 42 };
304 register_actor(actor);
305
306 let actor_ref = get_actor_unchecked::<TestActor>(&id);
307 assert_eq!(actor_ref.value, 42);
308 }
309
310 #[rstest]
311 fn test_mutation_through_reference() {
312 clear_actor_registry();
313
314 let id = Ustr::from("test-actor-mut");
315 let actor = TestActor { id, value: 0 };
316 register_actor(actor);
317
318 let mut actor_ref = get_actor_unchecked::<TestActor>(&id);
319 actor_ref.value = 999;
320
321 let actor_ref2 = get_actor_unchecked::<TestActor>(&id);
322 assert_eq!(actor_ref2.value, 999);
323 }
324
325 #[rstest]
326 fn test_try_get_returns_none_for_missing() {
327 clear_actor_registry();
328
329 let id = Ustr::from("nonexistent");
330 let result = try_get_actor_unchecked::<TestActor>(&id);
331 assert!(result.is_none());
332 }
333
334 #[rstest]
335 fn test_try_get_returns_none_for_wrong_type() {
336 clear_actor_registry();
337
338 #[derive(Debug)]
339 struct OtherActor {
340 id: Ustr,
341 }
342
343 impl Actor for OtherActor {
344 fn id(&self) -> Ustr {
345 self.id
346 }
347 fn handle(&mut self, _msg: &dyn Any) {}
348 fn as_any(&self) -> &dyn Any {
349 self
350 }
351 }
352
353 let id = Ustr::from("other-actor");
354 let actor = OtherActor { id };
355 register_actor(actor);
356
357 let result = try_get_actor_unchecked::<TestActor>(&id);
358 assert!(result.is_none());
359 }
360}