//! Blocking dependency injection container. //! //! # Examples //! ``` //! use std::collections::HashMap; //! use std::error::Error; //! //! use syrette::{injectable, DIContainer}; //! //! trait IDatabaseService //! { //! fn get_all_records(&self, table_name: String) -> HashMap; //! } //! //! struct DatabaseService {} //! //! #[injectable(IDatabaseService)] //! impl DatabaseService //! { //! fn new() -> Self //! { //! Self {} //! } //! } //! //! impl IDatabaseService for DatabaseService //! { //! fn get_all_records(&self, table_name: String) -> HashMap //! { //! // Do stuff here //! HashMap::::new() //! } //! } //! //! fn main() -> Result<(), Box> //! { //! let mut di_container = DIContainer::new(); //! //! di_container //! .bind::() //! .to::() //! .map_err(|err| err.to_string())?; //! //! let database_service = di_container //! .get::() //! .map_err(|err| err.to_string())? //! .transient()?; //! //! Ok(()) //! } //! ``` use std::any::type_name; use crate::di_container::binding_storage::DIContainerBindingStorage; use crate::di_container::blocking::binding::builder::BindingBuilder; use crate::di_container::BindingOptions; use crate::errors::di_container::DIContainerError; use crate::provider::blocking::{IProvider, Providable}; use crate::ptr::SomePtr; use crate::util::use_double; use_double!(crate::dependency_history::DependencyHistory); pub mod binding; #[cfg(not(test))] pub(crate) type BindingOptionsWithLt<'a> = BindingOptions<'a>; #[cfg(test)] pub(crate) type BindingOptionsWithLt = BindingOptions<'static>; /// Blocking dependency injection container. #[derive(Default)] pub struct DIContainer { binding_storage: DIContainerBindingStorage>, } impl DIContainer { /// Returns a new `DIContainer`. #[must_use] pub fn new() -> Self { Self { binding_storage: DIContainerBindingStorage::new(), } } } #[cfg_attr(test, mockall::automock)] impl DIContainer { /// Returns a new [`BindingBuilder`] for the given interface. /// /// # Examples /// ``` /// # use syrette::{DIContainer, injectable}; /// # /// # struct DiskWriter {} /// # /// # #[injectable] /// # impl DiskWriter /// # { /// # fn new() -> Self /// # { /// # Self {} /// # } /// # } /// # /// # fn main() -> Result<(), Box> { /// let mut di_container = DIContainer::new(); /// /// di_container.bind::().to::()?; /// # /// # Ok(()) /// # } /// ``` #[allow(clippy::missing_panics_doc)] pub fn bind(&mut self) -> BindingBuilder<'_, Interface> where Interface: 'static + ?Sized, { #[cfg(test)] panic!("Nope"); #[cfg(not(test))] BindingBuilder::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::{DIContainer, injectable}; /// # /// # struct DeviceManager {} /// # /// # #[injectable] /// # impl DeviceManager /// # { /// # fn new() -> Self /// # { /// # Self {} /// # } /// # } /// # /// # fn main() -> Result<(), Box> { /// let mut di_container = DIContainer::new(); /// /// di_container.bind::().to::()?; /// /// let device_manager = di_container.get::()?.transient(); /// # /// # Ok(()) /// # } /// ``` pub fn get(&self) -> Result, DIContainerError> where Interface: 'static + ?Sized, { self.get_bound::(DependencyHistory::new(), BindingOptions::new()) } /// 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::{DIContainer, injectable}; /// # /// # struct DeviceManager {} /// # /// # #[injectable] /// # impl DeviceManager /// # { /// # fn new() -> Self /// # { /// # Self {} /// # } /// # } /// # /// # fn main() -> Result<(), Box> { /// let mut di_container = DIContainer::new(); /// /// di_container /// .bind::() /// .to::()? /// .in_transient_scope() /// .when_named("usb")?; /// /// let device_manager = di_container.get_named::("usb")?.transient(); /// # /// # Ok(()) /// # } /// ``` pub fn get_named( &self, name: &'static str, ) -> Result, DIContainerError> where Interface: 'static + ?Sized, { self.get_bound::( DependencyHistory::new(), BindingOptions::new().name(name), ) } /// 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 /// ```no_run /// # use syrette::di_container::blocking::DIContainer; /// # use syrette::dependency_history::DependencyHistory; /// # use syrette::di_container::BindingOptions; /// # /// # struct EventHandler {} /// # struct Button {} /// # /// # fn main() -> Result<(), Box> { /// # let di_container = DIContainer::new(); /// # /// let mut dependency_history = DependencyHistory::new(); /// /// dependency_history.push::(); /// /// di_container.get_bound::