Skip to main content

actor_kit/
executor.rs

1//! Actor executor abstraction.
2//!
3//! Provides the executor interface the scheduler uses to run actor messages
4//! through pluggable backends (native handlers, WASM hosts, test doubles).
5//! The upstream `WasmActorExecutor` was excised with the WASM engine; the
6//! trait below is its stable seam.
7
8use crate::error::Result;
9use crate::{ActorId, Message};
10
11/// Result of executing an actor task.
12#[derive(Debug)]
13pub enum ExecutionResult {
14    /// Execution completed successfully
15    Success {
16        /// Fuel consumed during execution
17        fuel_consumed: u64,
18        /// Optional response payload
19        response: Option<Vec<u8>>,
20    },
21    /// Execution ran out of fuel
22    FuelExhausted {
23        /// Fuel that was attempted to be consumed
24        requested: u64,
25    },
26    /// Execution failed with an error
27    Failed {
28        /// Error message
29        error: String,
30    },
31    /// Actor not found or not initialized
32    NotReady,
33}
34
35/// Trait for executing actor messages.
36///
37/// Implementations handle the actual invocation of actor code,
38/// whether through WASM, native code, or other mechanisms.
39pub trait ActorExecutor: Send + Sync {
40    /// Execute a message for an actor.
41    ///
42    /// # Arguments
43    /// * `actor_id` - The target actor's ID
44    /// * `message` - The message to process
45    ///
46    /// # Returns
47    /// The result of execution
48    fn execute(&self, actor_id: &ActorId, message: &Message) -> ExecutionResult;
49
50    /// Check if an actor is ready to execute.
51    fn is_ready(&self, actor_id: &ActorId) -> bool;
52
53    /// Get the fuel consumption for an actor.
54    fn get_fuel(&self, actor_id: &ActorId) -> Option<u64>;
55
56    /// Reset an actor's execution state.
57    fn reset(&self, actor_id: &ActorId) -> Result<()>;
58}
59
60/// Null executor for testing.
61///
62/// Does nothing but track calls.
63pub struct NullExecutor {
64    call_count: std::sync::atomic::AtomicU64,
65}
66
67impl NullExecutor {
68    /// Create a new null executor.
69    pub fn new() -> Self {
70        Self {
71            call_count: std::sync::atomic::AtomicU64::new(0),
72        }
73    }
74
75    /// Get the number of calls made.
76    pub fn call_count(&self) -> u64 {
77        self.call_count.load(std::sync::atomic::Ordering::Relaxed)
78    }
79}
80
81impl Default for NullExecutor {
82    fn default() -> Self {
83        Self::new()
84    }
85}
86
87impl ActorExecutor for NullExecutor {
88    fn execute(&self, _actor_id: &ActorId, _message: &Message) -> ExecutionResult {
89        self.call_count
90            .fetch_add(1, std::sync::atomic::Ordering::Relaxed);
91        ExecutionResult::Success {
92            fuel_consumed: 0,
93            response: None,
94        }
95    }
96
97    fn is_ready(&self, _actor_id: &ActorId) -> bool {
98        true
99    }
100
101    fn get_fuel(&self, _actor_id: &ActorId) -> Option<u64> {
102        Some(1_000_000)
103    }
104
105    fn reset(&self, _actor_id: &ActorId) -> Result<()> {
106        Ok(())
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    use super::*;
113    use crate::{MessagePayload, Priority};
114
115    #[test]
116    fn test_null_executor() {
117        let executor = NullExecutor::new();
118        let actor_id = ActorId::new();
119        let message = Message {
120            sender: None,
121            payload: MessagePayload::Start,
122            priority: Priority::Normal,
123        };
124
125        let result = executor.execute(&actor_id, &message);
126        assert!(matches!(result, ExecutionResult::Success { .. }));
127        assert_eq!(executor.call_count(), 1);
128    }
129
130    #[test]
131    fn test_null_executor_is_ready() {
132        let executor = NullExecutor::new();
133        let actor_id = ActorId::new();
134        assert!(executor.is_ready(&actor_id));
135    }
136
137    #[test]
138    fn test_execution_result_debug() {
139        let result = ExecutionResult::Success {
140            fuel_consumed: 100,
141            response: Some(vec![1, 2, 3]),
142        };
143        let debug_str = format!("{:?}", result);
144        assert!(debug_str.contains("Success"));
145    }
146}