service_rs/lib.rs
1//! # service-rs
2//!
3//! An async-first, lightweight dependency injection (DI) container for Rust.
4//!
5//! This library provides a simple and ergonomic way to manage dependencies in your Rust applications,
6//! inspired by [Microsoft.Extensions.DependencyInjection](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.dependencyinjection).
7//!
8//! ## Features
9//!
10//! - **Three service lifetimes**: Singleton, Scoped, and Transient
11//! - **Async-first design**: All service resolution is async using `tokio`
12//! - **Thread-safe**: Services are wrapped in `Arc<T>` for safe sharing across threads
13//! - **Automatic dependency injection**: Use the `#[derive(Injectable)]` macro for automatic constructor injection
14//! - **Trait object support**: Register implementations for trait objects
15//! - **Scoped services**: Create service scopes with scoped lifetime management
16//!
17//! ## Service Lifetimes
18//!
19//! - **Singleton**: One instance created and shared across the entire application
20//! - **Scoped**: One instance per scope; same instance within a scope, new instance for each scope
21//! - **Transient**: New instance created every time the service is requested
22//!
23//! ## Example
24//!
25//! ```no_run
26//! use service_rs::ServiceCollection;
27//! use std::sync::Arc;
28//!
29//! #[tokio::main]
30//! async fn main() {
31//! let mut collection = ServiceCollection::new();
32//! collection.add_singleton_with_factory::<i32, _, _>(|_| async {
33//! Ok(Box::new(42) as Box<dyn std::any::Any + Send + Sync>)
34//! });
35//!
36//! let provider = collection.build();
37//! let num: Arc<i32> = provider.get::<i32>().await.unwrap();
38//! assert_eq!(*num, 42);
39//! }
40//! ```
41
42#![feature(unsize)]
43#![feature(coerce_unsized)]
44
45use std::{
46 any::{Any, TypeId},
47 collections::HashMap,
48 future::Future,
49 pin::Pin,
50 sync::Arc,
51};
52
53use thiserror::Error;
54use tokio::sync::RwLock;
55
56#[cfg(feature = "proc-macro")]
57pub use service_rs_proc_macro::Injectable;
58
59/// Extension trait for types that can be automatically injected.
60///
61/// This trait is automatically implemented when you use the `#[derive(Injectable)]` macro.
62/// It generates a factory function that resolves all dependencies from the service provider.
63///
64/// # Example
65///
66/// ```no_run
67/// use service_rs::{Injectable, ServiceCollection};
68/// use std::sync::Arc;
69///
70/// #[derive(Injectable)]
71/// struct MyService {
72/// dependency: Arc<i32>,
73/// }
74/// ```
75#[cfg(feature = "proc-macro")]
76pub trait InjectableExtension: Sized + Send + Sync + 'static {
77 /// Creates a factory function for this type.
78 fn create_factory() -> ServiceFactory;
79}
80
81/// Defines the lifetime of a service in the dependency injection container.
82#[derive(Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
83pub enum ServiceLifetime {
84 /// A single instance is created and shared across the entire application.
85 Singleton,
86 /// A single instance is created per scope. Different scopes get different instances.
87 Scoped,
88 /// A new instance is created every time the service is requested.
89 Transient,
90}
91
92/// Errors that can occur during service registration and resolution.
93#[derive(Debug, Error)]
94pub enum ServiceError {
95 /// The requested service type was not registered in the container.
96 #[error("Service with type '{type_name}' not found")]
97 ServiceNotFound { type_name: &'static str },
98
99 /// Attempted to register a service type that already exists.
100 #[error("Service with type '{type_name}' already exists")]
101 ServiceAlreadyExists { type_name: &'static str },
102
103 /// Failed to downcast the service to the requested type.
104 #[error("Service resolution failed for type '{type_name}'")]
105 ServiceResolutionFailed { type_name: &'static str },
106
107 /// The service factory threw an error during initialization.
108 #[error("Service initialization failed for type '{type_name}' with error: {error}")]
109 ServiceInitializationFailed {
110 type_name: &'static str,
111 error: Box<dyn std::error::Error>,
112 },
113
114 /// Attempted to resolve a scoped service from the root provider instead of a scope.
115 #[error(
116 "Service with type '{type_name}' is resolved under ServiceProvider, but it's lifetime is ServiceLifetime::Scoped"
117 )]
118 ServiceInvalidScope { type_name: &'static str },
119}
120
121/// A factory function that creates service instances.
122///
123/// The factory receives a [`ServiceProviderContext`] and returns a pinned future
124/// that resolves to a boxed service instance or an error.
125pub type ServiceFactory = Box<
126 dyn Fn(
127 ServiceProviderContext,
128 ) -> Pin<
129 Box<
130 dyn Future<Output = Result<Box<dyn Any + Send + Sync>, Box<dyn std::error::Error>>>
131 + Send,
132 >,
133 > + Send
134 + Sync,
135>;
136
137/// Internal descriptor that stores service registration information.
138///
139/// This struct is used internally by the service container to track registered services.
140pub struct ServiceDescriptor {
141 pub(crate) lifetime: ServiceLifetime,
142 pub(crate) type_name: &'static str,
143 pub(crate) factory: ServiceFactory,
144}
145
146impl std::fmt::Debug for ServiceDescriptor {
147 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148 f.debug_struct("ServiceDescriptor")
149 .field("lifetime", &self.lifetime)
150 .field("type_name", &self.type_name)
151 .finish()
152 }
153}
154
155/// A collection of service descriptors used to build a service provider.
156///
157/// Use this to register services with different lifetimes before building
158/// the final [`ServiceProvider`].
159///
160/// # Example
161///
162/// ```no_run
163/// use service_rs::ServiceCollection;
164///
165/// let mut collection = ServiceCollection::new();
166/// collection.add_singleton_with_factory::<i32, _, _>(|_| async {
167/// Ok(Box::new(42) as Box<dyn std::any::Any + Send + Sync>)
168/// });
169///
170/// let provider = collection.build();
171/// ```
172#[derive(Debug, Default)]
173pub struct ServiceCollection {
174 pub(crate) services: HashMap<TypeId, ServiceDescriptor>,
175}
176
177/// Context passed to service factories to enable dependency resolution.
178///
179/// This enum represents either a root provider or a scoped provider,
180/// allowing factories to resolve dependencies from the appropriate context.
181#[derive(Clone)]
182pub enum ServiceProviderContext {
183 /// Root service provider context.
184 Root(Arc<ServiceProvider>),
185 /// Scoped service provider context.
186 Scoped(Arc<ScopedServiceProvider>),
187}
188
189impl ServiceProviderContext {
190 /// Resolves a service from the current context.
191 ///
192 /// # Errors
193 ///
194 /// Returns a [`ServiceError`] if the service cannot be resolved.
195 pub async fn get<T: Send + Sync + 'static>(&self) -> Result<Arc<T>, ServiceError> {
196 match self {
197 ServiceProviderContext::Root(provider) => provider.get::<T>().await,
198 ServiceProviderContext::Scoped(scoped) => scoped.get::<T>().await,
199 }
200 }
201}
202
203impl ServiceCollection {
204 /// Creates a new empty service collection.
205 pub fn new() -> Self {
206 Self {
207 services: HashMap::new(),
208 }
209 }
210
211 /// Registers a singleton service using a factory function.
212 ///
213 /// The factory is called once when the service is first requested, and the same
214 /// instance is returned for all subsequent requests.
215 ///
216 /// # Example
217 ///
218 /// ```no_run
219 /// use service_rs::ServiceCollection;
220 ///
221 /// let mut collection = ServiceCollection::new();
222 /// collection.add_singleton_with_factory::<i32, _, _>(|_| async {
223 /// Ok(Box::new(42) as Box<dyn std::any::Any + Send + Sync>)
224 /// });
225 /// ```
226 pub fn add_singleton_with_factory<T, F, Fut>(&mut self, factory: F) -> &mut Self
227 where
228 T: ?Sized + Send + Sync + 'static,
229 F: Fn(ServiceProviderContext) -> Fut + Send + Sync + 'static,
230 Fut: Future<Output = Result<Box<dyn Any + Send + Sync>, Box<dyn std::error::Error>>>
231 + Send
232 + 'static,
233 {
234 let type_id = TypeId::of::<T>();
235 let service = ServiceDescriptor {
236 lifetime: ServiceLifetime::Singleton,
237 type_name: std::any::type_name::<T>(),
238 factory: Box::new(move |ctx: ServiceProviderContext| Box::pin(factory(ctx))),
239 };
240 self.services.insert(type_id, service);
241 self
242 }
243
244 /// Registers a scoped service using a factory function.
245 ///
246 /// The factory is called once per scope when the service is first requested within that scope.
247 /// The same instance is returned for all requests within the same scope.
248 ///
249 /// # Example
250 ///
251 /// ```no_run
252 /// use service_rs::ServiceCollection;
253 ///
254 /// let mut collection = ServiceCollection::new();
255 /// collection.add_scoped_with_factory::<String, _, _>(|_| async {
256 /// Ok(Box::new("scoped".to_string()) as Box<dyn std::any::Any + Send + Sync>)
257 /// });
258 /// ```
259 pub fn add_scoped_with_factory<T, F, Fut>(&mut self, factory: F) -> &mut Self
260 where
261 T: ?Sized + Send + Sync + 'static,
262 F: Fn(ServiceProviderContext) -> Fut + Send + Sync + 'static,
263 Fut: Future<Output = Result<Box<dyn Any + Send + Sync>, Box<dyn std::error::Error>>>
264 + Send
265 + 'static,
266 {
267 let type_id = TypeId::of::<T>();
268 let service = ServiceDescriptor {
269 lifetime: ServiceLifetime::Scoped,
270 type_name: std::any::type_name::<T>(),
271 factory: Box::new(move |ctx: ServiceProviderContext| Box::pin(factory(ctx))),
272 };
273 self.services.insert(type_id, service);
274 self
275 }
276
277 /// Registers a transient service using a factory function.
278 ///
279 /// The factory is called every time the service is requested, creating a new instance each time.
280 ///
281 /// # Example
282 ///
283 /// ```no_run
284 /// use service_rs::ServiceCollection;
285 ///
286 /// let mut collection = ServiceCollection::new();
287 /// collection.add_transient_with_factory::<String, _, _>(|_| async {
288 /// Ok(Box::new("transient".to_string()) as Box<dyn std::any::Any + Send + Sync>)
289 /// });
290 /// ```
291 pub fn add_transient_with_factory<T, F, Fut>(&mut self, factory: F) -> &mut Self
292 where
293 T: ?Sized + Send + Sync + 'static,
294 F: Fn(ServiceProviderContext) -> Fut + Send + Sync + 'static,
295 Fut: Future<Output = Result<Box<dyn Any + Send + Sync>, Box<dyn std::error::Error>>>
296 + Send
297 + 'static,
298 {
299 let type_id = TypeId::of::<T>();
300 let service = ServiceDescriptor {
301 lifetime: ServiceLifetime::Transient,
302 type_name: std::any::type_name::<T>(),
303 factory: Box::new(move |ctx: ServiceProviderContext| Box::pin(factory(ctx))),
304 };
305 self.services.insert(type_id, service);
306 self
307 }
308
309 /// Registers a singleton service using the `Injectable` derive macro.
310 ///
311 /// The service type must implement [`InjectableExtension`] via the `#[derive(Injectable)]` macro.
312 /// Dependencies are automatically resolved from the service provider.
313 ///
314 /// # Example
315 ///
316 /// ```no_run
317 /// use service_rs::{Injectable, ServiceCollection};
318 /// use std::sync::Arc;
319 ///
320 /// #[derive(Injectable)]
321 /// struct MyService {
322 /// dependency: Arc<i32>,
323 /// }
324 ///
325 /// let mut collection = ServiceCollection::new();
326 /// collection.add_singleton::<MyService>();
327 /// ```
328 #[cfg(feature = "proc-macro")]
329 pub fn add_singleton<T>(&mut self) -> &mut Self
330 where
331 T: InjectableExtension,
332 {
333 let type_id = TypeId::of::<T>();
334 let service = ServiceDescriptor {
335 lifetime: ServiceLifetime::Singleton,
336 type_name: std::any::type_name::<T>(),
337 factory: T::create_factory(),
338 };
339 self.services.insert(type_id, service);
340 self
341 }
342
343 /// Registers a scoped service using the `Injectable` derive macro.
344 ///
345 /// The service type must implement [`InjectableExtension`] via the `#[derive(Injectable)]` macro.
346 /// Dependencies are automatically resolved from the service provider.
347 ///
348 /// # Example
349 ///
350 /// ```no_run
351 /// use service_rs::{Injectable, ServiceCollection};
352 /// use std::sync::Arc;
353 ///
354 /// #[derive(Injectable)]
355 /// struct MyService {
356 /// dependency: Arc<i32>,
357 /// }
358 ///
359 /// let mut collection = ServiceCollection::new();
360 /// collection.add_scoped::<MyService>();
361 /// ```
362 #[cfg(feature = "proc-macro")]
363 pub fn add_scoped<T>(&mut self) -> &mut Self
364 where
365 T: InjectableExtension,
366 {
367 let type_id = TypeId::of::<T>();
368 let service = ServiceDescriptor {
369 lifetime: ServiceLifetime::Scoped,
370 type_name: std::any::type_name::<T>(),
371 factory: T::create_factory(),
372 };
373 self.services.insert(type_id, service);
374 self
375 }
376
377 /// Registers a transient service using the `Injectable` derive macro.
378 ///
379 /// The service type must implement [`InjectableExtension`] via the `#[derive(Injectable)]` macro.
380 /// Dependencies are automatically resolved from the service provider.
381 ///
382 /// # Example
383 ///
384 /// ```no_run
385 /// use service_rs::{Injectable, ServiceCollection};
386 /// use std::sync::Arc;
387 ///
388 /// #[derive(Injectable)]
389 /// struct MyService {
390 /// dependency: Arc<i32>,
391 /// }
392 ///
393 /// let mut collection = ServiceCollection::new();
394 /// collection.add_transient::<MyService>();
395 /// ```
396 #[cfg(feature = "proc-macro")]
397 pub fn add_transient<T>(&mut self) -> &mut Self
398 where
399 T: InjectableExtension,
400 {
401 let type_id = TypeId::of::<T>();
402 let service = ServiceDescriptor {
403 lifetime: ServiceLifetime::Transient,
404 type_name: std::any::type_name::<T>(),
405 factory: T::create_factory(),
406 };
407 self.services.insert(type_id, service);
408 self
409 }
410
411 /// Registers a singleton service for a trait object.
412 ///
413 /// This allows you to register an implementation type that will be resolved as a trait object.
414 /// The implementation must derive `Injectable` and implement the trait.
415 ///
416 /// # Example
417 ///
418 /// ```no_run
419 /// use service_rs::{Injectable, ServiceCollection};
420 ///
421 /// trait Logger: Send + Sync {
422 /// fn log(&self, msg: &str);
423 /// }
424 ///
425 /// #[derive(Injectable)]
426 /// struct ConsoleLogger;
427 ///
428 /// impl Logger for ConsoleLogger {
429 /// fn log(&self, msg: &str) {
430 /// println!("{}", msg);
431 /// }
432 /// }
433 ///
434 /// let mut collection = ServiceCollection::new();
435 /// collection.add_singleton_interface::<dyn Logger, ConsoleLogger>();
436 /// ```
437 #[cfg(feature = "proc-macro")]
438 pub fn add_singleton_interface<T, TImpl>(&mut self) -> &mut Self
439 where
440 T: ?Sized + Send + Sync + 'static,
441 TImpl: InjectableExtension + Unpin + 'static + std::marker::Unsize<T>,
442 {
443 let type_id = TypeId::of::<Box<T>>();
444 let impl_factory = Arc::new(TImpl::create_factory());
445 let service = ServiceDescriptor {
446 lifetime: ServiceLifetime::Singleton,
447 type_name: std::any::type_name::<Box<T>>(),
448 factory: Box::new(move |ctx: ServiceProviderContext| {
449 let impl_factory = Arc::clone(&impl_factory);
450 Box::pin(async move {
451 let concrete = impl_factory(ctx).await?;
452 let downcasted = concrete.downcast::<TImpl>().map_err(|_| {
453 Box::new(std::io::Error::other(
454 "Failed to downcast",
455 )) as Box<dyn std::error::Error>
456 })?;
457 let trait_obj: Box<T> = downcasted;
458 Ok(Box::new(trait_obj) as Box<dyn Any + Send + Sync>)
459 })
460 }),
461 };
462 self.services.insert(type_id, service);
463 self
464 }
465
466 /// Registers a scoped service for a trait object.
467 ///
468 /// This allows you to register an implementation type that will be resolved as a trait object.
469 /// The implementation must derive `Injectable` and implement the trait.
470 ///
471 /// # Example
472 ///
473 /// ```no_run
474 /// use service_rs::{Injectable, ServiceCollection};
475 ///
476 /// trait Repository: Send + Sync {
477 /// fn save(&self);
478 /// }
479 ///
480 /// #[derive(Injectable)]
481 /// struct DbRepository;
482 ///
483 /// impl Repository for DbRepository {
484 /// fn save(&self) {}
485 /// }
486 ///
487 /// let mut collection = ServiceCollection::new();
488 /// collection.add_scoped_interface::<dyn Repository, DbRepository>();
489 /// ```
490 #[cfg(feature = "proc-macro")]
491 pub fn add_scoped_interface<T, TImpl>(&mut self) -> &mut Self
492 where
493 T: ?Sized + Send + Sync + 'static,
494 TImpl: InjectableExtension + Unpin + 'static + std::marker::Unsize<T>,
495 {
496 let type_id = TypeId::of::<Box<T>>();
497 let impl_factory = Arc::new(TImpl::create_factory());
498 let service = ServiceDescriptor {
499 lifetime: ServiceLifetime::Scoped,
500 type_name: std::any::type_name::<Box<T>>(),
501 factory: Box::new(move |ctx: ServiceProviderContext| {
502 let impl_factory = Arc::clone(&impl_factory);
503 Box::pin(async move {
504 let concrete = impl_factory(ctx).await?;
505 let downcasted = concrete.downcast::<TImpl>().map_err(|_| {
506 Box::new(std::io::Error::other(
507 "Failed to downcast",
508 )) as Box<dyn std::error::Error>
509 })?;
510 let trait_obj: Box<T> = downcasted;
511 Ok(Box::new(trait_obj) as Box<dyn Any + Send + Sync>)
512 })
513 }),
514 };
515 self.services.insert(type_id, service);
516 self
517 }
518
519 /// Registers a transient service for a trait object.
520 ///
521 /// This allows you to register an implementation type that will be resolved as a trait object.
522 /// The implementation must derive `Injectable` and implement the trait.
523 ///
524 /// # Example
525 ///
526 /// ```no_run
527 /// use service_rs::{Injectable, ServiceCollection};
528 ///
529 /// trait Handler: Send + Sync {
530 /// fn handle(&self);
531 /// }
532 ///
533 /// #[derive(Injectable)]
534 /// struct RequestHandler;
535 ///
536 /// impl Handler for RequestHandler {
537 /// fn handle(&self) {}
538 /// }
539 ///
540 /// let mut collection = ServiceCollection::new();
541 /// collection.add_transient_interface::<dyn Handler, RequestHandler>();
542 /// ```
543 #[cfg(feature = "proc-macro")]
544 pub fn add_transient_interface<T, TImpl>(&mut self) -> &mut Self
545 where
546 T: ?Sized + Send + Sync + 'static,
547 TImpl: InjectableExtension + Unpin + 'static + std::marker::Unsize<T>,
548 {
549 let type_id = TypeId::of::<Box<T>>();
550 let impl_factory = Arc::new(TImpl::create_factory());
551 let service = ServiceDescriptor {
552 lifetime: ServiceLifetime::Transient,
553 type_name: std::any::type_name::<Box<T>>(),
554 factory: Box::new(move |ctx: ServiceProviderContext| {
555 let impl_factory = Arc::clone(&impl_factory);
556 Box::pin(async move {
557 let concrete = impl_factory(ctx).await?;
558 let downcasted = concrete.downcast::<TImpl>().map_err(|_| {
559 Box::new(std::io::Error::other(
560 "Failed to downcast",
561 )) as Box<dyn std::error::Error>
562 })?;
563 let trait_obj: Box<T> = downcasted;
564 Ok(Box::new(trait_obj) as Box<dyn Any + Send + Sync>)
565 })
566 }),
567 };
568 self.services.insert(type_id, service);
569 self
570 }
571
572 /// Returns the number of registered services.
573 pub fn len(&self) -> usize {
574 self.services.len()
575 }
576
577 /// Returns true if the collection contains no services.
578 pub fn is_empty(&self) -> bool {
579 self.services.is_empty()
580 }
581
582 /// Builds the service provider from this collection.
583 ///
584 /// Consumes the collection and returns an [`Arc<ServiceProvider>`] that can be used
585 /// to resolve services.
586 ///
587 /// # Example
588 ///
589 /// ```no_run
590 /// use service_rs::ServiceCollection;
591 ///
592 /// let collection = ServiceCollection::new();
593 /// let provider = collection.build();
594 /// ```
595 pub fn build(self) -> Arc<ServiceProvider> {
596 Arc::new(ServiceProvider {
597 collection: self,
598 services: RwLock::new(HashMap::new()),
599 })
600 }
601}
602
603/// The root service provider for resolving services.
604///
605/// This is created by calling [`ServiceCollection::build()`] and provides methods
606/// to resolve singleton and transient services, as well as create scoped providers.
607///
608/// # Example
609///
610/// ```no_run
611/// use service_rs::ServiceCollection;
612/// use std::sync::Arc;
613///
614/// # async fn example() {
615/// let mut collection = ServiceCollection::new();
616/// collection.add_singleton_with_factory::<i32, _, _>(|_| async {
617/// Ok(Box::new(42) as Box<dyn std::any::Any + Send + Sync>)
618/// });
619/// let provider = collection.build();
620///
621/// let value: Arc<i32> = provider.get::<i32>().await.unwrap();
622/// # }
623/// ```
624#[derive(Debug, Default)]
625pub struct ServiceProvider {
626 pub(crate) collection: ServiceCollection,
627 pub(crate) services: RwLock<HashMap<TypeId, Arc<dyn Any + Send + Sync>>>,
628}
629
630impl ServiceProvider {
631 /// Creates a new service scope.
632 ///
633 /// Scopes are used to manage scoped service lifetimes. Services registered with
634 /// [`ServiceCollection::add_scoped`] or [`ServiceCollection::add_scoped_with_factory`]
635 /// will have one instance per scope.
636 ///
637 /// # Example
638 ///
639 /// ```no_run
640 /// use service_rs::ServiceCollection;
641 ///
642 /// # async fn example() {
643 /// let provider = ServiceCollection::new().build();
644 /// let scope = provider.create_scope();
645 /// # }
646 /// ```
647 pub fn create_scope(self: &Arc<Self>) -> Arc<ScopedServiceProvider> {
648 Arc::new(ScopedServiceProvider {
649 provider: Arc::clone(self),
650 services: RwLock::new(HashMap::new()),
651 })
652 }
653
654 /// Returns the number of resolved service instances currently cached in the provider.
655 ///
656 /// This count includes singleton services that have been instantiated.
657 /// It does not include services that are registered but not yet resolved.
658 ///
659 /// # Example
660 ///
661 /// ```no_run
662 /// use service_rs::ServiceCollection;
663 ///
664 /// # async fn example() {
665 /// let provider = ServiceCollection::new().build();
666 /// let count = provider.len().await;
667 /// # }
668 /// ```
669 pub async fn len(&self) -> usize {
670 self.services.read().await.len()
671 }
672
673 /// Returns the total number of registered service types in the collection.
674 ///
675 /// This count includes all registered services regardless of whether they have been resolved.
676 ///
677 /// # Example
678 ///
679 /// ```no_run
680 /// use service_rs::ServiceCollection;
681 ///
682 /// let mut collection = ServiceCollection::new();
683 /// collection.add_singleton_with_factory::<i32, _, _>(|_| async {
684 /// Ok(Box::new(42) as Box<dyn std::any::Any + Send + Sync>)
685 /// });
686 /// let provider = collection.build();
687 /// assert_eq!(provider.collection_len(), 1);
688 /// ```
689 pub fn collection_len(&self) -> usize {
690 self.collection.len()
691 }
692
693 /// Returns `true` if no service instances have been resolved yet.
694 ///
695 /// This checks whether the provider's instance cache is empty, not whether
696 /// services are registered in the collection.
697 ///
698 /// # Example
699 ///
700 /// ```no_run
701 /// use service_rs::ServiceCollection;
702 ///
703 /// # async fn example() {
704 /// let provider = ServiceCollection::new().build();
705 /// assert!(provider.is_empty().await);
706 /// # }
707 /// ```
708 pub async fn is_empty(&self) -> bool {
709 self.services.read().await.is_empty()
710 }
711
712 /// Resolves a service from the provider.
713 ///
714 /// Returns an [`Arc<T>`] containing the resolved service instance.
715 ///
716 /// # Errors
717 ///
718 /// - [`ServiceError::ServiceNotFound`] if the service type is not registered
719 /// - [`ServiceError::ServiceInvalidScope`] if attempting to resolve a scoped service from the root provider
720 /// - [`ServiceError::ServiceResolutionFailed`] if the service cannot be downcast to the requested type
721 /// - [`ServiceError::ServiceInitializationFailed`] if the factory function returns an error
722 ///
723 /// # Example
724 ///
725 /// ```no_run
726 /// use service_rs::ServiceCollection;
727 /// use std::sync::Arc;
728 ///
729 /// # async fn example() {
730 /// let mut collection = ServiceCollection::new();
731 /// collection.add_singleton_with_factory::<i32, _, _>(|_| async {
732 /// Ok(Box::new(42) as Box<dyn std::any::Any + Send + Sync>)
733 /// });
734 /// let provider = collection.build();
735 ///
736 /// let value: Arc<i32> = provider.get::<i32>().await.unwrap();
737 /// assert_eq!(*value, 42);
738 /// # }
739 /// ```
740 pub async fn get<T>(self: &Arc<Self>) -> Result<Arc<T>, ServiceError>
741 where
742 T: Send + Sync + 'static,
743 {
744 let type_id = TypeId::of::<T>();
745
746 // lookup from collection
747 let descriptor = self.collection.services.get(&type_id).map_or_else(
748 || {
749 Err(ServiceError::ServiceNotFound {
750 type_name: std::any::type_name::<T>(),
751 })
752 },
753 Ok,
754 )?;
755
756 match descriptor.lifetime {
757 ServiceLifetime::Singleton => {
758 if let Some(service) = self.services.read().await.get(&type_id) {
759 let cloned = Arc::clone(service);
760 return cloned.downcast::<T>().map_err(|_| {
761 ServiceError::ServiceResolutionFailed {
762 type_name: std::any::type_name::<T>(),
763 }
764 });
765 }
766
767 let service = (descriptor.factory)(ServiceProviderContext::Root(Arc::clone(self)))
768 .await
769 .map_err(|e| ServiceError::ServiceInitializationFailed {
770 type_name: std::any::type_name::<T>(),
771 error: e,
772 })?;
773
774 let arc_service: Arc<dyn Any + Send + Sync> = Arc::from(service);
775
776 self.services
777 .write()
778 .await
779 .insert(type_id, Arc::clone(&arc_service));
780
781 arc_service.downcast::<T>().map_err(|_| {
782 ServiceError::ServiceResolutionFailed {
783 type_name: std::any::type_name::<T>(),
784 }
785 })
786 }
787 ServiceLifetime::Scoped => Err(ServiceError::ServiceInvalidScope {
788 type_name: std::any::type_name::<T>(),
789 }),
790 ServiceLifetime::Transient => {
791 let service = (descriptor.factory)(ServiceProviderContext::Root(Arc::clone(self)))
792 .await
793 .map_err(|e| ServiceError::ServiceInitializationFailed {
794 type_name: std::any::type_name::<T>(),
795 error: e,
796 })?;
797
798 let arc_service: Arc<dyn Any + Send + Sync> = Arc::from(service);
799
800 arc_service.downcast::<T>().map_err(|_| {
801 ServiceError::ServiceResolutionFailed {
802 type_name: std::any::type_name::<T>(),
803 }
804 })
805 }
806 }
807 }
808}
809
810/// A scoped service provider for resolving scoped services.
811///
812/// Created by calling [`ServiceProvider::create_scope()`]. Services registered with
813/// scoped lifetime will have one instance per scope.
814///
815/// # Example
816///
817/// ```no_run
818/// use service_rs::ServiceCollection;
819/// use std::sync::Arc;
820///
821/// # async fn example() {
822/// let mut collection = ServiceCollection::new();
823/// collection.add_scoped_with_factory::<String, _, _>(|_| async {
824/// Ok(Box::new("scoped".to_string()) as Box<dyn std::any::Any + Send + Sync>)
825/// });
826/// let provider = collection.build();
827///
828/// let scope = provider.create_scope();
829/// let value: Arc<String> = scope.get::<String>().await.unwrap();
830/// # }
831/// ```
832#[derive(Debug, Default)]
833pub struct ScopedServiceProvider {
834 pub(crate) provider: Arc<ServiceProvider>,
835 pub(crate) services: RwLock<HashMap<TypeId, Arc<dyn Any + Send + Sync>>>,
836}
837
838impl ScopedServiceProvider {
839 /// Resolves a service from the scoped provider.
840 ///
841 /// Returns an [`Arc<T>`] containing the resolved service instance.
842 ///
843 /// - Singleton services are resolved from the root provider
844 /// - Scoped services are resolved once per scope and cached
845 /// - Transient services are resolved from the root provider (new instance each time)
846 ///
847 /// # Errors
848 ///
849 /// - [`ServiceError::ServiceNotFound`] if the service type is not registered
850 /// - [`ServiceError::ServiceResolutionFailed`] if the service cannot be downcast to the requested type
851 /// - [`ServiceError::ServiceInitializationFailed`] if the factory function returns an error
852 ///
853 /// # Example
854 ///
855 /// ```no_run
856 /// use service_rs::ServiceCollection;
857 /// use std::sync::Arc;
858 ///
859 /// # async fn example() {
860 /// let mut collection = ServiceCollection::new();
861 /// collection.add_scoped_with_factory::<String, _, _>(|_| async {
862 /// Ok(Box::new("scoped".to_string()) as Box<dyn std::any::Any + Send + Sync>)
863 /// });
864 /// let provider = collection.build();
865 ///
866 /// let scope = provider.create_scope();
867 /// let value1: Arc<String> = scope.get::<String>().await.unwrap();
868 /// let value2: Arc<String> = scope.get::<String>().await.unwrap();
869 /// assert_eq!(Arc::as_ptr(&value1), Arc::as_ptr(&value2)); // Same instance within scope
870 /// # }
871 /// ```
872 pub async fn get<T>(self: &Arc<Self>) -> Result<Arc<T>, ServiceError>
873 where
874 T: Send + Sync + 'static,
875 {
876 let type_id = TypeId::of::<T>();
877
878 // lookup from collection
879 let descriptor = self
880 .provider
881 .collection
882 .services
883 .get(&type_id)
884 .map_or_else(
885 || {
886 Err(ServiceError::ServiceNotFound {
887 type_name: std::any::type_name::<T>(),
888 })
889 },
890 Ok,
891 )?;
892
893 match descriptor.lifetime {
894 ServiceLifetime::Singleton => self.provider.get::<T>().await,
895 ServiceLifetime::Scoped => {
896 if let Some(service) = self.services.read().await.get(&type_id) {
897 let cloned = Arc::clone(service);
898 return cloned.downcast::<T>().map_err(|_| {
899 ServiceError::ServiceResolutionFailed {
900 type_name: std::any::type_name::<T>(),
901 }
902 });
903 }
904
905 let service =
906 (descriptor.factory)(ServiceProviderContext::Scoped(Arc::clone(self)))
907 .await
908 .map_err(|e| ServiceError::ServiceInitializationFailed {
909 type_name: std::any::type_name::<T>(),
910 error: e,
911 })?;
912
913 let arc_service: Arc<dyn Any + Send + Sync> = Arc::from(service);
914
915 self.services
916 .write()
917 .await
918 .insert(type_id, Arc::clone(&arc_service));
919
920 arc_service.downcast::<T>().map_err(|_| {
921 ServiceError::ServiceResolutionFailed {
922 type_name: std::any::type_name::<T>(),
923 }
924 })
925 }
926 ServiceLifetime::Transient => self.provider.get::<T>().await,
927 }
928 }
929}