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
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
// #![warn(missing_docs)]

//! [`Aurum`](crate) implements a distributed actor model. Its purpose is to make distributed programming
//! easy. [`Aurum`](crate) provides support for clustering, [CRDT] sharing among cluster members and [IoT]
//! device tracking. It has excellent type safety and full [serde] support.
//!
//! # Prerequisite Knowledge
//! We suggest having this knowledge before starting here:
//! - A basic understanding of the [actor model]
//! - Some knowledge of [serialization] and [serde]
//! - [`async/await`] in Rust
//!
//! # Why Would You Want to Use [`Aurum`](crate)?
//! ### Typed Actors
//! Actor references are typed, meaning they can only receive messages of a specific type. Untyped
//! actor references can receive messages of any type. Typed references add safety by turning some
//! runtime errors into compile time errors, and eliminate the need for catch-all match branches
//! that handle invalid messages. They also make code easier to read. Type information gives extra
//! hints of an actor’s role in the program. Actor references are statically typed. The type of
//! messages they receive must be defined at compile time. Generics are used to communicate the
//! types of messages. Most distributed actor model implementations use untyped actors.
//!
//! ### Actor Interfaces
//! It is not unusual for someone to want to store actor references in a data structure (for
//! instance, to keep track of subscribers to events). Collections in Rust must be homogeneous,
//! but there may be many different kinds of actors who want to subscribe to the same events.
//! You cannot keep actor references receiving different types in the same data structure, nor is
//! creating a different structure for every possible type desirable, nor do we want to create
//! separate actors that relay event messages to the real subscriber under its preferred type. Actor
//! interfaces allow the programmer to define a subset of message types receivable by an actor, and
//! create actor references which receive only that subset.
//!
//! Actor interfaces are also useful if some of the messages an actor receives are serializable, and
//! others are not. You can define a serializable subset, and create actor references that receive
//! that subset. [`Aurum`](crate) provides annotations on message types to create automatic
//! translations between subtypes and root message types.
//!
//! ### Forgeable References
//! [`Aurum`](crate) was created to address fill niches in existing actor models like [Akka]. While
//! [Akka] has support for both typed and untyped actors, typed actor references in [Akka] are not
//! forgeable (i.e. they cannot be created independently). With [Akka]'s typed actors, you need a
//! complex discovery protocol built underneath to acquire a reference to the actor for sending
//! messages.
//!
//! A single system may have many actors running on it, so these actors receive their messages on
//! same network socket. Messages are then routed locally using a registry. Each actor is registered
//! on their socket with a unique identifier. Our problem comes from [Akka] using strings for its
//! actor identifiers. Strings do not contain any type information about the actor, so how do we
//! know what type to give our reference we just forged? It's not safe to just guess.  [`Aurum`](crate) fixes
//! this problem by embedding type information within actor identifiers. Identifiers consist of both
//! a string and a type id for discriminating between identifiers with the same string name. The
//! type safety of forged references is enforced at compile time. None of this type safety comes at
//! the cost of performance.
//!
//! # Modules and Features
//! [`Aurum`](crate) is a heavily modularized piece of software. [`cluster`](crate::cluster) is built
//! on top of [`core`](crate::core) with no internal access. If you plan on building a library on top
//! of [`Aurum`](crate), [`cluster`](crate::cluster) is a good reference.
//! - [`cluster`](crate::cluster): Single data center clustering.
//!   - [`crdt`](crate::cluster::crdt): Intra-cluster CRDT sharing.
//!   - [`devices`](crate::cluster::devices): External node management. Useful for IoT.
//! - [`core`](crate::core): The basics. Start here if you're new to [`Aurum`](crate) and want to
//! learn how to use it.
//! - [`testkit`](crate::testkit): Tools for testing actors and injecting failures.
//!
//! [`async/await`]: https://rust-lang.github.io/async-book/
//! [serialization]: https://en.wikipedia.org/wiki/Serialization
//! [actor model]: https://en.wikipedia.org/wiki/Actor_model
//! [serde]: https://docs.serde.rs/serde/
//! [CRDT]: https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type
//! [Akka]: https://akka.io/
//! [IoT]: https://en.wikipedia.org/wiki/Internet_of_things

pub mod cluster;
pub mod core;
pub mod testkit;
extern crate aurum_actors_macros;

/// Creates a [`UnifiedType`](crate::core::UnifiedType) and implements traits for it.
///
/// This macro is central to how [`Aurum`](crate) functions. Because we have actor interfaces, there
/// are multiple possible interpretations of a sequence of bytes. In order to deserialize messages
/// correctly, we need to know how to interpret the bytes. We need to know what type was serialized.
/// Information on what type the message is must be included within the message.
/// [`UnifiedType`](crate::core::UnifiedType) instances are also used to safely deserialize
/// [`Destination`](crate::core::Destination) instances, which contain a
/// [`UnifiedType`](crate::core::UnifiedType) instance. Generic type information does not serialize,
/// so if a [`Destination`](crate::core::Destination) is deserialized and interpreted with a
/// generic type, we have to make sure that the [`ActorId`](crate::core::ActorId) and interface
/// match that generic type as well as match each other, to make sure we do not create a
/// [`Destination`](crate::core::Destination) that is invalid according to our type system.
///
/// Rust's [`TypeId`](std::any::TypeId) is not serializable, and does not come with any guarantees
/// at all. We had to create our own system of reflection to fix this problem, but it is fairly easy
/// to use. The [`UnifiedType`](crate::core::UnifiedType) created by this macro is an enum, whose
/// variant represent all possible root message types and actor interfaces usable in an application.
/// A type that is not in the [`UnifiedType`](crate::core::UnifiedType) may not be used in your
/// application. [`Aurum`](crate) uses the [`Case`](crate::core::Case) trait to enforce this restriction.
/// The end users must define a single [`UnifiedType`](crate::core::UnifiedType) for their
/// application. DO NOT communicate between two [`Node`](crate::core::Node) instances with different
/// types, things are bound to go wrong.
///
/// [`Case::VARIANT`](crate::core::Case::VARIANT) can be used to create instances of its
/// [`UnifiedType`](crate::core::UnifiedType) from type information without needing to access the
/// variants of that [`UnifiedType`](crate::core::UnifiedType), which are defined in the macro. This
/// is how [`ActorRef`](crate::core::ActorRef) and [`Destination`](crate::core::Destination)
/// construct [`UnifiedType`](crate::core::UnifiedType) instances for forging.
///
/// If you are writing a library built on top of
/// [`Aurum`](crate), you can use [`Case`](crate::core::Case) to restrict your user's
/// [`UnifiedType`](crate::core::UnifiedType) to make sure it is compatible with your messages.
/// Users are required to list your dependent message types in their invocation of
/// [`unify`](crate::unify). This is how [`cluster`](crate::cluster) would normally be implemented,
/// but the [`Case`](crate::core::Case) bounds for [`UnifiedType`](crate::core::UnifiedType) include
/// the dependent message types for [`cluster`](crate::cluster) for the sake on convenience.
///
/// ```
/// use async_trait::async_trait;
/// use aurum_actors::{unify, AurumInterface};
/// use aurum_actors::core::{Actor, ActorContext, Case, UnifiedType};
/// use im;
/// use serde::{Serialize, Deserialize};
/// 
/// #[derive(AurumInterface, Serialize, Deserialize)]
/// enum MsgTypeForSomeThirdPartyLibrary {
///   #[aurum]
///   Something(InterfaceForSomeThirdPartyLibrary)
/// }
/// 
/// #[derive(Serialize, Deserialize)]
/// struct InterfaceForSomeThirdPartyLibrary;
///
/// struct LibraryActor;
/// #[async_trait]
/// impl<U> Actor<U, MsgTypeForSomeThirdPartyLibrary> for LibraryActor
/// where
///   U: UnifiedType
///     + Case<MsgTypeForSomeThirdPartyLibrary>
///     + Case<InterfaceForSomeThirdPartyLibrary>
/// {
///   async fn recv(
///     &mut self, 
///     ctx: &ActorContext<U, MsgTypeForSomeThirdPartyLibrary>,
///     msg: MsgTypeForSomeThirdPartyLibrary
///   ) {
///     // logic
///   }
/// }
/// 
/// #[derive(AurumInterface)]
/// #[aurum(local)]
/// enum MyMsgType {
///   First,
///   Second
/// }
/// 
/// #[derive(AurumInterface)]
/// #[aurum(local)]
/// enum MyOtherMsgType {
///   Nonserializable(::std::fs::File),
///   #[aurum]
///   Serializable(InterfaceForSomeThirdPartyLibrary)
/// }
/// 
/// unify! { pub MyUnifiedType =
///   MyMsgType |
///   MyOtherMsgType |
///   MsgTypeForSomeThirdPartyLibrary
///   ;
///   String |
///   InterfaceForSomeThirdPartyLibrary
/// }
/// ```
///
/// The syntax for [`unify`](crate::unify) is as follows: an optional visibility modifier, a name
/// for the [`UnifiedType`](crate::core::UnifiedType) to be created, a `=`, followed by a list of
/// types implementing [`RootMessage`](crate::core::RootMessage) separated by `|`, a `;`, then a
/// list of remote interfaces separated by `|`.
pub use aurum_actors_macros::unify;

/// Implements [`RootMessage`](crate::core::RootMessage) and other traits for a root message type.
///
/// This derive macro is applicable to enums only. It generates all the necessary implementations
/// for the message type to interact with the [`UnifiedType`](crate::core::UnifiedType) and produce
/// interfaces. It uses the `#[aurum]` atrribute to configure interfaces. The `#[aurum]` attribute
/// has a single optional argument: `local`. Use `#[aurum(local)]` to notify [`AurumInterface`] that
/// the interface is exclusively local.
///
/// ```
/// use aurum_actors::AurumInterface;
/// 
/// type StrStaticRef = &'static str;
/// 
/// #[derive(AurumInterface)]
/// #[aurum(local)]
/// enum DiverseMsgType {
///   #[aurum]
///   MyStringInterface(String),
///   #[aurum(local)]
///   MyNonserializableInterface(StrStaticRef),
///   MyOtherMsg(u128),
/// }
/// ```
///
/// In this example, because one of the message options is not serializable, the message type as a
/// whole is not serializable. However, you can use an
/// [`ActorRef<MyUnifiedType, String>`](crate::core::ActorRef) to send a string from a remote
/// machine to whatever actor uses this message type. You can also create a
/// [`LocalRef<&’static str>`](crate::core::LocalRef), but not a usable
/// [`ActorRef`](crate::core::ActorRef).
///
/// [`AurumInterface`] creates an implementation of [`RootMessage`](crate::core::RootMessage) for
/// the type it is invoked on. The [`RootMessage`](crate::core::RootMessage) implementation is
/// blanketed over all types implementing [`UnifiedType`](crate::core::UnifiedType). The blanket is
/// bounded by [`Case`](crate::core::Case) implementation for the root type and each remote
/// interface. In the example, the implementation is bounded by
/// [`Case<MyMsgType>`](crate::core::Case) and [`Case<String>`](crate::core::Case) but not
/// [`Case<&'static str>`](crate::core::Case).
///
/// [`AurumInterface`] implements [`From`](std::convert::From) on every interface type, local or
/// not. In the example, [`From<String>`](std::convert::From) and
/// [`From<&'static str>`](std::convert::From) are implemented for `MyMsgType`.
/// 
/// This macro's parsing is a work in progress, so for now you will need to create aliases for some
/// types.
pub use aurum_actors_macros::AurumInterface;