use crate::{KeyedRef, KeyedRefMut, Mut, Ref, RefMut, ServiceProvider}; use spin::Once; use std::any::Any; /// Represents a holder for lazily-initialized service resolution. pub struct Lazy { services: ServiceProvider, resolve: fn(&ServiceProvider) -> T, value: Once, } impl Lazy { fn new(services: ServiceProvider, resolve: fn(&ServiceProvider) -> T) -> Self { Self { services, resolve, value: Once::new(), } } /// Resolves and returns a reference to the underlying, lazy-initialized service. pub fn value(&self) -> &T { self.value.call_once(|| (self.resolve)(&self.services)) } } fn to_vec(services: &ServiceProvider) -> Vec> { services.get_all::().collect() } fn to_vec_mut(services: &ServiceProvider) -> Vec> { services.get_all_mut::().collect() } fn to_keyed_vec(services: &ServiceProvider) -> Vec> { services.get_all_by_key::().collect() } fn to_keyed_vec_mut( services: &ServiceProvider, ) -> Vec> { services.get_all_by_key_mut::().collect() } /// Creates and returns a holder for a lazily-initialized, required service. /// /// # Arguments /// /// * `services` - The [`ServiceProvider`](crate::ServiceProvider) used to resolve the service #[inline] pub fn exactly_one(services: ServiceProvider) -> Lazy> { Lazy::new(services, ServiceProvider::get_required::) } /// Creates and returns a holder for a lazily-initialized, required, mutable service. /// /// # Arguments /// /// * `services` - The [`ServiceProvider`](crate::ServiceProvider) used to resolve the service #[inline] pub fn exactly_one_mut(services: ServiceProvider) -> Lazy> { Lazy::new(services, ServiceProvider::get_required_mut::) } /// Creates and returns a holder for a lazily-initialized, keyed, required service. /// /// # Arguments /// /// * `services` - The [`ServiceProvider`](crate::ServiceProvider) used to resolve the service #[inline] pub fn exactly_one_with_key( services: ServiceProvider, ) -> Lazy> { Lazy::new(services, ServiceProvider::get_required_by_key::) } /// Creates and returns a holder for a lazily-initialized, keyed, required, mutable service. /// /// # Arguments /// /// * `services` - The [`ServiceProvider`](crate::ServiceProvider) used to resolve the service #[inline] pub fn exactly_one_with_key_mut( services: ServiceProvider, ) -> Lazy> { Lazy::new( services, ServiceProvider::get_required_by_key_mut::, ) } /// Creates and returns a holder for a lazily-initialized, optional service. /// /// # Arguments /// /// * `services` - The [`ServiceProvider`](crate::ServiceProvider) used to resolve the service #[inline] pub fn zero_or_one(services: ServiceProvider) -> Lazy>> { Lazy::new(services, ServiceProvider::get::) } /// Creates and returns a holder for a lazily-initialized, optional, mutable service. /// /// # Arguments /// /// * `services` - The [`ServiceProvider`](crate::ServiceProvider) used to resolve the service #[inline] pub fn zero_or_one_mut(services: ServiceProvider) -> Lazy>> { Lazy::new(services, ServiceProvider::get_mut::) } /// Creates and returns a holder for a lazily-initialized, keyed, optional service. /// /// # Arguments /// /// * `services` - The [`ServiceProvider`](crate::ServiceProvider) used to resolve the service #[inline] pub fn zero_or_one_with_key( services: ServiceProvider, ) -> Lazy>> { Lazy::new(services, ServiceProvider::get_by_key::) } /// Creates and returns a holder for a lazily-initialized, keyed, optional, mutable service. /// /// # Arguments /// /// * `services` - The [`ServiceProvider`](crate::ServiceProvider) used to resolve the service #[inline] pub fn zero_or_one_with_key_mut( services: ServiceProvider, ) -> Lazy>> { Lazy::new(services, ServiceProvider::get_by_key_mut::) } /// Creates and returns a holder for multiple, lazily-initialized services. /// /// # Arguments /// /// * `services` - The [`ServiceProvider`](crate::ServiceProvider) used to resolve the services #[inline] pub fn zero_or_more(services: ServiceProvider) -> Lazy>> { Lazy::new(services, to_vec::) } /// Creates and returns a holder for multiple, lazily-initialized, mutable services. /// /// # Arguments /// /// * `services` - The [`ServiceProvider`](crate::ServiceProvider) used to resolve the services #[inline] pub fn zero_or_more_mut(services: ServiceProvider) -> Lazy>> { Lazy::new(services, to_vec_mut::) } /// Creates and returns a holder for multiple, lazily-initialized, keyed services. /// /// # Arguments /// /// * `services` - The [`ServiceProvider`](crate::ServiceProvider) used to resolve the services #[inline] pub fn zero_or_more_with_key( services: ServiceProvider, ) -> Lazy>> { Lazy::new(services, to_keyed_vec::) } /// Creates and returns a holder for multiple, lazily-initialized, keyed, mutable services. /// /// # Arguments /// /// * `services` - The [`ServiceProvider`](crate::ServiceProvider) used to resolve the services #[inline] pub fn zero_or_more_with_key_mut( services: ServiceProvider, ) -> Lazy>> { Lazy::new(services, to_keyed_vec_mut::) } /// Creates and return a holder for a lazy-initialized, optional service that is missing. #[inline] pub fn missing() -> Lazy>> { Lazy::new(ServiceProvider::default(), ServiceProvider::get::) } /// Creates and return a holder for a lazy-initialized, keyed, optional service that is missing. #[inline] pub fn missing_with_key() -> Lazy>> { Lazy::new( ServiceProvider::default(), ServiceProvider::get_by_key::, ) } /// Creates and return a holder for any empty collection of lazy-initialized services. #[inline] pub fn empty() -> Lazy>> { Lazy::new(ServiceProvider::default(), to_vec::) } /// Creates and return a holder for any empty collection of lazy-initialized, keyed services. #[inline] pub fn empty_with_key() -> Lazy>> { Lazy::new(ServiceProvider::default(), to_keyed_vec::) } /// Creates and returns a holder from an existing instance. /// /// # Arguments /// /// * `instance` - The existing instance used to initialize with pub fn init(instance: Box) -> Lazy> { Lazy { resolve: |_| unimplemented!(), services: ServiceProvider::default(), value: Once::initialized(Ref::from(instance)), } } /// Creates and returns a holder from an existing, mutable instance. /// /// # Arguments /// /// * `instance` - The existing instance used to initialize with pub fn init_mut(instance: Box>) -> Lazy> { Lazy { resolve: |_| unimplemented!(), services: ServiceProvider::default(), value: Once::initialized(RefMut::from(instance)), } } /// Creates and returns a holder from an existing instance with a key. /// /// # Arguments /// /// * `instance` - The existing instance used to initialize with pub fn init_with_key(instance: Box) -> Lazy> { Lazy { resolve: |_| unimplemented!(), services: ServiceProvider::default(), value: Once::initialized(KeyedRef::::new(Ref::from(instance))), } } /// Creates and returns a holder from an existing, mutable instance with a key. /// /// # Arguments /// /// * `instance` - The existing instance used to initialize with pub fn init_with_key_mut( instance: Box>, ) -> Lazy> { Lazy { resolve: |_| unimplemented!(), services: ServiceProvider::default(), value: Once::initialized(KeyedRefMut::::new(Ref::from(instance))), } } #[cfg(test)] mod tests { use super::*; use crate::*; use cfg_if::cfg_if; #[derive(Default)] struct Bar; struct Foo { bar: Lazy>, } struct Foo2 { bar: Lazy>>, } impl Bar { fn echo(&self) -> &str { "Delayed!" } } impl Foo { fn new(bar: Lazy>) -> Self { Self { bar } } fn echo(&self) -> &str { self.bar.value().echo() } } impl Foo2 { fn new(bar: Lazy>>) -> Self { Self { bar } } fn echo(&self) -> Option<&str> { match self.bar.value() { Some(bar) => Some(bar.echo()), _ => None, } } } trait IPityTheFoo { fn speak(&self) -> &str; } struct FooImpl; impl IPityTheFoo for FooImpl { fn speak(&self) -> &str { "I pity the foo!" } } #[test] fn lazy_should_return_required_service() { // arrange let provider = ServiceCollection::new() .add(transient_as_self::().from(|_| Ref::new(Bar::default()))) .add( transient_as_self::() .depends_on(crate::exactly_one::()) .from(|sp| Ref::new(Foo::new(lazy::exactly_one::(sp.clone())))), ) .build_provider() .unwrap(); // act let foo = provider.get_required::(); // assert assert_eq!("Delayed!", foo.echo()); } #[test] fn lazy_should_return_optional_service() { // arrange let provider = ServiceCollection::new() .add(transient_as_self::().from(|_| Ref::new(Bar::default()))) .add( transient_as_self::() .depends_on(crate::zero_or_one::()) .from(|sp| Ref::new(Foo2::new(lazy::zero_or_one::(sp.clone())))), ) .build_provider() .unwrap(); // act let foo = provider.get_required::(); // assert assert_eq!("Delayed!", foo.echo().unwrap()); } #[test] fn lazy_should_allow_missing_optional_service() { // arrange let provider = ServiceCollection::new() .add( transient_as_self::() .depends_on(crate::zero_or_one::()) .from(|sp| Ref::new(Foo2::new(lazy::zero_or_one::(sp.clone())))), ) .build_provider() .unwrap(); // act let foo = provider.get_required::(); // assert assert_eq!(None, foo.echo()); } #[test] fn missing_should_initialize_lazy() { // arrange let lazy = lazy::missing::(); // act let value = lazy.value(); // assert assert!(value.is_none()); } #[test] fn empty_should_initialize_lazy() { // arrange let lazy = lazy::empty::(); // act let value = lazy.value(); // assert assert!(value.is_empty()); } #[test] #[allow(clippy::vtable_address_comparisons)] fn lazy_should_return_same_scoped_service() { // arrange let provider = ServiceCollection::new() .add(scoped_factory(|_| Ref::new(Bar::default()))) .add( transient_as_self::() .depends_on(crate::exactly_one::()) .from(|sp| Ref::new(Foo::new(lazy::exactly_one::(sp.clone())))), ) .build_provider() .unwrap(); // act let foo = provider.get_required::(); let bar1 = provider.get_required::(); let bar2 = provider.clone().get_required::(); // assert assert!(Ref::ptr_eq(foo.bar.value(), &bar1)); assert!(Ref::ptr_eq(&bar1, &bar2)); } #[test] fn init_should_create_lazy_from_instance() { // arrange let instance: Box = Box::new(FooImpl); // act let lazy = lazy::init(instance); // assert assert_eq!(lazy.value().speak(), "I pity the foo!"); } #[test] fn init_with_key_should_create_lazy_from_instance() { // arrange let instance = FooImpl; // act let lazy = lazy::init_with_key::(Box::new(instance)); // assert assert_eq!(lazy.value().speak(), "I pity the foo!"); } #[test] fn init_mut_should_create_lazy_from_instance() { // arrange let instance: Box> = Box::new(Mut::new(FooImpl)); // act let lazy = lazy::init_mut(instance); // assert cfg_if! { if #[cfg(feature = "async")] { assert_eq!(lazy.value().read().unwrap().speak(), "I pity the foo!"); } else { assert_eq!(lazy.value().borrow().speak(), "I pity the foo!"); } } } #[test] fn init_with_key_mut_should_create_lazy_from_instance() { // arrange let instance: Box> = Box::new(Mut::new(FooImpl)); // act let lazy = lazy::init_with_key_mut::(instance); // assert cfg_if! { if #[cfg(feature = "async")] { assert_eq!(lazy.value().write().unwrap().speak(), "I pity the foo!"); } else { assert_eq!(lazy.value().borrow().speak(), "I pity the foo!"); } } } }