acty/
lib.rs

1//! # Acty
2//!
3//! Acty is a lightweight and high-performance Actor framework built on top of Tokio.
4//! It adheres to the core philosophies of "everything is an Actor" and "sender-driven lifecycle,"
5//! aiming to provide a simple, safe, and easy-to-use concurrency model.
6//!
7//! ## Core Concepts
8//!
9//! - **Actor**: Any type that implements the [`Actor`] trait is an Actor. Its core is the `run` method, which contains all the business logic and state management.
10//! - **Lifecycle**: An Actor's lifecycle is determined by its message senders (the `Outbox`). When all associated `Outbox` instances are dropped, the Actor's message channel (`inbox`) closes, and the Actor task gracefully terminates. This design eliminates the complexity of manually sending "stop" messages.
11//! - **Messaging**: By calling the [`ActorExt::start`] method to launch an Actor, you receive an [`UnboundedOutbox`] (or [`BoundedOutbox`]). This `Outbox` is the sole handle for sending messages to the Actor.
12//! - **State Management**: The Actor's state usually resides as local variables within the asynchronous scope of its `run` method, rather than as fields of the structure. This ensures cleaner state isolation and ownership management.
13//!
14//! ## Result Communication (Backchannel)
15//!
16//! An Actor can send its final computation result back to the caller through mechanisms like `oneshot` channels before its task ends.
17//!
18//! ### Example: An Actor that concatenates strings
19//! ```rust
20//! use std::pin::pin;
21//! use std::fmt::Write;
22//! use futures::{Stream, StreamExt};
23//! use acty::{Actor, ActorExt, AsyncClose};
24//!
25//! // 1. Define the Actor structure. It holds a oneshot::Sender to return the result.
26//! struct MyActor {
27//!    result: tokio::sync::oneshot::Sender<String>,
28//! }
29//!
30//! // 2. Implement the Actor trait, defining the message type and core logic.
31//! impl Actor for MyActor {
32//!     type Message = String;
33//!
34//!     async fn run(self, inbox: impl Stream<Item = Self::Message> + Send) {
35//!         // Pin the inbox to the stack for asynchronous iteration.
36//!         let mut inbox = pin!(inbox);
37//!
38//!         // The Actor's internal state is defined and managed within the run method.
39//!         let mut article = String::new();
40//!         let mut count = 1;
41//!
42//!         // Loop through messages until the inbox closes.
43//!         while let Some(msg) = inbox.next().await {
44//!             write!(article, "part {}: {}\n", count, msg.as_str()).unwrap();
45//!             count += 1;
46//!         }
47//!
48//!         // When all Outboxes are dropped, the loop ends. Send the final result here.
49//!         // Sending might fail if the receiver has already given up waiting, so we ignore the error.
50//!         let _ = self.result.send(article);
51//!     }
52//! }
53//!
54//! #[tokio::main]
55//! async fn main() {
56//!     // 3. Create the channel for receiving the result.
57//!     let (tx, rx) = tokio::sync::oneshot::channel();
58//!
59//!     // 4. Instantiate and launch the Actor, getting the Outbox handle.
60//!     let my_actor = MyActor { result: tx }.start();
61//!
62//!     // 5. Send messages to the Actor. Ignore potential send errors caused by early shutdown.
63//!     my_actor.send("It's a good day today.".to_string()).unwrap_or(());
64//!     my_actor.send("Let's have some tea first.".to_string()).unwrap_or(());
65//!
66//!     // 6. Close the outbox. Since it's the last Outbox, this triggers the Actor's shutdown.
67//!     // .close().await waits for the Actor task to fully complete.
68//!     my_actor.close().await;
69//!
70//!     // 7. Wait for and retrieve the Actor's final execution result.
71//!     assert_eq!(
72//!         "part 1: It's a good day today.\npart 2: Let's have some tea first.\n",
73//!         rx.await.expect("Actor task failed to send the result")
74//!     );
75//! }
76//! ```
77//!
78//! For more examples, please see the examples directory.
79
80mod actor;
81mod close;
82mod launch;
83mod outbox;
84mod start;
85
86pub use {
87    actor::Actor, close::AsyncClose, launch::Launch, outbox::BoundedOutbox,
88    outbox::UnboundedOutbox, start::ActorExt,
89};