pub mod actor;
pub mod distributed;
pub mod epmd;
pub mod error;
pub mod gen_server;
pub mod gen_statem;
pub mod health;
pub mod mailbox;
pub mod message;
pub mod pid;
pub mod registry;
pub mod scheduler;
pub mod serialization;
pub mod supervisor;
pub mod system;
pub mod telemetry;
pub use actor::{Actor, ActorContext};
pub use error::{ActorError, Result};
pub use gen_server::{CallResponse, ReplyHandle};
pub use health::{HealthConfig, HealthIssue, HealthStatus, IssueSeverity, SystemHealth};
pub use message::{ExitReason, Message, MonitorRef, Signal};
pub use pid::Pid;
pub use registry::Registry;
pub use scheduler::{Destination, TimerRef};
pub use serialization::{get_global_registry, register_message_type};
pub use supervisor::{RestartStrategy, Supervisor, SupervisorSpec};
pub use system::{ActorRef, ActorSystem};
pub use joerl_macro::gen_statem;
pub use async_trait;
#[cfg(test)]
mod tests {
use super::*;
use async_trait::async_trait;
struct PingPong {
count: usize,
max: usize,
}
#[async_trait]
impl Actor for PingPong {
async fn handle_message(&mut self, msg: Message, ctx: &mut ActorContext) {
if let Some(_other) = msg.downcast_ref::<Pid>() {
self.count += 1;
println!("{} received ping #{}", ctx.pid(), self.count);
if self.count < self.max {
let _ = ctx.pid(); }
}
}
}
#[tokio::test]
async fn test_basic_actor_lifecycle() {
let system = ActorSystem::new();
let actor = system.spawn(PingPong { count: 0, max: 3 });
actor.send(Box::new(42i32)).await.unwrap();
tokio::time::sleep(tokio::time::Duration::from_millis(10)).await;
assert!(system.is_actor_alive(actor.pid()));
}
#[tokio::test]
async fn test_actor_link_failure() {
use tokio::time::{Duration, sleep};
struct FailingActor;
#[async_trait]
impl Actor for FailingActor {
async fn handle_message(&mut self, msg: Message, ctx: &mut ActorContext) {
if let Some(cmd) = msg.downcast_ref::<&str>()
&& *cmd == "die"
{
ctx.stop(ExitReason::Panic("intentional".to_string()));
}
}
}
struct MonitoringActor {
monitored: Option<Pid>,
received_exit: bool,
}
#[async_trait]
impl Actor for MonitoringActor {
async fn started(&mut self, ctx: &mut ActorContext) {
ctx.trap_exit(true);
}
async fn handle_signal(&mut self, signal: Signal, _ctx: &mut ActorContext) {
if let Signal::Exit { from, .. } = signal
&& Some(from) == self.monitored
{
self.received_exit = true;
}
}
async fn handle_message(&mut self, _msg: Message, _ctx: &mut ActorContext) {}
}
let system = ActorSystem::new();
let failing = system.spawn(FailingActor);
let monitoring = system.spawn(MonitoringActor {
monitored: Some(failing.pid()),
received_exit: false,
});
system.link(monitoring.pid(), failing.pid()).unwrap();
failing.send(Box::new("die")).await.unwrap();
sleep(Duration::from_millis(50)).await;
}
}