use std::sync::{Arc, Mutex};
use std::time::Duration;
use async_trait::async_trait;
use dactor::actor::{Actor, ActorContext, ActorError, ActorRef, Handler};
use dactor::errors::ErrorAction;
use dactor::message::Message;
use dactor::supervision::ChildTerminated;
use dactor::TestRuntime;
struct DoWork(String);
impl Message for DoWork {
type Reply = ();
}
struct CrashNow;
impl Message for CrashNow {
type Reply = ();
}
struct GetCompleted;
impl Message for GetCompleted {
type Reply = u64;
}
struct Worker {
completed: u64,
}
impl Actor for Worker {
type Args = ();
type Deps = ();
fn create(_: (), _: ()) -> Self {
Worker { completed: 0 }
}
fn on_error(&mut self, error: &ActorError) -> ErrorAction {
println!(" [Worker] on_error: {} — resuming", error.message);
ErrorAction::Resume
}
}
#[async_trait]
impl Handler<DoWork> for Worker {
async fn handle(&mut self, msg: DoWork, _ctx: &mut ActorContext) {
self.completed += 1;
println!(
" [Worker] completed task '{}' (total: {})",
msg.0, self.completed
);
}
}
#[async_trait]
impl Handler<CrashNow> for Worker {
async fn handle(&mut self, _msg: CrashNow, _ctx: &mut ActorContext) {
panic!("intentional crash!");
}
}
#[async_trait]
impl Handler<GetCompleted> for Worker {
async fn handle(&mut self, _msg: GetCompleted, _ctx: &mut ActorContext) -> u64 {
self.completed
}
}
struct Supervisor {
events: Arc<Mutex<Vec<String>>>,
}
impl Actor for Supervisor {
type Args = Arc<Mutex<Vec<String>>>;
type Deps = ();
fn create(args: Arc<Mutex<Vec<String>>>, _: ()) -> Self {
Supervisor { events: args }
}
}
#[async_trait]
impl Handler<ChildTerminated> for Supervisor {
async fn handle(&mut self, msg: ChildTerminated, _ctx: &mut ActorContext) {
let reason = msg.reason.as_deref().unwrap_or("graceful shutdown");
let entry = format!("child '{}' terminated: {}", msg.child_name, reason);
println!(" [Supervisor] {}", entry);
self.events.lock().unwrap().push(entry);
}
}
#[tokio::main]
async fn main() {
println!("=== Supervision Example ===\n");
let runtime = TestRuntime::new();
let events = Arc::new(Mutex::new(Vec::<String>::new()));
let supervisor = runtime.spawn::<Supervisor>("supervisor", events.clone()).await.unwrap();
let worker = runtime.spawn::<Worker>("worker", ()).await.unwrap();
let worker_id = worker.id();
runtime.watch(&supervisor, worker_id);
println!("Supervisor is watching worker\n");
println!("--- Normal work ---");
worker.tell(DoWork("task-A".into())).unwrap();
worker.tell(DoWork("task-B".into())).unwrap();
tokio::time::sleep(Duration::from_millis(50)).await;
println!("\n--- Worker panic (Resume) ---");
worker.tell(CrashNow).unwrap();
tokio::time::sleep(Duration::from_millis(100)).await;
println!("\n--- Post-panic work ---");
worker.tell(DoWork("task-C".into())).unwrap();
tokio::time::sleep(Duration::from_millis(50)).await;
let completed = worker.ask(GetCompleted, None).unwrap().await.unwrap();
println!(" [Main] worker completed {} tasks", completed);
println!("\n--- Stopping worker ---");
worker.stop();
tokio::time::sleep(Duration::from_millis(100)).await;
println!("\n--- Supervisor events ---");
for e in events.lock().unwrap().iter() {
println!(" {}", e);
}
println!("\n=== Done ===");
}