zestors/messaging/mod.rs
1/*!
2# Overview
3In order to receive messages an actor must define a [`Protocol`] that specifies which messages it
4[`Accepts`]. All messages must in turn define how they are handled by implementing [`Message`].
5
6The [`Message`] trait specifies how a message should be handled by the actor. Since it is unique per
7message, every actor is guaranteed to respond in the same way. Normally [`Message`] is derived with the
8[`macro@Message`] macro, and most standard rust types like `u32`, `String` and `Vec<String>`, have a
9message-implementation.
10
11The [`Protocol`] specifies exactly which messages an actor accepts. Normally the [`Protocol`] trait
12can be automatically generated with the [`macro@protocol`] macro.
13
14# The `Message` macro
15When using the derive [`macro@Message`] macro it is possible to set a `#[msg(T)]` or `#[request(T)]`
16attribute which specifies how the actor should handle the message. There are three types for which this
17is implemented automatically:
18
19| Attribute | Result |
20|---|---|
21| `none` / `#[msg(())]` | A simple message that does not receive a reply, the [`Message::Payload`] is `M` and [`Message::Returned`] is `()`. |
22| `#[request(T)]` / `#[msg(Rx<T>)]` | A request of `T` where the [`Message::Payload`] is [`(M, Tx<T>)`](Tx) and the [`Message::Returned`] is [`Rx<T>`]. |
23| `#[msg(Tx<T>)]` | Same as `Rx` but swapped. |
24
25It is possible to create custom types usable in the `#[msg(..)]` attribute by implementing [`MessageDerive<M>`]
26for this type.
27
28# Sending
29The following send-methods can be used on an [`Address`], [`Child`], [`InboxType`] or any other type that
30implements [`ActorRef`] / [`ActorRefExt`].
31
32__Default send methods__: The default methods for sending a message.
33- [`try_send`](ActorRefExt::try_send): Attempts to send a message to the actor. If the inbox is closed/full
34or if a timeout is returned from the backpressure-mechanic, this method fails.
35- [`force_send`](ActorRefExt::force_send): Same as `try_send` but ignores any backpressure-mechanic.
36- [`send`](ActorRefExt::send): Attempts to send a message to the actor. If the inbox is full or if a timeout
37is returned from the backpressure-mechanic, this method will wait until there is space or until the timeout has
38expired. If the inbox is closed this method fails.
39- [`send_blocking`](ActorRefExt::send_blocking): Same as `send` but blocks the thread for non-async
40execution environments.
41
42__Checked-send methods__: Same as the regular send methods, but instead of checking at
43compile-time whether the messages are accepted by the actor, these check it at runtime. These methods are
44only valid for [`DynActor`](struct@DynActor) types.
45- [`try_send_checked`](ActorRefExt::try_send_checked)
46- [`force_send_checked`](ActorRefExt::force_send_checked)
47- [`send_checked`](ActorRefExt::send_checked)
48- [`send_blocking_checked`](ActorRefExt::send_blocking_checked)
49
50# Requesting
51A request is a [`Message`] which expects a reply to be sent from the [`Tx`] to the [`Rx`]. A request can
52be manually created with [`new_request`], but is usually automatically created when sending a message.
53Requests can be sent in the standard way -- by first sending the request and then waiting for the reply -- but
54this can also be done simpler with the following methods:
55- [`try_request`](ActorRefExt::try_request)
56- [`force_request`](ActorRefExt::force_request)
57- [`request`](ActorRefExt::request)
58
59These methods will send the request and subsequently await a response from the actor with a single method
60and `.await` point.
61
62# Envelope
63An [`Envelope`](struct@Envelope) is a [`Message`] containing an [`Address`] of where it should be sent. An envelope
64can be created with the [`ActorRefExt::envelope`] function.
65
66The [`macro@Envelope`] macro generates a custom trait that allows a user to directly call `.my_message(..)` on an
67actor-reference. This custom method constructs an [`Envelope`](struct@Envelope) from the [`Message`] parameters which
68can subsequently be sent. This macro is entirely optional and just exists for ergonomics.
69
70| [`actor_reference`](crate::actor_reference) __-->__ |
71|---|
72
73# Example
74```
75#![allow(unused)]
76use zestors::prelude::*;
77#[macro_use]
78extern crate zestors;
79
80// First we will define two different messages.
81// A simple message ..
82#[derive(Message, Envelope, Debug, Clone)]
83struct MyMessage {
84 param1: String,
85 param2: u32,
86}
87
88// .. and a request.
89#[derive(Message, Envelope, Debug, Clone)]
90#[request(u32)]
91struct MyRequest {
92 param1: String,
93 param2: u32,
94}
95
96// Now we are ready to define our protocol!
97// This protocol accepts three messages: `MyMessage`, `MyRequest` and `u32`.
98#[protocol]
99enum MyProtocol {
100 Msg1(MyMessage),
101 Msg2(MyRequest),
102 U32(u32),
103}
104// That is our actor-definition done.
105
106// We can now start using it!
107#[tokio::main]
108async fn main() {
109 // Let's spawn a basic actor that just prints any messages it receives ..
110 let (child, address) = spawn(|mut inbox: Inbox<MyProtocol>| async move {
111 loop {
112 match inbox.recv().await.unwrap() {
113 MyProtocol::Msg1(msg1) => {
114 println!("Received: {:?}", msg1);
115 }
116 MyProtocol::Msg2((msg2, tx)) => {
117 println!("Received: {:?}", msg2);
118 tx.send(msg2.param2 + 10).unwrap();
119 }
120 MyProtocol::U32(msg_u32) => {
121 println!("Received: {:?}", msg_u32);
122 }
123 }
124 }
125 });
126
127 let my_msg = MyMessage {
128 param1: "hi".to_string(),
129 param2: 10,
130 };
131 let my_request = MyRequest {
132 param1: "hi".to_string(),
133 param2: 10,
134 };
135
136 // .. and send it some messages!
137 address.send(10u32).await.unwrap();
138 address.send(my_msg.clone()).await.unwrap();
139
140 // We can also request (with boilerplate) ..
141 let reply: Rx<u32> = address.send(my_request.clone()).await.unwrap();
142 assert_eq!(20, reply.await.unwrap());
143
144 // .. or do it without the boilerplate!
145 assert_eq!(20, address.request(my_request.clone()).await.unwrap());
146
147 // It is also possible to send a message by creating an envelope and sending that ..
148 address.envelope(my_msg.clone()).send().await.unwrap();
149 address
150 .envelope(my_request.clone())
151 .request()
152 .await
153 .unwrap();
154
155 // .. or directly by using our derived Envelope trait!
156 address
157 .my_message("hi".to_string(), 10)
158 .send()
159 .await
160 .unwrap();
161 address
162 .my_request("hi".to_string(), 10)
163 .request()
164 .await
165 .unwrap();
166
167 // As a final example, we can use the Accepts-bound on a function:
168 send_using_accepts(&address, my_msg.clone()).await;
169}
170
171async fn send_using_accepts(address: &Address<impl Accepts<MyMessage>>, msg: MyMessage) {
172 address.send(msg).await.unwrap();
173}
174```
175*/
176
177#[allow(unused)]
178use crate::all::*;
179
180pub use zestors_codegen::{protocol, Envelope, Message};
181mod accepts;
182mod box_payload;
183mod envelope;
184mod errors;
185mod message;
186mod protocol;
187mod request;
188pub use accepts::*;
189pub use box_payload::*;
190pub use envelope::*;
191pub use errors::*;
192pub use message::*;
193pub use protocol::*;
194pub use request::*;