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}