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;