dmsc/core/
context.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//! # Service Context
19//! 
20//! The service context provides access to all core functionalities of DMSC,
21//! acting as a central hub for accessing various components such as logging,
22//! configuration, file system, and hooks.
23
24#![allow(non_snake_case)]
25
26use crate::fs::DMSCFileSystem;
27use crate::log::{DMSCLogConfig, DMSCLogger};
28use crate::config::DMSCConfigManager;
29use crate::hooks::DMSCHookBus;
30use crate::core::DMSCResult;
31use crate::observability::DMSCMetricsRegistry;
32use std::sync::Arc;
33
34/// Internal service context implementation. Not exposed directly to users.
35/// 
36/// This struct contains all the core components of the service context,
37/// but is wrapped by `DMSCServiceContext` for controlled access.
38#[derive(Clone)]
39pub struct ServiceContextInner {
40    /// File system accessor for secure file operations
41    pub fs: DMSCFileSystem,
42    /// Logger for structured logging
43    pub logger: Arc<DMSCLogger>,
44    /// Configuration manager for accessing application settings
45    pub config: Arc<DMSCConfigManager>,
46    /// Hook bus for emitting and handling lifecycle events
47    pub hooks: Arc<DMSCHookBus>,
48    /// Metrics registry for observability (optional)
49    pub metrics_registry: Option<Arc<DMSCMetricsRegistry>>,
50}
51
52impl ServiceContextInner {
53    /// Create a new `ServiceContextInner` instance with the provided components.
54    /// 
55    /// # Parameters
56    /// 
57    /// - `fs`: File system accessor
58    /// - `logger`: Structured logger
59    /// - `config`: Configuration manager
60    /// - `hooks`: Hook bus for lifecycle events
61    /// - `metrics_registry`: Optional metrics registry for observability
62    /// 
63    /// # Returns
64    /// 
65    /// A new `ServiceContextInner` instance.
66    pub fn new(fs: DMSCFileSystem, logger: DMSCLogger, config: DMSCConfigManager, hooks: DMSCHookBus, metrics_registry: Option<Arc<DMSCMetricsRegistry>>) -> Self {
67        ServiceContextInner { 
68            fs, 
69            logger: Arc::new(logger), 
70            config: Arc::new(config), 
71            hooks: Arc::new(hooks), 
72            metrics_registry 
73        }
74    }
75    
76
77}
78
79/// Public-facing service context for DMSC applications.
80/// 
81/// The `DMSCServiceContext` is the primary way for modules and business logic to
82/// access core DMSC functionalities. It follows the dependency injection pattern,
83/// providing a centralized access point to all core components.
84/// 
85/// ## Design Principle
86/// 
87/// The service context is designed to be immutable from the outside, with controlled
88/// access to mutable components through dedicated methods. This ensures thread safety
89/// while allowing for necessary mutations in specific contexts.
90/// 
91/// ## Usage
92/// 
93/// ```rust
94/// use dmsc::prelude::*;
95/// 
96/// async fn handle_request(ctx: &DMSCServiceContext) -> DMSCResult<()> {
97///     // Access logger
98///     ctx.logger().info("request", "Handling request");
99///     
100///     // Access configuration
101///     let config_value = ctx.config().config().get_str("app.name");
102///     
103///     // Access file system
104///     let file_path = ctx.fs().app_data_path("logs/app.log");
105///     
106///     Ok(())
107/// }
108/// ```
109#[cfg_attr(feature = "pyo3", pyo3::prelude::pyclass)]
110#[derive(Clone)]
111pub struct DMSCServiceContext {
112    /// Internal implementation details
113    inner: ServiceContextInner,
114}
115
116impl DMSCServiceContext {
117    /// Create a new `DMSCServiceContext` with the provided components.
118    /// 
119    /// This method is typically used by the framework itself during application startup,
120    /// but can be used for testing or custom initialization.
121    /// 
122    /// # Parameters
123    /// 
124    /// - `fs`: File system accessor
125    /// - `logger`: Structured logger
126    /// - `config`: Configuration manager
127    /// - `hooks`: Hook bus for lifecycle events
128    /// - `metrics_registry`: Optional metrics registry for observability
129    /// 
130    /// # Returns
131    /// 
132    /// A new `DMSCServiceContext` instance.
133    pub fn new_with(fs: DMSCFileSystem, logger: DMSCLogger, config: DMSCConfigManager, hooks: DMSCHookBus, metrics_registry: Option<Arc<DMSCMetricsRegistry>>) -> Self {
134        let inner = ServiceContextInner::new(fs, logger, config, hooks, metrics_registry);
135        DMSCServiceContext { inner }
136    }
137    
138
139
140    /// Create a new `DMSCServiceContext` with default configuration.
141    /// 
142    /// This is the most common way to create a service context, as it handles
143    /// the initialization of all core components automatically.
144    /// 
145    /// # Returns
146    /// 
147    /// A `DMSCResult` containing the new service context, or an error if initialization failed.
148    /// 
149    /// # Errors
150    /// 
151    /// - If the project root directory cannot be determined
152    /// - If there are issues initializing any of the core components
153    pub fn new_default() -> DMSCResult<Self> {
154        // Create default configuration manager
155        let config = DMSCConfigManager::new_default();
156        let cfg = config.config();
157
158        // Determine project root directory
159        let project_root = std::env::current_dir()
160            .map_err(|e| crate::core::DMSCError::Other(format!("detect project root failed: {e}")))?;
161        
162        // Determine application data root directory
163        let app_data_root = if let Some(root_str) = cfg.get_str("fs.app_data_root") {
164            project_root.join(root_str)
165        } else {
166            project_root.join(".dms")
167        };
168
169        // Initialize file system
170        let fs = DMSCFileSystem::new_with_roots(project_root, app_data_root);
171
172        // Initialize logging
173        let log_config = DMSCLogConfig::from_config(&cfg);
174        let logger = DMSCLogger::new(&log_config, fs.clone());
175        
176        // Initialize hook bus
177        let hooks = DMSCHookBus::new();
178        
179        Ok(DMSCServiceContext::new_with(fs, logger, config, hooks, None))
180    }
181
182    /// Get a reference to the file system accessor.
183    /// 
184    /// # Returns
185    /// 
186    /// A reference to the `DMSCFileSystem` instance.
187    pub fn fs(&self) -> &DMSCFileSystem {
188        &self.inner.fs
189    }
190    
191
192
193    /// Get a reference to the structured logger.
194    /// 
195    /// # Returns
196    /// 
197    /// A reference to the `DMSCLogger` instance.
198    pub fn logger(&self) -> &DMSCLogger {
199        self.inner.logger.as_ref()
200    }
201    
202
203
204    /// Get a reference to the configuration manager.
205    /// 
206    /// # Returns
207    /// 
208    /// A reference to the `DMSCConfigManager` instance.
209    pub fn config(&self) -> Arc<DMSCConfigManager> {
210        self.inner.config.clone()
211    }
212    
213
214
215    /// Get a reference to the hook bus for emitting events.
216    /// 
217    /// # Returns
218    /// 
219    /// A reference to the `DMSCHookBus` instance.
220    pub fn hooks(&self) -> Arc<DMSCHookBus> {
221        self.inner.hooks.clone()
222    }
223    
224
225
226    /// Get a mutable reference to the hook bus for registering handlers.
227    /// 
228    /// # Returns
229    /// 
230    /// A mutable reference to the `DMSCHookBus` instance.
231    pub fn hooks_mut(&mut self) -> &mut DMSCHookBus {
232        Arc::get_mut(&mut self.inner.hooks).expect("Cannot get mutable reference to hooks - shared ownership")
233    }
234
235    /// Get a mutable reference to the configuration manager.
236    /// 
237    /// # Returns
238    /// 
239    /// A mutable reference to the `DMSCConfigManager` instance.
240    pub fn config_mut(&mut self) -> &mut DMSCConfigManager {
241        Arc::get_mut(&mut self.inner.config).expect("Cannot get mutable reference to config - shared ownership")
242    }
243
244    /// Get a mutable reference to the file system accessor.
245    /// 
246    /// # Returns
247    /// 
248    /// A mutable reference to to the `DMSCFileSystem` instance.
249    pub fn fs_mut(&mut self) -> &mut DMSCFileSystem {
250        &mut self.inner.fs
251    }
252
253    /// Get a mutable reference to the structured logger.
254    /// 
255    /// # Returns
256    /// 
257    /// A mutable reference to the `DMSCLogger` instance.
258    pub fn logger_mut(&mut self) -> &mut DMSCLogger {
259        Arc::get_mut(&mut self.inner.logger).expect("Cannot get mutable reference to logger - shared ownership")
260    }
261
262    /// Get a reference to the metrics registry if available.
263    /// 
264    /// # Returns
265    /// 
266    /// An optional reference to the `DMSCMetricsRegistry` instance.
267    pub fn metrics_registry(&self) -> Option<Arc<DMSCMetricsRegistry>> {
268        self.inner.metrics_registry.clone()
269    }
270}
271
272#[cfg(feature = "pyo3")]
273/// Python bindings for DMSCServiceContext
274#[pyo3::prelude::pymethods]
275impl DMSCServiceContext {
276    /// Create a new DMSCServiceContext with default configuration
277    #[new]
278    fn py_new() -> pyo3::PyResult<Self> {
279        match Self::new_default() {
280            Ok(ctx) => Ok(ctx),
281            Err(err) => Err(pyo3::exceptions::PyRuntimeError::new_err(format!("Failed to create service context: {err}"))),
282        }
283    }
284
285    #[pyo3(name = "fs")]
286    fn fs_py(&self) -> crate::fs::DMSCFileSystem {
287        self.inner.fs.clone()
288    }
289
290    #[pyo3(name = "logger")]
291    fn logger_py(&self) -> crate::log::DMSCLogger {
292        (*self.inner.logger).clone()
293    }
294
295    #[pyo3(name = "config")]
296    fn config_py(&self) -> crate::config::DMSCConfigManager {
297        (*self.inner.config).clone()
298    }
299
300    #[pyo3(name = "metrics_registry")]
301    fn metrics_registry_py(&self) -> Option<crate::observability::DMSCMetricsRegistry> {
302        self.inner.metrics_registry.as_ref().map(|r| (**r).clone())
303    }
304
305    #[pyo3(name = "hooks")]
306    fn hooks_py(&self) -> crate::hooks::DMSCHookBus {
307        crate::hooks::DMSCHookBus::new()
308    }
309}