dmsc/hooks/
mod.rs

1//! Copyright © 2025-2026 Wenze Wei. All Rights Reserved.
2//!
3//! This file is part of DMSC.
4//! The DMSC project belongs to the Dunimd Team.
5//!
6//! Licensed under the Apache License, Version 2.0 (the "License");
7//! You may not use this file except in compliance with the License.
8//! You may obtain a copy of the License at
9//!
10//!     http://www.apache.org/licenses/LICENSE-2.0
11//!
12//! Unless required by applicable law or agreed to in writing, software
13//! distributed under the License is distributed on an "AS IS" BASIS,
14//! WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15//! See the License for the specific language governing permissions and
16//! limitations under the License.
17
18#![allow(non_snake_case)]
19
20//! # Hooks System
21//! 
22//! This module provides an event bus system for DMSC, enabling communication between components
23//! during various lifecycle events. It supports both synchronous and asynchronous module lifecycle
24//! phases, and allows for custom event handlers to be registered.
25//! 
26//! ## Key Components
27//! 
28//! - **DMSCHookKind**: Enum defining the different types of hooks
29//! - **DMSCModulePhase**: Enum defining the different module lifecycle phases
30//! - **DMSCHookEvent**: Struct representing a hook event
31//! - **DMSCHookBus**: Event bus for registering and emitting hooks
32//! 
33//! ## Design Principles
34//! 
35//! 1. **Event-Driven Architecture**: Uses an event bus pattern for loose coupling between components
36//! 2. **Lifecycle Support**: Covers all stages of module lifecycle, both synchronous and asynchronous
37//! 3. **Type Safety**: Uses enums for hook kinds and phases to ensure type safety
38//! 4. **Flexibility**: Allows registering multiple handlers for the same hook
39//! 5. **Contextual Information**: Events carry contextual information about the module and phase
40//! 6. **Error Propagation**: Hook handlers can return errors that propagate up the call stack
41//! 
42//! ## Usage
43//! 
44//! ```rust
45//! use dmsc::prelude::*;
46//! 
47//! fn example() -> DMSCResult<()> {
48//!     // Create a hook bus
49//!     let mut hook_bus = DMSCHookBus::new();
50//!     
51//!     // Register a hook handler
52//!     hook_bus.register(DMSCHookKind::Startup, "example.startup".to_string(), |ctx, event| {
53//!         // Handle startup event
54//!         Ok(())
55//!     });
56//!     
57//!     // Create a service context (usually provided by the runtime)
58//!     let ctx = DMSCServiceContext::new();
59//!     
60//!     // Emit a hook event
61//!     hook_bus.emit(&DMSCHookKind::Startup, &ctx)?;
62//!     
63//!     Ok(())
64//! }
65
66use std::collections::HashMap;
67
68use crate::core::{DMSCResult, DMSCServiceContext};
69
70// Type aliases for complex types
71/// Type alias for a hook handler function
72pub type DMSCHookHandler = Box<dyn Fn(&DMSCServiceContext, &DMSCHookEvent) -> DMSCResult<()> + Send + Sync>;
73
74/// Type alias for a hook handler entry (ID + handler)
75pub type DMSCHookHandlerEntry = (DMSCHookId, DMSCHookHandler);
76
77/// Type alias for a collection of hook handlers grouped by hook kind
78pub type DMSCHookHandlersMap = HashMap<DMSCHookKind, Vec<DMSCHookHandlerEntry>>;
79
80/// Hook kind definition.
81/// 
82/// This enum defines the different types of hooks that can be emitted during the application lifecycle.
83#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
84#[derive(Eq, Hash, PartialEq, Clone, Copy, Debug)]
85pub enum DMSCHookKind {
86    /// Emitted when the application starts up
87    Startup,
88    /// Emitted when the application shuts down
89    Shutdown,
90    /// Emitted before modules are initialized
91    BeforeModulesInit,
92    /// Emitted after modules are initialized
93    AfterModulesInit,
94    /// Emitted before modules are started
95    BeforeModulesStart,
96    /// Emitted after modules are started
97    AfterModulesStart,
98    /// Emitted before modules are shut down
99    BeforeModulesShutdown,
100    /// Emitted after modules are shut down
101    AfterModulesShutdown,
102    /// Emitted when configuration is reloaded
103    ConfigReload,
104}
105
106/// Module lifecycle phase definition.
107/// 
108/// This enum defines the different phases a module can go through during its lifecycle,
109/// including both synchronous and asynchronous phases.
110#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
111#[derive(Eq, Hash, PartialEq, Clone, Copy, Debug)]
112pub enum DMSCModulePhase {
113    /// Synchronous initialization phase
114    Init,
115    /// Synchronous phase before starting
116    BeforeStart,
117    /// Synchronous start phase
118    Start,
119    /// Synchronous phase after starting
120    AfterStart,
121    /// Synchronous phase before shutting down
122    BeforeShutdown,
123    /// Synchronous shutdown phase
124    Shutdown,
125    /// Synchronous phase after shutting down
126    AfterShutdown,
127    /// Asynchronous initialization phase
128    AsyncInit,
129    /// Asynchronous phase before starting
130    AsyncBeforeStart,
131    /// Asynchronous start phase
132    AsyncStart,
133    /// Asynchronous phase after starting
134    AsyncAfterStart,
135    /// Asynchronous phase before shutting down
136    AsyncBeforeShutdown,
137    /// Asynchronous shutdown phase
138    AsyncShutdown,
139    /// Asynchronous phase after shutting down
140    AsyncAfterShutdown,
141}
142
143impl DMSCModulePhase {
144    /// Returns the string representation of the module phase.
145    /// 
146    /// # Returns
147    /// 
148    /// A static string representing the module phase (e.g., "init", "start", "async_shutdown")
149    pub fn as_str(&self) -> &'static str {
150        match self {
151            DMSCModulePhase::Init => "init",
152            DMSCModulePhase::BeforeStart => "before_start",
153            DMSCModulePhase::Start => "start",
154            DMSCModulePhase::AfterStart => "after_start",
155            DMSCModulePhase::BeforeShutdown => "before_shutdown",
156            DMSCModulePhase::Shutdown => "shutdown",
157            DMSCModulePhase::AfterShutdown => "after_shutdown",
158            DMSCModulePhase::AsyncInit => "async_init",
159            DMSCModulePhase::AsyncBeforeStart => "async_before_start",
160            DMSCModulePhase::AsyncStart => "async_start",
161            DMSCModulePhase::AsyncAfterStart => "async_after_start",
162            DMSCModulePhase::AsyncBeforeShutdown => "async_before_shutdown",
163            DMSCModulePhase::AsyncShutdown => "async_shutdown",
164            DMSCModulePhase::AsyncAfterShutdown => "async_after_shutdown",
165        }
166    }
167}
168
169/// Hook event structure.
170/// 
171/// This struct represents an event that is emitted when a hook is triggered. It contains
172/// information about the hook kind, the module (if applicable), and the module phase (if applicable).
173#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
174#[derive(Clone, Debug)]
175pub struct DMSCHookEvent {
176    /// The kind of hook that was triggered
177    pub kind: DMSCHookKind,
178    /// The name of the module associated with the event (if any)
179    pub module: Option<String>,
180    /// The module phase associated with the event (if any)
181    pub phase: Option<DMSCModulePhase>,
182}
183
184impl DMSCHookEvent {
185    /// Creates a new hook event.
186    pub fn new(kind: DMSCHookKind, module: Option<String>, phase: Option<DMSCModulePhase>) -> Self {
187        Self { kind, module, phase }
188    }
189
190    /// Creates a config reload event.
191    pub fn config_reload(_path: String, _timestamp: chrono::DateTime<chrono::Utc>) -> Self {
192        Self { 
193            kind: DMSCHookKind::ConfigReload, 
194            module: Some("config_manager".to_string()), 
195            phase: None,
196        }
197    }
198}
199
200/// Type alias for hook IDs.
201/// 
202/// Hook IDs are used to identify hook handlers and can be used for debugging and logging purposes.
203pub type DMSCHookId = String;
204
205/// Hook bus for registering and emitting hooks.
206/// 
207/// This struct manages the registration of hook handlers and the emission of hook events.
208/// It allows multiple handlers to be registered for the same hook kind.
209#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
210pub struct DMSCHookBus {
211    /// Internal storage for hook handlers, organized by hook kind
212    handlers: DMSCHookHandlersMap,
213}
214
215impl Default for DMSCHookBus {
216    fn default() -> Self {
217        Self::new()
218    }
219}
220
221impl DMSCHookBus {
222    /// Creates a new hook bus instance.
223    /// 
224    /// Returns a new `DMSCHookBus` instance with no registered handlers.
225    pub fn new() -> Self {
226        DMSCHookBus { handlers: HashMap::new() }
227    }
228
229    /// Registers a hook handler for a specific hook kind.
230    /// 
231    /// # Parameters
232    /// 
233    /// - `kind`: The hook kind to register the handler for
234    /// - `id`: A unique ID for the hook handler
235    /// - `handler`: The handler function to execute when the hook is emitted
236    /// 
237    /// The handler function takes a `DMSCServiceContext` and a `DMSCHookEvent` and returns a `DMSCResult<()>`. 
238    pub fn register<F>(&mut self, kind: DMSCHookKind, id: DMSCHookId, handler: F)
239    where
240        F: Fn(&DMSCServiceContext, &DMSCHookEvent) -> DMSCResult<()> + Send + Sync + 'static,
241    {
242        self.handlers.entry(kind).or_default().push((id, Box::new(handler)));
243    }
244
245    /// Emits a hook event of the specified kind.
246    /// 
247    /// # Parameters
248    /// 
249    /// - `kind`: The hook kind to emit
250    /// - `ctx`: The service context to pass to the hook handlers
251    /// 
252    /// # Returns
253    /// 
254    /// A `DMSCResult<()>` indicating success or failure
255    pub fn emit(&self, kind: &DMSCHookKind, ctx: &DMSCServiceContext) -> DMSCResult<()> {
256        self.emit_with(kind, ctx, None, None)
257    }
258
259    /// Emits a hook event with additional contextual information.
260    /// 
261    /// # Parameters
262    /// 
263    /// - `kind`: The hook kind to emit
264    /// - `ctx`: The service context to pass to the hook handlers
265    /// - `module`: The name of the module associated with the event (if any)
266    /// - `phase`: The module phase associated with the event (if any)
267    /// 
268    /// # Returns
269    /// 
270    /// A `DMSCResult<()>` indicating success or failure
271    pub fn emit_with(
272        &self,
273        kind: &DMSCHookKind,
274        ctx: &DMSCServiceContext,
275        module: Option<&str>,
276        phase: Option<DMSCModulePhase>,
277    ) -> DMSCResult<()> {
278        let event = DMSCHookEvent {
279            kind: *kind,
280            module: module.map(|s| s.to_string()),
281            phase,
282        };
283        if let Some(list) = self.handlers.get(kind) {
284            for (_id, handler) in list {
285                handler(ctx, &event)?;
286            }
287        }
288        Ok(())
289    }
290}
291
292#[cfg(feature = "pyo3")]
293#[pyo3::prelude::pymethods]
294impl DMSCHookBus {
295    #[new]
296    fn py_new() -> Self {
297        Self::new()
298    }
299}