1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
//! agner — ~~processes~~ actors.
//! =====
//!
//! Note: Right now this is a research project, i.e. it is possible that the API will undergo
//! incompatible changes within the version 0.3.x.
//!
//! As it has been stated, agner is inspired by Erlang/OTP, so all similarities to the actual
//! frameworks, supported or obsolete, are purely intentional. :)
//!
//!
//! # [Actors](crate::actors)
//!
//! An [actor](https://en.wikipedia.org/wiki/Actor_model) is an activity with the following properties:
//! - runs in parallel (implemented as a [`Future`](std::future::Future));
//! - has a handle ([`ActorID`](crate::actors::ActorID));
//! - can receive messages;
//! - when terminates — yields an exit reason ([`Exit`](crate::actors::Exit));
//! - any two actors can be [linked](crate::actors::Context::link) with each other:
//! - if one of the linked actors exits with a reason other than
//! [`Exit::normal()`](crate::actors::Exit::normal()) — the other receives an exit-signal;
//! - if the process receiving an exit-signal does not ["trap
//! exits"](crate::actors::Context::trap_exit), it will also be terminated.
//!
//! ## Implementing an Actor
//! The actor's behaviour is defined by:
//! - the type of its argument;
//! - the type of the message it accepts;
//! - the behaviour function.
//!
//! In order to implement an actor one should define an async function that
//! - returns a value for which the trait [`Into<Exit>`](crate::actors::Exit) is
//! defined
//! - and accepts two arguments:
//! - a mutable reference to [`Context<Message>`](crate::actors::Context);
//! - `Argument`.
//!
//! Example:
//! ```
//! use agner::actors::{Context, Exit, Never};
//!
//! async fn shutdown_after_six_messages(context: &mut Context<String>, actor_name: String) {
//! for i in 0..6 {
//! let message = context.next_message().await;
//! eprintln!("Actor {:?} received {:?}", actor_name, message);
//! }
//! }
//! ```
//!
//! ## Spawning an Actor
//!
//! Actors cannot run on their own, they need an [actor system](crate::actors::System) to be spawned
//! in. This is necessary to avoid having a global state imposed by mere usage of the library.
//! A [`System`](crate::actors::System) is a scope within which the actors run.
//!
//! Example:
//! ```
//! use agner::actors::{System, Context, Exit, Never};
//!
//! async fn a_dummy(context: &mut Context<Option<String>>, actor_name: String) {
//! eprintln!("{}: hi!", actor_name);
//!
//! while let Some(message) = context.next_message().await {
//! eprintln!("{}: received {:?}", actor_name, message);
//! }
//!
//! eprintln!("{}: bye!", actor_name);
//! }
//!
//! #[tokio::main]
//! async fn main() {
//! // create a system with default configuration
//! let system = System::new(Default::default());
//!
//! let actor_id = system.spawn(
//! a_dummy,
//! "the-dummy".to_owned(),
//! Default::default())
//! .await.expect("Failed to spawn an actor");
//!
//! system.send(actor_id, Some("one".to_owned())).await;
//! system.send(actor_id, Some("two".to_owned())).await;
//! system.send(actor_id, Some("three".to_owned())).await;
//! system.send(actor_id, Option::<String>::None).await;
//!
//! let exit_reason = system.wait(actor_id).await;
//! eprintln!("{} exited: {:?}", actor_id, exit_reason);
//! }
//! ```
//!
//! ## Terminating an Actor
//!
//! ### "Willful" Termination
//!
//! #### Returning from the Behaviour Function
//!
//! If the actor's behaviour function returns — the actor terminates.
//! The return type of the behaviour function must implement the trait
//! [`Into<Exit>`](crate::actors::Exit).
//!
//! Example:
//! ```
//! use std::convert::Infallible;
//! use agner::actors::{Context, Exit};
//!
//! async fn unit_is_normal_exit(_context: &mut Context<Infallible>, _args:()) {}
//!
//! async fn result_into_exit(_context: &mut Context<Infallible>, _args:()) -> Result<(), Exit> {
//! Ok(()) // Equivalent to `Err(Exit::normal())`
//! }
//! ```
//!
//! #### Invoking `Context::exit`
//!
//! Example:
//! ```
//! use std::convert::Infallible;
//! use agner::actors::{Context, Exit};
//!
//! async fn normal_exit(context: &mut Context<Infallible>, args: ()) -> Infallible {
//! context.exit(Exit::normal()).await;
//! unreachable!()
//! }
//! ```
//!
//! ### Terminating from Outside
//!
//! An actor can be terminated by invoking [`System::exit(&self, ActorID,
//! Exit)`](crate::actors::System::exit).
//!
//! In this case an actor receives an exit-signal. If the actor ["traps
//! exits"](crate::actors::Context::trap_exit), it can perform a graceful shutdown (or even keep
//! running). That is if the exit reason is not [`Exit::kill()`](crate::actors::Exit::kill): in this
//! case the actor just terminates, and its linked actors in their turn receive exit-signals.
//!
//!
//!
//! # Supervision
//!
//! > As this whole project is based on the Erlang/OTP, it should not be a surprise that there
//! > are some (quite a lot of) similarities to the OTP Design Principles, namely:
//! > - [Supervision Trees](https://www.erlang.org/doc/design_principles/des_princ.html#supervision-trees);
//! > - [Special Processes / Starting the Process](https://www.erlang.org/doc/design_principles/spec_proc.html#starting-the-process);
//! > - [Supervisor Behaviour](https://www.erlang.org/doc/design_principles/sup_princ.html).
//!
//! ## Design Principles
//!
//! ### Starting and Stopping
//!
//! TBD:
//! - [`sup::common::start_child`](crate::sup::common::start_child)
//! - [`sup::common::stop_child`](crate::sup::common::stop_child))
//! - [`init_ack`](crate::init_ack)
//!
//! ### Uniform Supervisor
//!
//! TBD:
//! - [uniform supervisor](crate::sup::uniform)
//!
//! ### Mixed Supervisor
//!
//! TBD:
//! - [mixed supervisor](crate::sup::mixed)
//!
//! # Introspection
//!
//! TBD:
//! - [helm](crate::helm)
//!
//! # Testing
//!
//! TBD:
//! - [test-actor](crate::test_actor)
pub use agner_utils as utils;
pub use agner_actors as actors;
pub use agner_init_ack as init_ack;
pub use agner_reg as reg;
pub use agner_sup as sup;
pub use agner_helm as helm;
pub use agner_test_actor as test_actor;
pub use log;
pub use agner_log as log;