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