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 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
#![deny(warnings)] #![deny(missing_docs)] #![allow(clippy::needless_doctest_main)] #![cfg_attr(feature = "unstable", feature(fn_traits))] #![cfg_attr(feature = "unstable", feature(unboxed_closures))] //! A simple, ergonomic, idiomatic, macro for generating the boilerplate //! to use rust futures tasks in a concurrent actor style. //! //! ## Hello World Example //! //! ```rust //! # use ghost_actor::*; //! // Most of the GhostActor magic happens in this macro. //! // Sender and Handler traits will be generated here. //! ghost_chan! { //! pub chan HelloWorldApi<GhostError> { //! fn hello_world() -> String; //! } //! } //! //! // ... We'll skip implementing a handler for now ... //! # struct HelloWorldImpl; //! # impl GhostControlHandler for HelloWorldImpl {} //! # impl GhostHandler<HelloWorldApi> for HelloWorldImpl {} //! # impl HelloWorldApiHandler for HelloWorldImpl { //! # fn handle_hello_world( //! # &mut self, //! # ) -> HelloWorldApiHandlerResult<String> { //! # Ok(must_future::MustBoxFuture::new(async move { //! # Ok("hello world!".to_string()) //! # })) //! # } //! # } //! # pub async fn spawn_hello_world( //! # ) -> Result<GhostSender<HelloWorldApi>, GhostError> { //! # let builder = actor_builder::GhostActorBuilder::new(); //! # let sender = builder //! # .channel_factory() //! # .create_channel::<HelloWorldApi>() //! # .await?; //! # tokio::task::spawn(builder.spawn(HelloWorldImpl)); //! # Ok(sender) //! # } //! //! #[tokio::main] //! async fn main() -> Result<(), GhostError> { //! // spawn our actor, getting the actor sender. //! let sender = spawn_hello_world().await?; //! //! // we can make async calls on the sender //! assert_eq!("hello world!", &sender.hello_world().await?); //! println!("{}", sender.hello_world().await?); //! //! Ok(()) //! } //! ``` //! //! What's going on Here? //! //! - The `ghost_chan!` macro writes some types and boilerplate for us. //! - We'll dig into implementing actor handlers below. //! - We are able to spawn an actor that runs as a futures task. //! - We can make async requests on that actor, and get results inline. //! //! ## The `ghost_chan!` Macro //! //! ```rust //! # use ghost_actor::*; //! ghost_chan! { //! pub chan HelloWorldApi<GhostError> { //! fn hello_world() -> String; //! } //! } //! # pub fn main() {} //! ``` //! //! The `ghost_chan!` macro takes care of writing the boilerplate for using //! async functions to communicate with an "actor" running as a futures //! task. The tests/examples here use tokio for the task executor, but //! the GhostActorBuilder returns a driver future for the actor task that you //! can manage any way you'd like. //! //! The `ghost_chan!` macro generates some important types, many of which //! are derived by pasting words on to the end of your actor name. //! We'll use the actor name `HelloWorldApi` from above as an example: //! //! - `HelloWorldApiSender` - The "Sender" trait generated for your actor //! allows users with a `GhostSender<HelloWorldApi>` instance to make //! async calls. Basically, this "Sender" trait provides the API that //! makes the whole actor system work. //! - `HelloWorldApiHandler` - This "Handler" trait is what allows you //! to implement an actor task that can respond to requests sent by //! the "Sender". //! - `HelloWorldApi` - You may have noticed above, the "Sender" instance //! that users of your api will receive is typed as //! `GhostSender<HelloWorldApi>`. The item that receives the name of your //! actor without having anything pasted on to it is actually a `GhostEvent` //! enum designed for carrying messages from your "Sender" to your //! "Handler", and then delivering the result back to your API user. //! //! ## Implementing an Actor Handler //! //! ```rust //! # use ghost_actor::*; //! # ghost_chan! { //! # pub chan HelloWorldApi<GhostError> { //! # fn hello_world() -> String; //! # } //! # } //! /// We need a struct to implement our handler upon. //! struct HelloWorldImpl; //! //! /// All handlers must implement GhostControlHandler. //! /// This provides a default no-op handle_ghost_actor_shutdown impl. //! impl GhostControlHandler for HelloWorldImpl {} //! //! /// Implement GhostHandler for your specific GhostEvent type. //! /// Don't worry, the compiler will let you know if you forget this : ) //! impl GhostHandler<HelloWorldApi> for HelloWorldImpl {} //! //! /// Now implement your actual handler - //! /// auto generated by the `ghost_chan!` macro. //! impl HelloWorldApiHandler for HelloWorldImpl { //! fn handle_hello_world(&mut self) -> HelloWorldApiHandlerResult<String> { //! Ok(must_future::MustBoxFuture::new(async move { //! // return our results //! Ok("hello world!".to_string()) //! })) //! } //! } //! # pub fn main() {} //! ``` //! //! Pretty straight forward. We implement a couple required traits, //! then our "Handler" trait that actually defines the logic of our actor. //! Then, we're ready to spawn it! //! //! ## Spawning an Actor //! //! ```rust //! # use ghost_actor::*; //! # ghost_chan! { //! # pub chan HelloWorldApi<GhostError> { //! # fn hello_world() -> String; //! # } //! # } //! # struct HelloWorldImpl; //! # impl GhostControlHandler for HelloWorldImpl {} //! # impl GhostHandler<HelloWorldApi> for HelloWorldImpl {} //! # impl HelloWorldApiHandler for HelloWorldImpl { //! # fn handle_hello_world( //! # &mut self, //! # ) -> HelloWorldApiHandlerResult<String> { //! # Ok(must_future::MustBoxFuture::new(async move { //! # Ok("hello world!".to_string()) //! # })) //! # } //! # } //! /// Use the GhostActorBuilder to construct the actor task. //! pub async fn spawn_hello_world( //! ) -> Result<GhostSender<HelloWorldApi>, GhostError> { //! // first we need a builder //! let builder = actor_builder::GhostActorBuilder::new(); //! //! // now let's register an event channel with this actor. //! let sender = builder //! .channel_factory() //! .create_channel::<HelloWorldApi>() //! .await?; //! //! // actually spawn the actor driver task //! // providing our implementation //! tokio::task::spawn(builder.spawn(HelloWorldImpl)); //! //! // return the sender that controls the actor //! Ok(sender) //! } //! # pub fn main() {} //! ``` //! //! Note how we actually get access to the cheaply-clonable "Sender" //! before we have to construct our actor "Handler" item. This means //! you can create channels that will be able to message the actor, //! and include those senders in your handler struct. More on this later. //! //! ## The Complete Hello World Example //! //! - [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) //! //! ## Custom Errors //! //! A single ghost channel / actor api will use a single error / result type. //! You can use the provided `ghost_actor::GhostError` type - or you can //! specify a custom error type. //! //! Your custom error type must support `From<GhostError>`. //! //! ```rust //! # use ghost_actor::*; //! #[derive(Debug, thiserror::Error)] //! pub enum MyError { //! /// Custom error types MUST implement `From<GhostError>` //! #[error(transparent)] //! GhostError(#[from] GhostError), //! //! /// Of course, you can also have your own variants as well //! #[error("My Error Type")] //! MyErrorType, //! } //! //! ghost_chan! { //! /// The error type for actor apis is specified in the macro //! /// as the single generic following the actor name: //! pub chan MyActor<MyError> { //! fn my_fn() -> (); //! } //! } //! # pub fn main() {} //! ``` //! //! ## Efficiency! - Ghost Actor's Synchronous Handler Blocks //! //! GhostActor handler traits are carefully costructed to allow `&'a mut self` //! access to the handler item, but return a `'static` future. That `'static` //! means references to the handler item cannot be captured in any async code. //! //! This can be frustrating for new users, but serves a specific purpose! //! //! We are being good rust futures authors and working around any blocking //! code in the manner our executor frameworks recommend, so our actor //! handler can process messages at lightning speed! //! //! Our actor doesn't have to context switch, because it has all its mutable //! internal state right here in this thread handling all these messages. And, //! when it's done with one message, it moves right onto the next without //! interuption. When the message queue is drained it schedules a wakeup for //! when there is more data to process. //! //! In writing our code to support this pattern, we find that our code natually //! tends toward patterns that support parallel work being done to make better //! use of modern multi-core processors. //! //! See especially the "Internal Sender Pattern" in the next section below. //! //! ## Advanced Patterns for Working with Ghost Actors //! //! - [Internal Sender Pattern](https://github.com/holochain/ghost_actor/blob/master/crates/ghost_actor/examples/pattern_internal_sender.rs) - //! Facilitates undertaking async work in GhostActor handler functions. //! - [Event Publish/Subscribe Pattern](https://github.com/holochain/ghost_actor/blob/master/crates/ghost_actor/examples/pattern_event_pub_sub.rs) - //! Facilitates an actor's ability to async emit notifications/requests, //! and a "parent" actor being able to handle events from a child actor. //! - [Clone Channel Factory Pattern](https://github.com/holochain/ghost_actor/blob/master/crates/ghost_actor/examples/pattern_clone_channel_factory.rs) - //! Facilitates an actor's ability to absorb additional channel //! receivers post-spawn. /// Re-exported dependencies to help with macro references. pub mod dependencies { pub use futures; pub use must_future; pub use paste; pub use thiserror; pub use tracing; pub use tracing_futures; } mod types; pub use types::*; mod chan_macro; pub use chan_macro::*; pub mod actor_builder; mod tests;