1use crate::container::scope::ServiceScope;
2use crate::errors::CoreError;
3use std::any::{Any, TypeId};
4
5#[derive(Debug, Clone, PartialEq, Eq, Hash)]
7pub struct ServiceId {
8 pub type_id: TypeId,
9 pub type_name: &'static str,
10 pub name: Option<String>,
11}
12
13impl ServiceId {
14 pub fn of<T: 'static + ?Sized>() -> Self {
16 Self {
17 type_id: TypeId::of::<T>(),
18 type_name: std::any::type_name::<T>(),
19 name: None,
20 }
21 }
22
23 pub fn named<T: 'static + ?Sized>(name: impl Into<String>) -> Self {
25 Self {
26 type_id: TypeId::of::<T>(),
27 type_name: std::any::type_name::<T>(),
28 name: Some(name.into()),
29 }
30 }
31
32 pub fn matches_named<T: 'static + ?Sized>(&self, name: &str) -> bool {
34 self.type_id == TypeId::of::<T>() && self.name.as_deref() == Some(name)
35 }
36
37 pub fn type_name(&self) -> &'static str {
39 self.type_name
40 }
41
42 pub fn by_ids(type_id: TypeId, type_name: &'static str) -> Self {
44 Self {
45 type_id,
46 type_name,
47 name: None,
48 }
49 }
50
51 pub fn named_by_ids(type_id: TypeId, type_name: &'static str, name: String) -> Self {
53 Self {
54 type_id,
55 type_name,
56 name: Some(name),
57 }
58 }
59}
60
61pub type ServiceFactory =
64 Box<dyn Fn() -> Result<Box<dyn Any + Send + Sync>, CoreError> + Send + Sync>;
65
66pub enum ServiceActivationStrategy {
68 Factory(ServiceFactory),
70 AutoWired,
72}
73
74impl std::fmt::Debug for ServiceActivationStrategy {
75 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76 match self {
77 ServiceActivationStrategy::Factory(_) => write!(f, "Factory(<factory_fn>)"),
78 ServiceActivationStrategy::AutoWired => write!(f, "AutoWired"),
79 }
80 }
81}
82
83pub struct ServiceDescriptor {
85 pub service_id: ServiceId,
87 pub implementation_id: TypeId,
89 pub lifetime: ServiceScope,
91 pub activation_strategy: ServiceActivationStrategy,
93 pub dependencies: Vec<ServiceId>,
95}
96
97impl std::fmt::Debug for ServiceDescriptor {
98 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99 f.debug_struct("ServiceDescriptor")
100 .field("service_id", &self.service_id)
101 .field("implementation_id", &self.implementation_id)
102 .field("lifetime", &self.lifetime)
103 .field("activation_strategy", &self.activation_strategy)
104 .field("dependencies", &self.dependencies)
105 .finish()
106 }
107}
108
109impl ServiceDescriptor {
110 pub fn bind<TInterface: ?Sized + 'static, TImpl: Send + Sync + Default + 'static>(
112 ) -> ServiceDescriptorBuilder<TInterface, TImpl> {
113 ServiceDescriptorBuilder::new()
114 }
115
116 pub fn bind_named<TInterface: ?Sized + 'static, TImpl: Send + Sync + Default + 'static>(
118 name: impl Into<String>,
119 ) -> ServiceDescriptorBuilder<TInterface, TImpl> {
120 ServiceDescriptorBuilder::new().with_name(name)
121 }
122
123 pub fn singleton<TInterface: ?Sized + 'static, TImpl: Send + Sync + Default + 'static>(
125 ) -> ServiceDescriptorBuilder<TInterface, TImpl> {
126 ServiceDescriptorBuilder::new().with_lifetime(ServiceScope::Singleton)
127 }
128
129 pub fn transient<TInterface: ?Sized + 'static, TImpl: Send + Sync + Default + 'static>(
131 ) -> ServiceDescriptorBuilder<TInterface, TImpl> {
132 ServiceDescriptorBuilder::new().with_lifetime(ServiceScope::Transient)
133 }
134
135 pub fn autowired<T: 'static>(dependencies: Vec<ServiceId>) -> ServiceDescriptor {
137 ServiceDescriptor {
138 service_id: ServiceId::of::<T>(),
139 implementation_id: TypeId::of::<T>(),
140 lifetime: ServiceScope::Transient,
141 activation_strategy: ServiceActivationStrategy::AutoWired,
142 dependencies,
143 }
144 }
145
146 pub fn autowired_singleton<T: 'static>(dependencies: Vec<ServiceId>) -> ServiceDescriptor {
148 ServiceDescriptor {
149 service_id: ServiceId::of::<T>(),
150 implementation_id: TypeId::of::<T>(),
151 lifetime: ServiceScope::Singleton,
152 activation_strategy: ServiceActivationStrategy::AutoWired,
153 dependencies,
154 }
155 }
156}
157
158pub struct ServiceDescriptorBuilder<TInterface: ?Sized, TImpl> {
160 name: Option<String>,
161 lifetime: ServiceScope,
162 dependencies: Vec<ServiceId>,
163 _phantom: std::marker::PhantomData<(*const TInterface, TImpl)>,
164}
165
166impl<TInterface: ?Sized + 'static, TImpl: Send + Sync + Default + 'static> Default
167 for ServiceDescriptorBuilder<TInterface, TImpl>
168{
169 fn default() -> Self {
170 Self::new()
171 }
172}
173
174impl<TInterface: ?Sized + 'static, TImpl: Send + Sync + Default + 'static>
175 ServiceDescriptorBuilder<TInterface, TImpl>
176{
177 pub fn new() -> Self {
179 Self {
180 name: None,
181 lifetime: ServiceScope::Transient,
182 dependencies: Vec::new(),
183 _phantom: std::marker::PhantomData,
184 }
185 }
186
187 pub fn with_name(mut self, name: impl Into<String>) -> Self {
189 self.name = Some(name.into());
190 self
191 }
192
193 pub fn with_lifetime(mut self, lifetime: ServiceScope) -> Self {
195 self.lifetime = lifetime;
196 self
197 }
198
199 pub fn depends_on<T: 'static>(mut self) -> Self {
201 self.dependencies.push(ServiceId::of::<T>());
202 self
203 }
204
205 pub fn depends_on_named<T: 'static>(mut self, name: impl Into<String>) -> Self {
207 self.dependencies.push(ServiceId::named::<T>(name));
208 self
209 }
210
211 pub fn build(self) -> ServiceDescriptor {
213 let service_id = if let Some(name) = self.name {
214 ServiceId::named::<TInterface>(name)
215 } else {
216 ServiceId::of::<TInterface>()
217 };
218
219 let factory: ServiceFactory = Box::new(move || {
220 let instance = TImpl::default();
221 Ok(Box::new(instance) as Box<dyn Any + Send + Sync>)
222 });
223
224 ServiceDescriptor {
225 service_id,
226 implementation_id: TypeId::of::<TImpl>(),
227 lifetime: self.lifetime,
228 activation_strategy: ServiceActivationStrategy::Factory(factory),
229 dependencies: self.dependencies,
230 }
231 }
232}
233
234pub struct ServiceDescriptorFactoryBuilder<TInterface: ?Sized> {
236 name: Option<String>,
237 lifetime: ServiceScope,
238 dependencies: Vec<ServiceId>,
239 factory: Option<ServiceFactory>,
240 _phantom: std::marker::PhantomData<*const TInterface>,
241}
242
243impl<TInterface: ?Sized + 'static> Default for ServiceDescriptorFactoryBuilder<TInterface> {
244 fn default() -> Self {
245 Self::new()
246 }
247}
248
249impl<TInterface: ?Sized + 'static> ServiceDescriptorFactoryBuilder<TInterface> {
250 pub fn new() -> Self {
252 Self {
253 name: None,
254 lifetime: ServiceScope::Transient,
255 dependencies: Vec::new(),
256 factory: None,
257 _phantom: std::marker::PhantomData,
258 }
259 }
260
261 pub fn with_name(mut self, name: impl Into<String>) -> Self {
263 self.name = Some(name.into());
264 self
265 }
266
267 pub fn with_lifetime(mut self, lifetime: ServiceScope) -> Self {
269 self.lifetime = lifetime;
270 self
271 }
272
273 pub fn with_factory<F, T>(mut self, factory: F) -> Self
275 where
276 F: Fn() -> Result<T, CoreError> + Send + Sync + 'static,
277 T: Send + Sync + 'static,
278 {
279 let wrapped_factory: ServiceFactory = Box::new(move || {
280 let instance = factory()?;
281 Ok(Box::new(instance) as Box<dyn Any + Send + Sync>)
282 });
283 self.factory = Some(wrapped_factory);
284 self
285 }
286
287 pub fn build(self) -> Result<ServiceDescriptor, CoreError> {
289 let factory = self
290 .factory
291 .ok_or_else(|| CoreError::InvalidServiceDescriptor {
292 message: "Factory function is required".to_string(),
293 })?;
294
295 let service_id = if let Some(name) = self.name {
296 ServiceId::named::<TInterface>(name)
297 } else {
298 ServiceId::of::<TInterface>()
299 };
300
301 Ok(ServiceDescriptor {
302 service_id,
303 implementation_id: TypeId::of::<()>(), lifetime: self.lifetime,
305 activation_strategy: ServiceActivationStrategy::Factory(factory),
306 dependencies: self.dependencies,
307 })
308 }
309}
310
311#[cfg(test)]
312mod tests {
313 use super::*;
314
315 #[allow(dead_code)]
316 trait TestTrait: Send + Sync {
317 fn test_method(&self) -> String;
318 }
319
320 #[derive(Debug, Default)]
321 struct TestImpl;
322
323 unsafe impl Send for TestImpl {}
324 unsafe impl Sync for TestImpl {}
325
326 impl TestTrait for TestImpl {
327 fn test_method(&self) -> String {
328 "test".to_string()
329 }
330 }
331
332 #[test]
333 fn test_service_id_creation() {
334 let id1 = ServiceId::of::<TestImpl>();
335 let id2 = ServiceId::named::<TestImpl>("test");
336
337 assert_eq!(id1.type_id, TypeId::of::<TestImpl>());
338 assert_eq!(id1.name, None);
339
340 assert_eq!(id2.type_id, TypeId::of::<TestImpl>());
341 assert_eq!(id2.name, Some("test".to_string()));
342
343 assert_ne!(id1, id2);
344 }
345
346 #[test]
347 fn test_type_name_capture() {
348 let id1 = ServiceId::of::<TestImpl>();
349 let id2 = ServiceId::named::<TestImpl>("test");
350 let id3 = ServiceId::of::<dyn TestTrait>();
351 let id4 = ServiceId::of::<String>();
352
353 assert!(id1.type_name().contains("TestImpl"));
355 assert!(id2.type_name().contains("TestImpl"));
356 assert!(id3.type_name().contains("TestTrait"));
357 assert_eq!(id4.type_name(), "alloc::string::String");
358
359 assert_eq!(id1.type_name(), id1.type_name);
361 assert_eq!(id2.type_name(), id2.type_name);
362 }
363
364 #[test]
365 fn test_service_descriptor_builder() {
366 let descriptor = ServiceDescriptor::bind::<dyn TestTrait, TestImpl>()
367 .with_lifetime(ServiceScope::Singleton)
368 .depends_on::<String>()
369 .build();
370
371 assert_eq!(descriptor.lifetime, ServiceScope::Singleton);
372 assert_eq!(descriptor.implementation_id, TypeId::of::<TestImpl>());
373 assert_eq!(descriptor.dependencies.len(), 1);
374 assert_eq!(descriptor.dependencies[0], ServiceId::of::<String>());
375 }
376
377 #[test]
378 fn test_factory_service_descriptor() {
379 let descriptor = ServiceDescriptorFactoryBuilder::<dyn TestTrait>::new()
380 .with_lifetime(ServiceScope::Transient)
381 .with_factory(|| -> Result<TestImpl, CoreError> { Ok(TestImpl) })
382 .build()
383 .unwrap();
384
385 assert_eq!(descriptor.lifetime, ServiceScope::Transient);
386 }
387}