diff --git a/ferrunix-core/src/dependencies.rs b/ferrunix-core/src/dependencies.rs index b3c0da6..4f70bff 100644 --- a/ferrunix-core/src/dependencies.rs +++ b/ferrunix-core/src/dependencies.rs @@ -55,6 +55,14 @@ pub trait Dep: Registerable + private::Sealed { /// This function is allowed to panic, if the type isn't registered. fn new(registry: &Registry) -> Self; + /// Looks up the dependency in `registry`, and constructs a new [`Dep`]. + /// + /// This function is allowed to panic, if the type isn't registered. + #[cfg(feature = "tokio")] + fn new_async( + registry: &Registry, + ) -> impl std::future::Future + Send + Sync; + /// Returns [`std::any::TypeId`] of the dependency type. fn type_id() -> TypeId; } @@ -115,6 +123,24 @@ impl Dep for Transient { } } + /// Create a new [`Transient`], asynchronously. + /// + /// # Panic + /// This function panics if the `T` isn't registered. + #[cfg(feature = "tokio")] + fn new_async( + registry: &Registry, + ) -> impl std::future::Future + Send + Sync { + async move { + Self { + inner: registry.get_transient_async::().await.expect( + "transient dependency must only be constructed if it's \ + fulfillable", + ), + } + } + } + /// Returns [`std::any::TypeId`] of the inner type `T`. fn type_id() -> TypeId { TypeId::of::() @@ -184,6 +210,24 @@ impl Dep for Singleton { } } + /// Create a new [`Singleton`], asynchronously. + /// + /// # Panic + /// This function panics if the `T` isn't registered. + #[cfg(feature = "tokio")] + fn new_async( + registry: &Registry, + ) -> impl std::future::Future + Send + Sync { + async move { + Self { + inner: registry.get_singleton_async::().await.expect( + "singleton dependency must only be constructed if it's \ + fulfillable", + ), + } + } + } + /// Returns [`std::any::TypeId`] of the inner type `T`. fn type_id() -> TypeId { TypeId::of::() diff --git a/ferrunix-core/src/dependency_builder.rs b/ferrunix-core/src/dependency_builder.rs index 58b16ab..a1e03dc 100644 --- a/ferrunix-core/src/dependency_builder.rs +++ b/ferrunix-core/src/dependency_builder.rs @@ -45,6 +45,33 @@ pub trait DepBuilder { where R: Sized; + /// When implemented, this should validate that all dependencies which are + /// part of `Self` exist to construct the type `R`. If the dependencies + /// cannot be fulfilled, `None` must be returned. + /// + /// If the dependencies can be fulfilled, they must be constructed as an + /// N-ary tuple (same length and types as `Self`) and passed as the + /// argument to `ctor`. `ctor` is a user provided constructor for the + /// type `R`. + /// + /// An implementation for tuples is provided by `DepBuilderImpl!`. + /// + /// We advise against *manually* implementing `build`. + #[cfg(feature = "tokio")] + fn build_async( + registry: &Registry, + ctor: fn( + Self, + ) -> std::pin::Pin< + Box + Send + Sync>, + >, + _: private::SealToken, + ) -> std::pin::Pin< + Box> + Send + Sync + '_>, + > + where + R: Sized; + /// Constructs a [`Vec`] of [`std::any::TypeId`]s from the types in `Self`. /// The resulting vector must have the same length as `Self`. /// @@ -66,6 +93,21 @@ where Some(ctor(())) } + #[cfg(feature = "tokio")] + fn build_async( + _registry: &Registry, + ctor: fn( + Self, + ) -> std::pin::Pin< + Box + Send + Sync>, + >, + _: private::SealToken, + ) -> std::pin::Pin< + Box> + Send + Sync + '_>, + > { + Box::pin(async move { Some(ctor(()).await) }) + } + fn as_typeids(_: private::SealToken) -> Vec { Vec::new() } @@ -93,6 +135,33 @@ macro_rules! DepBuilderImpl { Some(ctor(deps)) } + #[cfg(feature = "tokio")] + fn build_async( + registry: &Registry, + ctor: fn( + Self, + ) -> std::pin::Pin< + Box + Send + Sync>, + >, + _: private::SealToken, + ) -> std::pin::Pin< + Box> + Send + Sync + '_>, + > { + if !registry.validate::() { + return Box::pin(async move { None }); + } + + Box::pin(async move { + let deps = ( + $( + <$ts>::new_async(registry).await, + )* + ); + + Some(ctor(deps).await) + }) + } + fn as_typeids(_: private::SealToken) -> ::std::vec::Vec<::std::any::TypeId> { ::std::vec![ $(<$ts>::type_id(),)* ] } diff --git a/ferrunix-core/src/registry.rs b/ferrunix-core/src/registry.rs index 175cc6b..48c3ba6 100644 --- a/ferrunix-core/src/registry.rs +++ b/ferrunix-core/src/registry.rs @@ -473,23 +473,36 @@ where &self, ctor: fn( Deps, - ) - -> Box + Send + Sync>, + ) -> std::pin::Pin< + Box + Send + Sync>, + >, ) { - let transient = - Object::Transient(Box::new(move |this| -> Option { - #[allow(clippy::option_if_let_else)] - match Deps::build( - this, - ctor, - dependency_builder::private::SealToken, - ) { - Some(obj) => Some(Box::new(obj)), - None => None, - } - })); + let transient = AsyncObject::AsyncTransient(Box::new( + move |this: &'_ Registry| -> std::pin::Pin< + Box< + dyn std::future::Future> + + Send + + Sync + + '_, + >, + > { + Box::pin(async move { + #[allow(clippy::option_if_let_else)] + match Deps::build_async( + this, + ctor, + dependency_builder::private::SealToken, + ) + .await + { + Some(obj) => Some(Box::new(obj) as BoxedAny), + None => None, + } + }) + }, + )); { - let mut lock = self.registry.objects.write(); + let mut lock = self.registry.objects_async.write().await; lock.insert(TypeId::of::(), transient); }