handler/
handler.rs

1#[macro_use]
2extern crate zestors;
3use zestors::{export::async_trait, prelude::*};
4
5// Let's start by defining a message ..
6#[derive(Message, Debug)]
7#[request(u32)]
8pub struct PrintString {
9    val: String,
10}
11
12// .. and a protocol.
13#[protocol]
14#[derive(Debug)]
15pub enum MyProtocol {
16    A(u32),
17    B(PrintString),
18    C(Action<MyHandler>),
19}
20
21// Now we can define our handler ..
22#[derive(Debug)]
23pub struct MyHandler {
24    handled: u32,
25}
26
27// .. and implement the main trait `Handler` (or use #[derive(Handler)])
28#[async_trait]
29impl Handler for MyHandler {
30    type State = Inbox<MyProtocol>;
31    type Exception = eyre::Report;
32    type Stop = ();
33    type Exit = u32;
34
35    async fn handle_exit(
36        self,
37        _state: &mut Self::State,
38        reason: Result<Self::Stop, Self::Exception>,
39    ) -> ExitFlow<Self> {
40        match reason {
41            // Upon ok, we exit normally.
42            Ok(()) => ExitFlow::Exit(self.handled),
43            // When an error occured, we also exit but log it.
44            Err(exception) => {
45                println!("[ERROR] Actor exited with an exception: {exception}");
46                ExitFlow::Exit(self.handled)
47            }
48        }
49    }
50
51    async fn handle_event(&mut self, state: &mut Self::State, event: Event) -> HandlerResult<Self> {
52        // For most events we stop our actor.
53        // When the actor is halted we just close the inbox and continue until it is empty.
54        match event {
55            Event::Halted => {
56                state.close();
57                Ok(Flow::Continue)
58            }
59            Event::ClosedAndEmpty => Ok(Flow::Stop(())),
60            Event::Dead => Ok(Flow::Stop(())),
61        }
62    }
63}
64
65// Let's handle a u32 message ..
66#[async_trait]
67impl HandleMessage<u32> for MyHandler {
68    async fn handle_msg(
69        &mut self,
70        _state: &mut Self::State,
71        msg: u32,
72    ) -> Result<Flow<Self>, Self::Exception> {
73        self.handled += msg;
74        Ok(Flow::Continue)
75    }
76}
77
78// .. and our custom request.
79#[async_trait]
80impl HandleMessage<PrintString> for MyHandler {
81    async fn handle_msg(
82        &mut self,
83        _state: &mut Self::State,
84        (msg, tx): (PrintString, Tx<u32>),
85    ) -> Result<Flow<Self>, Self::Exception> {
86        println!("{}", msg.val);
87        let _ = tx.send(self.handled);
88        self.handled += 1;
89        Ok(Flow::Continue)
90    }
91}
92
93// That was all!
94#[tokio::main]
95async fn main() {
96    // Let's spawn our actor:
97    let (child, address) = MyHandler { handled: 0 }.spawn();
98    // We can send it a basic message:
99    address.send(10u32).await.unwrap();
100
101    // Send it a bunch of requests that will print the message.
102    for i in 0..10 {
103        let response = address
104            .request(PrintString {
105                val: String::from("Printing a message"),
106            })
107            .await
108            .unwrap();
109        println!("Got response {i} = {response}");
110    }
111
112    // Or send it a custom closure with the `action!` macro
113    child
114        .send(action!(|handler: &mut MyHandler, _state| async move {
115            println!("This is now 20: `{}`", handler.handled);
116            Ok(Flow::Continue)
117        }))
118        .await
119        .unwrap();
120
121    // And finally our actor can be halted and will exit!
122    child.halt();
123    assert!(matches!(child.await, Ok(20)));
124}