# Puppet - A simple actor framework Puppet is yet another actor framework built without any boxing or dynamic dispatch of items, instead it uses a small macro to essentially generate the boilerplate for an enum-based system. ## Features - Generic-aware, which means you can have generic actors return generic messages. - Box-less, no requirement for `async_trait` or boxing for dynamic dispatch. - Linter friendly, your editors should be able to easily infer the return types from all exposed methods. - Sync and Async mailbox methods. ## Basic Example Let's create a small actor for creating hello messages... ```rust use puppet::{puppet_actor, ActorMailbox, Message}; pub struct MyActor; #[puppet_actor] impl MyActor { #[puppet] async fn on_say_hello(&self, msg: SayHello) -> String { format!("Hello, {}!", msg.name) } } pub struct SayHello { name: String } impl Message for SayHello { type Output = String; } #[tokio::main] async fn main() { // Create the actor. let actor = MyActor; // Spawn it on the current runtime, which returns to us a mailbox which // we can use to communicate with the actor. let mailbox: ActorMailbox = actor.spawn_actor().await; let message = SayHello { name: "Harri".to_string(), }; // Send a message to the actor and wait for a response. let response = mailbox.send(message).await; println!("Got message back! {}", response); } ``` ## Generic Example Now what if we want to do some more advanced things with our actors? Well luckily for us, we can use generics. ```rust use puppet::{puppet_actor, ActorMailbox, Message}; pub struct AppenderService { seen_data: Vec, } #[puppet_actor] impl AppenderService where // The additional `Send` and `'static` bounds are required due to the nature // of the actor running as a tokio task which has it's own requirements. T: Clone + Send + 'static, { fn new() -> Self { Self { seen_data: Vec::new(), } } #[puppet] async fn on_append_and_return(&mut self, msg: AppendAndReturn) -> Vec { self.seen_data.push(msg.value); self.seen_data.clone() } } #[derive(Clone)] pub struct AppendAndReturn { value: T, } impl Message for AppendAndReturn where T: Clone, { type Output = Vec; } #[tokio::main] async fn main() { let actor = AppenderService::::new(); let mailbox: ActorMailbox> = actor.spawn_actor().await; let message = AppendAndReturn { value: "Harri".to_string(), }; for _ in 0..3 { let response = mailbox.send(message.clone()).await; println!("Got values: {:?}", response); } } ```