//! Asynchronous dependency injection container. //! //! # Examples //! ``` //! use std::collections::HashMap; //! use std::error::Error; //! //! use syrette::{injectable, AsyncDIContainer}; //! //! trait IDatabaseService: Send + Sync //! { //! fn get_all_records(&self, table_name: String) -> HashMap; //! } //! //! struct DatabaseService {} //! //! #[injectable(IDatabaseService, async = true)] //! impl DatabaseService //! { //! fn new() -> Self //! { //! Self {} //! } //! } //! //! impl IDatabaseService for DatabaseService //! { //! fn get_all_records(&self, table_name: String) -> HashMap //! { //! // Do stuff here //! HashMap::::new() //! } //! } //! //! #[tokio::main] //! async fn main() -> Result<(), Box> //! { //! let mut di_container = AsyncDIContainer::new(); //! //! di_container //! .bind::() //! .to::()?; //! //! let database_service = di_container //! .get::() //! .await? //! .transient()?; //! //! Ok(()) //! } //! ``` use std::any::type_name; use crate::di_container::asynchronous::binding::builder::AsyncBindingBuilder; use crate::di_container::binding_storage::DIContainerBindingStorage; use crate::di_container::BindingOptions; use crate::errors::async_di_container::AsyncDIContainerError; use crate::provider::r#async::{AsyncProvidable, IAsyncProvider}; use crate::ptr::SomePtr; use crate::util::use_double; use_double!(crate::dependency_history::DependencyHistory); pub mod binding; /// Async dependency injection container. #[derive(Default)] pub struct AsyncDIContainer { binding_storage: DIContainerBindingStorage>, } impl AsyncDIContainer { /// Returns a new `AsyncDIContainer`. #[must_use] pub fn new() -> Self { Self { binding_storage: DIContainerBindingStorage::new(), } } } #[cfg_attr(test, mockall::automock)] impl AsyncDIContainer { /// Returns a new [`AsyncBindingBuilder`] for the given interface. /// /// # Examples /// ``` /// # use syrette::{AsyncDIContainer, injectable}; /// # /// # struct DiskWriter {} /// # /// # #[injectable(async = true)] /// # impl DiskWriter /// # { /// # fn new() -> Self /// # { /// # Self {} /// # } /// # } /// # /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { /// let mut di_container = AsyncDIContainer::new(); /// /// di_container.bind::().to::()?; /// # /// # Ok(()) /// # } /// ``` #[allow(clippy::missing_panics_doc)] pub fn bind(&mut self) -> AsyncBindingBuilder<'_, Interface> where Interface: 'static + ?Sized + Send + Sync, { #[cfg(test)] panic!("Bind function is unusable when testing"); #[cfg(not(test))] AsyncBindingBuilder::new(self, DependencyHistory::new) } /// Returns the type bound with `Interface`. /// /// # Errors /// Will return `Err` if: /// - No binding for `Interface` exists /// - Resolving the binding for `Interface` fails /// - Casting the binding for `Interface` fails /// /// # Examples /// ``` /// # use syrette::{AsyncDIContainer, injectable}; /// # /// # struct DeviceManager {} /// # /// # #[injectable(async = true)] /// # impl DeviceManager /// # { /// # fn new() -> Self /// # { /// # Self {} /// # } /// # } /// # /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { /// let mut di_container = AsyncDIContainer::new(); /// /// di_container.bind::().to::()?; /// /// let device_manager = di_container.get::().await?.transient(); /// # /// # Ok(()) /// # } /// ``` pub async fn get( &self, ) -> Result, AsyncDIContainerError> where Interface: 'static + ?Sized + Send + Sync, { self.get_bound::(DependencyHistory::new(), BindingOptions::new()) .await } /// Returns the type bound with `Interface` and the specified name. /// /// # Errors /// Will return `Err` if: /// - No binding for `Interface` with name `name` exists /// - Resolving the binding for `Interface` fails /// - Casting the binding for `Interface` fails /// /// # Examples /// ``` /// # use syrette::{AsyncDIContainer, injectable}; /// # /// # struct DeviceManager {} /// # /// # #[injectable(async = true)] /// # impl DeviceManager /// # { /// # fn new() -> Self /// # { /// # Self {} /// # } /// # } /// # /// # #[tokio::main] /// # async fn main() -> Result<(), Box> { /// let mut di_container = AsyncDIContainer::new(); /// /// di_container /// .bind::() /// .to::()? /// .in_transient_scope() /// .when_named("usb"); /// /// let device_manager = di_container /// .get_named::("usb") /// .await? /// .transient(); /// # /// # Ok(()) /// # } /// ``` pub async fn get_named( &self, name: &'static str, ) -> Result, AsyncDIContainerError> where Interface: 'static + ?Sized + Send + Sync, { self.get_bound::( DependencyHistory::new(), BindingOptions::new().name(name), ) .await } /// Returns the type bound with `Interface` where the binding has the specified /// options. /// /// `dependency_history` is passed to the bound type when it is being resolved. /// /// # Errors /// Will return `Err` if: /// - No binding for `Interface` exists /// - Resolving the binding for `Interface` fails /// - Casting the binding for `Interface` fails /// /// # Examples /// ``` /// # use syrette::di_container::asynchronous::AsyncDIContainer; /// # use syrette::dependency_history::DependencyHistory; /// # use syrette::di_container::BindingOptions; /// # /// # struct EventHandler {} /// # struct Button {} /// # /// # Box::pin(async { /// # let di_container = AsyncDIContainer::new(); /// # /// let mut dependency_history = DependencyHistory::new(); /// /// dependency_history.push::(); /// /// di_container /// .get_bound::