use anyhow::Result;
use rsactor::{
message_handlers, spawn, Actor, ActorRef, AskHandler, TellHandler, WeakAskHandler,
WeakTellHandler,
};
struct Ping {
timestamp: u64,
}
struct GetStatus;
#[derive(Debug, Clone)]
struct Status {
name: String,
message_count: u32,
}
#[derive(Actor)]
struct CounterActor {
name: String,
count: u32,
}
#[message_handlers]
impl CounterActor {
#[handler]
async fn handle_ping(&mut self, msg: Ping, _: &ActorRef<Self>) {
self.count += 1;
println!(
"[{}] Received ping with timestamp: {} (total: {})",
self.name, msg.timestamp, self.count
);
}
#[handler]
async fn handle_get_status(&mut self, _msg: GetStatus, _: &ActorRef<Self>) -> Status {
Status {
name: self.name.clone(),
message_count: self.count,
}
}
}
#[derive(Actor)]
struct LoggerActor {
prefix: String,
log_count: u32,
}
#[message_handlers]
impl LoggerActor {
#[handler]
async fn handle_ping(&mut self, msg: Ping, _: &ActorRef<Self>) {
self.log_count += 1;
println!(
"[{}] LOG: Ping received at {} (logged {} times)",
self.prefix, msg.timestamp, self.log_count
);
}
#[handler]
async fn handle_get_status(&mut self, _msg: GetStatus, _: &ActorRef<Self>) -> Status {
Status {
name: format!("{}-logger", self.prefix),
message_count: self.log_count,
}
}
}
#[tokio::main]
async fn main() -> Result<()> {
tracing_subscriber::fmt()
.with_max_level(tracing::Level::DEBUG)
.with_target(false)
.init();
println!("\n=== Handler Traits Demo ===\n");
let (counter_actor, counter_handle) = spawn::<CounterActor>(CounterActor {
name: "Counter-1".to_string(),
count: 0,
});
let (logger_actor, logger_handle) = spawn::<LoggerActor>(LoggerActor {
prefix: "App".to_string(),
log_count: 0,
});
println!("--- Demo 1: TellHandler (Fire-and-forget) ---\n");
let tell_handlers: Vec<Box<dyn TellHandler<Ping>>> = vec![
(&counter_actor).into(), (&logger_actor).into(), ];
for (i, handler) in tell_handlers.iter().enumerate() {
println!(
"Sending ping to handler {} (identity: {})",
i,
handler.as_control().identity()
);
handler
.tell(Ping {
timestamp: 1000 + i as u64,
})
.await?;
}
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
println!("\n--- Demo 2: AskHandler (Request-Response) ---\n");
let ask_handlers: Vec<Box<dyn AskHandler<GetStatus, Status>>> =
vec![(&counter_actor).into(), (&logger_actor).into()];
println!("Querying status from all handlers:");
for handler in &ask_handlers {
let status = handler.ask(GetStatus).await?;
println!(" - {}: {} messages", status.name, status.message_count);
}
println!("\n--- Demo 3: Clone Handlers ---\n");
let handler: Box<dyn TellHandler<Ping>> = (&counter_actor).into();
let handler_clone = handler.clone_boxed();
let handlers_vec: Vec<Box<dyn TellHandler<Ping>>> = vec![handler];
let handlers_vec_clone = handlers_vec.clone();
println!(
"Original handler identity: {}",
handlers_vec[0].as_control().identity()
);
println!(
"Cloned handler identity: {}",
handlers_vec_clone[0].as_control().identity()
);
handler_clone.tell(Ping { timestamp: 2000 }).await?;
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
println!("\n--- Demo 4: WeakTellHandler (Weak References) ---\n");
let weak_handlers: Vec<Box<dyn WeakTellHandler<Ping>>> = vec![
ActorRef::downgrade(&counter_actor).into(),
ActorRef::downgrade(&logger_actor).into(),
];
println!("Sending pings via weak handlers (upgrade pattern):");
for (i, weak_handler) in weak_handlers.iter().enumerate() {
if let Some(strong) = weak_handler.upgrade() {
println!(" Handler {} upgraded successfully, sending ping...", i);
strong
.tell(Ping {
timestamp: 3000 + i as u64,
})
.await?;
} else {
println!(" Handler {} failed to upgrade (actor dead)", i);
}
}
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
println!("\n--- Demo 5: Batch Operations with Single Upgrade ---\n");
let weak_handler: Box<dyn WeakTellHandler<Ping>> = ActorRef::downgrade(&counter_actor).into();
if let Some(strong) = weak_handler.upgrade() {
println!("Upgraded weak handler, sending batch of 3 pings:");
for i in 0..3 {
strong
.tell(Ping {
timestamp: 4000 + i,
})
.await?;
}
}
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
println!("\n--- Demo 6: Downgrade Strong to Weak ---\n");
let strong_handler: Box<dyn TellHandler<Ping>> = (&counter_actor).into();
println!(
"Strong handler identity: {}",
strong_handler.as_control().identity()
);
println!(
"Strong handler is_alive: {}",
strong_handler.as_control().is_alive()
);
let weak_from_strong: Box<dyn WeakTellHandler<Ping>> = strong_handler.downgrade();
println!(
"Weak handler identity: {}",
weak_from_strong.as_weak_control().identity()
);
println!(
"Weak handler is_alive: {}",
weak_from_strong.as_weak_control().is_alive()
);
if let Some(upgraded) = weak_from_strong.upgrade() {
println!("Successfully upgraded back to strong handler");
upgraded.tell(Ping { timestamp: 5000 }).await?;
}
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
println!("\n--- Demo 7: WeakAskHandler ---\n");
let weak_ask_handlers: Vec<Box<dyn WeakAskHandler<GetStatus, Status>>> = vec![
ActorRef::downgrade(&counter_actor).into(),
ActorRef::downgrade(&logger_actor).into(),
];
println!("Querying status via weak ask handlers:");
for weak_handler in &weak_ask_handlers {
if let Some(strong) = weak_handler.upgrade() {
let status = strong.ask(GetStatus).await?;
println!(" - {}: {} messages", status.name, status.message_count);
}
}
println!("\n--- Demo 8: Debug Formatting ---\n");
let tell_handler: Box<dyn TellHandler<Ping>> = (&counter_actor).into();
let weak_tell_handler: Box<dyn WeakTellHandler<Ping>> =
ActorRef::downgrade(&counter_actor).into();
println!("TellHandler debug: {:?}", tell_handler);
println!("WeakTellHandler debug: {:?}", weak_tell_handler);
println!("\n--- Cleanup ---\n");
counter_actor.stop().await?;
logger_actor.stop().await?;
let counter_result = counter_handle.await?;
let logger_result = logger_handle.await?;
println!(
"Counter actor stopped: {:?}",
counter_result.stopped_normally()
);
println!(
"Logger actor stopped: {:?}",
logger_result.stopped_normally()
);
println!("\nWeak handler after actor stop:");
for weak_handler in &weak_handlers {
println!(
" - identity: {}, is_alive: {}, can_upgrade: {}",
weak_handler.as_weak_control().identity(),
weak_handler.as_weak_control().is_alive(),
weak_handler.upgrade().is_some()
);
}
println!("\n=== Handler Demo Complete ===");
Ok(())
}