rsactor 0.14.1

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

The `ActorResult<T: Actor>` enum is returned when an actor's lifecycle completes. This is typically what you get when you `.await` the `JoinHandle` returned by `rsactor::spawn()`.

It provides detailed information about how the actor terminated.

```rust
/// Represents the phase during which an actor failure occurred.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FailurePhase {
    /// Actor failed during the `on_start` lifecycle hook.
    OnStart,
    /// Actor failed during execution in the `on_run` lifecycle hook.
    OnRun,
    /// Actor failed during the `on_stop` lifecycle hook.
    OnStop,
}

/// Result type returned when an actor's lifecycle completes.
#[derive(Debug)]
pub enum ActorResult<T: Actor> {
    /// Actor completed successfully and can be recovered.
    Completed {
        /// The successfully completed actor instance
        actor: T,
        /// Whether the actor was killed (`true`) or stopped gracefully (`false`)
        killed: bool,
    },
    /// Actor failed during one of its lifecycle phases.
    Failed {
        /// The actor instance (if recoverable), or None if not recoverable.
        /// This will be `None` specifically when the failure occurred during `on_start`,
        /// as the actor wasn't fully initialized.
        actor: Option<T>,
        /// The error that caused the failure
        error: T::Error,
        /// The lifecycle phase during which the failure occurred
        phase: FailurePhase,
        /// Whether the actor was killed (`true`) or was attempting to stop gracefully (`false`)
        killed: bool,
    },
}
```

### Variants

1.  **`ActorResult::Completed { actor: T, killed: bool }`**
    *   Indicates that the actor finished its lifecycle without any errors defined by `T::Error`.
    *   `actor`: The instance of the actor. You can retrieve it if you need to inspect its final state.
    *   `killed`: A boolean indicating if the actor was terminated via a `kill()` signal (`true`) or if it stopped gracefully (e.g., via `stop()` or by naturally ending its run loop) (`false`).

2.  **`ActorResult::Failed { actor: Option<T>, error: T::Error, phase: FailurePhase, killed: bool }`**
    *   Indicates that the actor encountered an error (`T::Error`) during its operation.
    *   `actor`: An `Option<T>` containing the actor instance if it was recoverable. This will be `None` if the failure occurred during the `OnStart` phase, as the actor instance wouldn't have been fully created.
    *   `error`: The specific error of type `T::Error` (defined in your `impl Actor for YourActor`) that caused the failure.
    *   `phase`: A `FailurePhase` enum (`OnStart`, `OnRun`, `OnStop`) indicating when the error occurred in the actor's lifecycle.
    *   `killed`: A boolean indicating if the actor was attempting to stop due to a `kill()` signal when the failure occurred.

### Utility Methods

`ActorResult` provides several helpful methods:

*   `is_completed()`: Returns `true` if the actor completed successfully.
*   `is_failed()`: Returns `true` if the actor failed.
*   `was_killed()`: Returns `true` if the actor was killed, regardless of completion or failure.
*   `stopped_normally()`: Returns `true` if `Completed` and not `killed`.
*   `is_startup_failed()`: Returns `true` if failed during `OnStart`.
*   `is_runtime_failed()`: Returns `true` if failed during `OnRun`.
*   `is_stop_failed()`: Returns `true` if failed during `OnStop`.
*   `actor()`: Returns `Option<&T>` to the actor instance if available.
*   `into_actor()`: Consumes self and returns `Option<T>`.
*   `error()`: Returns `Option<&T::Error>` if failed.
*   `into_error()`: Consumes self and returns `Option<T::Error>`.
*   `has_actor()`: Returns `true` if the result contains an actor instance.
*   `to_result()`: Converts `ActorResult<T>` into `std::result::Result<T, T::Error>`.

### Example Usage

```rust
use rsactor::{spawn, Actor, ActorRef, ActorWeak, ActorResult, FailurePhase, message_handlers};
use anyhow::{Result, anyhow};

#[derive(Debug)]
struct MyErrActor { should_fail_on_start: bool, should_fail_on_run: bool }

impl Actor for MyErrActor {
    type Args = (bool, bool); // (should_fail_on_start, should_fail_on_run)
    type Error = anyhow::Error;

    async fn on_start(args: Self::Args, ar: &ActorRef<Self>) -> Result<Self, Self::Error> {
        tracing::info!("MyErrActor (id: {}) on_start called", ar.identity());
        if args.0 {
            return Err(anyhow!("Deliberate failure in on_start"));
        }
        Ok(MyErrActor { should_fail_on_start: args.0, should_fail_on_run: args.1 })
    }

    async fn on_run(&mut self, ar: &ActorWeak<Self>) -> Result<bool, Self::Error> {
        tracing::info!("MyErrActor (id: {}) on_run called", ar.identity());
        if self.should_fail_on_run {
            return Err(anyhow!("Deliberate failure in on_run"));
        }
        Ok(true) // Return true to indicate run loop should stop
    }
}

// Message type
struct Ping;

#[message_handlers]
impl MyErrActor {
    #[handler]
    async fn handle_ping(&mut self, _msg: Ping, _: &ActorRef<Self>) {}
}


async fn check_actor_termination(fail_on_start: bool, fail_on_run: bool) {
    let (actor_ref, join_handle) = spawn::<MyErrActor>((fail_on_start, fail_on_run));
    let id = actor_ref.identity();

    match join_handle.await {
        Ok(actor_result) => match actor_result {
            ActorResult::Completed { actor, killed } => {
                tracing::info!(
                    "Actor (id: {}) COMPLETED. Killed: {}. Final state: {:?}",
                    id, killed, actor
                );
            }
            ActorResult::Failed { actor, error, phase, killed } => {
                tracing::info!(
                    "Actor (id: {}) FAILED. Phase: {:?}, Killed: {}. Error: {}. State: {:?}",
                    id, phase, killed, error, actor
                );
                if fail_on_start {
                    assert!(matches!(phase, FailurePhase::OnStart));
                    assert!(actor.is_none());
                }
                if fail_on_run && !fail_on_start {
                     assert!(matches!(phase, FailurePhase::OnRun));
                     assert!(actor.is_some());
                }
            }
        },
        Err(join_error) => {
            tracing::info!("Actor (id: {}) task JOIN ERROR: {:?}", id, join_error);
        }
    }
}

#[tokio::main]
async fn main() {
    tracing_subscriber::fmt()
        .with_max_level(tracing::Level::DEBUG)
        .init();

    tracing::info!("--- Checking normal completion ---");
    check_actor_termination(false, false).await;
    tracing::info!("--- Checking failure in on_start ---");
    check_actor_termination(true, false).await;
    tracing::info!("--- Checking failure in on_run ---");
    check_actor_termination(false, true).await;
}
```

This `ActorResult` provides a comprehensive way to understand the termination reason and final state of an actor, which is crucial for supervision strategies and debugging.