1use async_trait::async_trait;
8use std::collections::HashMap;
9
10use crate::registry::{
11 EffectRegistry, Handler, HandlerContext, HandlerError, RegisterAllOptions, RegistrableHandler,
12 RegistryError,
13};
14use aura_core::effects::registry as effect_registry;
15use aura_core::{DeviceId, EffectType, ExecutionMode};
16use aura_mpst::LocalSessionType;
17
18pub struct CompositeHandler {
20 registry: EffectRegistry,
22 session_handler: Option<Box<dyn Handler>>,
24 device_id: DeviceId,
26}
27
28impl CompositeHandler {
29 pub fn new(device_id: DeviceId, execution_mode: ExecutionMode) -> Self {
32 Self {
33 registry: EffectRegistry::new(execution_mode),
34 session_handler: None,
35 device_id,
36 }
37 }
38
39 pub fn for_testing(device_id: DeviceId) -> Self {
41 Self::new(device_id, ExecutionMode::Testing)
42 }
43
44 pub fn for_production(device_id: DeviceId) -> Self {
46 Self::new(device_id, ExecutionMode::Production)
47 }
48
49 pub fn for_simulation(device_id: DeviceId, seed: u64) -> Self {
51 Self::new(device_id, ExecutionMode::Simulation { seed })
52 }
53
54 pub fn register_handler(
56 &mut self,
57 effect_type: EffectType,
58 handler: Box<dyn Handler>,
59 ) -> Result<(), CompositeError> {
60 if !handler.supports_effect(effect_type) {
61 return Err(CompositeError::UnsupportedEffect { effect_type });
62 }
63 if effect_type == EffectType::Choreographic {
64 self.session_handler = Some(handler);
65 return Ok(());
66 }
67
68 let adapter = Box::new(HandlerRegistrableAdapter::new(handler));
69 self.registry
70 .register_handler(effect_type, adapter)
71 .map_err(|e| CompositeError::HandlerExecutionFailed {
72 effect_type,
73 source: HandlerError::ExecutionFailed {
74 source: Box::new(e),
75 },
76 })?;
77
78 Ok(())
79 }
80
81 pub fn register_all(&mut self, options: RegisterAllOptions) -> Result<(), RegistryError> {
85 self.registry.register_all(options)
86 }
87
88 pub fn unregister_handler(
90 &mut self,
91 effect_type: EffectType,
92 ) -> Option<Box<dyn RegistrableHandler>> {
93 if effect_type == EffectType::Choreographic {
94 self.session_handler.take();
95 return None;
96 }
97 self.registry.unregister_handler(effect_type)
98 }
99
100 pub fn has_handler(&self, effect_type: EffectType) -> bool {
102 if effect_type == EffectType::Choreographic {
103 return self.session_handler.is_some();
104 }
105 self.registry.is_registered(effect_type)
106 }
107
108 pub fn registered_effect_types(&self) -> Vec<EffectType> {
110 let mut effects = self.registry.registered_effect_types();
111 if self.session_handler.is_some() {
112 effects.push(EffectType::Choreographic);
113 }
114 effects
115 }
116
117 pub fn device_id(&self) -> DeviceId {
119 self.device_id
120 }
121}
122
123#[derive(Debug, thiserror::Error)]
125pub enum CompositeError {
126 #[error("Effect type {effect_type:?} not supported by handler")]
128 UnsupportedEffect { effect_type: EffectType },
129
130 #[error("No handler registered for effect type {effect_type:?}")]
132 NoHandlerRegistered { effect_type: EffectType },
133
134 #[error("Handler execution failed for effect type {effect_type:?}")]
136 HandlerExecutionFailed {
137 effect_type: EffectType,
138 #[source]
139 source: HandlerError,
140 },
141}
142
143#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
144#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
145impl Handler for CompositeHandler {
146 async fn execute_effect(
148 &self,
149 effect_type: EffectType,
150 operation: &str,
151 parameters: &[u8],
152 ctx: &HandlerContext,
153 ) -> Result<Vec<u8>, HandlerError> {
154 self.registry
155 .execute_effect(effect_type, operation, parameters, ctx)
156 .await
157 }
158
159 async fn execute_session(
160 &self,
161 session: LocalSessionType,
162 ctx: &HandlerContext,
163 ) -> Result<(), HandlerError> {
164 if let Some(handler) = self.session_handler.as_ref() {
166 handler.execute_session(session, ctx).await
167 } else {
168 Err(HandlerError::SessionExecution {
170 source: "No choreographic handler registered".into(),
171 })
172 }
173 }
174
175 fn supports_effect(&self, effect_type: EffectType) -> bool {
176 if effect_type == EffectType::Choreographic {
177 return self.session_handler.is_some();
178 }
179 self.registry.supports_effect(effect_type)
180 }
181
182 fn execution_mode(&self) -> ExecutionMode {
183 self.registry.execution_mode()
184 }
185}
186
187pub struct CompositeHandlerBuilder {
189 device_id: DeviceId,
190 execution_mode: ExecutionMode,
191 handlers: HashMap<EffectType, Box<dyn Handler>>,
192}
193
194impl CompositeHandlerBuilder {
195 pub fn new(device_id: DeviceId) -> Self {
197 Self {
198 device_id,
199 execution_mode: ExecutionMode::Testing,
200 handlers: HashMap::new(),
201 }
202 }
203
204 pub fn execution_mode(mut self, mode: ExecutionMode) -> Self {
206 self.execution_mode = mode;
207 self
208 }
209
210 pub fn with_handler(
212 mut self,
213 effect_type: EffectType,
214 handler: Box<dyn Handler>,
215 ) -> Result<Self, CompositeError> {
216 if !handler.supports_effect(effect_type) {
217 return Err(CompositeError::UnsupportedEffect { effect_type });
218 }
219 self.handlers.insert(effect_type, handler);
220 Ok(self)
221 }
222
223 pub fn build(self) -> CompositeHandler {
225 let mut composite = CompositeHandler::new(self.device_id, self.execution_mode);
226 for (effect_type, handler) in self.handlers {
227 let _ = composite.register_handler(effect_type, handler);
229 }
230 composite
231 }
232}
233
234pub struct CompositeHandlerAdapter {
236 composite: CompositeHandler,
237}
238
239impl CompositeHandlerAdapter {
240 pub fn new(composite: CompositeHandler) -> Self {
242 Self { composite }
243 }
244
245 pub fn for_testing(device_id: DeviceId) -> Self {
247 Self::new(CompositeHandler::for_testing(device_id))
248 }
249
250 pub fn for_production(device_id: DeviceId) -> Self {
252 Self::new(CompositeHandler::for_production(device_id))
253 }
254
255 pub fn for_simulation(device_id: DeviceId, seed: u64) -> Self {
257 Self::new(CompositeHandler::for_simulation(device_id, seed))
258 }
259
260 pub fn register_handler(
262 &mut self,
263 effect_type: EffectType,
264 handler: Box<dyn Handler>,
265 ) -> Result<(), CompositeError> {
266 self.composite.register_handler(effect_type, handler)
267 }
268
269 pub fn into_composite(self) -> CompositeHandler {
271 self.composite
272 }
273
274 pub fn composite(&self) -> &CompositeHandler {
276 &self.composite
277 }
278
279 pub fn composite_mut(&mut self) -> &mut CompositeHandler {
281 &mut self.composite
282 }
283}
284
285#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
286#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
287impl Handler for CompositeHandlerAdapter {
288 async fn execute_effect(
289 &self,
290 effect_type: EffectType,
291 operation: &str,
292 parameters: &[u8],
293 ctx: &HandlerContext,
294 ) -> Result<Vec<u8>, HandlerError> {
295 self.composite
296 .execute_effect(effect_type, operation, parameters, ctx)
297 .await
298 }
299
300 async fn execute_session(
301 &self,
302 session: LocalSessionType,
303 ctx: &HandlerContext,
304 ) -> Result<(), HandlerError> {
305 self.composite.execute_session(session, ctx).await
306 }
307
308 fn supports_effect(&self, effect_type: EffectType) -> bool {
309 self.composite.supports_effect(effect_type)
310 }
311
312 fn execution_mode(&self) -> ExecutionMode {
313 self.composite.execution_mode()
314 }
315}
316
317#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
318#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
319impl RegistrableHandler for CompositeHandlerAdapter {
320 async fn execute_operation_bytes(
321 &self,
322 effect_type: EffectType,
323 operation: &str,
324 parameters: &[u8],
325 ctx: &HandlerContext,
326 ) -> Result<Vec<u8>, HandlerError> {
327 self.execute_effect(effect_type, operation, parameters, ctx)
328 .await
329 }
330
331 fn supported_operations(&self, effect_type: EffectType) -> Vec<String> {
332 effect_registry::operations_for(effect_type)
333 .iter()
334 .map(|op| (*op).to_string())
335 .collect()
336 }
337
338 fn supports_effect(&self, effect_type: EffectType) -> bool {
339 self.composite.supports_effect(effect_type)
340 }
341
342 fn execution_mode(&self) -> ExecutionMode {
343 self.composite.execution_mode()
344 }
345}
346
347struct HandlerRegistrableAdapter {
349 handler: Box<dyn Handler>,
350}
351
352impl HandlerRegistrableAdapter {
353 fn new(handler: Box<dyn Handler>) -> Self {
354 Self { handler }
355 }
356}
357
358#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
359#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
360impl RegistrableHandler for HandlerRegistrableAdapter {
361 async fn execute_operation_bytes(
362 &self,
363 effect_type: EffectType,
364 operation: &str,
365 parameters: &[u8],
366 ctx: &HandlerContext,
367 ) -> Result<Vec<u8>, HandlerError> {
368 self.handler
369 .execute_effect(effect_type, operation, parameters, ctx)
370 .await
371 }
372
373 fn supported_operations(&self, effect_type: EffectType) -> Vec<String> {
374 effect_registry::operations_for(effect_type)
375 .iter()
376 .map(|op| (*op).to_string())
377 .collect()
378 }
379
380 fn supports_effect(&self, effect_type: EffectType) -> bool {
381 self.handler.supports_effect(effect_type)
382 }
383
384 fn execution_mode(&self) -> ExecutionMode {
385 self.handler.execution_mode()
386 }
387}
388
389#[cfg(test)]
390mod tests {
391 use super::*;
392 use crate::registry::Handler;
393
394 struct TestConsoleHandler;
396
397 #[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
398 #[cfg_attr(not(target_arch = "wasm32"), async_trait)]
399 impl Handler for TestConsoleHandler {
400 async fn execute_effect(
401 &self,
402 _effect_type: EffectType,
403 operation: &str,
404 _parameters: &[u8],
405 _ctx: &HandlerContext,
406 ) -> Result<Vec<u8>, HandlerError> {
407 match operation {
408 "log_info" | "log_warn" | "log_error" => Ok(vec![]),
409 _ => Err(HandlerError::UnknownOperation {
410 effect_type: EffectType::Console,
411 operation: operation.to_string(),
412 }),
413 }
414 }
415
416 async fn execute_session(
417 &self,
418 _session: LocalSessionType,
419 _ctx: &HandlerContext,
420 ) -> Result<(), HandlerError> {
421 Err(HandlerError::SessionExecution {
422 source: "Console handler does not execute sessions".into(),
423 })
424 }
425
426 fn supports_effect(&self, effect_type: EffectType) -> bool {
427 effect_type == EffectType::Console
428 }
429
430 fn execution_mode(&self) -> ExecutionMode {
431 ExecutionMode::Testing
432 }
433 }
434
435 #[test]
438 fn test_composite_handler_creation() {
439 let device_id = DeviceId::new_from_entropy([1u8; 32]);
440
441 let handler = CompositeHandler::for_testing(device_id);
442 assert_eq!(handler.execution_mode(), ExecutionMode::Testing);
443 assert_eq!(handler.device_id(), device_id);
444
445 let handler = CompositeHandler::for_production(device_id);
446 assert_eq!(handler.execution_mode(), ExecutionMode::Production);
447
448 let handler = CompositeHandler::for_simulation(device_id, 42);
449 assert_eq!(
450 handler.execution_mode(),
451 ExecutionMode::Simulation { seed: 42 }
452 );
453 }
454
455 #[test]
457 fn test_composite_handler_builder() {
458 let device_id = DeviceId::new_from_entropy([2u8; 32]);
459
460 let builder =
461 CompositeHandlerBuilder::new(device_id).execution_mode(ExecutionMode::Production);
462
463 let composite = builder.build();
467 assert_eq!(composite.execution_mode(), ExecutionMode::Production);
468 assert_eq!(composite.device_id(), device_id);
469 }
470
471 #[test]
473 fn test_composite_handler_adapter() {
474 let device_id = DeviceId::new_from_entropy([3u8; 32]);
475
476 let adapter = CompositeHandlerAdapter::for_testing(device_id);
477 assert_eq!(Handler::execution_mode(&adapter), ExecutionMode::Testing);
478
479 let adapter = CompositeHandlerAdapter::for_production(device_id);
480 assert_eq!(Handler::execution_mode(&adapter), ExecutionMode::Production);
481
482 let adapter = CompositeHandlerAdapter::for_simulation(device_id, 42);
483 assert_eq!(
484 Handler::execution_mode(&adapter),
485 ExecutionMode::Simulation { seed: 42 }
486 );
487 }
488
489 #[test]
491 fn test_handler_registration() {
492 let device_id = DeviceId::new_from_entropy([4u8; 32]);
493 let mut composite = CompositeHandler::for_testing(device_id);
494
495 assert!(!composite.has_handler(EffectType::Console));
497 assert!(composite.registered_effect_types().is_empty());
498
499 let handler = Box::new(TestConsoleHandler);
501 composite
502 .register_handler(EffectType::Console, handler)
503 .unwrap();
504
505 assert!(composite.has_handler(EffectType::Console));
506 assert_eq!(
507 composite.registered_effect_types(),
508 vec![EffectType::Console]
509 );
510 }
511
512 #[test]
515 fn test_supported_operations() {
516 let device_id = DeviceId::new_from_entropy([5u8; 32]);
517 let adapter = CompositeHandlerAdapter::for_testing(device_id);
518
519 let console_ops = adapter.supported_operations(EffectType::Console);
521 assert!(console_ops.contains(&"log_info".to_string()));
522
523 let random_ops = adapter.supported_operations(EffectType::Random);
524 assert!(random_ops.contains(&"random_bytes".to_string()));
525
526 let unknown_ops = adapter.supported_operations(EffectType::PropertyChecking);
528 assert!(unknown_ops.is_empty());
529 }
530}