ghost_actor/lib.rs
1#![deny(warnings)]
2#![deny(missing_docs)]
3#![allow(clippy::needless_doctest_main)]
4#![cfg_attr(feature = "unstable", feature(fn_traits))]
5#![cfg_attr(feature = "unstable", feature(unboxed_closures))]
6//! A simple, ergonomic, idiomatic, macro for generating the boilerplate
7//! to use rust futures tasks in a concurrent actor style.
8//!
9//! ## Hello World Example
10//!
11//! ```rust
12//! # use ghost_actor::*;
13//! // Most of the GhostActor magic happens in this macro.
14//! // Sender and Handler traits will be generated here.
15//! ghost_chan! {
16//! pub chan HelloWorldApi<GhostError> {
17//! fn hello_world() -> String;
18//! }
19//! }
20//!
21//! // ... We'll skip implementing a handler for now ...
22//! # struct HelloWorldImpl;
23//! # impl GhostControlHandler for HelloWorldImpl {}
24//! # impl GhostHandler<HelloWorldApi> for HelloWorldImpl {}
25//! # impl HelloWorldApiHandler for HelloWorldImpl {
26//! # fn handle_hello_world(
27//! # &mut self,
28//! # ) -> HelloWorldApiHandlerResult<String> {
29//! # Ok(must_future::MustBoxFuture::new(async move {
30//! # Ok("hello world!".to_string())
31//! # }))
32//! # }
33//! # }
34//! # pub async fn spawn_hello_world(
35//! # ) -> Result<GhostSender<HelloWorldApi>, GhostError> {
36//! # let builder = actor_builder::GhostActorBuilder::new();
37//! # let sender = builder
38//! # .channel_factory()
39//! # .create_channel::<HelloWorldApi>()
40//! # .await?;
41//! # tokio::task::spawn(builder.spawn(HelloWorldImpl));
42//! # Ok(sender)
43//! # }
44//!
45//! #[tokio::main]
46//! async fn main() -> Result<(), GhostError> {
47//! // spawn our actor, getting the actor sender.
48//! let sender = spawn_hello_world().await?;
49//!
50//! // we can make async calls on the sender
51//! assert_eq!("hello world!", &sender.hello_world().await?);
52//! println!("{}", sender.hello_world().await?);
53//!
54//! Ok(())
55//! }
56//! ```
57//!
58//! What's going on Here?
59//!
60//! - The `ghost_chan!` macro writes some types and boilerplate for us.
61//! - We'll dig into implementing actor handlers below.
62//! - We are able to spawn an actor that runs as a futures task.
63//! - We can make async requests on that actor, and get results inline.
64//!
65//! ## The `ghost_chan!` Macro
66//!
67//! ```rust
68//! # use ghost_actor::*;
69//! ghost_chan! {
70//! pub chan HelloWorldApi<GhostError> {
71//! fn hello_world() -> String;
72//! }
73//! }
74//! # pub fn main() {}
75//! ```
76//!
77//! The `ghost_chan!` macro takes care of writing the boilerplate for using
78//! async functions to communicate with an "actor" running as a futures
79//! task. The tests/examples here use tokio for the task executor, but
80//! the GhostActorBuilder returns a driver future for the actor task that you
81//! can manage any way you'd like.
82//!
83//! The `ghost_chan!` macro generates some important types, many of which
84//! are derived by pasting words on to the end of your actor name.
85//! We'll use the actor name `HelloWorldApi` from above as an example:
86//!
87//! - `HelloWorldApiSender` - The "Sender" trait generated for your actor
88//! allows users with a `GhostSender<HelloWorldApi>` instance to make
89//! async calls. Basically, this "Sender" trait provides the API that
90//! makes the whole actor system work.
91//! - `HelloWorldApiHandler` - This "Handler" trait is what allows you
92//! to implement an actor task that can respond to requests sent by
93//! the "Sender".
94//! - `HelloWorldApi` - You may have noticed above, the "Sender" instance
95//! that users of your api will receive is typed as
96//! `GhostSender<HelloWorldApi>`. The item that receives the name of your
97//! actor without having anything pasted on to it is actually a `GhostEvent`
98//! enum designed for carrying messages from your "Sender" to your
99//! "Handler", and then delivering the result back to your API user.
100//!
101//! ## Implementing an Actor Handler
102//!
103//! ```rust
104//! # use ghost_actor::*;
105//! # ghost_chan! {
106//! # pub chan HelloWorldApi<GhostError> {
107//! # fn hello_world() -> String;
108//! # }
109//! # }
110//! /// We need a struct to implement our handler upon.
111//! struct HelloWorldImpl;
112//!
113//! /// All handlers must implement GhostControlHandler.
114//! /// This provides a default no-op handle_ghost_actor_shutdown impl.
115//! impl GhostControlHandler for HelloWorldImpl {}
116//!
117//! /// Implement GhostHandler for your specific GhostEvent type.
118//! /// Don't worry, the compiler will let you know if you forget this : )
119//! impl GhostHandler<HelloWorldApi> for HelloWorldImpl {}
120//!
121//! /// Now implement your actual handler -
122//! /// auto generated by the `ghost_chan!` macro.
123//! impl HelloWorldApiHandler for HelloWorldImpl {
124//! fn handle_hello_world(&mut self) -> HelloWorldApiHandlerResult<String> {
125//! Ok(must_future::MustBoxFuture::new(async move {
126//! // return our results
127//! Ok("hello world!".to_string())
128//! }))
129//! }
130//! }
131//! # pub fn main() {}
132//! ```
133//!
134//! Pretty straight forward. We implement a couple required traits,
135//! then our "Handler" trait that actually defines the logic of our actor.
136//! Then, we're ready to spawn it!
137//!
138//! ## Spawning an Actor
139//!
140//! ```rust
141//! # use ghost_actor::*;
142//! # ghost_chan! {
143//! # pub chan HelloWorldApi<GhostError> {
144//! # fn hello_world() -> String;
145//! # }
146//! # }
147//! # struct HelloWorldImpl;
148//! # impl GhostControlHandler for HelloWorldImpl {}
149//! # impl GhostHandler<HelloWorldApi> for HelloWorldImpl {}
150//! # impl HelloWorldApiHandler for HelloWorldImpl {
151//! # fn handle_hello_world(
152//! # &mut self,
153//! # ) -> HelloWorldApiHandlerResult<String> {
154//! # Ok(must_future::MustBoxFuture::new(async move {
155//! # Ok("hello world!".to_string())
156//! # }))
157//! # }
158//! # }
159//! /// Use the GhostActorBuilder to construct the actor task.
160//! pub async fn spawn_hello_world(
161//! ) -> Result<GhostSender<HelloWorldApi>, GhostError> {
162//! // first we need a builder
163//! let builder = actor_builder::GhostActorBuilder::new();
164//!
165//! // now let's register an event channel with this actor.
166//! let sender = builder
167//! .channel_factory()
168//! .create_channel::<HelloWorldApi>()
169//! .await?;
170//!
171//! // actually spawn the actor driver task
172//! // providing our implementation
173//! tokio::task::spawn(builder.spawn(HelloWorldImpl));
174//!
175//! // return the sender that controls the actor
176//! Ok(sender)
177//! }
178//! # pub fn main() {}
179//! ```
180//!
181//! Note how we actually get access to the cheaply-clonable "Sender"
182//! before we have to construct our actor "Handler" item. This means
183//! you can create channels that will be able to message the actor,
184//! and include those senders in your handler struct. More on this later.
185//!
186//! ## The Complete Hello World Example
187//!
188//! - [https://github.com/holochain/ghost_actor/blob/master/crates/ghost_actor/examples/hello_world.rs](https://github.com/holochain/ghost_actor/blob/master/crates/ghost_actor/examples/hello_world.rs)
189//!
190//! ## Custom Errors
191//!
192//! A single ghost channel / actor api will use a single error / result type.
193//! You can use the provided `ghost_actor::GhostError` type - or you can
194//! specify a custom error type.
195//!
196//! Your custom error type must support `From<GhostError>`.
197//!
198//! ```rust
199//! # use ghost_actor::*;
200//! #[derive(Debug, thiserror::Error)]
201//! pub enum MyError {
202//! /// Custom error types MUST implement `From<GhostError>`
203//! #[error(transparent)]
204//! GhostError(#[from] GhostError),
205//!
206//! /// Of course, you can also have your own variants as well
207//! #[error("My Error Type")]
208//! MyErrorType,
209//! }
210//!
211//! ghost_chan! {
212//! /// The error type for actor apis is specified in the macro
213//! /// as the single generic following the actor name:
214//! pub chan MyActor<MyError> {
215//! fn my_fn() -> ();
216//! }
217//! }
218//! # pub fn main() {}
219//! ```
220//!
221//! ## Efficiency! - Ghost Actor's Synchronous Handler Blocks
222//!
223//! GhostActor handler traits are carefully costructed to allow `&'a mut self`
224//! access to the handler item, but return a `'static` future. That `'static`
225//! means references to the handler item cannot be captured in any async code.
226//!
227//! This can be frustrating for new users, but serves a specific purpose!
228//!
229//! We are being good rust futures authors and working around any blocking
230//! code in the manner our executor frameworks recommend, so our actor
231//! handler can process messages at lightning speed!
232//!
233//! Our actor doesn't have to context switch, because it has all its mutable
234//! internal state right here in this thread handling all these messages. And,
235//! when it's done with one message, it moves right onto the next without
236//! interuption. When the message queue is drained it schedules a wakeup for
237//! when there is more data to process.
238//!
239//! In writing our code to support this pattern, we find that our code natually
240//! tends toward patterns that support parallel work being done to make better
241//! use of modern multi-core processors.
242//!
243//! See especially the "Internal Sender Pattern" in the next section below.
244//!
245//! ## Advanced Patterns for Working with Ghost Actors
246//!
247//! - [Internal Sender Pattern](https://github.com/holochain/ghost_actor/blob/master/crates/ghost_actor/examples/pattern_internal_sender.rs) -
248//! Facilitates undertaking async work in GhostActor handler functions.
249//! - [Event Publish/Subscribe Pattern](https://github.com/holochain/ghost_actor/blob/master/crates/ghost_actor/examples/pattern_event_pub_sub.rs) -
250//! Facilitates an actor's ability to async emit notifications/requests,
251//! and a "parent" actor being able to handle events from a child actor.
252//! - [Clone Channel Factory Pattern](https://github.com/holochain/ghost_actor/blob/master/crates/ghost_actor/examples/pattern_clone_channel_factory.rs) -
253//! Facilitates an actor's ability to absorb additional channel
254//! receivers post-spawn.
255
256/// Re-exported dependencies to help with macro references.
257pub mod dependencies {
258 pub use futures;
259 pub use must_future;
260 pub use paste;
261 pub use thiserror;
262 pub use tracing;
263 pub use tracing_futures;
264}
265
266mod types;
267pub use types::*;
268
269mod chan_macro;
270pub use chan_macro::*;
271
272pub mod actor_builder;
273
274mod tests;