diff options
-rw-r--r-- | macros/src/injectable/implementation.rs | 34 | ||||
-rw-r--r-- | src/di_container/asynchronous/mod.rs | 85 | ||||
-rw-r--r-- | src/di_container/binding_storage.rs | 11 | ||||
-rw-r--r-- | src/di_container/blocking/mod.rs | 66 | ||||
-rw-r--r-- | src/di_container/mod.rs | 33 | ||||
-rw-r--r-- | src/errors/di_container.rs | 4 | ||||
-rw-r--r-- | src/test_utils.rs | 9 |
7 files changed, 194 insertions, 48 deletions
diff --git a/macros/src/injectable/implementation.rs b/macros/src/injectable/implementation.rs index bf168a4..9e97f45 100644 --- a/macros/src/injectable/implementation.rs +++ b/macros/src/injectable/implementation.rs @@ -420,14 +420,18 @@ impl<Dep: IDependency> InjectableImpl<Dep> let method_call = parse_str::<ExprMethodCall>( format!( - "{}.get_bound::<{}>({}.clone(), {})", + concat!( + "{}.get_bound::<{}>({}.clone(), ", + "syrette::di_container::BindingOptions::new(){})" + ), DI_CONTAINER_VAR_NAME, dep_interface_str, DEPENDENCY_HISTORY_VAR_NAME, - dependency.get_name().as_ref().map_or_else( - || "None".to_string(), - |name| format!("Some(\"{}\")", name.value()) - ) + dependency + .get_name() + .as_ref() + .map(|name| format!(".name(\"{}\")", name.value())) + .unwrap_or_default() ) .as_str(), )?; @@ -900,7 +904,10 @@ mod tests parse2::<Expr>(output)?, parse2::<Expr>(quote! { #di_container_var_ident - .get_bound::<Foo>(#dep_history_var_ident.clone(), None) + .get_bound::<Foo>( + #dep_history_var_ident.clone(), + syrette::di_container::BindingOptions::new() + ) .map_err(|err| InjectableError::ResolveFailed { reason: Box::new(err), affected: self_type_name @@ -948,7 +955,10 @@ mod tests parse2::<Expr>(output)?, parse2::<Expr>(quote! { #di_container_var_ident - .get_bound::<Foo>(#dep_history_var_ident.clone(), Some("special")) + .get_bound::<Foo>( + #dep_history_var_ident.clone(), + syrette::di_container::BindingOptions::new().name("special") + ) .map_err(|err| InjectableError::ResolveFailed { reason: Box::new(err), affected: self_type_name @@ -994,7 +1004,10 @@ mod tests parse2::<Expr>(output)?, parse2::<Expr>(quote! { #di_container_var_ident - .get_bound::<Foo>(#dep_history_var_ident.clone(), None) + .get_bound::<Foo>( + #dep_history_var_ident.clone(), + syrette::di_container::BindingOptions::new() + ) .await .map_err(|err| InjectableError::AsyncResolveFailed { reason: Box::new(err), @@ -1044,7 +1057,10 @@ mod tests parse2::<Expr>(output)?, parse2::<Expr>(quote! { #di_container_var_ident - .get_bound::<Foo>(#dep_history_var_ident.clone(), Some("foobar")) + .get_bound::<Foo>( + #dep_history_var_ident.clone(), + syrette::di_container::BindingOptions::new().name("foobar") + ) .await .map_err(|err| InjectableError::AsyncResolveFailed { reason: Box::new(err), diff --git a/src/di_container/asynchronous/mod.rs b/src/di_container/asynchronous/mod.rs index 2939ddd..e5f7f5d 100644 --- a/src/di_container/asynchronous/mod.rs +++ b/src/di_container/asynchronous/mod.rs @@ -59,6 +59,7 @@ use async_trait::async_trait; 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::future::BoxFuture; use crate::private::cast::arc::CastArc; @@ -116,14 +117,50 @@ pub trait IAsyncDIContainer: 'a: 'b, Self: 'b; - #[doc(hidden)] - async fn get_bound<Interface>( - self: &Arc<Self>, + /// 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::di_container::asynchronous::IAsyncDIContainer; + /// # 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::<EventHandler>(); + /// + /// di_container + /// .get_bound::<Button>(dependency_history, BindingOptions::new().name("huge")) + /// .await?; + /// # + /// # Ok::<_, Box<dyn std::error::Error>>(()) + /// # }); + /// ``` + fn get_bound<'this, 'fut, Interface>( + self: &'this Arc<Self>, dependency_history: DependencyHistory, - name: Option<&'static str>, - ) -> Result<SomePtr<Interface>, AsyncDIContainerError> + binding_options: BindingOptions<'static>, + ) -> BoxFuture<'fut, Result<SomePtr<Interface>, AsyncDIContainerError>> where - Interface: 'static + ?Sized + Send + Sync; + Interface: 'static + 'this + ?Sized + Send + Sync, + 'this: 'fut, + Self: 'fut; } /// Async dependency injection container. @@ -163,7 +200,7 @@ impl IAsyncDIContainer for AsyncDIContainer Self: 'b, { Box::pin(async { - self.get_bound::<Interface>(DependencyHistory::new(), None) + self.get_bound::<Interface>(DependencyHistory::new(), BindingOptions::new()) .await }) } @@ -178,24 +215,34 @@ impl IAsyncDIContainer for AsyncDIContainer Self: 'b, { Box::pin(async { - self.get_bound::<Interface>(DependencyHistory::new(), Some(name)) - .await + self.get_bound::<Interface>( + DependencyHistory::new(), + BindingOptions::new().name(name), + ) + .await }) } - async fn get_bound<Interface>( - self: &Arc<Self>, + fn get_bound<'this, 'fut, Interface>( + self: &'this Arc<Self>, dependency_history: DependencyHistory, - name: Option<&'static str>, - ) -> Result<SomePtr<Interface>, AsyncDIContainerError> + binding_options: BindingOptions<'static>, + ) -> BoxFuture<'fut, Result<SomePtr<Interface>, AsyncDIContainerError>> where - Interface: 'static + ?Sized + Send + Sync, + Interface: 'static + 'this + ?Sized + Send + Sync, + 'this: 'fut, + Self: 'fut, { - let binding_providable = self - .get_binding_providable::<Interface>(name, dependency_history) - .await?; - - self.handle_binding_providable(binding_providable).await + Box::pin(async move { + let binding_providable = self + .get_binding_providable::<Interface>( + binding_options.name, + dependency_history, + ) + .await?; + + self.handle_binding_providable(binding_providable).await + }) } } diff --git a/src/di_container/binding_storage.rs b/src/di_container/binding_storage.rs index 2bc208f..3c3c565 100644 --- a/src/di_container/binding_storage.rs +++ b/src/di_container/binding_storage.rs @@ -6,7 +6,7 @@ pub struct DIContainerBindingStorage<Provider> where Provider: 'static + ?Sized, { - inner: AHashMap<BindingIdentification, Box<Provider>>, + inner: AHashMap<BindingIdentification<'static>, Box<Provider>>, } impl<Provider> DIContainerBindingStorage<Provider> @@ -21,7 +21,10 @@ where } #[allow(clippy::borrowed_box)] - pub fn get<Interface>(&self, name: Option<&'static str>) -> Option<&Box<Provider>> + pub fn get<'me, Interface>( + &'me self, + name: Option<&'me str>, + ) -> Option<&'me Box<Provider>> where Interface: 'static + ?Sized, { @@ -77,10 +80,10 @@ where } #[derive(Debug, PartialEq, Eq, Hash)] -struct BindingIdentification +struct BindingIdentification<'a> { type_id: TypeId, - name: Option<&'static str>, + name: Option<&'a str>, } #[cfg(test)] diff --git a/src/di_container/blocking/mod.rs b/src/di_container/blocking/mod.rs index 5a27f78..5b0acc8 100644 --- a/src/di_container/blocking/mod.rs +++ b/src/di_container/blocking/mod.rs @@ -56,6 +56,7 @@ use std::rc::Rc; 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::private::cast::boxed::CastBox; use crate::private::cast::rc::CastRc; @@ -103,16 +104,57 @@ pub trait IDIContainer: Sized + 'static + details::DIContainerInternals where Interface: 'static + ?Sized; - #[doc(hidden)] + /// 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::di_container::blocking::IDIContainer; + /// # use syrette::dependency_history::DependencyHistory; + /// # use syrette::di_container::BindingOptions; + /// # + /// # struct EventHandler {} + /// # struct Button {} + /// # + /// # fn main() -> Result<(), Box<dyn std::error::Error>> { + /// # let di_container = DIContainer::new(); + /// # + /// let mut dependency_history = DependencyHistory::new(); + /// + /// dependency_history.push::<EventHandler>(); + /// + /// di_container.get_bound::<Button>( + /// dependency_history, + /// BindingOptions::new().name("huge_red"), + /// )?; + /// # + /// # Ok(()) + /// # } + /// ``` fn get_bound<Interface>( self: &Rc<Self>, dependency_history: DependencyHistory, - name: Option<&'static str>, + binding_options: BindingOptionsWithLt, ) -> Result<SomePtr<Interface>, DIContainerError> where Interface: 'static + ?Sized; } +#[cfg(not(test))] +pub(crate) type BindingOptionsWithLt<'a> = BindingOptions<'a>; + +#[cfg(test)] +pub(crate) type BindingOptionsWithLt = BindingOptions<'static>; + /// Blocking dependency injection container. pub struct DIContainer { @@ -144,7 +186,7 @@ impl IDIContainer for DIContainer where Interface: 'static + ?Sized, { - self.get_bound::<Interface>(DependencyHistory::new(), None) + self.get_bound::<Interface>(DependencyHistory::new(), BindingOptions::new()) } fn get_named<Interface>( @@ -154,20 +196,24 @@ impl IDIContainer for DIContainer where Interface: 'static + ?Sized, { - self.get_bound::<Interface>(DependencyHistory::new(), Some(name)) + self.get_bound::<Interface>( + DependencyHistory::new(), + BindingOptions::new().name(name), + ) } - #[doc(hidden)] fn get_bound<Interface>( self: &Rc<Self>, dependency_history: DependencyHistory, - name: Option<&'static str>, + binding_options: BindingOptions, ) -> Result<SomePtr<Interface>, DIContainerError> where Interface: 'static + ?Sized, { - let binding_providable = - self.get_binding_providable::<Interface>(name, dependency_history)?; + let binding_providable = self.get_binding_providable::<Interface>( + binding_options.name, + dependency_history, + )?; #[cfg(feature = "factory")] return self.handle_binding_providable(binding_providable); @@ -270,7 +316,7 @@ impl DIContainer fn get_binding_providable<Interface>( self: &Rc<Self>, - name: Option<&'static str>, + name: Option<&str>, dependency_history: DependencyHistory, ) -> Result<Providable<Self>, DIContainerError> where @@ -283,7 +329,7 @@ impl DIContainer || { Err(DIContainerError::BindingNotFound { interface: type_name::<Interface>(), - name, + name: name.as_ref().map(ToString::to_string), }) }, Ok, diff --git a/src/di_container/mod.rs b/src/di_container/mod.rs index 5820bc8..63733f5 100644 --- a/src/di_container/mod.rs +++ b/src/di_container/mod.rs @@ -6,5 +6,38 @@ pub mod asynchronous; pub mod blocking; +/// DI container binding options. +/// +/// # Examples +/// ``` +/// # use syrette::di_container::BindingOptions; +/// # +/// BindingOptions::new().name("foo"); +/// ``` +#[derive(Debug, Default, Clone)] +pub struct BindingOptions<'a> +{ + name: Option<&'a str>, +} + +impl<'a> BindingOptions<'a> +{ + /// Returns a new `BindingOptions`. + #[must_use] + pub fn new() -> Self + { + Self { name: None } + } + + /// Returns `Self` with the specified name set. + #[must_use] + pub fn name(mut self, name: &'a str) -> Self + { + self.name = Some(name); + + self + } +} + // Private. pub(crate) mod binding_storage; diff --git a/src/errors/di_container.rs b/src/errors/di_container.rs index 311917a..7e29ffa 100644 --- a/src/errors/di_container.rs +++ b/src/errors/di_container.rs @@ -38,7 +38,7 @@ pub enum DIContainerError /// No binding exists for a interface (and optionally a name). #[error( "No binding exists for interface '{interface}' {}", - .name.map_or_else(String::new, |name| format!("with name '{name}'")) + name.as_ref().map(|name| format!("with name '{name}'")).unwrap_or_default() )] BindingNotFound { @@ -46,7 +46,7 @@ pub enum DIContainerError interface: &'static str, /// The name of the binding if one exists. - name: Option<&'static str>, + name: Option<String>, }, } diff --git a/src/test_utils.rs b/src/test_utils.rs index 1fe4417..1e0e04d 100644 --- a/src/test_utils.rs +++ b/src/test_utils.rs @@ -288,7 +288,7 @@ pub mod mocks use super::*; use crate::di_container::blocking::binding::builder::BindingBuilder; use crate::di_container::blocking::details::DIContainerInternals; - use crate::di_container::blocking::IDIContainer; + use crate::di_container::blocking::{BindingOptionsWithLt, IDIContainer}; use crate::errors::di_container::DIContainerError; use crate::provider::blocking::IProvider; use crate::ptr::SomePtr; @@ -317,10 +317,10 @@ pub mod mocks Interface: 'static + ?Sized; #[doc(hidden)] - fn get_bound<Interface>( + fn get_bound<'opts, Interface>( self: &Rc<Self>, dependency_history: DependencyHistory, - name: Option<&'static str>, + binding_options: BindingOptionsWithLt, ) -> Result<SomePtr<Interface>, DIContainerError> where Interface: 'static + ?Sized; @@ -359,6 +359,7 @@ pub mod mocks use crate::di_container::asynchronous::binding::builder::AsyncBindingBuilder; use crate::di_container::asynchronous::details::DIContainerInternals; use crate::di_container::asynchronous::IAsyncDIContainer; + use crate::di_container::BindingOptions; use crate::errors::async_di_container::AsyncDIContainerError; use crate::provider::r#async::IAsyncProvider; use crate::ptr::SomePtr; @@ -394,7 +395,7 @@ pub mod mocks async fn get_bound<Interface>( self: &Arc<Self>, dependency_history: DependencyHistory, - name: Option<&'static str>, + binding_options: BindingOptions<'static> ) -> Result<SomePtr<Interface>, AsyncDIContainerError> where Interface: 'static + ?Sized + Send + Sync; |