1use aura_core::effects::ExecutionMode;
7use aura_core::types::identifiers::{AuthorityId, ContextId};
8use aura_core::AuraError;
9use futures::future::BoxFuture;
10use std::sync::Arc;
11
12use super::registry::{EffectOperation, EffectRegistry, EffectType};
14
15pub 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#[derive(Debug)]
41pub struct EffectExecutor {
42 authority_id: AuthorityId,
43 execution_mode: ExecutionMode,
44 registry: Arc<EffectRegistry>,
45}
46
47impl EffectExecutor {
48 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 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 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 pub fn authority_id(&self) -> AuthorityId {
88 self.authority_id
89 }
90
91 pub fn execution_mode(&self) -> ExecutionMode {
93 self.execution_mode
94 }
95
96 pub fn registry(&self) -> Arc<EffectRegistry> {
98 self.registry.clone()
99 }
100
101 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 pub fn production(authority_id: AuthorityId, registry: Arc<EffectRegistry>) -> Self {
108 Self::new(authority_id, ExecutionMode::Production, registry)
109 }
110
111 pub fn testing(authority_id: AuthorityId, registry: Arc<EffectRegistry>) -> Self {
113 Self::new(authority_id, ExecutionMode::Testing, registry)
114 }
115
116 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#[derive(Debug)]
124#[allow(dead_code)] pub struct EffectExecutorBuilder {
126 authority_id: Option<AuthorityId>,
127 execution_mode: Option<ExecutionMode>,
128 registry: Option<Arc<EffectRegistry>>,
129}
130
131impl EffectExecutorBuilder {
132 #[allow(dead_code)] pub fn new() -> Self {
135 Self {
136 authority_id: None,
137 execution_mode: None,
138 registry: None,
139 }
140 }
141
142 #[allow(dead_code)] pub fn with_authority(mut self, authority_id: AuthorityId) -> Self {
145 self.authority_id = Some(authority_id);
146 self
147 }
148
149 #[allow(dead_code)] pub fn with_execution_mode(mut self, mode: ExecutionMode) -> Self {
152 self.execution_mode = Some(mode);
153 self
154 }
155
156 #[allow(dead_code)] pub fn with_registry(mut self, registry: Arc<EffectRegistry>) -> Self {
159 self.registry = Some(registry);
160 self
161 }
162
163 #[allow(dead_code)] 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#[derive(Debug, Clone)]
190pub enum EffectResult {
191 Success(String),
192 Error(String),
193 Partial(String),
194}
195
196impl EffectResult {
197 pub fn is_success(&self) -> bool {
199 matches!(self, EffectResult::Success(_))
200 }
201
202 pub fn is_error(&self) -> bool {
204 matches!(self, EffectResult::Error(_))
205 }
206
207 pub fn is_partial(&self) -> bool {
209 matches!(self, EffectResult::Partial(_))
210 }
211
212 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}