-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathcontext.rs
More file actions
253 lines (243 loc) · 8.94 KB
/
Copy pathcontext.rs
File metadata and controls
253 lines (243 loc) · 8.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
//! Execution Context
//!
//! This module provides the `ExecutionContext` wrapper around the Container for
//! dependency injection in command handlers. It offers a clean interface for
//! accessing services needed during command execution.
//!
//! ## Purpose
//!
//! The `ExecutionContext` serves as an abstraction layer between the Container
//! (which holds raw services) and command handlers (which need typed access).
//! This separation provides:
//!
//! - **Clean Interface**: Command handlers get strongly-typed service access
//! - **Thread Safety**: All services are properly wrapped for concurrent access
//! - **Future-Proofing**: Easy to add new services without changing handler signatures
//! - **Testing Support**: Easy to inject test doubles through Container
//!
//! ## Design
//!
//! ```text
//! Container (bootstrap) → ExecutionContext (dispatch) → Command Handlers
//!
//! Raw services Clean typed access Business logic
//! ```
//!
//! ## Usage Example
//!
//! ```rust,no_run
//! use torrust_tracker_deployer_lib::bootstrap::Container;
//! use torrust_tracker_deployer_lib::presentation::views::VerbosityLevel;
//! use torrust_tracker_deployer_lib::presentation::dispatch::ExecutionContext;
//! use std::sync::Arc;
//! use std::path::Path;
//!
//! # fn example() -> Result<(), Box<dyn std::error::Error>> {
//! // Create execution context from container
//! let container = Container::new(VerbosityLevel::Normal, Path::new("."));
//! let context = ExecutionContext::new(Arc::new(container));
//!
//! // Command handlers access services through context
//! let user_output = context.user_output();
//! user_output.lock().borrow_mut().progress("Processing...");
//! # Ok(())
//! # }
//! ```
use std::cell::RefCell;
use std::sync::Arc;
use parking_lot::ReentrantMutex;
use crate::bootstrap::Container;
use crate::infrastructure::persistence::repository_factory::RepositoryFactory;
use crate::presentation::views::UserOutput;
use crate::shared::clock::Clock;
/// ### Design Consideration: Shared State Access
///
/// Currently, there is no shared mutable state in the system that requires `Arc<Mutex<T>>`
/// patterns. However, if shared state is needed in the future, it can be added to the
/// Container and accessed through standard Rust concurrency patterns:
///
/// # Examples
///
/// ```rust
/// use std::sync::Arc;
/// use std::path::Path;
/// use torrust_tracker_deployer_lib::bootstrap::Container;
/// use torrust_tracker_deployer_lib::presentation::views::VerbosityLevel;
/// use torrust_tracker_deployer_lib::presentation::dispatch::ExecutionContext;
///
/// let container = Arc::new(Container::new(VerbosityLevel::Normal, Path::new(".")));
/// let context = ExecutionContext::new(container);
///
/// // Access user output service
/// let user_output = context.user_output();
/// user_output.lock().borrow_mut().success("Operation completed");
/// ```
#[derive(Clone)]
pub struct ExecutionContext {
container: Arc<Container>,
}
impl ExecutionContext {
/// Create a new execution context from a container
///
/// # Arguments
///
/// * `container` - Application service container with initialized services
///
/// # Examples
///
/// ```rust,no_run
/// use torrust_tracker_deployer_lib::bootstrap::Container;
/// use torrust_tracker_deployer_lib::presentation::views::VerbosityLevel;
/// use torrust_tracker_deployer_lib::presentation::dispatch::ExecutionContext;
/// use std::sync::Arc;
/// use std::path::Path;
///
/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
/// let container = Container::new(VerbosityLevel::Normal, Path::new("."));
/// let context = ExecutionContext::new(Arc::new(container));
/// # Ok(())
/// # }
/// ```
#[must_use]
pub fn new(container: Arc<Container>) -> Self {
Self { container }
}
/// Get reference to the underlying container
///
/// Provides access to the raw container for cases where direct access
/// to container methods is needed.
///
/// # Examples
///
/// ```rust,no_run
/// use std::path::Path;
/// use torrust_tracker_deployer_lib::bootstrap::Container;
/// use torrust_tracker_deployer_lib::presentation::views::VerbosityLevel;
/// use torrust_tracker_deployer_lib::presentation::dispatch::ExecutionContext;
/// use std::sync::Arc;
///
/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
/// let container = Container::new(VerbosityLevel::Normal, Path::new("."));
/// let context = ExecutionContext::new(Arc::new(container));
///
/// let container_ref = context.container();
/// // Use container_ref as needed
/// # Ok(())
/// # }
/// ```
#[must_use]
pub fn container(&self) -> &Arc<Container> {
&self.container
}
/// Get shared reference to user output service
///
/// Returns the user output service for displaying messages, progress,
/// and results to users. The service is wrapped in `Arc<Mutex<T>>` for
/// thread-safe shared access.
///
/// # Examples
///
/// ```rust,no_run
/// use torrust_tracker_deployer_lib::bootstrap::Container;
/// use torrust_tracker_deployer_lib::presentation::views::VerbosityLevel;
/// use torrust_tracker_deployer_lib::presentation::dispatch::ExecutionContext;
/// use std::sync::Arc;
/// use std::path::Path;
///
/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
/// let container = Container::new(VerbosityLevel::Normal, Path::new("."));
/// let context = ExecutionContext::new(Arc::new(container));
///
/// let user_output = context.user_output();
/// user_output.lock().borrow_mut().success("Operation completed");
/// # Ok(())
/// # }
/// ```
#[must_use]
pub fn user_output(&self) -> Arc<ReentrantMutex<RefCell<UserOutput>>> {
self.container.user_output()
}
/// Get shared reference to repository factory service
///
/// Returns the repository factory service for creating environment
/// repositories. The service is wrapped in `Arc<T>` for shared access.
///
/// # Examples
///
/// ```rust,no_run
/// use torrust_tracker_deployer_lib::bootstrap::Container;
/// use torrust_tracker_deployer_lib::presentation::views::VerbosityLevel;
/// use torrust_tracker_deployer_lib::presentation::dispatch::ExecutionContext;
/// use std::sync::Arc;
/// use std::path::Path;
///
/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
/// let container = Container::new(VerbosityLevel::Normal, Path::new("."));
/// let context = ExecutionContext::new(Arc::new(container));
///
/// let repository_factory = context.repository_factory();
/// // Use repository_factory to create repositories
/// # Ok(())
/// # }
/// ```
#[must_use]
pub fn repository_factory(&self) -> Arc<RepositoryFactory> {
self.container.repository_factory()
}
/// Get shared reference to environment repository
///
/// Returns the environment repository for persistence operations.
/// The repository is wrapped in `Arc<dyn EnvironmentRepository>` for shared access.
///
/// # Examples
///
/// ```rust,no_run
/// use torrust_tracker_deployer_lib::bootstrap::Container;
/// use torrust_tracker_deployer_lib::presentation::views::VerbosityLevel;
/// use torrust_tracker_deployer_lib::presentation::dispatch::ExecutionContext;
/// use std::sync::Arc;
/// use std::path::Path;
///
/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
/// let container = Container::new(VerbosityLevel::Normal, Path::new("."));
/// let context = ExecutionContext::new(Arc::new(container));
///
/// let repository = context.repository();
/// // Use repository for environment persistence
/// # Ok(())
/// # }
/// ```
#[must_use]
pub fn repository(
&self,
) -> Arc<dyn crate::domain::environment::repository::EnvironmentRepository + Send + Sync> {
self.container.repository()
}
/// Get shared reference to clock service
///
/// Returns the clock service for time-related operations.
/// The service is wrapped in `Arc<dyn Clock>` for shared access.
///
/// # Examples
///
/// ```rust,no_run
/// use torrust_tracker_deployer_lib::bootstrap::Container;
/// use torrust_tracker_deployer_lib::presentation::views::VerbosityLevel;
/// use torrust_tracker_deployer_lib::presentation::dispatch::ExecutionContext;
/// use std::sync::Arc;
/// use std::path::Path;
///
/// # fn example() -> Result<(), Box<dyn std::error::Error>> {
/// let container = Container::new(VerbosityLevel::Normal, Path::new("."));
/// let context = ExecutionContext::new(Arc::new(container));
///
/// let clock = context.clock();
/// // Use clock for time operations
/// # Ok(())
/// # }
/// ```
#[must_use]
pub fn clock(&self) -> Arc<dyn Clock> {
self.container.clock()
}
}