Expand description
§abcgen - Actor’s Boilerplate Code Generator
abcgen helps you to build Actor object by producing all the boilerplate code needed by this patter, meaning that all the code involved in defining/sending/raceiving/unwrapping messages and managing lifetime of the actor is hidden from user.
The user should only focus on the logic of the service that the actor is going to provide.
abcgen produces Actor objects that are based on the async/await syntax and the tokio library.
The actor objects generated do not require any scheduler o manager to run, they are standalone and can be used in any (tokio) context.
§Basic example
The following example is minimale and does not shocase all the features of the library. Check the README for more details.
#[abcgen::actor_module]
#[allow(unused)]
mod actor {
use abcgen::{actor, message_handler, send_task, AbcgenError, PinnedFuture, Task};
#[actor]
pub struct MyActor {
pub some_value: i32,
}
impl MyActor {
pub async fn start(&mut self, task_sender: TaskSender) {
println!("Starting");
// here you can spawn some tasks using tokio::spawn
// or enqueue some tasks into the actor's main loop by sending them to task_sender
tokio::spawn(async move {
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
send_task!( task_sender(this) => { this.example_task().await; } );
});
}
pub async fn shutdown(&mut self) {
println!("Shutting down");
}
#[message_handler]
async fn get_value(&mut self, name: &'static str) -> Result<i32, &'static str> {
match name {
"some_value" => Ok(self.some_value),
_ => Err("Invalid name"),
}
}
async fn example_task(&mut self) {
println!("Example task executed");
}
}
}
#[tokio::main]
async fn main() {
let actor: actor::MyActorProxy = actor::MyActor { some_value: 32 }.run();
let the_value = actor.get_value("some_value").await.unwrap();
assert!(matches!(the_value, Ok(32)));
let the_value = actor.get_value("some_other_value").await.unwrap();
assert!(matches!(the_value, Err("Invalid name")));
}
§The user should provide:
- a struct or enum definition marked with
actorattribute - implement start(…) and shutdown(…) methods for the
actor - implement, for the
actor, a set of methods marked withmessage_handlerattribute; these are going to handle the messages that theactorcan receive. - optionally, an enum definition marked with
eventsattribute to define the events that theactorcan signal
§The procedural macro will generate:
- implementation of
run(self)method for theactorwhich will return an ActorProxy - implementation of message handling logic for the
actor:- calling the
start(...)method before entering theactor’s loop - calling the
shutdown(&mut self)method after exiting theactor’s loop - handling of stop signal
- handling of messages (support replies)
- handling of tasks (functions that can be enqueued to be invoked in the
actor’s loop so the can access&mut Actor)
- calling the
- an ActorProxy object that implements all of the methods that were marked with
message_handlerattribute - a message enum that contains all the messages that the
actorcan receive (which is not meant to be used directly by the user)
More details can be found in the README file.
Macros§
- send_
task - A macro to simplify sending tasks to the actor
Following below example you need to provide a refence to
task_senderand
the name for the reference to the actor (thisin the example). The macro will create a closure that returx aPinnedFutureexpressed with |this| Box::pin(async move { … }) and send it trough thetask_sender. Example:
Enums§
- Abcgen
Error - Error type for the code generated by the abcgen
Type Aliases§
- Pinned
Future - Helper type for async tasks The tasks that are meant to be sent to the actor need to return this type
- Task
- The type of the tasks that are sent to the actor
Attribute Macros§
- actor
- This attribute is used to mark the struct or enum that is going to be the actor.
- actor_
module - The
actor_moduleattribute is used to mark a module that contains the actor definition. It will generate the necessary code to implement the actor pattern, that is: - events
- This attribute is used to mark the enum that defines the events that the actor can signal. It can be applied to
- message_
handler - This attribute is used to mark the methods that are going to handle the messages that the actor can receive.