rsactor 0.14.1

A Simple and Efficient In-Process Actor Model Implementation for Rust.
Documentation
# Basic Usage

This example demonstrates the fundamental concepts of rsActor through a simple counter actor that maintains internal state and processes messages.

## Overview

The basic example shows:
- How to define an actor with internal state
- Implementing the `Actor` trait with lifecycle methods
- Creating and handling different message types
- Using the `#[message_handlers]` macro
- Spawning actors and managing their lifecycle
- Periodic tasks within actors using `on_run`

## Code Example

```rust
use anyhow::Result;
use rsactor::{message_handlers, Actor, ActorRef, ActorWeak};
use tokio::time::{interval, Duration};
use tracing::info;

// Message types
struct Increment; // Message to increment the actor's counter
struct Decrement; // Message to decrement the actor's counter

// Define the actor struct
struct MyActor {
    count: u32,                        // Internal state of the actor
    start_up: std::time::Instant,      // Track the start time
    tick_300ms: tokio::time::Interval, // Interval for 300ms ticks
    tick_1s: tokio::time::Interval,    // Interval for 1s ticks
}

// Implement the Actor trait for MyActor
impl Actor for MyActor {
    type Args = Self;
    type Error = anyhow::Error;

    // Called when the actor is started
    async fn on_start(args: Self::Args, _actor_ref: &ActorRef<Self>) -> Result<Self, Self::Error> {
        info!("MyActor started. Initial count: {}.", args.count);
        Ok(args)
    }

    // Called repeatedly when the message queue is empty (idle handler).
    // Returns Ok(true) to continue calling on_run, Ok(false) to stop idle processing.
    // Note: receives &ActorWeak<Self>, not &ActorRef<Self>.
    async fn on_run(&mut self, _actor_weak: &ActorWeak<Self>) -> Result<bool, Self::Error> {
        // Use tokio::select! to handle multiple async operations
        tokio::select! {
            _ = self.tick_300ms.tick() => {
                println!("300ms tick. Elapsed: {:?}", self.start_up.elapsed());
            }
            _ = self.tick_1s.tick() => {
                println!("1s tick. Elapsed: {:?}", self.start_up.elapsed());
            }
        }
        Ok(true) // Continue calling on_run
    }
}

// Message handling using the #[message_handlers] macro with #[handler] attributes
#[message_handlers]
impl MyActor {
    #[handler]
    async fn handle_increment(&mut self, _msg: Increment, _: &ActorRef<Self>) -> u32 {
        self.count += 1;
        println!("MyActor handled Increment. Count is now {}.", self.count);
        self.count
    }

    #[handler]
    async fn handle_decrement(&mut self, _msg: Decrement, _: &ActorRef<Self>) -> u32 {
        self.count -= 1;
        println!("MyActor handled Decrement. Count is now {}.", self.count);
        self.count
    }
}

#[tokio::main]
async fn main() -> Result<()> {
    tracing_subscriber::fmt()
        .with_max_level(tracing::Level::DEBUG)
        .with_target(false)
        .init();

    // Create actor instance with initial state
    let my_actor = MyActor {
        count: 100,
        start_up: std::time::Instant::now(),
        tick_300ms: interval(Duration::from_millis(300)),
        tick_1s: interval(Duration::from_secs(1)),
    };

    // Spawn the actor
    let (actor_ref, join_handle) = rsactor::spawn::<MyActor>(my_actor);
    println!("MyActor spawned with ID: {}", actor_ref.identity());

    // Wait a bit to see some ticks
    tokio::time::sleep(Duration::from_millis(700)).await;

    // Send messages and await replies
    println!("Sending Increment message...");
    let count_after_inc: u32 = actor_ref.ask(Increment).await?;
    println!("Reply after Increment: {}", count_after_inc);

    println!("Sending Decrement message...");
    let count_after_dec: u32 = actor_ref.ask(Decrement).await?;
    println!("Reply after Decrement: {}", count_after_dec);

    // Wait for more ticks
    tokio::time::sleep(Duration::from_millis(700)).await;

    // Stop the actor gracefully
    println!("Stopping actor...");
    actor_ref.stop().await?;

    // Wait for completion and check result
    let result = join_handle.await?;
    match result {
        rsactor::ActorResult::Completed { actor, killed } => {
            println!("Actor completed. Final count: {}. Killed: {}",
                     actor.count, killed);
        }
        rsactor::ActorResult::Failed { actor, error, phase, killed } => {
            println!("Actor failed: {}. Phase: {:?}, Killed: {}",
                     error, phase, killed);
            if let Some(actor) = actor {
                println!("Final count: {}", actor.count);
            }
        }
    }

    Ok(())
}
```

## Key Concepts Demonstrated

### 1. Actor State Management
The `MyActor` struct encapsulates:
- `count`: The main business state
- `start_up`: Timing information
- `tick_300ms` and `tick_1s`: Periodic timers

### 2. Lifecycle Methods
- **`on_start`**: Initializes the actor when spawned
- **`on_run`**: Runs continuously, handling periodic tasks with `tokio::select!`

### 3. Message Handling
- `Increment` and `Decrement` messages modify the actor's state
- Each message handler returns the new count value
- Messages are processed sequentially, ensuring thread safety

### 4. Actor Communication
- **`ask`**: Send a message and wait for a reply
- **`tell`**: Send a message without waiting (fire-and-forget)
- **`stop`**: Gracefully shutdown the actor

### 5. Error Handling
The example demonstrates proper error handling throughout the actor lifecycle and shows how to access the final actor state after completion.

## Running the Example

```bash
cargo run --example basic
```

You'll see output showing the periodic ticks, message processing, and graceful shutdown.

## Next Steps

This basic example forms the foundation for more complex actor patterns. Next, explore:
- [Async Worker]async_worker.md for I/O-intensive tasks
- [Blocking Task]blocking_task.md for CPU-intensive operations
- [Actor with Timeout]actor_with_timeout.md for timeout handling