elif_core/container/
scope.rs

1/// Service scope enumeration
2#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
3pub enum ServiceScope {
4    /// Single instance shared across the application
5    #[default]
6    Singleton,
7    /// New instance created for each request
8    Transient,
9    /// Instance scoped to a particular context (e.g., request scope)
10    Scoped,
11}
12
13/// Service lifetime type alias for compatibility
14pub type ServiceLifetime = ServiceScope;
15
16impl ServiceScope {
17    /// Check if the scope is singleton
18    pub fn is_singleton(&self) -> bool {
19        matches!(self, ServiceScope::Singleton)
20    }
21
22    /// Check if the scope is transient
23    pub fn is_transient(&self) -> bool {
24        matches!(self, ServiceScope::Transient)
25    }
26
27    /// Check if the scope is scoped
28    pub fn is_scoped(&self) -> bool {
29        matches!(self, ServiceScope::Scoped)
30    }
31
32    /// Get the scope name as a string
33    pub fn as_str(&self) -> &'static str {
34        match self {
35            ServiceScope::Singleton => "singleton",
36            ServiceScope::Transient => "transient",
37            ServiceScope::Scoped => "scoped",
38        }
39    }
40}
41
42impl std::fmt::Display for ServiceScope {
43    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44        write!(f, "{}", self.as_str())
45    }
46}
47
48impl std::str::FromStr for ServiceScope {
49    type Err = crate::errors::CoreError;
50
51    fn from_str(s: &str) -> Result<Self, Self::Err> {
52        match s.to_lowercase().as_str() {
53            "singleton" => Ok(ServiceScope::Singleton),
54            "transient" => Ok(ServiceScope::Transient),
55            "scoped" => Ok(ServiceScope::Scoped),
56            _ => Err(crate::errors::CoreError::InvalidServiceScope {
57                scope: s.to_string(),
58            }),
59        }
60    }
61}
62
63/// Unique scope identifier
64#[derive(Debug, Clone, PartialEq, Eq, Hash)]
65pub struct ScopeId(uuid::Uuid);
66
67impl ScopeId {
68    /// Create a new scope ID
69    pub fn new() -> Self {
70        Self(uuid::Uuid::new_v4())
71    }
72
73    /// Get the inner UUID
74    pub fn as_uuid(&self) -> uuid::Uuid {
75        self.0
76    }
77}
78
79impl Default for ScopeId {
80    fn default() -> Self {
81        Self::new()
82    }
83}
84
85impl std::fmt::Display for ScopeId {
86    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87        write!(f, "{}", self.0)
88    }
89}
90
91/// Scoped service manager for managing services within a specific scope
92#[derive(Debug)]
93pub struct ScopedServiceManager {
94    scope_id: ScopeId,
95    services: std::sync::RwLock<
96        std::collections::HashMap<std::any::TypeId, Box<dyn std::any::Any + Send + Sync>>,
97    >,
98    parent: Option<std::sync::Arc<ScopedServiceManager>>,
99}
100
101impl ScopedServiceManager {
102    /// Create a new scoped service manager
103    pub fn new() -> Self {
104        Self {
105            scope_id: ScopeId::new(),
106            services: std::sync::RwLock::new(std::collections::HashMap::new()),
107            parent: None,
108        }
109    }
110
111    /// Create a child scope with this scope as parent
112    /// This requires the parent to be wrapped in an Arc
113    pub fn create_child(parent: std::sync::Arc<Self>) -> Self {
114        Self {
115            scope_id: ScopeId::new(),
116            services: std::sync::RwLock::new(std::collections::HashMap::new()),
117            parent: Some(parent),
118        }
119    }
120
121    /// Get the scope ID
122    pub fn scope_id(&self) -> &ScopeId {
123        &self.scope_id
124    }
125
126    /// Add a service to this scope
127    pub fn add_service<T>(&self, service: T)
128    where
129        T: Send + Sync + 'static,
130    {
131        let type_id = std::any::TypeId::of::<T>();
132        let mut services = self.services.write().unwrap();
133        services.insert(type_id, Box::new(service));
134    }
135
136    /// Store a service as Arc in this scope
137    pub fn add_arc_service<T>(&self, service: std::sync::Arc<T>)
138    where
139        T: Send + Sync + 'static,
140    {
141        let type_id = std::any::TypeId::of::<T>();
142        let mut services = self.services.write().unwrap();
143        services.insert(type_id, Box::new(service));
144    }
145
146    /// Get a service from this scope, checking parent scopes if not found
147    /// Services must be stored as Arc<T> for this to work
148    pub fn get_arc_service<T>(&self) -> Option<std::sync::Arc<T>>
149    where
150        T: Send + Sync + 'static,
151    {
152        let type_id = std::any::TypeId::of::<T>();
153
154        // Check current scope first
155        {
156            let services = self.services.read().unwrap();
157            if let Some(service) = services.get(&type_id) {
158                if let Some(arc) = service.downcast_ref::<std::sync::Arc<T>>() {
159                    return Some(arc.clone());
160                }
161            }
162        }
163
164        // Check parent scopes recursively
165        self.parent.as_ref()?.get_arc_service::<T>()
166    }
167
168    /// Check if a service exists in this scope or parent scopes
169    pub fn has_service<T>(&self) -> bool
170    where
171        T: Send + Sync + 'static,
172    {
173        let type_id = std::any::TypeId::of::<T>();
174
175        // Check current scope first
176        {
177            let services = self.services.read().unwrap();
178            if services.contains_key(&type_id) {
179                return true;
180            }
181        }
182
183        // Check parent scopes
184        self.parent.as_ref().is_some_and(|p| p.has_service::<T>())
185    }
186
187    /// Check if a service exists in this specific scope (not parent scopes)
188    pub fn has_service_local<T>(&self) -> bool
189    where
190        T: Send + Sync + 'static,
191    {
192        let type_id = std::any::TypeId::of::<T>();
193        let services = self.services.read().unwrap();
194        services.contains_key(&type_id)
195    }
196
197    /// Clear all services from this scope
198    pub fn clear(&self) {
199        let mut services = self.services.write().unwrap();
200        services.clear();
201    }
202
203    /// Get the number of services in this scope
204    pub fn service_count(&self) -> usize {
205        let services = self.services.read().unwrap();
206        services.len()
207    }
208
209    /// Get parent scope
210    pub fn parent(&self) -> Option<&std::sync::Arc<ScopedServiceManager>> {
211        self.parent.as_ref()
212    }
213}
214
215// Note: ScopedServiceManager intentionally does not implement Clone
216// to prevent accidental creation of empty service managers.
217// Use Arc<ScopedServiceManager> for sharing.
218
219impl Default for ScopedServiceManager {
220    fn default() -> Self {
221        Self::new()
222    }
223}
224
225#[cfg(test)]
226mod tests {
227    use super::*;
228
229    #[test]
230    fn test_service_scope_from_str() {
231        assert_eq!(
232            "singleton".parse::<ServiceScope>().unwrap(),
233            ServiceScope::Singleton
234        );
235        assert_eq!(
236            "transient".parse::<ServiceScope>().unwrap(),
237            ServiceScope::Transient
238        );
239        assert_eq!(
240            "scoped".parse::<ServiceScope>().unwrap(),
241            ServiceScope::Scoped
242        );
243
244        assert!("invalid".parse::<ServiceScope>().is_err());
245    }
246
247    #[test]
248    fn test_service_scope_display() {
249        assert_eq!(format!("{}", ServiceScope::Singleton), "singleton");
250        assert_eq!(format!("{}", ServiceScope::Transient), "transient");
251        assert_eq!(format!("{}", ServiceScope::Scoped), "scoped");
252    }
253
254    #[test]
255    fn test_scoped_service_manager() {
256        let manager = ScopedServiceManager::new();
257
258        // Use Arc for services to enable proper sharing
259        manager.add_arc_service(std::sync::Arc::new("test_string".to_string()));
260        manager.add_arc_service(std::sync::Arc::new(42u32));
261
262        assert!(manager.has_service::<String>());
263        assert!(manager.has_service::<u32>());
264        assert!(!manager.has_service::<i32>());
265
266        assert_eq!(
267            *manager.get_arc_service::<String>().unwrap(),
268            "test_string".to_string()
269        );
270        assert_eq!(*manager.get_arc_service::<u32>().unwrap(), 42u32);
271
272        assert_eq!(manager.service_count(), 2);
273
274        manager.clear();
275        assert_eq!(manager.service_count(), 0);
276    }
277
278    #[test]
279    fn test_scope_inheritance() {
280        let parent = std::sync::Arc::new(ScopedServiceManager::new());
281
282        // Add services to parent
283        parent.add_arc_service(std::sync::Arc::new("parent_string".to_string()));
284        parent.add_arc_service(std::sync::Arc::new(100u32));
285
286        // Create child scope
287        let child = std::sync::Arc::new(ScopedServiceManager::create_child(parent.clone()));
288
289        // Add service to child
290        child.add_arc_service(std::sync::Arc::new(200u32));
291
292        // Child should see its own services
293        assert_eq!(*child.get_arc_service::<u32>().unwrap(), 200u32);
294
295        // Child should also see parent's services
296        assert_eq!(
297            *child.get_arc_service::<String>().unwrap(),
298            "parent_string".to_string()
299        );
300
301        // Parent should not see child's services
302        assert_eq!(*parent.get_arc_service::<u32>().unwrap(), 100u32);
303
304        // Service counts
305        assert_eq!(parent.service_count(), 2);
306        assert_eq!(child.service_count(), 1); // Only its own services
307
308        // has_service should check parent too
309        assert!(child.has_service::<String>()); // From parent
310        assert!(child.has_service::<u32>()); // From self
311        assert!(!child.has_service_local::<String>()); // Not in child itself
312        assert!(child.has_service_local::<u32>()); // In child itself
313    }
314}