Skip to main content

appcore_core/
bus.rs

1// =============================================================================
2//        #######
3//     ###       ###     F: bus.rs
4//    ##   ## ##   ##    P: AppCore-Runtime
5//         ## ##
6//                       C: 2026/05/29 20:47:35 by dnettoRaw
7//    ##   ## ##   ##    U: 2026/07/23 23:50:45 by dnettoRaw
8//      ###########      S: 1.0.1-rc.8
9// =============================================================================
10
11//! Minimal synchronous command bus contract.
12
13use std::collections::HashMap;
14use std::fmt;
15use std::sync::Arc;
16
17use crate::context::RuntimeContext;
18use crate::envelope::CommandEnvelope;
19use crate::error::{RuntimeError, RuntimeResult};
20use crate::handler::{CommandHandler, CommandResult};
21use crate::ids::CommandName;
22
23/// In-memory command bus that routes envelopes to registered handlers.
24#[derive(Default)]
25pub struct CommandBus {
26    handlers: HashMap<CommandName, Arc<dyn CommandHandler + Send + Sync>>,
27}
28
29impl fmt::Debug for CommandBus {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        f.debug_struct("CommandBus")
32            .field("handler_count", &self.handlers.len())
33            .finish()
34    }
35}
36
37impl CommandBus {
38    /// Creates an empty command bus.
39    pub fn new() -> Self {
40        Self::default()
41    }
42
43    /// Registers one handler, rejecting duplicate command names.
44    pub fn register_handler<H: CommandHandler + 'static>(
45        &mut self,
46        handler: H,
47    ) -> RuntimeResult<()> {
48        let name = handler.command_name();
49        if self.handlers.contains_key(&name) {
50            return Err(RuntimeError::HandlerAlreadyRegistered(name));
51        }
52
53        self.handlers.insert(name, Arc::new(handler));
54        Ok(())
55    }
56
57    /// Reports whether a handler exists for `name`.
58    pub fn contains_handler(&self, name: &CommandName) -> bool {
59        self.handlers.contains_key(name)
60    }
61
62    /// Returns the number of registered handlers.
63    pub fn len(&self) -> usize {
64        self.handlers.len()
65    }
66
67    /// Reports whether no handlers are registered.
68    pub fn is_empty(&self) -> bool {
69        self.handlers.is_empty()
70    }
71
72    /// Dispatches an envelope to the handler matching its command name.
73    pub fn dispatch(
74        &self,
75        command: &CommandEnvelope,
76        context: &dyn RuntimeContext,
77    ) -> RuntimeResult<CommandResult> {
78        let name = command.command_name();
79        let Some(handler) = self.handlers.get(name) else {
80            return Err(RuntimeError::HandlerNotFound(name.clone()));
81        };
82
83        handler.handle(command, context)
84    }
85}
86
87#[cfg(test)]
88#[path = "bus_tests.rs"]
89mod tests;