use async_trait::async_trait;
use joerl::gen_server::{CallResponse, GenServer, GenServerContext, GenServerRef, spawn};
use joerl::{ActorSystem, ExitReason, Message};
use std::sync::Arc;
use std::time::Duration;
struct Counter;
#[derive(Debug)]
enum CounterCall {
GetValue,
Start { server_ref: GenServerRef<Counter> },
}
#[derive(Debug)]
enum CounterCast {
Increment { server_ref: GenServerRef<Counter> },
}
#[async_trait]
impl GenServer for Counter {
type State = (i32, i32); type Call = CounterCall;
type Cast = CounterCast;
type CallReply = i32;
async fn init(&mut self, _ctx: &mut GenServerContext<'_, Self>) -> Self::State {
println!("[Counter] Initializing");
(0, 10) }
async fn handle_call(
&mut self,
call: Self::Call,
state: &mut Self::State,
_ctx: &mut GenServerContext<'_, Self>,
) -> CallResponse<Self::CallReply> {
match call {
CounterCall::GetValue => {
println!("[Counter] Current value: {}", state.0);
CallResponse::Reply(state.0)
}
CounterCall::Start { server_ref } => {
println!("[Counter] Starting self-incrementing loop");
let _ = server_ref
.cast(CounterCast::Increment {
server_ref: server_ref.clone(),
})
.await;
CallResponse::Reply(state.0)
}
}
}
async fn handle_cast(
&mut self,
cast: Self::Cast,
state: &mut Self::State,
ctx: &mut GenServerContext<'_, Self>,
) {
match cast {
CounterCast::Increment { server_ref } => {
state.0 += 1;
println!("[Counter] Incremented to {}", state.0);
if state.0 < state.1 {
let _ = server_ref
.cast(CounterCast::Increment {
server_ref: server_ref.clone(),
})
.await;
} else {
println!("[Counter] Reached max value {}, stopping", state.1);
ctx.stop(ExitReason::Normal);
}
}
}
}
async fn terminate(
&mut self,
reason: &ExitReason,
state: &mut Self::State,
_ctx: &mut GenServerContext<'_, Self>,
) {
println!(
"[Counter] Terminated with reason: {}, final value: {}",
reason, state.0
);
}
}
struct Ticker;
#[derive(Debug)]
enum TickerCall {
Start,
}
#[derive(Debug)]
enum TickerCast {}
#[async_trait]
impl GenServer for Ticker {
type State = (u32, u32); type Call = TickerCall;
type Cast = TickerCast;
type CallReply = u32;
async fn init(&mut self, _ctx: &mut GenServerContext<'_, Self>) -> Self::State {
println!("[Ticker] Initializing");
(0, 5) }
async fn handle_call(
&mut self,
call: Self::Call,
state: &mut Self::State,
ctx: &mut GenServerContext<'_, Self>,
) -> CallResponse<Self::CallReply> {
match call {
TickerCall::Start => {
println!("[Ticker] Starting periodic ticker");
let timer_ref = ctx.send_info_after(Box::new("tick"), Duration::from_millis(100));
println!("[Ticker] Scheduled timer: {:?}", timer_ref);
CallResponse::Reply(state.0)
}
}
}
async fn handle_cast(
&mut self,
_cast: Self::Cast,
_state: &mut Self::State,
_ctx: &mut GenServerContext<'_, Self>,
) {
}
async fn handle_info(
&mut self,
msg: Message,
state: &mut Self::State,
ctx: &mut GenServerContext<'_, Self>,
) {
if let Some(tick) = msg.downcast_ref::<&str>()
&& *tick == "tick"
{
state.0 += 1;
println!("[Ticker] Tick #{}", state.0);
if state.0 < state.1 {
ctx.send_info_after(Box::new("tick"), Duration::from_millis(100));
} else {
println!("[Ticker] Reached max ticks ({}), stopping", state.1);
ctx.stop(ExitReason::Normal);
}
}
}
async fn terminate(
&mut self,
reason: &ExitReason,
state: &mut Self::State,
_ctx: &mut GenServerContext<'_, Self>,
) {
println!(
"[Ticker] Terminated with reason: {}, final ticks: {}",
reason, state.0
);
}
}
#[tokio::main]
async fn main() {
println!("=== GenServer Self-Messaging Examples ===\n");
let system = Arc::new(ActorSystem::new());
println!("--- Example 1: Immediate Self-Messaging ---");
let counter_ref = spawn(&system, Counter);
println!("[Main] Spawned counter with PID: {}\n", counter_ref.pid());
let value = counter_ref.call(CounterCall::GetValue).await.unwrap();
println!("[Main] Initial value: {}\n", value);
let _ = counter_ref
.call(CounterCall::Start {
server_ref: counter_ref.clone(),
})
.await;
tokio::time::sleep(Duration::from_millis(200)).await;
println!("\n--- Example 2: Scheduled Self-Messaging (Ticker) ---");
let ticker_ref = spawn(&system, Ticker);
println!("[Main] Spawned ticker with PID: {}\n", ticker_ref.pid());
let _ = ticker_ref.call(TickerCall::Start).await;
tokio::time::sleep(Duration::from_millis(650)).await;
println!("\n=== All examples completed ===");
}