ping_pong/
ping_pong.rs

1use actor12::prelude::*;
2use actor12::{Multi, MpscChannel, Call, spawn, Link};
3use std::future::Future;
4use std::time::Duration;
5use tokio::time::sleep;
6
7// Ping actor
8pub struct PingActor {
9    pong_actor: Option<Link<PongActor>>,
10    ping_count: u32,
11}
12
13// Pong actor  
14pub struct PongActor {
15    pong_count: u32,
16}
17
18// Messages
19#[derive(Debug)]
20pub struct StartPing(pub Link<PongActor>);
21
22#[derive(Debug)]
23pub struct Ping(pub u32);
24
25#[derive(Debug)]
26pub struct Pong(pub u32);
27
28// Ping Actor implementation
29impl Actor for PingActor {
30    type Spec = ();
31    type Message = Multi<Self>;
32    type Channel = MpscChannel<Self::Message>;
33    type Cancel = ();
34    type State = ();
35
36    fn state(_spec: &Self::Spec) -> Self::State {}
37
38    fn init(_ctx: Init<'_, Self>) -> impl Future<Output = Result<Self, Self::Cancel>> + Send + 'static {
39        async move {
40            println!("PingActor initialized");
41            Ok(PingActor {
42                pong_actor: None,
43                ping_count: 0,
44            })
45        }
46    }
47}
48
49impl Handler<StartPing> for PingActor {
50    type Reply = Result<(), anyhow::Error>;
51
52    async fn handle(&mut self, _ctx: Call<'_, Self, Self::Reply>, msg: StartPing) -> Self::Reply {
53        self.pong_actor = Some(msg.0);
54        
55        // Start the ping-pong game
56        if let Some(ref pong) = self.pong_actor {
57            self.ping_count += 1;
58            println!("Ping #{}", self.ping_count);
59            let _ = pong.ask_dyn(Ping(self.ping_count)).await;
60        }
61        
62        Ok(())
63    }
64}
65
66impl Handler<Pong> for PingActor {
67    type Reply = Result<(), anyhow::Error>;
68
69    async fn handle(&mut self, _ctx: Call<'_, Self, Self::Reply>, msg: Pong) -> Self::Reply {
70        println!("Received Pong #{}", msg.0);
71        
72        // Continue ping-pong for a few rounds
73        if self.ping_count < 5 {
74            if let Some(ref pong) = self.pong_actor {
75                self.ping_count += 1;
76                println!("Ping #{}", self.ping_count);
77                let _ = pong.ask_dyn(Ping(self.ping_count)).await;
78            }
79        } else {
80            println!("Ping-pong game finished!");
81        }
82        
83        Ok(())
84    }
85}
86
87// Pong Actor implementation
88impl Actor for PongActor {
89    type Spec = ();
90    type Message = Multi<Self>;
91    type Channel = MpscChannel<Self::Message>;
92    type Cancel = ();
93    type State = ();
94
95    fn state(_spec: &Self::Spec) -> Self::State {}
96
97    fn init(_ctx: Init<'_, Self>) -> impl Future<Output = Result<Self, Self::Cancel>> + Send + 'static {
98        async move {
99            println!("PongActor initialized");
100            Ok(PongActor { pong_count: 0 })
101        }
102    }
103}
104
105impl Handler<Ping> for PongActor {
106    type Reply = Result<(), anyhow::Error>;
107
108    async fn handle(&mut self, _ctx: Call<'_, Self, Self::Reply>, msg: Ping) -> Self::Reply {
109        self.pong_count += 1;
110        println!("Received Ping #{}, sending Pong #{}", msg.0, self.pong_count);
111        
112        // We need the ping actor reference to send back Pong
113        // For this example, we'll use the actor context to get the sender
114        Ok(())
115    }
116}
117
118// Since we need bidirectional communication, let's modify the approach
119#[derive(Debug)]
120pub struct PingPong {
121    other_actor: Option<Link<PingPong>>,
122    is_ping: bool,
123    count: u32,
124}
125
126#[derive(Debug)]
127pub struct Connect(pub Link<PingPong>);
128
129#[derive(Debug)]
130pub struct Ball(pub u32);
131
132impl Actor for PingPong {
133    type Spec = bool; // true for ping, false for pong
134    type Message = Multi<Self>;
135    type Channel = MpscChannel<Self::Message>;
136    type Cancel = ();
137    type State = ();
138
139    fn state(_spec: &Self::Spec) -> Self::State {}
140
141    fn init(ctx: Init<'_, Self>) -> impl Future<Output = Result<Self, Self::Cancel>> + Send + 'static {
142        let is_ping = ctx.spec;
143        async move {
144            let name = if is_ping { "Ping" } else { "Pong" };
145            println!("{} actor initialized", name);
146            Ok(PingPong {
147                other_actor: None,
148                is_ping,
149                count: 0,
150            })
151        }
152    }
153}
154
155impl Handler<Connect> for PingPong {
156    type Reply = Result<(), anyhow::Error>;
157
158    async fn handle(&mut self, _ctx: Call<'_, Self, Self::Reply>, msg: Connect) -> Self::Reply {
159        self.other_actor = Some(msg.0);
160        
161        // If this is the ping actor, start the game
162        if self.is_ping {
163            if let Some(ref other) = self.other_actor {
164                self.count = 1;
165                println!("Ping sends ball #{}", self.count);
166                let _ = other.ask_dyn(Ball(self.count)).await;
167            }
168        }
169        
170        Ok(())
171    }
172}
173
174impl Handler<Ball> for PingPong {
175    type Reply = Result<(), anyhow::Error>;
176
177    async fn handle(&mut self, _ctx: Call<'_, Self, Self::Reply>, msg: Ball) -> Self::Reply {
178        let name = if self.is_ping { "Ping" } else { "Pong" };
179        println!("{} receives ball #{}", name, msg.0);
180        
181        if msg.0 < 10 {
182            if let Some(ref other) = self.other_actor {
183                let next_count = msg.0 + 1;
184                println!("{} sends ball #{}", name, next_count);
185                let _ = other.ask_dyn(Ball(next_count)).await;
186            }
187        } else {
188            println!("{} stops the game at ball #{}", name, msg.0);
189        }
190        
191        Ok(())
192    }
193}
194
195#[tokio::main]
196async fn main() -> anyhow::Result<()> {
197    // Spawn ping and pong actors
198    let ping = spawn::<PingPong>(true);
199    let pong = spawn::<PingPong>(false);
200
201    // Connect them
202    let _ = ping.ask_dyn(Connect(pong.clone())).await;
203    let _ = pong.ask_dyn(Connect(ping)).await;
204
205    // Wait for the game to finish
206    sleep(Duration::from_secs(2)).await;
207
208    Ok(())
209}