Skip to main content

aura_agent/runtime/
executor.rs

1//! Effect execution infrastructure
2//!
3//! Provides execution infrastructure for running effects within the authority-centric
4//! runtime, managing execution contexts, and coordinating effect handler invocation.
5
6use aura_core::effects::ExecutionMode;
7use aura_core::types::identifiers::{AuthorityId, ContextId};
8use aura_core::AuraError;
9use futures::future::BoxFuture;
10use std::sync::Arc;
11
12// Use the registry module's EffectRegistry (not builder's)
13use super::registry::{EffectOperation, EffectRegistry, EffectType};
14
15/// Handler trait for dynamic effect dispatch.
16pub trait EffectHandler<T>: Send + Sync {
17    fn handle(
18        &self,
19        context: &super::EffectContext,
20        params: T,
21    ) -> BoxFuture<'static, Result<EffectResult, AuraError>>;
22}
23
24impl<T, F> EffectHandler<T> for F
25where
26    F: Fn(&super::EffectContext, T) -> BoxFuture<'static, Result<EffectResult, AuraError>>
27        + Send
28        + Sync,
29{
30    fn handle(
31        &self,
32        context: &super::EffectContext,
33        params: T,
34    ) -> BoxFuture<'static, Result<EffectResult, AuraError>> {
35        (self)(context, params)
36    }
37}
38
39/// Executor for effect operations
40#[derive(Debug)]
41pub struct EffectExecutor {
42    authority_id: AuthorityId,
43    execution_mode: ExecutionMode,
44    registry: Arc<EffectRegistry>,
45}
46
47impl EffectExecutor {
48    /// Create a new effect executor
49    pub fn new(
50        authority_id: AuthorityId,
51        execution_mode: ExecutionMode,
52        registry: Arc<EffectRegistry>,
53    ) -> Self {
54        Self {
55            authority_id,
56            execution_mode,
57            registry,
58        }
59    }
60
61    /// Execute an effect operation
62    pub async fn execute<T>(
63        &self,
64        context: &super::EffectContext,
65        effect_type: EffectType,
66        operation: EffectOperation,
67        params: T,
68    ) -> Result<EffectResult, AuraError>
69    where
70        T: Send + Sync + 'static,
71    {
72        // Validate context matches executor authority
73        if context.authority_id() != self.authority_id {
74            return Err(AuraError::invalid("Context authority mismatch".to_string()));
75        }
76
77        let handler = self
78            .registry
79            .get_effect_handler::<T>(effect_type, operation)
80            .map_err(|e| AuraError::invalid(e.to_string()))?
81            .ok_or_else(|| AuraError::invalid(format!("{}.{}", effect_type, operation)))?;
82
83        handler.handle(context, params).await
84    }
85
86    /// Get the authority ID
87    pub fn authority_id(&self) -> AuthorityId {
88        self.authority_id
89    }
90
91    /// Get the execution mode
92    pub fn execution_mode(&self) -> ExecutionMode {
93        self.execution_mode
94    }
95
96    /// Get the registry
97    pub fn registry(&self) -> Arc<EffectRegistry> {
98        self.registry.clone()
99    }
100
101    /// Create an execution context
102    pub fn create_context(&self, context_id: ContextId) -> super::EffectContext {
103        super::EffectContext::new(self.authority_id, context_id, self.execution_mode)
104    }
105
106    /// Production constructor
107    pub fn production(authority_id: AuthorityId, registry: Arc<EffectRegistry>) -> Self {
108        Self::new(authority_id, ExecutionMode::Production, registry)
109    }
110
111    /// Testing constructor
112    pub fn testing(authority_id: AuthorityId, registry: Arc<EffectRegistry>) -> Self {
113        Self::new(authority_id, ExecutionMode::Testing, registry)
114    }
115
116    /// Simulation constructor
117    pub fn simulation(authority_id: AuthorityId, seed: u64, registry: Arc<EffectRegistry>) -> Self {
118        Self::new(authority_id, ExecutionMode::Simulation { seed }, registry)
119    }
120}
121
122/// Builder for effect executors
123#[derive(Debug)]
124#[allow(dead_code)] // Part of future effect system API
125pub struct EffectExecutorBuilder {
126    authority_id: Option<AuthorityId>,
127    execution_mode: Option<ExecutionMode>,
128    registry: Option<Arc<EffectRegistry>>,
129}
130
131impl EffectExecutorBuilder {
132    /// Create a new executor builder
133    #[allow(dead_code)] // Part of future effect system API
134    pub fn new() -> Self {
135        Self {
136            authority_id: None,
137            execution_mode: None,
138            registry: None,
139        }
140    }
141
142    /// Set the authority ID
143    #[allow(dead_code)] // Part of future effect system API
144    pub fn with_authority(mut self, authority_id: AuthorityId) -> Self {
145        self.authority_id = Some(authority_id);
146        self
147    }
148
149    /// Set the execution mode
150    #[allow(dead_code)] // Part of future effect system API
151    pub fn with_execution_mode(mut self, mode: ExecutionMode) -> Self {
152        self.execution_mode = Some(mode);
153        self
154    }
155
156    /// Set the effect registry
157    #[allow(dead_code)] // Part of future effect system API
158    pub fn with_registry(mut self, registry: Arc<EffectRegistry>) -> Self {
159        self.registry = Some(registry);
160        self
161    }
162
163    /// Build the executor
164    #[allow(dead_code)] // Part of future effect system API
165    pub fn build(self) -> Result<EffectExecutor, AuraError> {
166        let authority_id = self
167            .authority_id
168            .ok_or_else(|| AuraError::invalid("Authority ID required".to_string()))?;
169
170        let execution_mode = self
171            .execution_mode
172            .ok_or_else(|| AuraError::invalid("Execution mode required".to_string()))?;
173
174        let registry = self
175            .registry
176            .ok_or_else(|| AuraError::invalid("Registry required".to_string()))?;
177
178        Ok(EffectExecutor::new(authority_id, execution_mode, registry))
179    }
180}
181
182impl Default for EffectExecutorBuilder {
183    fn default() -> Self {
184        Self::new()
185    }
186}
187
188/// Result of an effect execution
189#[derive(Debug, Clone)]
190pub enum EffectResult {
191    Success(String),
192    Error(String),
193    Partial(String),
194}
195
196impl EffectResult {
197    /// Check if the result represents success
198    pub fn is_success(&self) -> bool {
199        matches!(self, EffectResult::Success(_))
200    }
201
202    /// Check if the result represents an error
203    pub fn is_error(&self) -> bool {
204        matches!(self, EffectResult::Error(_))
205    }
206
207    /// Check if the result represents a partial result
208    pub fn is_partial(&self) -> bool {
209        matches!(self, EffectResult::Partial(_))
210    }
211
212    /// Get the result message
213    pub fn message(&self) -> &str {
214        match self {
215            EffectResult::Success(msg) => msg,
216            EffectResult::Error(msg) => msg,
217            EffectResult::Partial(msg) => msg,
218        }
219    }
220}
221
222#[cfg(test)]
223mod tests {
224    use super::*;
225    use crate::runtime::registry::EffectRegistry;
226    use crate::EffectContext;
227    use aura_core::effects::ExecutionMode;
228    use aura_core::types::identifiers::{AuthorityId, ContextId};
229    use futures::future::BoxFuture;
230    use std::sync::Arc;
231
232    fn double_handler(
233        _context: &EffectContext,
234        value: u64,
235    ) -> BoxFuture<'static, Result<EffectResult, AuraError>> {
236        Box::pin(async move { Ok(EffectResult::Success(format!("ok:{value}"))) })
237    }
238
239    #[tokio::test]
240    async fn effect_executor_dispatches_registered_handler() {
241        let authority_id = AuthorityId::new_from_entropy([1u8; 32]);
242        let context_id = ContextId::new_from_entropy([2u8; 32]);
243        let registry = Arc::new(EffectRegistry::new(ExecutionMode::Testing));
244
245        registry
246            .register_effect_handler::<u64, _>(
247                EffectType::Crypto,
248                EffectOperation::from("double"),
249                double_handler,
250            )
251            .expect("register handler");
252
253        let executor = EffectExecutor::testing(authority_id, registry);
254        let context = executor.create_context(context_id);
255
256        let result = executor
257            .execute(
258                &context,
259                EffectType::Crypto,
260                EffectOperation::from("double"),
261                7u64,
262            )
263            .await
264            .expect("execute handler");
265
266        assert!(matches!(result, EffectResult::Success(msg) if msg == "ok:7"));
267    }
268}