use std::any::Any; use std::sync::Arc; use crate::Error; /// The primary trait defining data that can be passed to and from actor processes pub trait Message: 'static + Sized + Clone + Send + Sync {} impl Message for T {} pub trait ActorInterface: 'static + Send { type Incoming: Message; type Response: Message; fn handle(&mut self, ctx: &Ctx, msg: Self::Incoming); } /// A unique address for this actor. 32 bytes is overkill for a small application, /// but most systems are powerful, and this allows us to create a global map of /// actor processes in the future, without worrying about collision. #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct ActorAddress(pub [u8; 32]); impl std::fmt::Display for ActorAddress { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { for b in &self.0[..8] { write!(f, "{:02x}", b)?; } write!(f, "\u{2026}") } } impl ActorAddress { pub fn new_random() -> Self { let mut bytes = [0u8; 32]; crate::get_random(&mut bytes); Self(bytes) } } /// The actor process as represented in the Runtime — thin wrapper around user state. pub struct Actor { inner: A, /// Factory for creating fresh instances on restart. None = not restartable. restart_factory: Option A + Send + Sync>>, max_restarts: u32, restart_count: u32, } impl Actor { pub fn new(inner: A) -> Self { Self { inner, restart_factory: None, max_restarts: 0, restart_count: 0, } } pub fn new_restartable( inner: A, factory: Arc A + Send + Sync>, max_restarts: u32, ) -> Self { Self { inner, restart_factory: Some(factory), max_restarts, restart_count: 0, } } } /// Trait for type-erased actors — single-message handler. /// /// Returns `Some(type_name)` if handled, `None` on type mismatch. pub trait AnyActor: Send { fn handle_any(&mut self, ctx: &Ctx, msg: Box) -> Option<&'static str>; /// Attempt to create a fresh instance for restart after panic. /// Returns `None` if restart is not supported or restart limit exceeded. fn try_restart(&self) -> Option> { None } } impl AnyActor for Actor where A: ActorInterface, { fn handle_any(&mut self, ctx: &Ctx, msg: Box) -> Option<&'static str> { if let Ok(typed) = msg.downcast::() { self.inner.handle(ctx, *typed); Some(std::any::type_name::()) } else { None } } fn try_restart(&self) -> Option> { let factory = self.restart_factory.as_ref()?; if self.restart_count >= self.max_restarts { return None; } let fresh = factory(); Some(Box::new(Actor { inner: fresh, restart_factory: Some(factory.clone()), max_restarts: self.max_restarts, restart_count: self.restart_count + 1, })) } } /// Object-safe inner trait for sending type-erased messages. pub trait ContextInner { fn send_any(&self, addr: ActorAddress, msg: Box) -> Result<(), Error>; fn spawn_any(&self, addr: ActorAddress, actor: Box); } /// Actor syscall interface — passed to `ActorInterface::handle()`. /// /// Wraps a `&dyn ContextInner` to solve the object-safety problem while /// providing a typed public API. pub struct Ctx<'a> { inner: &'a dyn ContextInner, self_addr: ActorAddress, } impl<'a> Ctx<'a> { pub(crate) fn new(inner: &'a dyn ContextInner, self_addr: ActorAddress) -> Self { Self { inner, self_addr } } pub fn raw_inner(&self) -> &dyn ContextInner { self.inner } /// Returns the address of the actor currently being ticked. pub fn self_addr(&self) -> ActorAddress { self.self_addr } /// Send a typed message to an actor address. pub fn send(&self, addr: ActorAddress, msg: M) -> Result<(), Error> { self.inner.send_any(addr, Box::new(msg)) } /// Spawn a new actor, returning its address. pub fn spawn(&self, actor: A) -> Result { let addr = ActorAddress::new_random(); let boxed: Box = Box::new(Actor::new(actor)); self.inner.spawn_any(addr, boxed); Ok(addr) } /// Spawn a restartable actor. On panic, recreated via `factory` up to /// `max_restarts` times before permanent poisoning. pub fn spawn_restartable( &self, actor: A, factory: F, max_restarts: u32, ) -> Result where A: ActorInterface, F: Fn() -> A + Send + Sync + 'static, { let addr = ActorAddress::new_random(); let boxed: Box = Box::new(Actor::new_restartable( actor, Arc::new(factory), max_restarts, )); self.inner.spawn_any(addr, boxed); Ok(addr) } }